From 62e4c68907d8d33709c2c1f92a161dff00b3d5f2 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 22:01:36 +0200 Subject: Adding upstream version 0.11.2. Signed-off-by: Daniel Baumann --- src/CMakeLists.txt | 633 + 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 | 208 + src/base/humanize.time.hh | 88 + src/base/humanize.time.tests.cc | 125 + src/base/injector.bind.hh | 173 + src/base/injector.hh | 230 + src/base/intern_string.cc | 303 + src/base/intern_string.hh | 865 + src/base/intern_string.tests.cc | 152 + src/base/is_utf8.cc | 313 + src/base/is_utf8.hh | 59 + 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 | 686 + src/base/lnav_log.hh | 188 + 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 | 1032 + 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 | 679 + src/base/string_util.cc | 304 + 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 | 249 + src/bottom_status_source.hh | 94 + src/bound_tags.hh | 54 + src/breadcrumb.hh | 134 + src/breadcrumb_curses.cc | 461 + 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 | 358 + src/curl_looper.hh | 192 + 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 | 544 + 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 | 432 + src/files_sub_source.hh | 117 + src/filter_observer.cc | 113 + src/filter_observer.hh | 78 + src/filter_status_source.cc | 332 + src/filter_status_source.hh | 82 + src/filter_sub_source.cc | 669 + src/filter_sub_source.hh | 92 + src/fmtlib/Makefile.am | 22 + src/fmtlib/fmt/args.h | 234 + src/fmtlib/fmt/chrono.h | 2267 ++ src/fmtlib/fmt/color.h | 633 + src/fmtlib/fmt/compile.h | 607 + src/fmtlib/fmt/core.h | 2951 ++ src/fmtlib/fmt/format-inl.h | 1681 + src/fmtlib/fmt/format.h | 4735 +++ src/fmtlib/fmt/locale.h | 2 + src/fmtlib/fmt/os.h | 451 + src/fmtlib/fmt/ostream.h | 209 + src/fmtlib/fmt/printf.h | 679 + src/fmtlib/fmt/ranges.h | 732 + src/fmtlib/fmt/std.h | 349 + src/fmtlib/fmt/xchar.h | 259 + src/fmtlib/format.cc | 43 + src/fmtlib/os.cc | 390 + 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/bunyan_log.json | 105 + src/formats/candlepin_log.json | 49 + src/formats/choose_repo_log.json | 24 + src/formats/cloudflare_log.json | 236 + src/formats/cloudvm_ram_log.json | 22 + 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 | 46 + 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 | 45 + 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 | 536 + src/help.txt | 1005 + 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 | 988 + 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 | 172 + 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 | 1413 + src/line_buffer.hh | 370 + src/listview_curses.cc | 733 + src/listview_curses.hh | 570 + src/lnav.cc | 3387 ++ src/lnav.events.cc | 177 + src/lnav.events.hh | 128 + src/lnav.hh | 286 + src/lnav.indexing.cc | 457 + src/lnav.indexing.hh | 45 + src/lnav.management_cli.cc | 910 + src/lnav.management_cli.hh | 53 + src/lnav_commands.cc | 5769 +++ src/lnav_commands.hh | 42 + src/lnav_config.cc | 1670 + src/lnav_config.hh | 145 + 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 | 199 + src/log_data_table.hh | 79 + src/log_format.cc | 3336 ++ src/log_format.hh | 579 + src/log_format_ext.hh | 434 + src/log_format_fwd.hh | 330 + src/log_format_impls.cc | 1775 + src/log_format_loader.cc | 1488 + src/log_format_loader.hh | 81 + src/log_gutter_source.hh | 77 + src/log_level.cc | 111 + src/log_level.hh | 51 + 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 | 1189 + src/logfile.cfg.hh | 45 + src/logfile.hh | 462 + src/logfile_fwd.hh | 160 + src/logfile_stats.hh | 40 + src/logfile_sub_source.cc | 2467 ++ 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 | 297 + 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 | 368 + src/pcrepp/test_pcre2pp.cc | 260 + 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 | 908 + src/readline_callbacks.hh | 50 + src/readline_context.hh | 183 + src/readline_curses.cc | 1535 + src/readline_curses.hh | 344 + src/readline_highlighters.cc | 480 + src/readline_highlighters.hh | 48 + src/readline_possibilities.cc | 510 + 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 | 644 + 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 | 79 + 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 | 1864 + src/session_data.hh | 100 + src/shared_buffer.cc | 203 + src/shared_buffer.hh | 217 + 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 | 230 + 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 | 520 + 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 | 875 + src/textfile_sub_source.hh | 173 + src/textview_curses.cc | 1143 + src/textview_curses.hh | 776 + src/textview_curses_fwd.hh | 49 + src/themes/README.md | 5 + src/themes/default-theme.json | 245 + src/themes/eldar.json | 200 + src/themes/grayscale.json | 172 + src/themes/monocai.json | 272 + src/themes/night-owl.json | 223 + src/themes/solarized-dark.json | 214 + src/themes/solarized-light.json | 214 + src/themes/themes.am | 10 + src/third-party/ArenaAlloc/arenaalloc.h | 186 + src/third-party/ArenaAlloc/arenaallocimpl.h | 290 + 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 | 63 + 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 | 1247 + src/view_curses.hh | 482 + src/view_helpers.cc | 1214 + src/view_helpers.crumbs.hh | 37 + src/view_helpers.examples.hh | 41 + src/view_helpers.hh | 101 + src/view_helpers.hist.hh | 53 + src/views_vtab.cc | 1113 + src/views_vtab.hh | 39 + src/vis_line.hh | 43 + src/vt52_curses.cc | 314 + src/vt52_curses.hh | 169 + 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 | 1574 + src/yajlpp/yajlpp.hh | 682 + src/yajlpp/yajlpp_def.hh | 1408 + src/yaml-extension-functions.cc | 103 + 672 files changed, 325089 insertions(+) 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/bunyan_log.json create mode 100644 src/formats/candlepin_log.json create mode 100644 src/formats/choose_repo_log.json create mode 100644 src/formats/cloudflare_log.json create mode 100644 src/formats/cloudvm_ram_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 (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..58cfd7b --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,633 @@ +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" + "%H:%M:%S.%f" + "%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..98f6c96 --- /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; + nonstd::optional role; + 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..b65f4ba --- /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) { + auto& 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..68cfe0f --- /dev/null +++ b/src/base/humanize.time.cc @@ -0,0 +1,208 @@ +/** + * 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 (remaining > 0 && amount < 10 && curr_interval->symbol[0]) { + retval.insert(0, "0"); + } + } + + 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..853dd04 --- /dev/null +++ b/src/base/humanize.time.tests.cc @@ -0,0 +1,125 @@ +/** + * 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 == "1d01h00m00s"); + val = humanize::time::duration::from_tv({25 * 60 * 60 + 25 * 60, 123000}) + .to_string(); + CHECK(val == "1d01h25m00s"); + 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..676d2bc --- /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->sf_string[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..4ae40da --- /dev/null +++ b/src/base/intern_string.hh @@ -0,0 +1,865 @@ +/** + * 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]; } + + void pop_back() + { + if (!this->empty()) { + 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); + } + + const 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..8816803 --- /dev/null +++ b/src/base/intern_string.tests.cc @@ -0,0 +1,152 @@ +/** + * 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::from_str_range(in1, 7, -1); + auto split = sf.split_lines(); + + CHECK(1 == split.size()); + CHECK("World!" == 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..f55dfe0 --- /dev/null +++ b/src/base/is_utf8.cc @@ -0,0 +1,313 @@ +/* + * 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(string_fragment str, nonstd::optional terminator) +{ + const auto* ustr = str.udata(); + utf8_scan_result retval; + ssize_t i = 0; + + while (i < str.length()) { + if (ustr[i] == '\x1b') { + retval.usr_has_ansi = true; + } + + if (terminator && ustr[i] == terminator.value()) { + if (retval.usr_message == nullptr) { + retval.usr_valid_frag = str.sub_range(0, i); + } + retval.usr_remaining = str.substr(i + 1); + break; + } + + if (retval.usr_message != nullptr) { + i += 1; + continue; + } + + retval.usr_valid_frag = str.sub_range(0, i); + if (ustr[i] <= 0x7F) /* 00..7F */ { + i += 1; + } else if (ustr[i] >= 0xC2 && ustr[i] <= 0xDF) /* C2..DF 80..BF */ { + if (i + 1 < str.length()) /* Expect a 2nd byte */ { + if (ustr[i + 1] < 0x80 || ustr[i + 1] > 0xBF) { + retval.usr_message + = "After a first byte between C2 and DF, expecting a " + "2nd byte between 80 and BF"; + retval.usr_faulty_bytes = 2; + continue; + } + } else { + retval.usr_message + = "After a first byte between C2 and DF, expecting a 2nd " + "byte."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 2; + } else if (ustr[i] == 0xE0) /* E0 A0..BF 80..BF */ { + if (i + 2 < str.length()) /* Expect a 2nd and 3rd byte */ { + if (ustr[i + 1] < 0xA0 || ustr[i + 1] > 0xBF) { + retval.usr_message + = "After a first byte of E0, expecting a 2nd byte " + "between A0 and BF."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte of E0, expecting a 3nd byte " + "between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + } else { + retval.usr_message + = "After a first byte of E0, expecting two following " + "bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 3; + } else if (ustr[i] >= 0xE1 + && ustr[i] <= 0xEC) /* E1..EC 80..BF 80..BF */ + { + if (i + 2 < str.length()) /* Expect a 2nd and 3rd byte */ { + if (ustr[i + 1] < 0x80 || ustr[i + 1] > 0xBF) { + retval.usr_message + = "After a first byte between E1 and EC, expecting the " + "2nd byte between 80 and BF."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte between E1 and EC, expecting the " + "3rd byte between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + } else { + retval.usr_message + = "After a first byte between E1 and EC, expecting two " + "following bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 3; + } else if (ustr[i] == 0xED) /* ED 80..9F 80..BF */ { + if (i + 2 < str.length()) /* Expect a 2nd and 3rd byte */ { + if (ustr[i + 1] < 0x80 || ustr[i + 1] > 0x9F) { + retval.usr_message + = "After a first byte of ED, expecting 2nd byte " + "between 80 and 9F."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte of ED, expecting 3rd byte " + "between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + } else { + retval.usr_message + = "After a first byte of ED, expecting two following " + "bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 3; + } else if (ustr[i] >= 0xEE + && ustr[i] <= 0xEF) /* EE..EF 80..BF 80..BF */ + { + if (i + 2 < str.length()) /* Expect a 2nd and 3rd byte */ { + if (ustr[i + 1] < 0x80 || ustr[i + 1] > 0xBF) { + retval.usr_message + = "After a first byte between EE and EF, expecting 2nd " + "byte between 80 and BF."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte between EE and EF, expecting 3rd " + "byte between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + } else { + retval.usr_message + = "After a first byte between EE and EF, two following " + "bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 3; + } else if (ustr[i] == 0xF0) /* F0 90..BF 80..BF 80..BF */ { + if (i + 3 < str.length()) /* Expect a 2nd, 3rd 3th byte */ { + if (ustr[i + 1] < 0x90 || ustr[i + 1] > 0xBF) { + retval.usr_message + = "After a first byte of F0, expecting 2nd byte " + "between 90 and BF."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte of F0, expecting 3rd byte " + "between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + if (ustr[i + 3] < 0x80 || ustr[i + 3] > 0xBF) { + retval.usr_message + = "After a first byte of F0, expecting 4th byte " + "between 80 and BF."; + retval.usr_faulty_bytes = 4; + continue; + } + } else { + retval.usr_message + = "After a first byte of F0, expecting three following " + "bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 4; + } else if (ustr[i] >= 0xF1 + && ustr[i] <= 0xF3) /* F1..F3 80..BF 80..BF 80..BF */ + { + if (i + 3 < str.length()) /* Expect a 2nd, 3rd 3th byte */ { + if (ustr[i + 1] < 0x80 || ustr[i + 1] > 0xBF) { + retval.usr_message + = "After a first byte of F1, F2, or F3, expecting a " + "2nd byte between 80 and BF."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte of F1, F2, or F3, expecting a " + "3rd byte between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + if (ustr[i + 3] < 0x80 || ustr[i + 3] > 0xBF) { + retval.usr_message + = "After a first byte of F1, F2, or F3, expecting a " + "4th byte between 80 and BF."; + retval.usr_faulty_bytes = 4; + continue; + } + } else { + retval.usr_message + = "After a first byte of F1, F2, or F3, expecting three " + "following bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 4; + } else if (ustr[i] == 0xF4) /* F4 80..8F 80..BF 80..BF */ { + if (i + 3 < str.length()) /* Expect a 2nd, 3rd 3th byte */ { + if (ustr[i + 1] < 0x80 || ustr[i + 1] > 0x8F) { + retval.usr_message + = "After a first byte of F4, expecting 2nd byte " + "between 80 and 8F."; + retval.usr_faulty_bytes = 2; + continue; + } + if (ustr[i + 2] < 0x80 || ustr[i + 2] > 0xBF) { + retval.usr_message + = "After a first byte of F4, expecting 3rd byte " + "between 80 and BF."; + retval.usr_faulty_bytes = 3; + continue; + } + if (ustr[i + 3] < 0x80 || ustr[i + 3] > 0xBF) { + retval.usr_message + = "After a first byte of F4, expecting 4th byte " + "between 80 and BF."; + retval.usr_faulty_bytes = 4; + continue; + } + } else { + retval.usr_message + = "After a first byte of F4, expecting three following " + "bytes."; + retval.usr_faulty_bytes = 1; + continue; + } + i += 4; + } else { + retval.usr_message + = "Expecting bytes in the following ranges: 00..7F C2..F4."; + retval.usr_faulty_bytes = 1; + continue; + } + } + if (retval.usr_message == nullptr) { + retval.usr_valid_frag = str.sub_range(0, i); + } + return retval; +} diff --git a/src/base/is_utf8.hh b/src/base/is_utf8.hh new file mode 100644 index 0000000..56a959f --- /dev/null +++ b/src/base/is_utf8.hh @@ -0,0 +1,59 @@ +/* + * 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 "intern_string.hh" +#include "optional.hpp" + +struct utf8_scan_result { + const char* usr_message{nullptr}; + size_t usr_faulty_bytes{0}; + string_fragment usr_valid_frag{string_fragment::invalid()}; + nonstd::optional usr_remaining; + bool usr_has_ansi{false}; + + const char* remaining_ptr(const string_fragment& frag) const + { + if (this->usr_remaining) { + return this->usr_remaining->begin(); + } else { + return nullptr; + } + } + bool is_valid() const { return this->usr_message == nullptr; } +}; + +utf8_scan_result is_utf8(string_fragment frag, + 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..2e5dbba --- /dev/null +++ b/src/base/lnav_log.cc @@ -0,0 +1,686 @@ +/** + * 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); + }; +} + +void +log_backtrace(lnav_log_level_t level) +{ +#ifdef HAVE_EXECINFO_H + int frame_count; + void* frames[128]; + + frame_count = backtrace(frames, 128); + auto bt = backtrace_symbols(frames, frame_count); + for (int lpc = 0; lpc < frame_count; lpc++) { + log_msg(level, __FILE__, __LINE__, "%s", bt[lpc]); + } +#endif +} + +#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..551b4f8 --- /dev/null +++ b/src/base/lnav_log.hh @@ -0,0 +1,188 @@ +/** + * 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 +#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); +void log_backtrace(lnav_log_level_t level); + +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 require_true(lhs) \ + ((void) ((lhs) ? 0 : lnav_require_unary(#lhs, lhs, __FILE__, __LINE__))) +#define require_false(lhs) \ + ((void) ((!lhs) ? 0 : lnav_require_unary(#lhs, lhs, __FILE__, __LINE__))) +#define lnav_require_unary(e, lhs, file, line) \ + (log_msg(lnav_log_level_t::ERROR, \ + file, \ + line, \ + "failed precondition `%s' (lhs=%s)", \ + e, \ + std::to_string(lhs).c_str()), \ + log_abort(), \ + 1) + +#define require_ge(lhs, rhs) \ + ((void) ((lhs >= rhs) \ + ? 0 \ + : lnav_require_binary( \ + #lhs " >= " #rhs, lhs, rhs, __FILE__, __LINE__))) +#define require_gt(lhs, rhs) \ + ((void) ((lhs > rhs) ? 0 \ + : lnav_require_binary( \ + #lhs " > " #rhs, lhs, rhs, __FILE__, __LINE__))) +#define require_lt(lhs, rhs) \ + ((void) ((lhs < rhs) ? 0 \ + : lnav_require_binary( \ + #lhs " < " #rhs, lhs, rhs, __FILE__, __LINE__))) + +#define lnav_require_binary(e, lhs, rhs, file, line) \ + (log_msg(lnav_log_level_t::ERROR, \ + file, \ + line, \ + "failed precondition `%s' (lhs=%s; rhs=%s)", \ + e, \ + std::to_string(lhs).c_str(), \ + std::to_string(rhs).c_str()), \ + 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..e3e8ac5 --- /dev/null +++ b/src/base/result.h @@ -0,0 +1,1032 @@ +/* + 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 {}; +}; // namespace types + +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; +}; +} // namespace impl + +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 local_concept { + +template +struct EqualityComparable : std::false_type {}; + +template +struct EqualityComparable< + T, + typename std::enable_if< + true, + typename details::void_t() + == std::declval())>::type>::type> + : std::true_type {}; + +} // namespace local_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::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::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::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(local_concept::EqualityComparable::value, + "T must be EqualityComparable for Result to be comparable"); + static_assert(local_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(local_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(local_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..5bff345 --- /dev/null +++ b/src/base/string_attr_type.hh @@ -0,0 +1,679 @@ +/** + * 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_CURSOR_LINE, + 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 +snippet_border(S str) +{ + return std::make_pair(std::move(str), + VC_ROLE.template value(role_t::VCR_SNIPPET_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..8af686c --- /dev/null +++ b/src/base/string_util.cc @@ -0,0 +1,304 @@ +/** + * 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) +{ + while (true) { + auto frag = string_fragment::from_bytes(buffer, length); + auto scan_res = is_utf8(frag); + + if (scan_res.is_valid()) { + break; + } + for (size_t lpc = 0; lpc < scan_res.usr_faulty_bytes; lpc++) { + buffer[scan_res.usr_valid_frag.sf_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..6773164 --- /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((unsigned char)a[ai]) || a[ai] != '.') { + return 0; + } + } + + for (; bi < b_len; bi++) { + if (!isdigit((unsigned char)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..080be39 --- /dev/null +++ b/src/bottom_status_source.cc @@ -0,0 +1,249 @@ +/** + * 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_selection()); + } + + 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()) { + auto vl = tc->get_selection(); + auto lb = std::lower_bound(bv.begin(), bv.end(), vl); + if (lb != bv.end() && *lb == vl) { + 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..6481e6b --- /dev/null +++ b/src/breadcrumb_curses.cc @@ -0,0 +1,461 @@ +/** + * 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.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..574fefb --- /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.in()); + } 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..27e41fe --- /dev/null +++ b/src/curl_looper.cc @@ -0,0 +1,358 @@ +/** + * 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; +} + +long +curl_request::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; +} + +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..fbfbf2e --- /dev/null +++ b/src/curl_looper.hh @@ -0,0 +1,192 @@ +/** + * 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); + + 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..04eb516 --- /dev/null +++ b/src/document.sections.cc @@ -0,0 +1,544 @@ +/** + * 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) { + if (term) { + 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); + if (this->sw_depth > 0) { + this->sw_depth -= 1; + } + 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..ad4c312 --- /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_selection()); + 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_selection())) { + 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.in(), 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..03d0303 --- /dev/null +++ b/src/files_sub_source.cc @@ -0,0 +1,432 @@ +/** + * 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_selection(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..cbe1c8f --- /dev/null +++ b/src/filter_observer.cc @@ -0,0 +1,113 @@ +/** + * 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, + const 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) { + auto sbr_copy = sbr; + if (lf.get_format() != nullptr) { + lf.get_format()->get_subline(*ll_begin, sbr_copy); + } + sbr_copy.erase_ansi(); + 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_copy); + } + } + } +} + +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..1e1c302 --- /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, + const 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..10e53a1 --- /dev/null +++ b/src/filter_sub_source.cc @@ -0,0 +1,669 @@ +/** + * 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_left(25); + this->fss_editor->set_width(-1); + 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->window_change(); + 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->window_change(); + 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); + if (top_view == &lnav_data.ld_views[LNV_LOG]) { + 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->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.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..55e8a50 --- /dev/null +++ b/src/fmtlib/fmt/chrono.h @@ -0,0 +1,2267 @@ +// 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 + +// Check if std::chrono::local_t is available. +#ifndef FMT_USE_LOCAL_TIME +# ifdef __cpp_lib_chrono +# define FMT_USE_LOCAL_TIME (__cpp_lib_chrono >= 201907L) +# else +# define FMT_USE_LOCAL_TIME 0 +# endif +#endif + +// Check if std::chrono::utc_timestamp is available. +#ifndef FMT_USE_UTC_TIME +# ifdef __cpp_lib_chrono +# define FMT_USE_UTC_TIME (__cpp_lib_chrono >= 201907L) +# else +# define FMT_USE_UTC_TIME 0 +# endif +#endif + +// 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 (detail::const_check(!std::is_unsigned::value) && + 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. + unicode_to_utf8> + u; + if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) + FMT_THROW(format_error("failed to format time")); + return copy_str(u.c_str(), u.c_str() + u.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 get_iterator(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_BEGIN_EXPORT + +/** + 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_; +} + +#if FMT_USE_LOCAL_TIME +template +inline auto localtime(std::chrono::local_time time) -> std::tm { + return localtime(std::chrono::system_clock::to_time_t( + std::chrono::current_zone()->to_sys(time))); +} +#endif + +/** + 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 + +// DEPRECATED! +template +FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, + format_specs& specs) -> 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; + } + if (align != align::none) { + if (p != begin) { + auto c = *begin; + if (c == '}') return begin; + if (c == '{') { + throw_format_error("invalid fill character '{'"); + return begin; + } + specs.fill = {begin, to_unsigned(p - begin)}; + begin = p + 1; + } else { + ++begin; + } + break; + } else if (p == begin) { + break; + } + p = begin; + } + specs.align = align; + return begin; +} + +// 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 +}; + +// Glibc extensions for formatting numeric values. +enum class pad_type { + unspecified, + // Do not pad a numeric result string. + none, + // Pad a numeric result string with zeros even if the conversion specifier + // character uses space-padding by default. + zero, + // Pad a numeric result string with spaces. + space, +}; + +template +auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { + if (pad == pad_type::none) return out; + return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); +} + +template +auto write_padding(OutputIt out, pad_type pad) -> OutputIt { + if (pad != pad_type::none) *out++ = pad == pad_type::space ? ' ' : '0'; + return out; +} + +// 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) { + if (begin == end || *begin == '}') return begin; + if (*begin != '%') FMT_THROW(format_error("invalid format")); + auto ptr = begin; + pad_type pad = pad_type::unspecified; + 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 '_': + pad = pad_type::space; + ++ptr; + break; + case '-': + pad = pad_type::none; + ++ptr; + break; + case '0': + pad = pad_type::zero; + ++ptr; + break; + } + 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, pad); + break; + case 'I': + handler.on_12_hour(numeric_system::standard, pad); + break; + case 'M': + handler.on_minute(numeric_system::standard, pad); + break; + case 'S': + handler.on_second(numeric_system::standard, pad); + 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(numeric_system::standard); + 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; + case 'z': + handler.on_utc_offset(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, pad); + break; + case 'I': + handler.on_12_hour(numeric_system::alternative, pad); + break; + case 'M': + handler.on_minute(numeric_system::alternative, pad); + break; + case 'S': + handler.on_second(numeric_system::alternative, pad); + break; + case 'z': + handler.on_utc_offset(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(numeric_system) { 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, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} + 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(numeric_system) {} + 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 + +// 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(std::is_unsigned::value || + (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); +} + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +// 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; +}; + +// Format subseconds which are given as an integer type with an appropriate +// number of digits. +template +void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { + 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)>>; + + const auto fractional = + d - std::chrono::duration_cast(d); + const auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional).count(); + auto n = static_cast>(subseconds); + const int num_digits = detail::count_digits(n); + + int leading_zeroes = (std::max)(0, num_fractional_digits - num_digits); + if (precision < 0) { + FMT_ASSERT(!std::is_floating_point::value, ""); + if (std::ratio_less::value) { + *out++ = '.'; + out = std::fill_n(out, leading_zeroes, '0'); + out = format_decimal(out, n, num_digits).end; + } + } else { + *out++ = '.'; + leading_zeroes = (std::min)(leading_zeroes, precision); + out = std::fill_n(out, leading_zeroes, '0'); + int remaining = precision - leading_zeroes; + if (remaining != 0 && remaining < num_digits) { + n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); + out = format_decimal(out, n, remaining).end; + return; + } + out = format_decimal(out, n, num_digits).end; + remaining -= num_digits; + out = std::fill_n(out, remaining, '0'); + } +} + +// Format subseconds which are given as a floating point type with an +// appropriate number of digits. We cannot pass the Duration here, as we +// explicitly need to pass the Rep value in the chrono_formatter. +template +void write_floating_seconds(memory_buffer& buf, Duration duration, + int num_fractional_digits = -1) { + using rep = typename Duration::rep; + FMT_ASSERT(std::is_floating_point::value, ""); + + auto val = duration.count(); + + if (num_fractional_digits < 0) { + // For `std::round` with fallback to `round`: + // On some toolchains `std::round` is not available (e.g. GCC 6). + using namespace std; + num_fractional_digits = + count_fractional_digits::value; + if (num_fractional_digits < 6 && static_cast(round(val)) != val) + num_fractional_digits = 6; + } + + format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"), + std::fmod(val * static_cast(Duration::period::num) / + static_cast(Duration::period::den), + static_cast(60)), + num_fractional_digits); +} + +template +class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + const bool is_classic_; + OutputIt out_; + const Duration* subsecs_; + 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 write2(int value, pad_type pad) { + unsigned int v = to_unsigned(value) % 100; + if (v >= 10) { + const char* d = digits2(v); + *out_++ = *d++; + *out_++ = *d; + } else { + out_ = detail::write_padding(out_, pad); + *out_++ = static_cast('0' + v); + } + } + + 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, numeric_system ns) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + if (ns != numeric_system::standard) *out_++ = ':'; + write2(static_cast(offset % 60)); + } + template ::value)> + void format_utc_offset_impl(const T& tm, numeric_system ns) { + write_utc_offset(tm.tm_gmtoff, ns); + } + template ::value)> + void format_utc_offset_impl(const T& tm, numeric_system ns) { +#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, ns); +#else + if (ns == numeric_system::standard) return format_localized('z'); + + // Extract timezone offset from timezone conversion functions. + std::tm gtm = tm; + std::time_t gt = std::mktime(>m); + std::tm ltm = gmtime(gt); + std::time_t lt = std::mktime(<m); + long offset = gt - lt; + write_utc_offset(offset, ns); +#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, + const Duration* subsecs = nullptr) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + subsecs_(subsecs), + 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(numeric_system ns) { format_utc_offset_impl(tm_, ns); } + 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, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour(), pad); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12(), pad); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_min(), pad); + format_localized('M', 'O'); + } + + void on_second(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) { + write2(tm_sec(), pad); + if (subsecs_) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, *subsecs_); + if (buf.size() > 1) { + // Remove the leading "0", write something like ".123". + out_ = std::copy(buf.begin() + 1, buf.end(), out_); + } + } else { + write_fractional_seconds(out_, *subsecs_); + } + } + } else { + // Currently no formatting of subseconds when a locale is set. + 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() { + on_24_hour_time(); + *out_++ = ':'; + on_second(numeric_system::standard, pad_type::unspecified); + } + + 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 { + bool has_precision_integral = false; + + 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, pad_type) {} + FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_second(numeric_system, pad_type) {} + 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() const { + if (has_precision_integral) { + FMT_THROW(format_error("precision not allowed for this argument type")); + } + } + FMT_CONSTEXPR void on_duration_unit() {} +}; + +template ::value&& has_isfinite::value)> +inline bool isfinite(T) { + return true; +} + +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 +} + +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 = 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, pad_type pad = pad_type::unspecified) { + 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 = detail::write_padding(out, pad, width - num_digits); + } + 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(numeric_system) {} + 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, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour(), 2, pad); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour(), 24); + format_tm(time, &tm_writer_type::on_24_hour, ns, pad); + } + + void on_12_hour(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(hour12(), 2, pad); + auto time = tm(); + time.tm_hour = to_nonnegative_int(hour12(), 12); + format_tm(time, &tm_writer_type::on_12_hour, ns, pad); + } + + void on_minute(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) return write(minute(), 2, pad); + auto time = tm(); + time.tm_min = to_nonnegative_int(minute(), 60); + format_tm(time, &tm_writer_type::on_minute, ns, pad); + } + + void on_second(numeric_system ns, pad_type pad) { + if (handle_nan_inf()) return; + + if (ns == numeric_system::standard) { + if (std::is_floating_point::value) { + auto buf = memory_buffer(); + write_floating_seconds(buf, std::chrono::duration(val), + precision); + if (negative) *out++ = '-'; + if (buf.size() < 2 || buf[1] == '.') { + out = detail::write_padding(out, pad); + } + out = std::copy(buf.begin(), buf.end(), out); + } else { + write(second(), 2, pad); + write_fractional_seconds( + out, std::chrono::duration(val), precision); + } + return; + } + auto time = tm(); + time.tm_sec = to_nonnegative_int(second(), 60); + format_tm(time, &tm_writer_type::on_second, ns, pad); + } + + 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, pad_type::unspecified); + } + + 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: + 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; + + 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}; + + begin = detail::parse_align(begin, end, specs); + if (begin == end) return {begin, begin}; + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + if (begin == end) return {begin, begin}; + + auto checker = detail::chrono_format_checker(); + if (*begin == '.') { + checker.has_precision_integral = !std::is_floating_point::value; + begin = + detail::parse_precision(begin, end, precision, precision_ref, ctx); + } + if (begin != end && *begin == 'L') { + ++begin; + localized = true; + } + end = detail::parse_chrono_format(begin, end, 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->format_str = detail::string_literal{}; + } + + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num != 1 || period::den != 1 || + std::is_floating_point::value) { + const auto epoch = val.time_since_epoch(); + auto subsecs = std::chrono::duration_cast( + epoch - std::chrono::duration_cast(epoch)); + + if (subsecs.count() < 0) { + auto second = std::chrono::seconds(1); + if (epoch.count() < ((Duration::min)() + second).count()) + FMT_THROW(format_error("duration is too small")); + subsecs += second; + val -= second; + } + + return formatter::do_format( + gmtime(std::chrono::time_point_cast(val)), ctx, + &subsecs); + } + + return formatter::format( + gmtime(std::chrono::time_point_cast(val)), ctx); + } +}; + +#if FMT_USE_LOCAL_TIME +template +struct formatter, Char> + : formatter { + FMT_CONSTEXPR formatter() { + this->format_str = detail::string_literal{}; + } + + template + auto format(std::chrono::local_time val, FormatContext& ctx) const + -> decltype(ctx.out()) { + using period = typename Duration::period; + if (period::num != 1 || period::den != 1 || + std::is_floating_point::value) { + const auto epoch = val.time_since_epoch(); + const auto subsecs = std::chrono::duration_cast( + epoch - std::chrono::duration_cast(epoch)); + + return formatter::do_format( + localtime(std::chrono::time_point_cast(val)), + ctx, &subsecs); + } + + return formatter::format( + localtime(std::chrono::time_point_cast(val)), + ctx); + } +}; +#endif + +#if FMT_USE_UTC_TIME +template +struct formatter, + Char> + : formatter, + Char> { + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter< + std::chrono::time_point, + Char>::format(std::chrono::utc_clock::to_sys(val), ctx); + } +}; +#endif + +template struct formatter { + private: + format_specs specs; + detail::arg_ref width_ref; + + protected: + basic_string_view format_str; + + FMT_CONSTEXPR auto do_parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + auto begin = ctx.begin(), end = ctx.end(); + if (begin == end || *begin == '}') return begin; + + begin = detail::parse_align(begin, end, specs); + if (begin == end) return end; + + begin = detail::parse_dynamic_spec(begin, end, specs.width, width_ref, ctx); + if (begin == end) return end; + + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); + // Replace default format_str only if the new spec is not empty. + if (end != begin) format_str = {begin, detail::to_unsigned(end - begin)}; + return end; + } + + template + auto do_format(const std::tm& tm, FormatContext& ctx, + const Duration* subsecs) const -> decltype(ctx.out()) { + auto specs_copy = specs; + basic_memory_buffer buf; + auto out = std::back_inserter(buf); + detail::handle_dynamic_spec(specs_copy.width, + width_ref, ctx); + + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = + detail::tm_writer(loc, out, tm, subsecs); + detail::parse_chrono_format(format_str.begin(), format_str.end(), w); + return detail::write( + ctx.out(), basic_string_view(buf.data(), buf.size()), specs_copy); + } + + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return this->do_parse(ctx); + } + + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + return do_format(tm, ctx, nullptr); + } +}; + +FMT_END_EXPORT +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..d175448 --- /dev/null +++ b/src/fmtlib/fmt/color.h @@ -0,0 +1,633 @@ +// 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_BEGIN_EXPORT + +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 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 + +inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, + format_args args) { + // Legacy wide streams are not supported. + auto buf = memory_buffer(); + detail::vformat_to(buf, ts, fmt, args); + if (detail::is_utf8()) { + detail::print(f, string_view(buf.begin(), buf.size())); + return; + } + buf.push_back('\0'); + int result = std::fputs(buf.data(), f); + if (result < 0) + FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); +} + +/** + \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, out); +} + +/** + \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: {0:.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_END_EXPORT +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..94e13c0 --- /dev/null +++ b/src/fmtlib/fmt/compile.h @@ -0,0 +1,607 @@ +// 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 +FMT_CONSTEXPR 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; +}; + +enum { 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()), + next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()}; +} + +template struct arg_id_handler { + arg_ref arg_id; + + constexpr int on_auto() { + FMT_ASSERT(false, "handler cannot be used with automatic indexing"); + return 0; + } + constexpr int on_index(int id) { + arg_id = arg_ref(id); + return 0; + } + constexpr int on_name(basic_string_view id) { + arg_id = arg_ref(id); + return 0; + } +}; + +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 != ':') { + FMT_THROW(format_error("expected ':'")); + } else { + constexpr auto result = parse_specs::type>( + str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID); + if constexpr (result.end >= str.size() || str[result.end] != '}') { + FMT_THROW(format_error("expected '}'")); + return 0; + } else { + 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_BEGIN_EXPORT + +#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)> +FMT_CONSTEXPR20 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_END_EXPORT +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..46723d5 --- /dev/null +++ b/src/fmtlib/fmt/core.h @@ -0,0 +1,2951 @@ +// 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 100000 + +#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 +# 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 + +// Disable [[noreturn]] on MSVC/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 + +#ifndef FMT_NODISCARD +# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +# else +# define FMT_NODISCARD +# endif +#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 + +// An inline std::forward replacement. +#define FMT_FORWARD(...) static_cast(__VA_ARGS__) + +#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 v10 { +# define FMT_END_NAMESPACE \ + } \ + } +#endif + +#ifndef FMT_MODULE_EXPORT +# define FMT_MODULE_EXPORT +# define FMT_BEGIN_EXPORT +# define FMT_END_EXPORT +#endif + +#if !defined(FMT_HEADER_ONLY) && defined(_WIN32) +# ifdef FMT_LIB_EXPORT +# define FMT_API __declspec(dllexport) +# elif defined(FMT_SHARED) +# define FMT_API __declspec(dllimport) +# endif +#else +# if defined(FMT_LIB_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) && \ + (!defined(__apple_build_version__) || \ + __apple_build_version__ >= 14000029L) && \ + FMT_CPLUSPLUS >= 202002L) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang before 14. +# 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) && \ + !defined(__NVCOMPILER) && !defined(__LCC__) +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +# else +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 +# endif +#endif + +#if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L +# define FMT_INLINE_VARIABLE inline +#else +# define FMT_INLINE_VARIABLE +#endif + +// Enable minimal optimizations for more compact code in debug mode. +FMT_GCC_PRAGMA("GCC push_options") +#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \ + !defined(__CUDACC__) +FMT_GCC_PRAGMA("GCC optimize(\"Og\")") +#endif + +FMT_BEGIN_NAMESPACE + +// 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; + +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(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0 +#endif + +#ifdef __cpp_lib_byte +inline auto format_as(std::byte b) -> unsigned char { + return static_cast(b); +} +#endif + +namespace detail { +// 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 { +// Workaround for incompatibility between libstdc++ consteval-based +// std::is_constant_evaluated() implementation and clang-14. +// https://github.com/fmtlib/fmt/issues/3247 +#if FMT_CPLUSPLUS >= 202002L && defined(_GLIBCXX_RELEASE) && \ + _GLIBCXX_RELEASE >= 12 && \ + (FMT_CLANG_VERSION >= 1400 && FMT_CLANG_VERSION < 1500) + ignore_unused(default_value); + return __builtin_is_constant_evaluated(); +#elif defined(__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(std::is_unsigned::value || value >= 0, "negative value"); + return static_cast::type>(value); +} + +FMT_CONSTEXPR inline auto is_utf8() -> bool { + FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; + + // Avoid buggy sign extensions in MSVC's constant evaluation mode (#2297). + using uchar = unsigned char; + return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && + uchar(section[1]) == 0xA7); +} +} // namespace detail + +/** + 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). + */ +FMT_MODULE_EXPORT +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; + } + + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with( + basic_string_view sv) const noexcept { + return size_ >= sv.size_ && + std::char_traits::compare(data_, sv.data_, sv.size_) == 0; + } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept { + return size_ >= 1 && std::char_traits::eq(*data_, c); + } + FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const { + return starts_with(basic_string_view(s)); + } + + // 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; + } +}; + +FMT_MODULE_EXPORT +using string_view = basic_string_view; + +/** Specifies if ``T`` is a character type. Can be specialized by users. */ +FMT_MODULE_EXPORT +template struct is_char : std::false_type {}; +template <> struct is_char : std::true_type {}; + +namespace detail { + +// A base class for compile-time strings. +struct compile_string {}; + +template +struct is_compile_string : std::is_base_of {}; + +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 is intentionally disabled as to_string_view is not an extension point. +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; +} + +constexpr auto set(type rhs) -> int { return 1 << static_cast(rhs); } +constexpr auto in(type t, int set) -> bool { + return ((set >> static_cast(t)) & 1) != 0; +} + +// Bitsets of types. +enum { + sint_set = + set(type::int_type) | set(type::long_long_type) | set(type::int128_type), + uint_set = set(type::uint_type) | set(type::ulong_long_type) | + set(type::uint128_type), + bool_set = set(type::bool_type), + char_set = set(type::char_type), + float_set = set(type::float_type) | set(type::double_type) | + set(type::long_double_type), + string_set = set(type::string_type), + cstring_set = set(type::cstring_type), + pointer_set = set(type::pointer_type) +}; + +FMT_NORETURN FMT_API void throw_format_error(const char* message); + +struct error_handler { + constexpr 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); + } +}; +} // namespace detail + +/** 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 + */ +FMT_MODULE_EXPORT +template class basic_format_parse_context { + 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 = const Char*; + + explicit constexpr basic_format_parse_context( + basic_string_view format_str, int next_arg_id = 0) + : 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) { + detail::throw_format_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) { + detail::throw_format_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 check_dynamic_spec(int arg_id); +}; + +FMT_MODULE_EXPORT +using format_parse_context = basic_format_parse_context; + +namespace detail { +// 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, + int next_arg_id = 0) + : base(format_str, next_arg_id), num_args_(num_args), types_(types) {} + + constexpr auto num_args() const -> int { return num_args_; } + constexpr auto arg_type(int id) const -> type { return types_[id]; } + + FMT_CONSTEXPR auto next_arg_id() -> int { + int id = base::next_arg_id(); + if (id >= num_args_) throw_format_error("argument not found"); + return id; + } + + FMT_CONSTEXPR void check_arg_id(int id) { + base::check_arg_id(id); + if (id >= num_args_) throw_format_error("argument not found"); + } + using base::check_arg_id; + + FMT_CONSTEXPR void check_dynamic_spec(int arg_id) { + detail::ignore_unused(arg_id); +#if !defined(__LCC__) + if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id])) + throw_format_error("width/precision is not integer"); +#endif + } +}; +} // namespace detail + +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 || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context; + if (id >= static_cast(this)->num_args()) + detail::throw_format_error("argument not found"); + } +} + +template +FMT_CONSTEXPR void basic_format_parse_context::check_dynamic_spec( + int arg_id) { + if (detail::is_constant_evaluated() && + (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 1200)) { + using context = detail::compile_parse_context; + static_cast(this)->check_dynamic_spec(arg_id); + } +} + +FMT_MODULE_EXPORT template class basic_format_arg; +FMT_MODULE_EXPORT template class basic_format_args; +FMT_MODULE_EXPORT template class dynamic_format_arg_store; + +// A formatter for objects of type T. +FMT_MODULE_EXPORT +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; + +namespace detail { + +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); + if (size > 0) 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; + + FMT_INLINE auto begin() noexcept -> T* { return ptr_; } + FMT_INLINE auto end() noexcept -> T* { return ptr_ + size_; } + + FMT_INLINE auto begin() const noexcept -> const T* { return ptr_; } + FMT_INLINE 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[](Idx index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](Idx 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 , Buf>::value)> +auto get_buffer(std::back_insert_iterator out) -> buffer& { + return get_container(out); +} + +template +FMT_INLINE auto get_iterator(Buf& buf, OutputIt) -> decltype(buf.out()) { + return buf.out(); +} +template +auto get_iterator(buffer&, OutputIt out) -> OutputIt { + return out; +} + +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_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, typename Context::template formatter_type>; + } + value(unformattable); + value(unformattable_char); + 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; + +template struct format_as_result { + template ::value || std::is_class::value)> + static auto map(U*) -> decltype(format_as(std::declval())); + static auto map(...) -> void; + + using type = decltype(map(static_cast(nullptr))); +}; +template using format_as_t = typename format_as_result::type; + +template +struct has_format_as + : bool_constant, void>::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 {}; + } + + 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; + } + + // Use SFINAE instead of a const T* parameter to avoid a conflict with the + // 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; + } + + // Only map owning types because mapping views can be unsafe. + template , + FMT_ENABLE_IF(std::is_arithmetic::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) { + return map(format_as(val)); + } + + template > + struct formattable + : bool_constant() || + (has_formatter::value && + !std::is_const>::value)> {}; + + 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 { + return {}; + } + + template , + FMT_ENABLE_IF((std::is_class::value || std::is_enum::value || + std::is_union::value) && + !is_string::value && !is_char::value && + !is_named_arg::value && + !std::is_arithmetic>::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(this->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 }; +} // namespace detail + +// 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>; + + 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 + */ +FMT_MODULE_EXPORT +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()); +} + +namespace detail { + +template +auto copy_str(InputIt begin, InputIt end, appender out) -> appender { + get_container(out).append(begin, end); + return out; +} + +template +FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt { + return detail::copy_str(rng.begin(), rng.end(), 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 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 FMT_INLINE 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 { + auto&& arg = arg_mapper().map(FMT_FORWARD(val)); + using arg_type = remove_cvref_t; + + constexpr bool formattable_char = + !std::is_same::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + // Formatting of arbitrary pointers is disallowed. If you want to format a + // pointer cast it to `void*` or `const void*`. In particular, this forbids + // formatting of `[const] volatile char*` 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 { + auto arg = basic_format_arg(); + arg.type_ = mapped_type_constant::value; + arg.value_ = make_value(value); + return arg; +} + +// The DEPRECATED 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); +} +} // namespace detail +FMT_BEGIN_EXPORT + +// Formatting context. +template class basic_format_context { + private: + OutputIt out_; + basic_format_args args_; + detail::locale_ref loc_; + + public: + using iterator = OutputIt; + using format_arg = basic_format_arg; + using format_args = basic_format_args; + using parse_context_type = basic_format_parse_context; + template using formatter_type = formatter; + + /** The character type for the output. */ + using char_type = Char; + + 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, format_args ctx_args, + detail::locale_ref loc = {}) + : 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 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; + +template +using is_formattable = bool_constant>() + .map(std::declval()))>::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>( + FMT_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(T&&... args) + -> format_arg_store...> { + return {FMT_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}; +} +FMT_END_EXPORT + +/** + \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). +FMT_MODULE_EXPORT 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; + +namespace detail { + +// 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(); + FMT_ASSERT(size <= max_size, "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]; + } +}; +} // namespace detail + +enum class presentation_type : unsigned char { + none, + 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 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 format_specs() + : width(0), + precision(-1), + type(presentation_type::none), + align(align::none), + sign(sign::none), + alt(false), + localized(false) {} +}; + +namespace detail { + +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 idx = 0) : index(idx) {} + 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 reusing the same parsed specifiers with +// different sets of arguments (precompilation of format strings). +template +struct dynamic_format_specs : format_specs { + arg_ref width_ref; + arg_ref precision_ref; +}; + +// Converts a character to ASCII. Returns '\0' on conversion failure. +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} +template ::value)> +constexpr auto to_ascii(Char c) -> char { + return c <= 0xff ? static_cast(c) : '\0'; +} + +// Returns the number of code units in a code point or 1 on error. +template +FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { + if (const_check(sizeof(Char) != 1)) return 1; + auto c = static_cast(*begin); + return static_cast((0x3a55000000000000ull >> (2 * (c >> 3))) & 0x3) + 1; +} + +// 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; +} + +FMT_CONSTEXPR inline auto parse_align(char c) -> align_t { + switch (c) { + case '<': + return align::left; + case '>': + return align::right; + case '^': + return align::center; + } + return align::none; +} + +template constexpr auto is_name_start(Char c) -> bool { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; +} + +template +FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + Char c = *begin; + if (c >= '0' && c <= '9') { + int index = 0; + constexpr int max = (std::numeric_limits::max)(); + if (c != '0') + index = parse_nonnegative_int(begin, end, max); + else + ++begin; + if (begin == end || (*begin != '}' && *begin != ':')) + throw_format_error("invalid format string"); + else + handler.on_index(index); + return begin; + } + if (!is_name_start(c)) { + throw_format_error("invalid format string"); + return begin; + } + auto it = begin; + do { + ++it; + } while (it != end && (is_name_start(*it) || ('0' <= *it && *it <= '9'))); + handler.on_name({begin, to_unsigned(it - begin)}); + return it; +} + +template +FMT_CONSTEXPR FMT_INLINE auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { + FMT_ASSERT(begin != end, ""); + Char c = *begin; + if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); + handler.on_auto(); + return begin; +} + +template struct dynamic_spec_id_handler { + basic_format_parse_context& ctx; + arg_ref& ref; + + FMT_CONSTEXPR void on_auto() { + int id = ctx.next_arg_id(); + ref = arg_ref(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_index(int id) { + ref = arg_ref(id); + ctx.check_arg_id(id); + ctx.check_dynamic_spec(id); + } + FMT_CONSTEXPR void on_name(basic_string_view id) { + ref = arg_ref(id); + ctx.check_arg_id(id); + } +}; + +// Parses [integer | "{" [arg_id] "}"]. +template +FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, + int& value, arg_ref& ref, + basic_format_parse_context& ctx) + -> const Char* { + FMT_ASSERT(begin != end, ""); + if ('0' <= *begin && *begin <= '9') { + int val = parse_nonnegative_int(begin, end, -1); + if (val != -1) + value = val; + else + throw_format_error("number is too big"); + } else if (*begin == '{') { + ++begin; + auto handler = dynamic_spec_id_handler{ctx, ref}; + if (begin != end) begin = parse_arg_id(begin, end, handler); + if (begin != end && *begin == '}') return ++begin; + throw_format_error("invalid format string"); + } + return begin; +} + +template +FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, + int& value, arg_ref& ref, + basic_format_parse_context& ctx) + -> const Char* { + ++begin; + if (begin == end || *begin == '}') { + throw_format_error("invalid precision"); + return begin; + } + return parse_dynamic_spec(begin, end, value, ref, ctx); +} + +enum class state { start, align, sign, hash, zero, width, precision, locale }; + +// Parses standard format specifiers. +template +FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( + const Char* begin, const Char* end, dynamic_format_specs& specs, + basic_format_parse_context& ctx, type arg_type) -> const Char* { + auto c = '\0'; + if (end - begin > 1) { + auto next = to_ascii(begin[1]); + c = parse_align(next) == align::none ? to_ascii(*begin) : '\0'; + } else { + if (begin == end) return begin; + c = to_ascii(*begin); + } + + struct { + state current_state = state::start; + FMT_CONSTEXPR void operator()(state s, bool valid = true) { + if (current_state >= s || !valid) + throw_format_error("invalid format specifier"); + current_state = s; + } + } enter_state; + + using pres = presentation_type; + constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; + struct { + const Char*& begin; + dynamic_format_specs& specs; + type arg_type; + + FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* { + if (!in(arg_type, set)) throw_format_error("invalid format specifier"); + specs.type = type; + return begin + 1; + } + } parse_presentation_type{begin, specs, arg_type}; + + for (;;) { + switch (c) { + case '<': + case '>': + case '^': + enter_state(state::align); + specs.align = parse_align(c); + ++begin; + break; + case '+': + case '-': + case ' ': + enter_state(state::sign, in(arg_type, sint_set | float_set)); + switch (c) { + case '+': + specs.sign = sign::plus; + break; + case '-': + specs.sign = sign::minus; + break; + case ' ': + specs.sign = sign::space; + break; + } + ++begin; + break; + case '#': + enter_state(state::hash, is_arithmetic_type(arg_type)); + specs.alt = true; + ++begin; + break; + case '0': + enter_state(state::zero); + if (!is_arithmetic_type(arg_type)) + throw_format_error("format specifier requires numeric argument"); + if (specs.align == align::none) { + // Ignore 0 if align is specified for compatibility with std::format. + specs.align = align::numeric; + specs.fill[0] = Char('0'); + } + ++begin; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '{': + enter_state(state::width); + begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); + break; + case '.': + enter_state(state::precision, + in(arg_type, float_set | string_set | cstring_set)); + begin = parse_precision(begin, end, specs.precision, specs.precision_ref, + ctx); + break; + case 'L': + enter_state(state::locale, is_arithmetic_type(arg_type)); + specs.localized = true; + ++begin; + break; + case 'd': + return parse_presentation_type(pres::dec, integral_set); + case 'o': + return parse_presentation_type(pres::oct, integral_set); + case 'x': + return parse_presentation_type(pres::hex_lower, integral_set); + case 'X': + return parse_presentation_type(pres::hex_upper, integral_set); + case 'b': + return parse_presentation_type(pres::bin_lower, integral_set); + case 'B': + return parse_presentation_type(pres::bin_upper, integral_set); + case 'a': + return parse_presentation_type(pres::hexfloat_lower, float_set); + case 'A': + return parse_presentation_type(pres::hexfloat_upper, float_set); + case 'e': + return parse_presentation_type(pres::exp_lower, float_set); + case 'E': + return parse_presentation_type(pres::exp_upper, float_set); + case 'f': + return parse_presentation_type(pres::fixed_lower, float_set); + case 'F': + return parse_presentation_type(pres::fixed_upper, float_set); + case 'g': + return parse_presentation_type(pres::general_lower, float_set); + case 'G': + return parse_presentation_type(pres::general_upper, float_set); + case 'c': + return parse_presentation_type(pres::chr, integral_set); + case 's': + return parse_presentation_type(pres::string, + bool_set | string_set | cstring_set); + case 'p': + return parse_presentation_type(pres::pointer, pointer_set | cstring_set); + case '?': + return parse_presentation_type(pres::debug, + char_set | string_set | cstring_set); + case '}': + return begin; + default: { + if (*begin == '}') return begin; + // Parse fill and alignment. + auto fill_end = begin + code_point_length(begin); + if (end - fill_end <= 0) { + throw_format_error("invalid format specifier"); + return begin; + } + if (*begin == '{') { + throw_format_error("invalid fill character '{'"); + return begin; + } + auto align = parse_align(to_ascii(*fill_end)); + enter_state(state::align, align != align::none); + specs.fill = {begin, to_unsigned(fill_end - begin)}; + specs.align = align; + begin = fill_end + 1; + } + } + if (begin == end) return begin; + c = to_ascii(*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 on_auto() { arg_id = handler.on_arg_id(); } + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + }; + + ++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) { + 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 mapped_type = conditional_t< + mapped_type_constant::value != type::custom_type, + decltype(arg_mapper().map(std::declval())), + typename strip_named_arg::type>; + return formatter().parse(ctx); +} + +// Checks char specs and returns true iff the presentation type is char-like. +template +FMT_CONSTEXPR auto check_char_specs(const format_specs& specs) -> bool { + if (specs.type != presentation_type::none && + specs.type != presentation_type::chr && + specs.type != presentation_type::debug) { + return false; + } + if (specs.align == align::numeric || specs.sign != sign::none || specs.alt) + throw_format_error("invalid format specifier for char"); + return true; +} + +constexpr FMT_INLINE_VARIABLE 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 (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: + using parse_context_type = compile_parse_context; + static constexpr int num_args = sizeof...(Args); + + // Format specifier parsing function. + // 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_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 fmt) + : context_(fmt, num_args, types_), + parse_funcs_{&parse_format_specs...}, + types_{mapped_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 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(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) { + throw_format_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) { + using char_t = typename S::char_type; + FMT_CONSTEXPR auto s = basic_string_view(format_str); + using checker = format_string_checker...>; + FMT_CONSTEXPR bool error = (parse_format_string(s, checker(s)), true); + ignore_unused(error); +} + +template struct vformat_args { + using type = basic_format_args< + basic_format_context>, Char>>; +}; +template <> struct vformat_args { using type = format_args; }; + +// Use vformat_args and avoid type_identity to keep symbols short. +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type 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 +} // namespace detail + +FMT_BEGIN_EXPORT + +// A formatter specialization for natively supported types. +template +struct formatter::value != + detail::type::custom_type>> { + private: + detail::dynamic_format_specs specs_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { + auto type = detail::type_constant::value; + auto end = + detail::parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, type); + if (type == detail::type::char_type) detail::check_char_specs(specs_); + return end; + } + + template ::value, + FMT_ENABLE_IF(U == detail::type::string_type || + U == detail::type::cstring_type || + U == detail::type::char_type)> + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.type = set ? presentation_type::debug : presentation_type::none; + } + + 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(const Type& 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 runtime_format_string { + 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(runtime_format_string fmt) : str_(fmt.str) {} + + FMT_INLINE operator basic_string_view() const { return str_; } + FMT_INLINE auto get() const -> basic_string_view { 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) -> 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) -> runtime_format_string<> { 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 { + auto&& buf = detail::get_buffer(out); + detail::vformat_to(buf, fmt, args, {}); + return detail::get_iterator(buf, out); +} + +/** + \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, 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); +} + +/** + Formats ``args`` according to specifications in ``fmt`` and writes the + output to the file ``f`` followed by a newline. + */ +template +FMT_INLINE void println(std::FILE* f, format_string fmt, T&&... args) { + return fmt::print(f, "{}\n", fmt::format(fmt, std::forward(args)...)); +} + +/** + Formats ``args`` according to specifications in ``fmt`` and writes the output + to ``stdout`` followed by a newline. + */ +template +FMT_INLINE void println(format_string fmt, T&&... args) { + return fmt::println(stdout, fmt, std::forward(args)...); +} + +FMT_END_EXPORT +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..5bae3c7 --- /dev/null +++ b/src/fmtlib/fmt/format-inl.h @@ -0,0 +1,1681 @@ +// 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 // errno +#include +#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 + +FMT_FUNC auto write_loc(appender out, loc_value value, + const format_specs<>& specs, locale_ref loc) -> bool { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + auto locale = loc.get(); + // We cannot use the num_put facet because it may produce output in + // a wrong encoding. + using facet = format_facet; + if (std::has_facet(locale)) + return std::use_facet(locale).put(out, value, specs); + return facet(locale).put(out, value, specs); +#endif + return false; +} +} // namespace detail + +template typename Locale::id format_facet::id; + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template format_facet::format_facet(Locale& loc) { + auto& numpunct = std::use_facet>(loc); + grouping_ = numpunct.grouping(); + if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); +} + +template <> +FMT_API FMT_FUNC auto format_facet::do_put( + appender out, loc_value val, const format_specs<>& specs) const -> bool { + return val.visit( + detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); +} +#endif + +FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt, + format_args args) { + auto ec = std::error_code(error_code, std::generic_category()); + return std::system_error(ec, vformat(fmt, 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)); +} + +// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. +namespace dragonbox { +// 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; +} + +// Various fast log computations. +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; +} + +FMT_INLINE_VARIABLE 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}, + {0x9a65406d44a5c903, 0x737f74f1dc043329}, + {0xc0fe908895cf3b44, 0x505f522e53053ff3}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0}, + {0x96c6e0eab509e64d, 0x5eca783430dc19f6}, + {0xbc789925624c5fe0, 0xb67d16413d132073}, + {0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890}, + {0x933e37a534cbaae7, 0x8e91b962f7b6f15a}, + {0xb80dc58e81fe95a1, 0x723627bbb5a4adb1}, + {0xe61136f2227e3b09, 0xcec3b1aaa30dd91d}, + {0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2}, + {0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e}, + {0xe0accfa875af45a7, 0x93eb1b80a33b8606}, + {0x8c6c01c9498d8b88, 0xbc72f130660533c4}, + {0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5}, + { 0xdb68c2ca82ed2a05, + 0xa67398db9f6820e2 } +#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}, + {0xf13e34aabb430a15, 0x647726b9e7c68ff0} +#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; + } +}; + +FMT_FUNC uint128_fallback get_cached_power(int k) noexcept { + return cache_accessor::get_cached_power(k); +} + +// 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, ""); + // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. + // See https://github.com/fmtlib/fmt/issues/3163 for more details. + const uint32_t mod_inv_5 = 0xcccccccd; + // Casts are needed to workaround a bug in MSVC 19.22 and older. + const uint32_t mod_inv_25 = + static_cast(uint64_t(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 typename cache_accessor::compute_mul_parity_result x_mul = + cache_accessor::compute_mul_parity(two_fc - 1, cache, beta); + + if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint))) + 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 +} // namespace detail + +template <> struct formatter { + FMT_CONSTEXPR auto parse(format_parse_context& ctx) + -> format_parse_context::iterator { + return ctx.begin(); + } + + auto format(const detail::bigint& n, format_context& ctx) const + -> format_context::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); +} + +namespace detail { +#ifndef _WIN32 +FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } +#else +using dword = conditional_t; +extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // + void*, const void*, dword, dword*, void*); + +FMT_FUNC bool write_console(std::FILE* f, string_view text) { + auto fd = _fileno(f); + if (!_isatty(fd)) return false; + auto u16 = utf8_to_utf16(text); + auto written = dword(); + return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), + static_cast(u16.size()), &written, nullptr); +} + +// Print assuming legacy (non-Unicode) encoding. +FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, + basic_format_args>(args)); + fwrite_fully(buffer.data(), 1, buffer.size(), f); +} +#endif + +FMT_FUNC void print(std::FILE* f, string_view text) { + if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f); +} +} // namespace detail + +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + auto buffer = memory_buffer(); + detail::vformat_to(buffer, fmt, args); + detail::print(f, {buffer.data(), buffer.size()}); +} + +FMT_FUNC void vprint(string_view fmt, format_args args) { + vprint(stdout, fmt, 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..ed8b29e --- /dev/null +++ b/src/fmtlib/fmt/format.h @@ -0,0 +1,4735 @@ +/* + 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::initializer_list +#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" + +#ifndef FMT_BEGIN_DETAIL_NAMESPACE +# define FMT_BEGIN_DETAIL_NAMESPACE namespace detail { +# define FMT_END_DETAIL_NAMESPACE } +#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_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 + +#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 + +#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 ^ static_cast(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 + +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> {}; + +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 struct string_literal { + static constexpr CharT value[sizeof...(C)] = {C...}; + constexpr operator basic_string_view() const { + return {value, sizeof...(C)}; + } +}; + +#if FMT_CPLUSPLUS < 201703L +template +constexpr CharT string_literal::value[sizeof...(C)]; +#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(); + // The cast suppresses a bogus -Wclass-memaccess on GCC. + std::memcpy(static_cast(&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 constexpr auto operator~(const uint128_fallback& n) + -> uint128_fallback { + return {~n.hi_, ~n.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_}; + if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64); + return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)}; + } + FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback { + if (shift == 64) return {lo_, 0}; + if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64); + 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_CONSTEXPR void operator&=(uint128_fallback n) { + lo_ &= n.lo_; + hi_ &= n.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) && !defined(__ibmxl__) + unsigned long long carry; + lo_ = __builtin_addcll(lo_, n, 0, &carry); + hi_ += carry; +#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__) + 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; +} + +template +FMT_CONSTEXPR20 inline auto countl_zero_fallback(UInt n) -> int { + int lz = 0; + constexpr UInt msb_mask = static_cast(1) << (num_bits() - 1); + for (; (n & msb_mask) == 0; n <<= 1) lz++; + return lz; +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint32_t n) -> int { +#ifdef FMT_BUILTIN_CLZ + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZ(n); +#endif + return countl_zero_fallback(n); +} + +FMT_CONSTEXPR20 inline auto countl_zero(uint64_t n) -> int { +#ifdef FMT_BUILTIN_CLZLL + if (!is_constant_evaluated()) return FMT_BUILTIN_CLZLL(n); +#endif + return countl_zero_fallback(n); +} + +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 = "\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" + [static_cast(*s) >> 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. + const char* next = s + len + !len; + + using uchar = unsigned char; + + // Assume a four-byte character and load four bytes. Unused bits are + // shifted out. + *c = uint32_t(uchar(s[0]) & masks[len]) << 18; + *c |= uint32_t(uchar(s[1]) & 0x3f) << 12; + *c |= uint32_t(uchar(s[2]) & 0x3f) << 6; + *c |= uint32_t(uchar(s[3]) & 0x3f) << 0; + *c >>= shiftc[len]; + + // Accumulate the various error conditions. + *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 FMT_INLINE_VARIABLE 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, error ? 1 : to_unsigned(end - buf_ptr))); + return result ? (error ? buf_ptr + 1 : 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; + } + }; + // We could avoid branches by using utf8_decode directly. + 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); +} + +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>; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + +#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_USE_FLOAT128 +# ifdef __clang__ +// Clang emulates GCC, so it has to appear early. +# if FMT_HAS_INCLUDE() +# define FMT_USE_FLOAT128 1 +# endif +# elif defined(__GNUC__) +// GNU C++: +# if defined(_GLIBCXX_USE_FLOAT128) && !defined(__STRICT_ANSI__) +# define FMT_USE_FLOAT128 1 +# endif +# endif +# ifndef FMT_USE_FLOAT128 +# 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_BEGIN_EXPORT + +// 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 { + 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); + } + + 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()); + } +}; + +using memory_buffer = basic_memory_buffer; + +template +struct is_contiguous> : std::true_type { +}; + +FMT_END_EXPORT +namespace detail { +FMT_API bool write_console(std::FILE* f, string_view text); +FMT_API void print(std::FILE*, string_view); +} // namespace detail +FMT_BEGIN_EXPORT + +// Suppress a misleading warning in older versions of clang. +#if FMT_CLANG_VERSION +# pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +/** An error reported from a formatting function. */ +class FMT_API format_error : public std::runtime_error { + public: + using std::runtime_error::runtime_error; +}; + +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 + +class loc_value { + private: + basic_format_arg value_; + + public: + template ::value)> + loc_value(T value) : value_(detail::make_arg(value)) {} + + template ::value)> + loc_value(T) {} + + template auto visit(Visitor&& vis) -> decltype(vis(0)) { + return visit_format_arg(vis, value_); + } +}; + +// A locale facet that formats values in UTF-8. +// It is parameterized on the locale to avoid the heavy include. +template class format_facet : public Locale::facet { + private: + std::string separator_; + std::string grouping_; + std::string decimal_point_; + + protected: + virtual auto do_put(appender out, loc_value val, + const format_specs<>& specs) const -> bool; + + public: + static FMT_API typename Locale::id id; + + explicit format_facet(Locale& loc); + explicit format_facet(string_view sep = "", + std::initializer_list g = {3}, + std::string decimal_point = ".") + : separator_(sep.data(), sep.size()), + grouping_(g.begin(), g.end()), + decimal_point_(decimal_point) {} + + auto put(appender out, loc_value val, const format_specs<>& specs) const + -> bool { + return do_put(out, val, specs); + } +}; + +FMT_BEGIN_DETAIL_NAMESPACE + +// 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)> +FMT_CONSTEXPR 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()}; } +}; + +// A converter from UTF-16/UTF-32 (host endian) to UTF-8. +template +class unicode_to_utf8 { + private: + Buffer buffer_; + + public: + unicode_to_utf8() {} + explicit unicode_to_utf8(basic_string_view s) { + static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, + "Expect utf16 or utf32"); + + if (!convert(s)) + FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" + : "invalid utf32")); + } + operator string_view() const { return string_view(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char* c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a bool instead of throwing exception on + // conversion error. This method may still throw in case of memory allocation + // error. + bool convert(basic_string_view s) { + if (!convert(buffer_, s)) return false; + buffer_.push_back(0); + return true; + } + static bool convert(Buffer& buf, basic_string_view s) { + for (auto p = s.begin(); p != s.end(); ++p) { + uint32_t c = static_cast(*p); + if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { + return false; + } + 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 { + return false; + } + } + return true; + } +}; + +// 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 +} + +namespace dragonbox { +// 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; +} + +inline int floor_log2_pow10(int e) noexcept { + FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent"); + return (e * 1741647) >> 19; +} + +// 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; +} + +FMT_API uint128_fallback get_cached_power(int k) noexcept; + +// 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 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 = 341; + 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 float_uint = typename dragonbox::float_info::carrier_uint; + return ((float_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}; + + // For checking rounding thresholds. + // The kth entry is chosen to be the smallest integer such that the + // upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k. + static constexpr uint32_t fractional_part_rounding_thresholds[8] = { + 2576980378, // ceil(2^31 + 2^32/10^1) + 2190433321, // ceil(2^31 + 2^32/10^2) + 2151778616, // ceil(2^31 + 2^32/10^3) + 2147913145, // ceil(2^31 + 2^32/10^4) + 2147526598, // ceil(2^31 + 2^32/10^5) + 2147487943, // ceil(2^31 + 2^32/10^6) + 2147484078, // ceil(2^31 + 2^32/10^7) + 2147483691 // ceil(2^31 + 2^32/10^8) + }; +}; + +#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[]; +template +constexpr uint32_t basic_data::fractional_part_rounding_thresholds[]; +#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; + // Using *(x + index) instead of x[index] avoids an issue with some compilers + // using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode). + return {*(data::pow10_significands + index), + *(data::pow10_exponents + index)}; +} + +template +using convert_float_result = + conditional_t::value || + std::numeric_limits::digits == + std::numeric_limits::digits, + 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 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 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 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 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_MAYBE_UNUSED = 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 (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 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 format_specs& specs, locale_ref loc = {}) + -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; + 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 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 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: + std::string grouping_; + std::basic_string thousands_sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + next_state initial_state() const { return {grouping_.begin(), 0}; } + + // Returns the next digit group separator position. + int next(next_state& state) const { + if (thousands_sep_.empty()) return max_value(); + if (state.group == grouping_.end()) return state.pos += 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) return; + auto sep = thousands_sep(loc); + grouping_ = sep.grouping; + if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep); + } + digit_grouping(std::string grouping, std::basic_string sep) + : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} + + bool has_separator() const { return !thousands_sep_.empty(); } + + 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 = + copy_str(thousands_sep_.data(), + thousands_sep_.data() + thousands_sep_.size(), out); + --sep_index; + } + *out++ = static_cast(digits[to_unsigned(i)]); + } + return out; + } +}; + +// Writes a decimal integer with digit grouping. +template +auto write_int(OutputIt out, UInt value, unsigned prefix, + const 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) { + char sign = static_cast(prefix); + *it++ = static_cast(sign); + } + return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); + }); +} + +// Writes a localized value. +FMT_API auto write_loc(appender out, loc_value value, + const format_specs<>& specs, locale_ref loc) -> bool; +template +inline auto write_loc(OutputIt, loc_value, const format_specs&, + locale_ref) -> bool { + return false; +} + +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 struct loc_writer { + buffer_appender out; + const format_specs& specs; + std::basic_string sep; + std::string grouping; + std::basic_string decimal_point; + + template ::value)> + auto operator()(T value) -> bool { + auto arg = make_write_int_arg(value, specs.sign); + write_int(out, static_cast>(arg.abs_value), arg.prefix, + specs, digit_grouping(grouping, sep)); + return true; + } + + template ::value)> + auto operator()(T) -> bool { + return false; + } +}; + +template +FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, + const format_specs& specs, + locale_ref) -> 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: { + 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 format specifier"); + } + return out; +} +template +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( + OutputIt out, write_int_arg arg, const 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 format_specs& specs, + locale_ref loc) -> OutputIt { + if (specs.localized && write_loc(out, value, specs, loc)) return out; + 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 format_specs& specs, + locale_ref loc) -> OutputIt { + if (specs.localized && write_loc(out, value, specs, loc)) return out; + 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 FMT_CONSTEXPR void operator=(const T&) {} + }; + + FMT_CONSTEXPR counting_iterator() : count_(0) {} + + FMT_CONSTEXPR size_t count() const { return count_; } + + FMT_CONSTEXPR counting_iterator& operator++() { + ++count_; + return *this; + } + FMT_CONSTEXPR counting_iterator operator++(int) { + auto it = *this; + ++*this; + return it; + } + + FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it, + difference_type n) { + it.count_ += static_cast(n); + return it; + } + + FMT_CONSTEXPR value_type operator*() const { return {}; } +}; + +template +FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, + const 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 format_specs& specs, locale_ref) + -> OutputIt { + return write(out, s, specs); +} +template +FMT_CONSTEXPR auto write(OutputIt out, const Char* s, + const format_specs& specs, locale_ref) + -> OutputIt { + return specs.type != presentation_type::pointer + ? 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); +} + +// 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 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 format specifier"); + break; + } + return result; +} + +template +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, + 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.has_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.has_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 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 = 0; + 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(exp)); + 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 bool has_separator() const { return false; } + + 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 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; + } + } + int even = static_cast((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); +} + +// Formats a floating-point number using the hexfloat format. +template ::value)> +FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, + float_specs specs, buffer& buf) { + // float is passed as double to reduce the number of instantiations and to + // simplify implementation. + static_assert(!std::is_same::value, ""); + + using info = dragonbox::float_info; + + // Assume Float is in the format [sign][exponent][significand]. + using carrier_uint = typename info::carrier_uint; + + constexpr auto num_float_significand_bits = + detail::num_significand_bits(); + + basic_fp f(value); + f.e += num_float_significand_bits; + if (!has_implicit_bit()) --f.e; + + constexpr auto num_fraction_bits = + num_float_significand_bits + (has_implicit_bit() ? 1 : 0); + constexpr auto num_xdigits = (num_fraction_bits + 3) / 4; + + constexpr auto leading_shift = ((num_xdigits - 1) * 4); + const auto leading_mask = carrier_uint(0xF) << leading_shift; + const auto leading_xdigit = + static_cast((f.f & leading_mask) >> leading_shift); + if (leading_xdigit > 1) f.e -= (32 - countl_zero(leading_xdigit) - 1); + + int print_xdigits = num_xdigits - 1; + if (precision >= 0 && print_xdigits > precision) { + const int shift = ((print_xdigits - precision - 1) * 4); + const auto mask = carrier_uint(0xF) << shift; + const auto v = static_cast((f.f & mask) >> shift); + + if (v >= 8) { + const auto inc = carrier_uint(1) << (shift + 4); + f.f += inc; + f.f &= ~(inc - 1); + } + + // Check long double overflow + if (!has_implicit_bit()) { + const auto implicit_bit = carrier_uint(1) << num_float_significand_bits; + if ((f.f & implicit_bit) == implicit_bit) { + f.f >>= 4; + f.e += 4; + } + } + + print_xdigits = precision; + } + + char xdigits[num_bits() / 4]; + detail::fill_n(xdigits, sizeof(xdigits), '0'); + format_uint<4>(xdigits, f.f, num_xdigits, specs.upper); + + // Remove zero tail + while (print_xdigits > 0 && xdigits[print_xdigits] == '0') --print_xdigits; + + buf.push_back('0'); + buf.push_back(specs.upper ? 'X' : 'x'); + buf.push_back(xdigits[0]); + if (specs.showpoint || print_xdigits > 0 || print_xdigits < precision) + buf.push_back('.'); + buf.append(xdigits + 1, xdigits + 1 + print_xdigits); + for (; print_xdigits < precision; ++print_xdigits) buf.push_back('0'); + + buf.push_back(specs.upper ? 'P' : 'p'); + + uint32_t abs_e; + if (f.e < 0) { + buf.push_back('-'); + abs_e = static_cast(-f.e); + } else { + buf.push_back('+'); + abs_e = static_cast(f.e); + } + format_decimal(appender(buf), abs_e, detail::count_digits(abs_e)); +} + +template ::value)> +FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision, + float_specs specs, buffer& buf) { + format_hexfloat(static_cast(value), precision, specs, buf); +} + +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 if (is_constant_evaluated()) { + // 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; + } + } else { + // Extract significand bits and exponent bits. + using info = dragonbox::float_info; + auto br = bit_cast(static_cast(value)); + + const uint64_t significand_mask = + (static_cast(1) << num_significand_bits()) - 1; + uint64_t 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(); + significand |= + (static_cast(1) << num_significand_bits()); + significand <<= 1; + } else { + // Normalize subnormal inputs. + FMT_ASSERT(significand != 0, "zeros should not appear hear"); + int shift = countl_zero(significand); + FMT_ASSERT(shift >= num_bits() - num_significand_bits(), + ""); + shift -= (num_bits() - num_significand_bits() - 2); + exponent = (std::numeric_limits::min_exponent - + num_significand_bits()) - + shift; + significand <<= shift; + } + + // Compute the first several nonzero decimal significand digits. + // We call the number we get the first segment. + const int k = info::kappa - dragonbox::floor_log10_pow2(exponent); + exp = -k; + const int beta = exponent + dragonbox::floor_log2_pow10(k); + uint64_t first_segment; + bool has_more_segments; + int digits_in_the_first_segment; + { + const auto r = dragonbox::umul192_upper128( + significand << beta, dragonbox::get_cached_power(k)); + first_segment = r.high(); + has_more_segments = r.low() != 0; + + // The first segment can have 18 ~ 19 digits. + if (first_segment >= 1000000000000000000ULL) { + digits_in_the_first_segment = 19; + } else { + // When it is of 18-digits, we align it to 19-digits by adding a bogus + // zero at the end. + digits_in_the_first_segment = 18; + first_segment *= 10; + } + } + + // Compute the actual number of decimal digits to print. + if (fixed) { + adjust_precision(precision, exp + digits_in_the_first_segment); + } + + // Use Dragon4 only when there might be not enough digits in the first + // segment. + if (digits_in_the_first_segment > precision) { + use_dragon = false; + + if (precision <= 0) { + exp += digits_in_the_first_segment; + + if (precision < 0) { + // Nothing to do, since all we have are just leading zeros. + buf.try_resize(0); + } else { + // We may need to round-up. + buf.try_resize(1); + if ((first_segment | static_cast(has_more_segments)) > + 5000000000000000000ULL) { + buf[0] = '1'; + } else { + buf[0] = '0'; + } + } + } // precision <= 0 + else { + exp += digits_in_the_first_segment - precision; + + // When precision > 0, we divide the first segment into three + // subsegments, each with 9, 9, and 0 ~ 1 digits so that each fits + // in 32-bits which usually allows faster calculation than in + // 64-bits. Since some compiler (e.g. MSVC) doesn't know how to optimize + // division-by-constant for large 64-bit divisors, we do it here + // manually. The magic number 7922816251426433760 below is equal to + // ceil(2^(64+32) / 10^10). + const uint32_t first_subsegment = static_cast( + dragonbox::umul128_upper64(first_segment, 7922816251426433760ULL) >> + 32); + const uint64_t second_third_subsegments = + first_segment - first_subsegment * 10000000000ULL; + + uint64_t prod; + uint32_t digits; + bool should_round_up; + int number_of_digits_to_print = precision > 9 ? 9 : precision; + + // Print a 9-digits subsegment, either the first or the second. + auto print_subsegment = [&](uint32_t subsegment, char* buffer) { + int number_of_digits_printed = 0; + + // If we want to print an odd number of digits from the subsegment, + if ((number_of_digits_to_print & 1) != 0) { + // Convert to 64-bit fixed-point fractional form with 1-digit + // integer part. The magic number 720575941 is a good enough + // approximation of 2^(32 + 24) / 10^8; see + // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case + // for details. + prod = ((subsegment * static_cast(720575941)) >> 24) + 1; + digits = static_cast(prod >> 32); + *buffer = static_cast('0' + digits); + number_of_digits_printed++; + } + // If we want to print an even number of digits from the + // first_subsegment, + else { + // Convert to 64-bit fixed-point fractional form with 2-digits + // integer part. The magic number 450359963 is a good enough + // approximation of 2^(32 + 20) / 10^7; see + // https://jk-jeon.github.io/posts/2022/12/fixed-precision-formatting/#fixed-length-case + // for details. + prod = ((subsegment * static_cast(450359963)) >> 20) + 1; + digits = static_cast(prod >> 32); + copy2(buffer, digits2(digits)); + number_of_digits_printed += 2; + } + + // Print all digit pairs. + while (number_of_digits_printed < number_of_digits_to_print) { + prod = static_cast(prod) * static_cast(100); + digits = static_cast(prod >> 32); + copy2(buffer + number_of_digits_printed, digits2(digits)); + number_of_digits_printed += 2; + } + }; + + // Print first subsegment. + print_subsegment(first_subsegment, buf.data()); + + // Perform rounding if the first subsegment is the last subsegment to + // print. + if (precision <= 9) { + // Rounding inside the subsegment. + // We round-up if: + // - either the fractional part is strictly larger than 1/2, or + // - the fractional part is exactly 1/2 and the last digit is odd. + // We rely on the following observations: + // - If fractional_part >= threshold, then the fractional part is + // strictly larger than 1/2. + // - If the MSB of fractional_part is set, then the fractional part + // must be at least 1/2. + // - When the MSB of fractional_part is set, either + // second_third_subsegments being nonzero or has_more_segments + // being true means there are further digits not printed, so the + // fractional part is strictly larger than 1/2. + if (precision < 9) { + uint32_t fractional_part = static_cast(prod); + should_round_up = fractional_part >= + data::fractional_part_rounding_thresholds + [8 - number_of_digits_to_print] || + ((fractional_part >> 31) & + ((digits & 1) | (second_third_subsegments != 0) | + has_more_segments)) != 0; + } + // Rounding at the subsegment boundary. + // In this case, the fractional part is at least 1/2 if and only if + // second_third_subsegments >= 5000000000ULL, and is strictly larger + // than 1/2 if we further have either second_third_subsegments > + // 5000000000ULL or has_more_segments == true. + else { + should_round_up = second_third_subsegments > 5000000000ULL || + (second_third_subsegments == 5000000000ULL && + ((digits & 1) != 0 || has_more_segments)); + } + } + // Otherwise, print the second subsegment. + else { + // Compilers are not aware of how to leverage the maximum value of + // second_third_subsegments to find out a better magic number which + // allows us to eliminate an additional shift. 1844674407370955162 = + // ceil(2^64/10) < ceil(2^64*(10^9/(10^10 - 1))). + const uint32_t second_subsegment = + static_cast(dragonbox::umul128_upper64( + second_third_subsegments, 1844674407370955162ULL)); + const uint32_t third_subsegment = + static_cast(second_third_subsegments) - + second_subsegment * 10; + + number_of_digits_to_print = precision - 9; + print_subsegment(second_subsegment, buf.data() + 9); + + // Rounding inside the subsegment. + if (precision < 18) { + // The condition third_subsegment != 0 implies that the segment was + // of 19 digits, so in this case the third segment should be + // consisting of a genuine digit from the input. + uint32_t fractional_part = static_cast(prod); + should_round_up = fractional_part >= + data::fractional_part_rounding_thresholds + [8 - number_of_digits_to_print] || + ((fractional_part >> 31) & + ((digits & 1) | (third_subsegment != 0) | + has_more_segments)) != 0; + } + // Rounding at the subsegment boundary. + else { + // In this case, the segment must be of 19 digits, thus + // the third subsegment should be consisting of a genuine digit from + // the input. + should_round_up = third_subsegment > 5 || + (third_subsegment == 5 && + ((digits & 1) != 0 || has_more_segments)); + } + } + + // Round-up if necessary. + if (should_round_up) { + ++buf[precision - 1]; + for (int i = precision - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[precision++] = '0'; + else + ++exp; + } + } + buf.try_resize(to_unsigned(precision)); + } + } // if (digits_in_the_first_segment > precision) + else { + // Adjust the exponent for its use in Dragon4. + exp += digits_in_the_first_segment - 1; + } + } + 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 +FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, + format_specs specs, locale_ref loc) + -> OutputIt { + 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)); + format_hexfloat(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, format_specs specs, + locale_ref loc = {}) -> OutputIt { + if (const_check(!is_supported_floating_point(value))) return out; + return specs.localized && write_loc(out, value, specs, loc) + ? out + : write_float(out, value, specs, loc); +} + +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { + if (is_constant_evaluated()) return write(out, value, 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 = format_specs(); + using floaty = conditional_t::value, double, T>; + using floaty_uint = typename dragonbox::float_info::carrier_uint; + floaty_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, format_specs()); +} + +template +auto write(OutputIt out, monostate, 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 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) return write(out, basic_string_view(value)); + throw_format_error("string pointer is null"); + return out; +} + +template ::value)> +auto write(OutputIt out, const T* value, const format_specs& specs = {}, + locale_ref = {}) -> OutputIt { + 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> { + auto ctx = Context(out, {}, {}); + return typename Context::template 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 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 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