From e7b9a4b6cc5e934051d756e93873a2bd4f41b5c4 Mon Sep 17 00:00:00 2001 From: zhangshine Date: Sat, 5 Apr 2014 18:43:07 +0800 Subject: [PATCH] init commit --- .gitignore | 24 + LICENSE | 12 + ReadMe.md | 23 + src/MdCharm.pro | 10 + src/MdCharm/MdCharm.pro | 191 + src/MdCharm/ReadMe.md | 1 + src/MdCharm/about/aboutdialog.ui | 951 + src/MdCharm/about/aboutmdcharmdialog.cpp | 33 + src/MdCharm/about/aboutmdcharmdialog.h | 26 + src/MdCharm/baseeditor/baseautocompleter.cpp | 34 + src/MdCharm/baseeditor/baseautocompleter.h | 25 + src/MdCharm/baseeditor/baseeditor.cpp | 620 + src/MdCharm/baseeditor/baseeditor.h | 94 + .../baseeditor/markdownautocompleter.cpp | 86 + .../baseeditor/markdownautocompleter.h | 24 + src/MdCharm/baseeditor/markdowneditor.cpp | 448 + src/MdCharm/baseeditor/markdowneditor.h | 43 + src/MdCharm/basewebview/basewebview.cpp | 50 + src/MdCharm/basewebview/basewebview.h | 18 + src/MdCharm/basewebview/markdownwebview.cpp | 11 + src/MdCharm/basewebview/markdownwebview.h | 18 + src/MdCharm/browereditareawidget.cpp | 137 + src/MdCharm/browereditareawidget.h | 64 + src/MdCharm/conf/configuredialog.cpp | 106 + src/MdCharm/conf/configuredialog.h | 49 + src/MdCharm/conf/environmentpage.ui | 250 + src/MdCharm/conf/pages.cpp | 455 + src/MdCharm/conf/pages.h | 128 + src/MdCharm/conf/stylespage.ui | 48 + src/MdCharm/conf/texteditorpage.ui | 452 + src/MdCharm/configuration.cpp | 1060 + src/MdCharm/configuration.h | 165 + src/MdCharm/dock/projectdockwidget.cpp | 226 + src/MdCharm/dock/projectdockwidget.h | 76 + src/MdCharm/dock/projectdockwidget.ui | 29 + src/MdCharm/editareatabwidget.cpp | 312 + src/MdCharm/editareatabwidget.h | 88 + src/MdCharm/editareatabwidgetmanager.cpp | 742 + src/MdCharm/editareatabwidgetmanager.h | 99 + src/MdCharm/editareawidget.cpp | 205 + src/MdCharm/editareawidget.h | 173 + src/MdCharm/main.cpp | 133 + src/MdCharm/markdowneditareawidget.cpp | 812 + src/MdCharm/markdowneditareawidget.h | 132 + src/MdCharm/mdcharmapplication.cpp | 58 + src/MdCharm/mdcharmapplication.h | 25 + src/MdCharm/mdcharmform.cpp | 1299 + src/MdCharm/mdcharmform.h | 208 + src/MdCharm/network/checkupdates.cpp | 66 + src/MdCharm/network/checkupdates.h | 28 + src/MdCharm/resource.cpp | 48 + src/MdCharm/resource.h | 50 + src/MdCharm/util/filesystemmodel.cpp | 330 + src/MdCharm/util/filesystemmodel.h | 75 + src/MdCharm/util/filesystemtreeview.cpp | 140 + src/MdCharm/util/filesystemtreeview.h | 45 + src/MdCharm/util/gui/addnewfiledialog.cpp | 37 + src/MdCharm/util/gui/addnewfiledialog.h | 31 + src/MdCharm/util/gui/addnewfiledialog.ui | 71 + src/MdCharm/util/gui/exportdialog.cpp | 294 + src/MdCharm/util/gui/exportdialog.h | 55 + src/MdCharm/util/gui/exportdialog.ui | 119 + .../util/gui/exportdirectorydialog.cpp | 252 + src/MdCharm/util/gui/exportdirectorydialog.h | 56 + src/MdCharm/util/gui/exportdirectorydialog.ui | 229 + src/MdCharm/util/gui/findandreplace.cpp | 158 + src/MdCharm/util/gui/findandreplace.h | 52 + src/MdCharm/util/gui/findandreplaceform.ui | 208 + src/MdCharm/util/gui/gotodialog.cpp | 42 + src/MdCharm/util/gui/gotodialog.h | 27 + src/MdCharm/util/gui/gotodialog.ui | 38 + src/MdCharm/util/gui/insertcodedialog.cpp | 30 + src/MdCharm/util/gui/insertcodedialog.h | 23 + src/MdCharm/util/gui/insertcodedialog.ui | 45 + .../util/gui/insertlinkorpicturedialog.cpp | 112 + .../util/gui/insertlinkorpicturedialog.h | 36 + .../util/gui/insertlinkorpicturedialog.ui | 121 + .../util/gui/markdowncheatsheetdialog.cpp | 50 + .../util/gui/markdowncheatsheetdialog.h | 20 + src/MdCharm/util/gui/newversioninfodialog.cpp | 51 + src/MdCharm/util/gui/newversioninfodialog.h | 34 + src/MdCharm/util/gui/noticedialog.cpp | 21 + src/MdCharm/util/gui/noticedialog.h | 23 + src/MdCharm/util/gui/noticedialog.ui | 57 + src/MdCharm/util/gui/renamefiledialog.cpp | 52 + src/MdCharm/util/gui/renamefiledialog.h | 28 + src/MdCharm/util/gui/renamefiledialog.ui | 41 + src/MdCharm/util/gui/selectencodingdialog.cpp | 61 + src/MdCharm/util/gui/selectencodingdialog.h | 38 + src/MdCharm/util/gui/selectencodingdialog.ui | 69 + src/MdCharm/util/gui/shortcutlineedit.cpp | 29 + src/MdCharm/util/gui/shortcutlineedit.h | 21 + src/MdCharm/util/gui/statusbarlabel.cpp | 13 + src/MdCharm/util/gui/statusbarlabel.h | 17 + src/MdCharm/util/odt/odtwriter.cpp | 798 + src/MdCharm/util/odt/odtwriter.h | 80 + src/MdCharm/util/rotationtoolbutton.cpp | 79 + src/MdCharm/util/rotationtoolbutton.h | 33 + src/MdCharm/util/sessionfileparser.cpp | 122 + src/MdCharm/util/sessionfileparser.h | 35 + src/MdCharm/util/spellcheck/spellchecker.cpp | 239 + src/MdCharm/util/spellcheck/spellchecker.h | 60 + .../spellcheck/spellcheckselectordialog.cpp | 53 + .../spellcheck/spellcheckselectordialog.h | 27 + .../spellcheck/spellcheckselectordialog.ui | 81 + src/MdCharm/util/syntax/hightlighter.cpp | 139 + src/MdCharm/util/syntax/hightlighter.h | 54 + src/MdCharm/util/test/qregularexpression.cpp | 1620 ++ src/MdCharm/util/test/qregularexpression.h | 234 + src/MdCharm/util/zip/zipwriter.cpp | 523 + src/MdCharm/util/zip/zipwriter.h | 64 + src/MdCharm/utils.cpp | 582 + src/MdCharm/utils.h | 180 + src/MdCharm/version.bat | 1 + src/MdCharm/version_h.py | 110 + src/lib/core.pro | 57 + src/lib/core/codesyntaxhighlighter.cpp | 310 + src/lib/core/codesyntaxhighlighter.h | 58 + src/lib/core/highlighter.cpp | 15 + src/lib/core/highlighter.h | 16 + src/lib/core/languagedefinationxmlparser.cpp | 837 + src/lib/core/languagedefinationxmlparser.h | 234 + src/lib/core/markdowntohtml.cpp | 111 + src/lib/core/markdowntohtml.h | 48 + src/lib/crashdump/BreakpadHandler.cpp | 219 + src/lib/crashdump/BreakpadHandler.h | 67 + src/lib/crashdump/crashdump.pro | 39 + .../linux/crash_generation/client_info.h | 44 + .../crash_generation_client.cc | 89 + .../crash_generation_client.h | 69 + .../crash_generation_server.cc | 467 + .../crash_generation_server.h | 133 + .../client/linux/handler/exception_handler.cc | 523 + .../client/linux/handler/exception_handler.h | 258 + .../handler/exception_handler_unittest.cc | 786 + .../gbreakpad/client/linux/log/log.cc | 48 + .../gbreakpad/client/linux/log/log.h | 41 + .../linux/minidump_writer/directory_reader.h | 105 + .../directory_reader_unittest.cc | 77 + .../linux/minidump_writer/line_reader.h | 130 + .../minidump_writer/line_reader_unittest.cc | 197 + .../minidump_writer/linux_core_dumper.cc | 234 + .../linux/minidump_writer/linux_core_dumper.h | 122 + .../linux_core_dumper_unittest.cc | 109 + .../linux/minidump_writer/linux_dumper.cc | 358 + .../linux/minidump_writer/linux_dumper.h | 235 + .../linux_dumper_unittest_helper.cc | 89 + .../minidump_writer/linux_ptrace_dumper.cc | 293 + .../minidump_writer/linux_ptrace_dumper.h | 92 + .../linux_ptrace_dumper_unittest.cc | 445 + .../minidump_extension_linux.h | 75 + .../linux/minidump_writer/minidump_writer.cc | 1349 ++ .../linux/minidump_writer/minidump_writer.h | 78 + .../minidump_writer_unittest.cc | 390 + .../client/mac/crash_generation/ConfigFile.h | 83 + .../client/mac/crash_generation/ConfigFile.mm | 190 + .../client/mac/crash_generation/Inspector.h | 166 + .../client/mac/crash_generation/Inspector.mm | 431 + .../mac/crash_generation/InspectorMain.mm | 65 + .../client/mac/crash_generation/client_info.h | 47 + .../crash_generation_client.cc | 72 + .../crash_generation_client.h | 65 + .../crash_generation_server.cc | 160 + .../crash_generation_server.h | 141 + .../client/mac/handler/breakpad_nlist_64.cc | 413 + .../client/mac/handler/breakpad_nlist_64.h | 47 + .../client/mac/handler/dynamic_images.cc | 578 + .../client/mac/handler/dynamic_images.h | 317 + .../client/mac/handler/exception_handler.cc | 830 + .../client/mac/handler/exception_handler.h | 277 + .../client/mac/handler/mach_vm_compat.h | 49 + .../client/mac/handler/minidump_generator.cc | 1432 ++ .../client/mac/handler/minidump_generator.h | 218 + .../minidump_test.xcodeproj/project.pbxproj | 841 + .../mac/handler/minidump_tests32-Info.plist | 20 + .../mac/handler/minidump_tests64-Info.plist | 22 + .../mac/handler/obj-cTestCases-Info.plist | 20 + .../mac/handler/protected_memory_allocator.cc | 92 + .../mac/handler/protected_memory_allocator.h | 85 + .../handler/testcases/DynamicImagesTests.cc | 79 + .../handler/testcases/DynamicImagesTests.h | 52 + .../handler/testcases/breakpad_nlist_test.cc | 106 + .../handler/testcases/breakpad_nlist_test.h | 62 + .../client/mac/handler/testcases/dwarftests.h | 46 + .../mac/handler/testcases/dwarftests.mm | 60 + .../testcases/testdata/dump_syms_dwarf_data | Bin 0 -> 702795 bytes .../testdata/dump_syms_i386_breakpad.sym | 5300 +++++ .../client/minidump_file_writer-inl.h | 97 + .../gbreakpad/client/minidump_file_writer.cc | 273 + .../gbreakpad/client/minidump_file_writer.h | 251 + .../client/minidump_file_writer_unittest.cc | 179 + .../windows/common/auto_critical_section.h | 63 + .../client/windows/common/ipc_protocol.h | 181 + .../windows/crash_generation/ReadMe.txt | 58 + .../windows/crash_generation/client_info.cc | 211 + .../windows/crash_generation/client_info.h | 176 + .../crash_generation_client.cc | 345 + .../crash_generation_client.h | 166 + .../crash_generation_server.cc | 893 + .../crash_generation_server.h | 299 + .../crash_generation/minidump_generator.cc | 309 + .../crash_generation/minidump_generator.h | 135 + .../windows/handler/exception_handler.cc | 895 + .../windows/handler/exception_handler.h | 425 + .../crashdump/gbreakpad/common/basictypes.h | 39 + .../crashdump/gbreakpad/common/byte_cursor.h | 263 + .../gbreakpad/common/byte_cursor_unittest.cc | 776 + .../crashdump/gbreakpad/common/convert_UTF.c | 533 + .../crashdump/gbreakpad/common/convert_UTF.h | 143 + .../gbreakpad/common/dwarf/bytereader-inl.h | 175 + .../gbreakpad/common/dwarf/bytereader.cc | 245 + .../gbreakpad/common/dwarf/bytereader.h | 310 + .../common/dwarf/bytereader_unittest.cc | 697 + .../gbreakpad/common/dwarf/cfi_assembler.cc | 198 + .../gbreakpad/common/dwarf/cfi_assembler.h | 269 + .../common/dwarf/dwarf2diehandler.cc | 196 + .../gbreakpad/common/dwarf/dwarf2diehandler.h | 365 + .../common/dwarf/dwarf2diehandler_unittest.cc | 579 + .../gbreakpad/common/dwarf/dwarf2enums.h | 650 + .../gbreakpad/common/dwarf/dwarf2reader.cc | 2338 ++ .../gbreakpad/common/dwarf/dwarf2reader.h | 1050 + .../common/dwarf/dwarf2reader_cfi_unittest.cc | 2452 ++ .../common/dwarf/dwarf2reader_die_unittest.cc | 486 + .../common/dwarf/dwarf2reader_test_common.h | 149 + .../gbreakpad/common/dwarf/functioninfo.cc | 229 + .../gbreakpad/common/dwarf/functioninfo.h | 188 + .../common/dwarf/line_state_machine.h | 61 + .../crashdump/gbreakpad/common/dwarf/types.h | 55 + .../gbreakpad/common/dwarf_cfi_to_module.cc | 247 + .../gbreakpad/common/dwarf_cfi_to_module.h | 196 + .../common/dwarf_cfi_to_module_unittest.cc | 294 + .../gbreakpad/common/dwarf_cu_to_module.cc | 936 + .../gbreakpad/common/dwarf_cu_to_module.h | 276 + .../common/dwarf_cu_to_module_unittest.cc | 1728 ++ .../gbreakpad/common/dwarf_line_to_module.cc | 135 + .../gbreakpad/common/dwarf_line_to_module.h | 179 + .../common/dwarf_line_to_module_unittest.cc | 343 + .../crashdump/gbreakpad/common/language.cc | 83 + src/lib/crashdump/gbreakpad/common/language.h | 84 + .../gbreakpad/common/linux/dump_symbols.cc | 826 + .../gbreakpad/common/linux/dump_symbols.h | 56 + .../common/linux/dump_symbols_unittest.cc | 166 + .../gbreakpad/common/linux/eintr_wrapper.h | 47 + .../gbreakpad/common/linux/elf_core_dump.cc | 179 + .../gbreakpad/common/linux/elf_core_dump.h | 153 + .../common/linux/elf_core_dump_unittest.cc | 245 + .../common/linux/elf_symbols_to_module.cc | 168 + .../common/linux/elf_symbols_to_module.h | 58 + .../linux/elf_symbols_to_module_unittest.cc | 370 + .../gbreakpad/common/linux/file_id.cc | 281 + .../gbreakpad/common/linux/file_id.h | 77 + .../common/linux/file_id_unittest.cc | 284 + .../common/linux/google_crashdump_uploader.cc | 199 + .../common/linux/google_crashdump_uploader.h | 98 + .../linux/google_crashdump_uploader_test.cc | 166 + .../gbreakpad/common/linux/guid_creator.cc | 104 + .../gbreakpad/common/linux/guid_creator.h | 48 + .../gbreakpad/common/linux/http_upload.cc | 206 + .../gbreakpad/common/linux/http_upload.h | 87 + .../gbreakpad/common/linux/libcurl_wrapper.cc | 223 + .../gbreakpad/common/linux/libcurl_wrapper.h | 83 + .../common/linux/linux_libc_support.h | 178 + .../linux/linux_libc_support_unittest.cc | 157 + .../common/linux/memory_mapped_file.cc | 108 + .../common/linux/memory_mapped_file.h | 86 + .../linux/memory_mapped_file_unittest.cc | 175 + .../gbreakpad/common/linux/safe_readlink.cc | 53 + .../gbreakpad/common/linux/safe_readlink.h | 65 + .../common/linux/safe_readlink_unittest.cc | 89 + .../gbreakpad/common/linux/synth_elf.cc | 204 + .../gbreakpad/common/linux/synth_elf.h | 164 + .../common/linux/synth_elf_unittest.cc | 265 + .../common/linux/tests/crash_generator.cc | 276 + .../common/linux/tests/crash_generator.h | 116 + .../gbreakpad/common/mac/Breakpad.xcconfig | 56 + .../common/mac/BreakpadDebug.xcconfig | 32 + .../common/mac/BreakpadRelease.xcconfig | 33 + .../gbreakpad/common/mac/GTMDefines.h | 241 + .../common/mac/GTMGarbageCollection.h | 72 + .../gbreakpad/common/mac/GTMLogger.h | 458 + .../gbreakpad/common/mac/GTMLogger.m | 445 + .../common/mac/HTTPMultipartUpload.h | 61 + .../common/mac/HTTPMultipartUpload.m | 209 + .../crashdump/gbreakpad/common/mac/MachIPC.h | 301 + .../crashdump/gbreakpad/common/mac/MachIPC.mm | 306 + .../common/mac/SimpleStringDictionary.h | 195 + .../common/mac/SimpleStringDictionary.mm | 133 + .../gbreakpad/common/mac/bootstrap_compat.cc | 42 + .../gbreakpad/common/mac/bootstrap_compat.h | 54 + .../crashdump/gbreakpad/common/mac/byteswap.h | 48 + .../gbreakpad/common/mac/dump_syms.h | 172 + .../gbreakpad/common/mac/dump_syms.mm | 496 + .../crashdump/gbreakpad/common/mac/file_id.cc | 101 + .../crashdump/gbreakpad/common/mac/file_id.h | 78 + .../gbreakpad/common/mac/macho_id.cc | 366 + .../crashdump/gbreakpad/common/mac/macho_id.h | 120 + .../gbreakpad/common/mac/macho_reader.cc | 530 + .../gbreakpad/common/mac/macho_reader.h | 459 + .../common/mac/macho_reader_unittest.cc | 1898 ++ .../gbreakpad/common/mac/macho_utilities.cc | 90 + .../gbreakpad/common/mac/macho_utilities.h | 92 + .../gbreakpad/common/mac/macho_walker.cc | 267 + .../gbreakpad/common/mac/macho_walker.h | 117 + .../common/mac/scoped_task_suspend-inl.h | 56 + .../gbreakpad/common/mac/string_utilities.cc | 84 + .../gbreakpad/common/mac/string_utilities.h | 52 + .../common/mac/testing/GTMSenTestCase.h | 1004 + .../common/mac/testing/GTMSenTestCase.m | 366 + src/lib/crashdump/gbreakpad/common/md5.cc | 251 + src/lib/crashdump/gbreakpad/common/md5.h | 27 + src/lib/crashdump/gbreakpad/common/memory.h | 218 + .../crashdump/gbreakpad/common/memory_range.h | 145 + .../gbreakpad/common/memory_range_unittest.cc | 193 + .../gbreakpad/common/memory_unittest.cc | 89 + src/lib/crashdump/gbreakpad/common/module.cc | 294 + src/lib/crashdump/gbreakpad/common/module.h | 321 + .../gbreakpad/common/module_unittest.cc | 490 + .../gbreakpad/common/stabs_reader.cc | 311 + .../crashdump/gbreakpad/common/stabs_reader.h | 325 + .../gbreakpad/common/stabs_reader_unittest.cc | 610 + .../gbreakpad/common/stabs_to_module.cc | 201 + .../gbreakpad/common/stabs_to_module.h | 143 + .../common/stabs_to_module_unittest.cc | 258 + .../gbreakpad/common/string_conversion.cc | 154 + .../gbreakpad/common/string_conversion.h | 66 + .../gbreakpad/common/test_assembler.cc | 359 + .../gbreakpad/common/test_assembler.h | 481 + .../common/test_assembler_unittest.cc | 1662 ++ .../common/testdata/func-line-pairing.h | 676 + .../gbreakpad/common/tests/auto_tempdir.h | 98 + .../gbreakpad/common/tests/file_utils.cc | 152 + .../gbreakpad/common/tests/file_utils.h | 52 + .../gbreakpad/common/windows/guid_string.cc | 76 + .../gbreakpad/common/windows/guid_string.h | 58 + .../gbreakpad/common/windows/http_upload.cc | 412 + .../gbreakpad/common/windows/http_upload.h | 126 + .../common/windows/pdb_source_line_writer.cc | 1001 + .../common/windows/pdb_source_line_writer.h | 238 + .../common/windows/string_utils-inl.h | 142 + .../gbreakpad/common/windows/string_utils.cc | 133 + src/lib/crashdump/gbreakpad/gbreakpad.pri | 49 + .../google_breakpad/common/breakpad_types.h | 83 + .../common/minidump_cpu_amd64.h | 235 + .../google_breakpad/common/minidump_cpu_arm.h | 151 + .../google_breakpad/common/minidump_cpu_ppc.h | 163 + .../common/minidump_cpu_ppc64.h | 129 + .../common/minidump_cpu_sparc.h | 158 + .../google_breakpad/common/minidump_cpu_x86.h | 174 + .../common/minidump_exception_linux.h | 85 + .../common/minidump_exception_mac.h | 195 + .../common/minidump_exception_solaris.h | 94 + .../common/minidump_exception_win32.h | 116 + .../google_breakpad/common/minidump_format.h | 794 + .../google_breakpad/common/minidump_size.h | 107 + .../processor/basic_source_line_resolver.h | 84 + .../google_breakpad/processor/call_stack.h | 77 + .../google_breakpad/processor/code_module.h | 94 + .../google_breakpad/processor/code_modules.h | 98 + .../processor/exploitability.h | 73 + .../processor/fast_source_line_resolver.h | 99 + .../google_breakpad/processor/memory_region.h | 76 + .../google_breakpad/processor/minidump.h | 1032 + .../processor/minidump_processor.h | 169 + .../google_breakpad/processor/process_state.h | 168 + .../processor/source_line_resolver_base.h | 118 + .../source_line_resolver_interface.h | 111 + .../google_breakpad/processor/stack_frame.h | 121 + .../processor/stack_frame_cpu.h | 243 + .../google_breakpad/processor/stackwalker.h | 203 + .../processor/symbol_supplier.h | 98 + .../google_breakpad/processor/system_info.h | 97 + .../gbreakpad/processor/address_map-inl.h | 93 + .../gbreakpad/processor/address_map.h | 85 + .../processor/address_map_unittest.cc | 196 + .../gbreakpad/processor/basic_code_module.h | 110 + .../gbreakpad/processor/basic_code_modules.cc | 123 + .../gbreakpad/processor/basic_code_modules.h | 85 + .../processor/basic_source_line_resolver.cc | 451 + .../basic_source_line_resolver_types.h | 161 + .../basic_source_line_resolver_unittest.cc | 407 + .../gbreakpad/processor/binarystream.cc | 123 + .../gbreakpad/processor/binarystream.h | 91 + .../processor/binarystream_unittest.cc | 432 + .../gbreakpad/processor/call_stack.cc | 53 + .../gbreakpad/processor/cfi_frame_info-inl.h | 119 + .../gbreakpad/processor/cfi_frame_info.cc | 182 + .../gbreakpad/processor/cfi_frame_info.h | 275 + .../processor/cfi_frame_info_unittest.cc | 545 + .../processor/contained_range_map-inl.h | 197 + .../gbreakpad/processor/contained_range_map.h | 150 + .../processor/contained_range_map_unittest.cc | 263 + .../gbreakpad/processor/disassembler_x86.cc | 241 + .../gbreakpad/processor/disassembler_x86.h | 126 + .../processor/disassembler_x86_unittest.cc | 244 + .../gbreakpad/processor/exploitability.cc | 105 + .../processor/exploitability_unittest.cc | 255 + .../gbreakpad/processor/exploitability_win.cc | 290 + .../gbreakpad/processor/exploitability_win.h | 55 + .../processor/fast_source_line_resolver.cc | 260 + .../fast_source_line_resolver_types.h | 179 + .../fast_source_line_resolver_unittest.cc | 477 + .../gbreakpad/processor/linked_ptr.h | 193 + .../crashdump/gbreakpad/processor/logging.cc | 112 + .../crashdump/gbreakpad/processor/logging.h | 162 + .../gbreakpad/processor/map_serializers-inl.h | 266 + .../gbreakpad/processor/map_serializers.h | 168 + .../processor/map_serializers_unittest.cc | 388 + .../crashdump/gbreakpad/processor/minidump.cc | 4219 ++++ .../gbreakpad/processor/minidump_dump.cc | 214 + .../gbreakpad/processor/minidump_dump_test | 36 + .../gbreakpad/processor/minidump_processor.cc | 1133 + .../processor/minidump_processor_unittest.cc | 380 + .../gbreakpad/processor/minidump_stackwalk.cc | 587 + .../minidump_stackwalk_machine_readable_test | 37 + .../processor/minidump_stackwalk_test | 37 + .../gbreakpad/processor/minidump_unittest.cc | 907 + .../gbreakpad/processor/module_comparer.cc | 297 + .../gbreakpad/processor/module_comparer.h | 98 + .../gbreakpad/processor/module_factory.h | 72 + .../gbreakpad/processor/module_serializer.cc | 200 + .../gbreakpad/processor/module_serializer.h | 127 + .../gbreakpad/processor/pathname_stripper.cc | 56 + .../gbreakpad/processor/pathname_stripper.h | 53 + .../processor/pathname_stripper_unittest.cc | 87 + .../processor/postfix_evaluator-inl.h | 363 + .../gbreakpad/processor/postfix_evaluator.h | 178 + .../processor/postfix_evaluator_unittest.cc | 399 + .../gbreakpad/processor/process_state.cc | 64 + .../gbreakpad/processor/proto/README | 20 + .../processor/proto/process_state.proto | 207 + .../gbreakpad/processor/range_map-inl.h | 210 + .../crashdump/gbreakpad/processor/range_map.h | 132 + .../gbreakpad/processor/range_map_unittest.cc | 553 + .../gbreakpad/processor/scoped_ptr.h | 335 + .../processor/simple_serializer-inl.h | 252 + .../gbreakpad/processor/simple_serializer.h | 63 + .../processor/simple_symbol_supplier.cc | 200 + .../processor/simple_symbol_supplier.h | 139 + .../processor/source_line_resolver_base.cc | 311 + .../source_line_resolver_base_types.h | 149 + .../gbreakpad/processor/stackwalker.cc | 243 + .../gbreakpad/processor/stackwalker_amd64.cc | 243 + .../gbreakpad/processor/stackwalker_amd64.h | 99 + .../processor/stackwalker_amd64_unittest.cc | 561 + .../gbreakpad/processor/stackwalker_arm.cc | 293 + .../gbreakpad/processor/stackwalker_arm.h | 107 + .../processor/stackwalker_arm_unittest.cc | 764 + .../gbreakpad/processor/stackwalker_ppc.cc | 146 + .../gbreakpad/processor/stackwalker_ppc.h | 79 + .../processor/stackwalker_selftest.cc | 425 + .../processor/stackwalker_selftest_sol.s | 111 + .../gbreakpad/processor/stackwalker_sparc.cc | 139 + .../gbreakpad/processor/stackwalker_sparc.h | 78 + .../processor/stackwalker_unittest_utils.h | 180 + .../gbreakpad/processor/stackwalker_x86.cc | 588 + .../gbreakpad/processor/stackwalker_x86.h | 114 + .../processor/stackwalker_x86_unittest.cc | 1034 + .../processor/static_address_map-inl.h | 71 + .../gbreakpad/processor/static_address_map.h | 78 + .../processor/static_address_map_unittest.cc | 235 + .../static_contained_range_map-inl.h | 92 + .../processor/static_contained_range_map.h | 96 + .../static_contained_range_map_unittest.cc | 321 + .../gbreakpad/processor/static_map-inl.h | 176 + .../gbreakpad/processor/static_map.h | 144 + .../processor/static_map_iterator-inl.h | 147 + .../gbreakpad/processor/static_map_iterator.h | 112 + .../processor/static_map_unittest.cc | 386 + .../processor/static_range_map-inl.h | 130 + .../gbreakpad/processor/static_range_map.h | 106 + .../processor/static_range_map_unittest.cc | 421 + .../gbreakpad/processor/synth_minidump.cc | 349 + .../gbreakpad/processor/synth_minidump.h | 369 + .../processor/synth_minidump_unittest.cc | 336 + .../processor/synth_minidump_unittest_data.h | 412 + .../crashdump/gbreakpad/processor/tokenize.cc | 76 + .../crashdump/gbreakpad/processor/tokenize.h | 61 + .../gbreakpad/processor/windows_frame_info.h | 196 + .../third_party/lss/linux_syscall_support.h | 3500 +++ src/lib/dllglobal.h | 12 + src/lib/hunspell/hunspell.pro | 56 + src/lib/hunspell/res/hunspell.rc | 38 + src/lib/hunspell/src/README | 23 + src/lib/hunspell/src/affentry.cxx | 962 + src/lib/hunspell/src/affentry.hxx | 136 + src/lib/hunspell/src/affixmgr.cxx | 4521 ++++ src/lib/hunspell/src/affixmgr.hxx | 250 + src/lib/hunspell/src/atypes.hxx | 107 + src/lib/hunspell/src/baseaffix.hxx | 28 + src/lib/hunspell/src/config.h | 208 + src/lib/hunspell/src/csutil.cxx | 5834 +++++ src/lib/hunspell/src/csutil.hxx | 220 + src/lib/hunspell/src/dictmgr.cxx | 180 + src/lib/hunspell/src/dictmgr.hxx | 36 + src/lib/hunspell/src/filemgr.cxx | 49 + src/lib/hunspell/src/filemgr.hxx | 25 + src/lib/hunspell/src/hashmgr.cxx | 928 + src/lib/hunspell/src/hashmgr.hxx | 69 + src/lib/hunspell/src/htypes.hxx | 32 + src/lib/hunspell/src/hunspell.cxx | 2006 ++ src/lib/hunspell/src/hunspell.dsp | 164 + src/lib/hunspell/src/hunspell.h | 95 + src/lib/hunspell/src/hunspell.hxx | 172 + src/lib/hunspell/src/hunvisapi.h | 18 + src/lib/hunspell/src/hunzip.cxx | 193 + src/lib/hunspell/src/hunzip.hxx | 45 + src/lib/hunspell/src/langnum.hxx | 38 + src/lib/hunspell/src/license.hunspell | 59 + src/lib/hunspell/src/license.myspell | 61 + src/lib/hunspell/src/phonet.cxx | 292 + src/lib/hunspell/src/phonet.hxx | 52 + src/lib/hunspell/src/replist.cxx | 87 + src/lib/hunspell/src/replist.hxx | 27 + src/lib/hunspell/src/suggestmgr.cxx | 2004 ++ src/lib/hunspell/src/suggestmgr.hxx | 111 + src/lib/hunspell/src/utf_info.cxx | 19676 ++++++++++++++++ src/lib/hunspell/src/w_char.hxx | 21 + src/lib/lib.pro | 15 + src/lib/markdown/examples/smartypants.c | 72 + src/lib/markdown/examples/sundown.c | 80 + src/lib/markdown/html/houdini.h | 37 + src/lib/markdown/html/houdini_href_e.c | 108 + src/lib/markdown/html/houdini_html_e.c | 84 + src/lib/markdown/html/html.c | 671 + src/lib/markdown/html/html.h | 77 + src/lib/markdown/html/html_smartypants.c | 389 + src/lib/markdown/html_block_names.txt | 25 + src/lib/markdown/markdown.pro | 46 + src/lib/markdown/src/autolink.c | 296 + src/lib/markdown/src/autolink.h | 51 + src/lib/markdown/src/buffer.c | 225 + src/lib/markdown/src/buffer.h | 96 + src/lib/markdown/src/html_blocks.h | 206 + src/lib/markdown/src/markdown.c | 2853 +++ src/lib/markdown/src/markdown.h | 148 + src/lib/markdown/src/stack.c | 81 + src/lib/markdown/src/stack.h | 29 + src/lib/multimarkdown/.gitignore | 40 + src/lib/multimarkdown/.gitmodules | 6 + src/lib/multimarkdown/.travis.yml | 15 + src/lib/multimarkdown/LICENSE | 75 + src/lib/multimarkdown/README.markdown | 11 + src/lib/multimarkdown/TODO.txt | 16 + src/lib/multimarkdown/clean_dfx.sh | 2 + src/lib/multimarkdown/peg-multimarkdown.pro | 7 + src/lib/multimarkdown/peg/.gitignore | 17 + src/lib/multimarkdown/peg/README.md | 17 + src/lib/multimarkdown/peg/compile.c | 724 + src/lib/multimarkdown/peg/examples/accept.c | 11 + src/lib/multimarkdown/peg/examples/accept.peg | 8 + src/lib/multimarkdown/peg/examples/accept.ref | 32 + src/lib/multimarkdown/peg/examples/basic.leg | 361 + src/lib/multimarkdown/peg/examples/basic.ref | 10 + src/lib/multimarkdown/peg/examples/bench.bas | 8 + src/lib/multimarkdown/peg/examples/calc.leg | 46 + src/lib/multimarkdown/peg/examples/calc.ref | 3 + src/lib/multimarkdown/peg/examples/dc.c | 17 + src/lib/multimarkdown/peg/examples/dc.peg | 27 + src/lib/multimarkdown/peg/examples/dc.ref | 1 + src/lib/multimarkdown/peg/examples/dcv.c | 20 + src/lib/multimarkdown/peg/examples/dcv.peg | 34 + src/lib/multimarkdown/peg/examples/dcv.ref | 3 + .../multimarkdown/peg/examples/fibonacci.bas | 17 + src/lib/multimarkdown/peg/examples/left.c | 17 + src/lib/multimarkdown/peg/examples/left.peg | 3 + src/lib/multimarkdown/peg/examples/localctx.c | 13 + .../multimarkdown/peg/examples/localctx.ref | 10 + src/lib/multimarkdown/peg/examples/rule.c | 11 + src/lib/multimarkdown/peg/examples/rule.peg | 8 + src/lib/multimarkdown/peg/examples/rule.ref | 32 + .../multimarkdown/peg/examples/username.leg | 14 + src/lib/multimarkdown/peg/examples/wc.leg | 22 + src/lib/multimarkdown/peg/examples/wc.ref | 55 + src/lib/multimarkdown/peg/leg.c | 1213 + src/lib/multimarkdown/peg/leg.leg | 292 + src/lib/multimarkdown/peg/leg.pro | 21 + src/lib/multimarkdown/peg/peg.c | 173 + src/lib/multimarkdown/peg/peg.gyp | 81 + src/lib/multimarkdown/peg/peg.peg | 77 + src/lib/multimarkdown/peg/peg.peg-c | 915 + src/lib/multimarkdown/peg/peg.pro | 21 + src/lib/multimarkdown/peg/tree.c | 357 + src/lib/multimarkdown/peg/tree.h | 108 + src/lib/multimarkdown/peg/version.h | 3 + src/lib/multimarkdown/peg/win/getopt.c | 230 + src/lib/multimarkdown/peg/win/getopt.h | 134 + src/lib/multimarkdown/peg/win/libgen.h | 9 + src/lib/multimarkdown/peg/win/unistd.h | 6 + src/lib/multimarkdown/scripts/mmd | 22 + src/lib/multimarkdown/scripts/mmd2all | 36 + src/lib/multimarkdown/scripts/mmd2odf | 22 + src/lib/multimarkdown/scripts/mmd2opml | 22 + src/lib/multimarkdown/scripts/mmd2pdf | 72 + src/lib/multimarkdown/scripts/mmd2tex | 22 + src/lib/multimarkdown/src/GLibFacade.c | 213 + src/lib/multimarkdown/src/GLibFacade.h | 67 + src/lib/multimarkdown/src/MarkdownMacPrefix.h | 15 + src/lib/multimarkdown/src/glib.h | 11 + src/lib/multimarkdown/src/main.c | 14 + src/lib/multimarkdown/src/markdown.c | 344 + src/lib/multimarkdown/src/markdown_lib.c | 266 + src/lib/multimarkdown/src/markdown_lib.h | 52 + src/lib/multimarkdown/src/markdown_output.c | 2838 +++ src/lib/multimarkdown/src/markdown_parser.bat | 3 + src/lib/multimarkdown/src/markdown_parser.leg | 1358 ++ src/lib/multimarkdown/src/markdown_parser.sh | 7 + src/lib/multimarkdown/src/markdown_peg.h | 143 + src/lib/multimarkdown/src/multimarkdown.pro | 45 + .../multimarkdown/src/multimarkdown_lib.pri | 28 + .../multimarkdown/src/multimarkdown_lib.pro | 49 + src/lib/multimarkdown/src/odf.c | 185 + src/lib/multimarkdown/src/odf.h | 11 + src/lib/multimarkdown/src/parsing_functions.c | 222 + src/lib/multimarkdown/src/parsing_functions.h | 17 + src/lib/multimarkdown/src/utility_functions.c | 549 + src/lib/multimarkdown/src/utility_functions.h | 93 + src/lib/multimarkdown/src/win/getopt.c | 230 + src/lib/multimarkdown/src/win/getopt.h | 152 + src/lib/multimarkdown/src/win/libgen.h | 9 + src/lib/multimarkdown/src/win/stdbool.h | 10 + src/lib/multimarkdown/src/win/unistd.h | 45 + src/lib/multimarkdown/update_sm.sh | 4 + src/lib/pcre/AUTHORS | 45 + src/lib/pcre/COPYING | 5 + src/lib/pcre/LICENCE | 92 + src/lib/pcre/config.h | 34 + src/lib/pcre/import_from_pcre_tarball.sh | 154 + src/lib/pcre/pcre.h | 503 + src/lib/pcre/pcre.pro | 39 + src/lib/pcre/pcre16_byte_order.c | 45 + src/lib/pcre/pcre16_chartables.c | 45 + src/lib/pcre/pcre16_compile.c | 45 + src/lib/pcre/pcre16_config.c | 45 + src/lib/pcre/pcre16_dfa_exec.c | 45 + src/lib/pcre/pcre16_exec.c | 45 + src/lib/pcre/pcre16_fullinfo.c | 45 + src/lib/pcre/pcre16_get.c | 45 + src/lib/pcre/pcre16_globals.c | 45 + src/lib/pcre/pcre16_jit_compile.c | 45 + src/lib/pcre/pcre16_maketables.c | 45 + src/lib/pcre/pcre16_newline.c | 45 + src/lib/pcre/pcre16_ord2utf16.c | 95 + src/lib/pcre/pcre16_refcount.c | 45 + src/lib/pcre/pcre16_string_utils.c | 45 + src/lib/pcre/pcre16_study.c | 45 + src/lib/pcre/pcre16_tables.c | 45 + src/lib/pcre/pcre16_ucd.c | 45 + src/lib/pcre/pcre16_utf16_utils.c | 129 + src/lib/pcre/pcre16_valid_utf16.c | 146 + src/lib/pcre/pcre16_version.c | 45 + src/lib/pcre/pcre16_xclass.c | 45 + src/lib/pcre/pcre_byte_order.c | 288 + src/lib/pcre/pcre_chartables.c | 198 + src/lib/pcre/pcre_compile.c | 8162 +++++++ src/lib/pcre/pcre_config.c | 170 + src/lib/pcre/pcre_dfa_exec.c | 3490 +++ src/lib/pcre/pcre_exec.c | 6960 ++++++ src/lib/pcre/pcre_fullinfo.c | 202 + src/lib/pcre/pcre_get.c | 587 + src/lib/pcre/pcre_globals.c | 84 + src/lib/pcre/pcre_internal.h | 2332 ++ src/lib/pcre/pcre_jit_compile.c | 6915 ++++++ src/lib/pcre/pcre_maketables.c | 148 + src/lib/pcre/pcre_newline.c | 184 + src/lib/pcre/pcre_ord2utf8.c | 97 + src/lib/pcre/pcre_refcount.c | 89 + src/lib/pcre/pcre_string_utils.c | 168 + src/lib/pcre/pcre_study.c | 1527 ++ src/lib/pcre/pcre_tables.c | 568 + src/lib/pcre/pcre_ucd.c | 2981 +++ src/lib/pcre/pcre_valid_utf8.c | 299 + src/lib/pcre/pcre_version.c | 95 + src/lib/pcre/pcre_xclass.c | 198 + src/lib/pcre/sljit/sljitConfig.h | 96 + src/lib/pcre/sljit/sljitConfigInternal.h | 424 + src/lib/pcre/sljit/sljitExecAllocator.c | 277 + src/lib/pcre/sljit/sljitLir.c | 1594 ++ src/lib/pcre/sljit/sljitLir.h | 853 + src/lib/pcre/sljit/sljitNativeARM_Thumb2.c | 1913 ++ src/lib/pcre/sljit/sljitNativeARM_v5.c | 2424 ++ src/lib/pcre/sljit/sljitNativeMIPS_32.c | 405 + src/lib/pcre/sljit/sljitNativeMIPS_common.c | 1829 ++ src/lib/pcre/sljit/sljitNativePPC_32.c | 262 + src/lib/pcre/sljit/sljitNativePPC_64.c | 428 + src/lib/pcre/sljit/sljitNativePPC_common.c | 1872 ++ src/lib/pcre/sljit/sljitNativeX86_32.c | 517 + src/lib/pcre/sljit/sljitNativeX86_64.c | 842 + src/lib/pcre/sljit/sljitNativeX86_common.c | 2858 +++ src/lib/pcre/sljit/sljitUtils.c | 248 + src/lib/pcre/ucp.h | 165 + src/lib/rapidxml/rapidxml.hpp | 2596 ++ src/lib/rapidxml/rapidxml.pro | 7 + src/lib/rapidxml/rapidxml_iterators.hpp | 174 + src/lib/rapidxml/rapidxml_print.hpp | 421 + src/lib/rapidxml/rapidxml_utils.hpp | 122 + src/lib/zlib/README.md | 1 + src/lib/zlib/zlib.pro | 35 + src/lib/zlib/zlib/adler32.c | 169 + src/lib/zlib/zlib/compress.c | 80 + src/lib/zlib/zlib/crc32.c | 442 + src/lib/zlib/zlib/crc32.h | 441 + src/lib/zlib/zlib/deflate.c | 1834 ++ src/lib/zlib/zlib/deflate.h | 342 + src/lib/zlib/zlib/example.c | 565 + src/lib/zlib/zlib/gzclose.c | 25 + src/lib/zlib/zlib/gzguts.h | 149 + src/lib/zlib/zlib/gzlib.c | 537 + src/lib/zlib/zlib/gzread.c | 653 + src/lib/zlib/zlib/gzwrite.c | 531 + src/lib/zlib/zlib/infback.c | 632 + src/lib/zlib/zlib/inffast.c | 340 + src/lib/zlib/zlib/inffast.h | 11 + src/lib/zlib/zlib/inffixed.h | 94 + src/lib/zlib/zlib/inflate.c | 1480 ++ src/lib/zlib/zlib/inflate.h | 122 + src/lib/zlib/zlib/inftrees.c | 330 + src/lib/zlib/zlib/inftrees.h | 62 + src/lib/zlib/zlib/minigzip.c | 440 + src/lib/zlib/zlib/trees.c | 1244 + src/lib/zlib/zlib/trees.h | 128 + src/lib/zlib/zlib/uncompr.c | 59 + src/lib/zlib/zlib/zconf.h | 428 + src/lib/zlib/zlib/zconf.h.cmakein | 430 + src/lib/zlib/zlib/zconf.h.in | 428 + src/lib/zlib/zlib/zlib.3 | 151 + src/lib/zlib/zlib/zlib.h | 1622 ++ src/lib/zlib/zlib/zlib.pc.in | 13 + src/lib/zlib/zlib/zutil.c | 310 + src/lib/zlib/zlib/zutil.h | 275 + src/res/MdCharm.qrc | 73 + src/res/conf/add.png | Bin 0 -> 1041 bytes src/res/conf/delete.png | Bin 0 -> 1754 bytes src/res/conf/env.png | Bin 0 -> 2864 bytes src/res/conf/styles.png | Bin 0 -> 2507 bytes src/res/conf/text_editor.png | Bin 0 -> 1201 bytes src/res/config.png | Bin 0 -> 2657 bytes src/res/copy.png | Bin 0 -> 1563 bytes src/res/cut.png | Bin 0 -> 2055 bytes src/res/find.png | Bin 0 -> 2558 bytes src/res/find_close.png | Bin 0 -> 1011 bytes src/res/help.png | Bin 0 -> 2229 bytes src/res/highlighter/bash.xml | 91 + src/res/highlighter/cpp.xml | 154 + src/res/highlighter/cs.xml | 176 + src/res/highlighter/css.xml | 139 + src/res/highlighter/diff.xml | 50 + src/res/highlighter/http.xml | 50 + src/res/highlighter/ini.xml | 54 + src/res/highlighter/java.xml | 129 + src/res/highlighter/javascript.xml | 141 + src/res/highlighter/json.xml | 77 + src/res/highlighter/markdown.xml | 67 + src/res/highlighter/perl.xml | 414 + src/res/highlighter/php.xml | 225 + src/res/highlighter/python.xml | 173 + src/res/highlighter/ruby.xml | 260 + src/res/highlighter/sql.xml | 285 + src/res/highlighter/xml.xml | 131 + src/res/jquery.js | 167 + src/res/markdown/bold.png | Bin 0 -> 345 bytes src/res/markdown/code.png | Bin 0 -> 314 bytes src/res/markdown/code_syntax_notice.html | 8 + src/res/markdown/code_syntax_patch.css | 1 + src/res/markdown/default.css | 1 + src/res/markdown/italic.png | Bin 0 -> 288 bytes src/res/markdown/link.png | Bin 0 -> 2212 bytes src/res/markdown/markdown.html | 14 + src/res/markdown/markdown.ico | Bin 0 -> 137969 bytes src/res/markdown/markdown.js | 1 + src/res/markdown/markdown128.png | Bin 0 -> 5007 bytes src/res/markdown/markdown16.png | Bin 0 -> 632 bytes src/res/markdown/markdown32.png | Bin 0 -> 1670 bytes src/res/markdown/markdown48.png | Bin 0 -> 3092 bytes src/res/markdown/markdown64.png | Bin 0 -> 3410 bytes src/res/markdown/markdown_cheat_sheet.md | 271 + src/res/markdown/picture.png | Bin 0 -> 1256 bytes src/res/markdown/quote.png | Bin 0 -> 238 bytes src/res/markdown/read_mode.png | Bin 0 -> 873 bytes src/res/markdown/strike-through.png | Bin 0 -> 424 bytes src/res/markdown/tab.png | Bin 0 -> 206 bytes src/res/markdown/untab.png | Bin 0 -> 224 bytes src/res/markdown/write_mode.png | Bin 0 -> 918 bytes src/res/markdown/write_read.png | Bin 0 -> 817 bytes src/res/mdcharm-splash.png | Bin 0 -> 116792 bytes src/res/mdcharm.ico | Bin 0 -> 133343 bytes src/res/mdcharm.png | Bin 0 -> 1912 bytes src/res/modified.png | Bin 0 -> 620 bytes src/res/new.png | Bin 0 -> 1265 bytes src/res/next.png | Bin 0 -> 1180 bytes src/res/open.png | Bin 0 -> 1343 bytes src/res/open_dir.png | Bin 0 -> 1074 bytes src/res/paste.png | Bin 0 -> 1681 bytes src/res/prev.png | Bin 0 -> 1168 bytes src/res/print.png | Bin 0 -> 1477 bytes src/res/redo.png | Bin 0 -> 1351 bytes src/res/save.png | Bin 0 -> 1458 bytes src/res/saveas.png | Bin 0 -> 2194 bytes src/res/spell_check.png | Bin 0 -> 1795 bytes src/res/start_here_page/StartHerePage.css | 33 + src/res/start_here_page/StartHerePage.html | 36 + src/res/start_here_page/StartHerePage.js | 26 + src/res/start_here_page/issue.png | Bin 0 -> 1905 bytes src/res/undo.png | Bin 0 -> 1413 bytes src/res/unmodified.png | Bin 0 -> 619 bytes 804 files changed, 257860 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 ReadMe.md create mode 100644 src/MdCharm.pro create mode 100644 src/MdCharm/MdCharm.pro create mode 100644 src/MdCharm/ReadMe.md create mode 100644 src/MdCharm/about/aboutdialog.ui create mode 100644 src/MdCharm/about/aboutmdcharmdialog.cpp create mode 100644 src/MdCharm/about/aboutmdcharmdialog.h create mode 100644 src/MdCharm/baseeditor/baseautocompleter.cpp create mode 100644 src/MdCharm/baseeditor/baseautocompleter.h create mode 100644 src/MdCharm/baseeditor/baseeditor.cpp create mode 100644 src/MdCharm/baseeditor/baseeditor.h create mode 100644 src/MdCharm/baseeditor/markdownautocompleter.cpp create mode 100644 src/MdCharm/baseeditor/markdownautocompleter.h create mode 100644 src/MdCharm/baseeditor/markdowneditor.cpp create mode 100644 src/MdCharm/baseeditor/markdowneditor.h create mode 100644 src/MdCharm/basewebview/basewebview.cpp create mode 100644 src/MdCharm/basewebview/basewebview.h create mode 100644 src/MdCharm/basewebview/markdownwebview.cpp create mode 100644 src/MdCharm/basewebview/markdownwebview.h create mode 100644 src/MdCharm/browereditareawidget.cpp create mode 100644 src/MdCharm/browereditareawidget.h create mode 100644 src/MdCharm/conf/configuredialog.cpp create mode 100644 src/MdCharm/conf/configuredialog.h create mode 100644 src/MdCharm/conf/environmentpage.ui create mode 100644 src/MdCharm/conf/pages.cpp create mode 100644 src/MdCharm/conf/pages.h create mode 100644 src/MdCharm/conf/stylespage.ui create mode 100644 src/MdCharm/conf/texteditorpage.ui create mode 100644 src/MdCharm/configuration.cpp create mode 100644 src/MdCharm/configuration.h create mode 100644 src/MdCharm/dock/projectdockwidget.cpp create mode 100644 src/MdCharm/dock/projectdockwidget.h create mode 100644 src/MdCharm/dock/projectdockwidget.ui create mode 100644 src/MdCharm/editareatabwidget.cpp create mode 100644 src/MdCharm/editareatabwidget.h create mode 100644 src/MdCharm/editareatabwidgetmanager.cpp create mode 100644 src/MdCharm/editareatabwidgetmanager.h create mode 100644 src/MdCharm/editareawidget.cpp create mode 100644 src/MdCharm/editareawidget.h create mode 100644 src/MdCharm/main.cpp create mode 100644 src/MdCharm/markdowneditareawidget.cpp create mode 100644 src/MdCharm/markdowneditareawidget.h create mode 100644 src/MdCharm/mdcharmapplication.cpp create mode 100644 src/MdCharm/mdcharmapplication.h create mode 100644 src/MdCharm/mdcharmform.cpp create mode 100644 src/MdCharm/mdcharmform.h create mode 100644 src/MdCharm/network/checkupdates.cpp create mode 100644 src/MdCharm/network/checkupdates.h create mode 100644 src/MdCharm/resource.cpp create mode 100644 src/MdCharm/resource.h create mode 100644 src/MdCharm/util/filesystemmodel.cpp create mode 100644 src/MdCharm/util/filesystemmodel.h create mode 100644 src/MdCharm/util/filesystemtreeview.cpp create mode 100644 src/MdCharm/util/filesystemtreeview.h create mode 100644 src/MdCharm/util/gui/addnewfiledialog.cpp create mode 100644 src/MdCharm/util/gui/addnewfiledialog.h create mode 100644 src/MdCharm/util/gui/addnewfiledialog.ui create mode 100644 src/MdCharm/util/gui/exportdialog.cpp create mode 100644 src/MdCharm/util/gui/exportdialog.h create mode 100644 src/MdCharm/util/gui/exportdialog.ui create mode 100644 src/MdCharm/util/gui/exportdirectorydialog.cpp create mode 100644 src/MdCharm/util/gui/exportdirectorydialog.h create mode 100644 src/MdCharm/util/gui/exportdirectorydialog.ui create mode 100644 src/MdCharm/util/gui/findandreplace.cpp create mode 100644 src/MdCharm/util/gui/findandreplace.h create mode 100644 src/MdCharm/util/gui/findandreplaceform.ui create mode 100644 src/MdCharm/util/gui/gotodialog.cpp create mode 100644 src/MdCharm/util/gui/gotodialog.h create mode 100644 src/MdCharm/util/gui/gotodialog.ui create mode 100644 src/MdCharm/util/gui/insertcodedialog.cpp create mode 100644 src/MdCharm/util/gui/insertcodedialog.h create mode 100644 src/MdCharm/util/gui/insertcodedialog.ui create mode 100644 src/MdCharm/util/gui/insertlinkorpicturedialog.cpp create mode 100644 src/MdCharm/util/gui/insertlinkorpicturedialog.h create mode 100644 src/MdCharm/util/gui/insertlinkorpicturedialog.ui create mode 100644 src/MdCharm/util/gui/markdowncheatsheetdialog.cpp create mode 100644 src/MdCharm/util/gui/markdowncheatsheetdialog.h create mode 100644 src/MdCharm/util/gui/newversioninfodialog.cpp create mode 100644 src/MdCharm/util/gui/newversioninfodialog.h create mode 100644 src/MdCharm/util/gui/noticedialog.cpp create mode 100644 src/MdCharm/util/gui/noticedialog.h create mode 100644 src/MdCharm/util/gui/noticedialog.ui create mode 100644 src/MdCharm/util/gui/renamefiledialog.cpp create mode 100644 src/MdCharm/util/gui/renamefiledialog.h create mode 100644 src/MdCharm/util/gui/renamefiledialog.ui create mode 100644 src/MdCharm/util/gui/selectencodingdialog.cpp create mode 100644 src/MdCharm/util/gui/selectencodingdialog.h create mode 100644 src/MdCharm/util/gui/selectencodingdialog.ui create mode 100644 src/MdCharm/util/gui/shortcutlineedit.cpp create mode 100644 src/MdCharm/util/gui/shortcutlineedit.h create mode 100644 src/MdCharm/util/gui/statusbarlabel.cpp create mode 100644 src/MdCharm/util/gui/statusbarlabel.h create mode 100644 src/MdCharm/util/odt/odtwriter.cpp create mode 100644 src/MdCharm/util/odt/odtwriter.h create mode 100644 src/MdCharm/util/rotationtoolbutton.cpp create mode 100644 src/MdCharm/util/rotationtoolbutton.h create mode 100644 src/MdCharm/util/sessionfileparser.cpp create mode 100644 src/MdCharm/util/sessionfileparser.h create mode 100644 src/MdCharm/util/spellcheck/spellchecker.cpp create mode 100644 src/MdCharm/util/spellcheck/spellchecker.h create mode 100644 src/MdCharm/util/spellcheck/spellcheckselectordialog.cpp create mode 100644 src/MdCharm/util/spellcheck/spellcheckselectordialog.h create mode 100644 src/MdCharm/util/spellcheck/spellcheckselectordialog.ui create mode 100644 src/MdCharm/util/syntax/hightlighter.cpp create mode 100644 src/MdCharm/util/syntax/hightlighter.h create mode 100644 src/MdCharm/util/test/qregularexpression.cpp create mode 100644 src/MdCharm/util/test/qregularexpression.h create mode 100644 src/MdCharm/util/zip/zipwriter.cpp create mode 100644 src/MdCharm/util/zip/zipwriter.h create mode 100644 src/MdCharm/utils.cpp create mode 100644 src/MdCharm/utils.h create mode 100644 src/MdCharm/version.bat create mode 100644 src/MdCharm/version_h.py create mode 100644 src/lib/core.pro create mode 100644 src/lib/core/codesyntaxhighlighter.cpp create mode 100644 src/lib/core/codesyntaxhighlighter.h create mode 100644 src/lib/core/highlighter.cpp create mode 100644 src/lib/core/highlighter.h create mode 100644 src/lib/core/languagedefinationxmlparser.cpp create mode 100644 src/lib/core/languagedefinationxmlparser.h create mode 100644 src/lib/core/markdowntohtml.cpp create mode 100644 src/lib/core/markdowntohtml.h create mode 100644 src/lib/crashdump/BreakpadHandler.cpp create mode 100644 src/lib/crashdump/BreakpadHandler.h create mode 100644 src/lib/crashdump/crashdump.pro create mode 100644 src/lib/crashdump/gbreakpad/client/linux/crash_generation/client_info.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/log/log.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/log/log.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_extension_linux.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.cc create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.h create mode 100644 src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.mm create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.mm create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/InspectorMain.mm create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/client_info.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/mach_vm_compat.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests32-Info.plist create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests64-Info.plist create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/obj-cTestCases-Info.plist create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.cc create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.h create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.mm create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/testdata/dump_syms_dwarf_data create mode 100644 src/lib/crashdump/gbreakpad/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym create mode 100644 src/lib/crashdump/gbreakpad/client/minidump_file_writer-inl.h create mode 100644 src/lib/crashdump/gbreakpad/client/minidump_file_writer.cc create mode 100644 src/lib/crashdump/gbreakpad/client/minidump_file_writer.h create mode 100644 src/lib/crashdump/gbreakpad/client/minidump_file_writer_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/client/windows/common/auto_critical_section.h create mode 100644 src/lib/crashdump/gbreakpad/client/windows/common/ipc_protocol.h create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/ReadMe.txt create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.cc create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.h create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.cc create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.h create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.cc create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.h create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.cc create mode 100644 src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.h create mode 100644 src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.cc create mode 100644 src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.h create mode 100644 src/lib/crashdump/gbreakpad/common/basictypes.h create mode 100644 src/lib/crashdump/gbreakpad/common/byte_cursor.h create mode 100644 src/lib/crashdump/gbreakpad/common/byte_cursor_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/convert_UTF.c create mode 100644 src/lib/crashdump/gbreakpad/common/convert_UTF.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/bytereader-inl.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/bytereader.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/bytereader.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/bytereader_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2enums.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_cfi_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_die_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_test_common.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/line_state_machine.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf/types.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.cc create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.h create mode 100644 src/lib/crashdump/gbreakpad/common/dwarf_line_to_module_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/language.cc create mode 100644 src/lib/crashdump/gbreakpad/common/language.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/dump_symbols.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/dump_symbols.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/dump_symbols_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/eintr_wrapper.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/elf_core_dump_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/file_id.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/file_id.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/file_id_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader_test.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/guid_creator.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/guid_creator.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/http_upload.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/http_upload.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/linux_libc_support.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/linux_libc_support_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/safe_readlink.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/safe_readlink.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/safe_readlink_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/synth_elf.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/synth_elf.h create mode 100644 src/lib/crashdump/gbreakpad/common/linux/synth_elf_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.cc create mode 100644 src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/Breakpad.xcconfig create mode 100644 src/lib/crashdump/gbreakpad/common/mac/BreakpadDebug.xcconfig create mode 100644 src/lib/crashdump/gbreakpad/common/mac/BreakpadRelease.xcconfig create mode 100644 src/lib/crashdump/gbreakpad/common/mac/GTMDefines.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/GTMGarbageCollection.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/GTMLogger.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/GTMLogger.m create mode 100644 src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.m create mode 100644 src/lib/crashdump/gbreakpad/common/mac/MachIPC.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/MachIPC.mm create mode 100644 src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.mm create mode 100644 src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/byteswap.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/dump_syms.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/dump_syms.mm create mode 100644 src/lib/crashdump/gbreakpad/common/mac/file_id.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/file_id.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_id.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_id.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_reader.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_reader.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_reader_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_utilities.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_utilities.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_walker.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/macho_walker.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/scoped_task_suspend-inl.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/string_utilities.cc create mode 100644 src/lib/crashdump/gbreakpad/common/mac/string_utilities.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.h create mode 100644 src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.m create mode 100644 src/lib/crashdump/gbreakpad/common/md5.cc create mode 100644 src/lib/crashdump/gbreakpad/common/md5.h create mode 100644 src/lib/crashdump/gbreakpad/common/memory.h create mode 100644 src/lib/crashdump/gbreakpad/common/memory_range.h create mode 100644 src/lib/crashdump/gbreakpad/common/memory_range_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/memory_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/module.cc create mode 100644 src/lib/crashdump/gbreakpad/common/module.h create mode 100644 src/lib/crashdump/gbreakpad/common/module_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/stabs_reader.cc create mode 100644 src/lib/crashdump/gbreakpad/common/stabs_reader.h create mode 100644 src/lib/crashdump/gbreakpad/common/stabs_reader_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/stabs_to_module.cc create mode 100644 src/lib/crashdump/gbreakpad/common/stabs_to_module.h create mode 100644 src/lib/crashdump/gbreakpad/common/stabs_to_module_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/string_conversion.cc create mode 100644 src/lib/crashdump/gbreakpad/common/string_conversion.h create mode 100644 src/lib/crashdump/gbreakpad/common/test_assembler.cc create mode 100644 src/lib/crashdump/gbreakpad/common/test_assembler.h create mode 100644 src/lib/crashdump/gbreakpad/common/test_assembler_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/common/testdata/func-line-pairing.h create mode 100644 src/lib/crashdump/gbreakpad/common/tests/auto_tempdir.h create mode 100644 src/lib/crashdump/gbreakpad/common/tests/file_utils.cc create mode 100644 src/lib/crashdump/gbreakpad/common/tests/file_utils.h create mode 100644 src/lib/crashdump/gbreakpad/common/windows/guid_string.cc create mode 100644 src/lib/crashdump/gbreakpad/common/windows/guid_string.h create mode 100644 src/lib/crashdump/gbreakpad/common/windows/http_upload.cc create mode 100644 src/lib/crashdump/gbreakpad/common/windows/http_upload.h create mode 100644 src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.cc create mode 100644 src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.h create mode 100644 src/lib/crashdump/gbreakpad/common/windows/string_utils-inl.h create mode 100644 src/lib/crashdump/gbreakpad/common/windows/string_utils.cc create mode 100644 src/lib/crashdump/gbreakpad/gbreakpad.pri create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/breakpad_types.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_amd64.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_arm.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc64.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_sparc.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_x86.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_linux.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_mac.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_solaris.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_win32.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_format.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_size.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/basic_source_line_resolver.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/call_stack.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/code_module.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/code_modules.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/exploitability.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/fast_source_line_resolver.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/memory_region.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump_processor.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/process_state.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_base.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_interface.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame_cpu.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/stackwalker.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/symbol_supplier.h create mode 100644 src/lib/crashdump/gbreakpad/google_breakpad/processor/system_info.h create mode 100644 src/lib/crashdump/gbreakpad/processor/address_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/address_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/address_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/basic_code_module.h create mode 100644 src/lib/crashdump/gbreakpad/processor/basic_code_modules.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/basic_code_modules.h create mode 100644 src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_types.h create mode 100644 src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/binarystream.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/binarystream.h create mode 100644 src/lib/crashdump/gbreakpad/processor/binarystream_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/call_stack.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/cfi_frame_info-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/cfi_frame_info.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/cfi_frame_info.h create mode 100644 src/lib/crashdump/gbreakpad/processor/cfi_frame_info_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/contained_range_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/contained_range_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/contained_range_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/disassembler_x86.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/disassembler_x86.h create mode 100644 src/lib/crashdump/gbreakpad/processor/disassembler_x86_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/exploitability.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/exploitability_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/exploitability_win.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/exploitability_win.h create mode 100644 src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_types.h create mode 100644 src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/linked_ptr.h create mode 100644 src/lib/crashdump/gbreakpad/processor/logging.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/logging.h create mode 100644 src/lib/crashdump/gbreakpad/processor/map_serializers-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/map_serializers.h create mode 100644 src/lib/crashdump/gbreakpad/processor/map_serializers_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_dump.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_dump_test create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_processor.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_processor_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_stackwalk.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_machine_readable_test create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_test create mode 100644 src/lib/crashdump/gbreakpad/processor/minidump_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/module_comparer.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/module_comparer.h create mode 100644 src/lib/crashdump/gbreakpad/processor/module_factory.h create mode 100644 src/lib/crashdump/gbreakpad/processor/module_serializer.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/module_serializer.h create mode 100644 src/lib/crashdump/gbreakpad/processor/pathname_stripper.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/pathname_stripper.h create mode 100644 src/lib/crashdump/gbreakpad/processor/pathname_stripper_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/postfix_evaluator-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/postfix_evaluator.h create mode 100644 src/lib/crashdump/gbreakpad/processor/postfix_evaluator_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/process_state.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/proto/README create mode 100644 src/lib/crashdump/gbreakpad/processor/proto/process_state.proto create mode 100644 src/lib/crashdump/gbreakpad/processor/range_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/range_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/range_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/scoped_ptr.h create mode 100644 src/lib/crashdump/gbreakpad/processor/simple_serializer-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/simple_serializer.h create mode 100644 src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.h create mode 100644 src/lib/crashdump/gbreakpad/processor/source_line_resolver_base.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/source_line_resolver_base_types.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_amd64_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_arm.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_arm.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_arm_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_selftest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_selftest_sol.s create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_unittest_utils.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_x86.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_x86.h create mode 100644 src/lib/crashdump/gbreakpad/processor/stackwalker_x86_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/static_address_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_address_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_address_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/static_contained_range_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_contained_range_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_contained_range_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/static_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_map_iterator-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_map_iterator.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/static_range_map-inl.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_range_map.h create mode 100644 src/lib/crashdump/gbreakpad/processor/static_range_map_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/synth_minidump.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/synth_minidump.h create mode 100644 src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest_data.h create mode 100644 src/lib/crashdump/gbreakpad/processor/tokenize.cc create mode 100644 src/lib/crashdump/gbreakpad/processor/tokenize.h create mode 100644 src/lib/crashdump/gbreakpad/processor/windows_frame_info.h create mode 100644 src/lib/crashdump/gbreakpad/third_party/lss/linux_syscall_support.h create mode 100644 src/lib/dllglobal.h create mode 100644 src/lib/hunspell/hunspell.pro create mode 100644 src/lib/hunspell/res/hunspell.rc create mode 100644 src/lib/hunspell/src/README create mode 100644 src/lib/hunspell/src/affentry.cxx create mode 100644 src/lib/hunspell/src/affentry.hxx create mode 100644 src/lib/hunspell/src/affixmgr.cxx create mode 100644 src/lib/hunspell/src/affixmgr.hxx create mode 100644 src/lib/hunspell/src/atypes.hxx create mode 100644 src/lib/hunspell/src/baseaffix.hxx create mode 100644 src/lib/hunspell/src/config.h create mode 100644 src/lib/hunspell/src/csutil.cxx create mode 100644 src/lib/hunspell/src/csutil.hxx create mode 100644 src/lib/hunspell/src/dictmgr.cxx create mode 100644 src/lib/hunspell/src/dictmgr.hxx create mode 100644 src/lib/hunspell/src/filemgr.cxx create mode 100644 src/lib/hunspell/src/filemgr.hxx create mode 100644 src/lib/hunspell/src/hashmgr.cxx create mode 100644 src/lib/hunspell/src/hashmgr.hxx create mode 100644 src/lib/hunspell/src/htypes.hxx create mode 100644 src/lib/hunspell/src/hunspell.cxx create mode 100644 src/lib/hunspell/src/hunspell.dsp create mode 100644 src/lib/hunspell/src/hunspell.h create mode 100644 src/lib/hunspell/src/hunspell.hxx create mode 100644 src/lib/hunspell/src/hunvisapi.h create mode 100644 src/lib/hunspell/src/hunzip.cxx create mode 100644 src/lib/hunspell/src/hunzip.hxx create mode 100644 src/lib/hunspell/src/langnum.hxx create mode 100644 src/lib/hunspell/src/license.hunspell create mode 100644 src/lib/hunspell/src/license.myspell create mode 100644 src/lib/hunspell/src/phonet.cxx create mode 100644 src/lib/hunspell/src/phonet.hxx create mode 100644 src/lib/hunspell/src/replist.cxx create mode 100644 src/lib/hunspell/src/replist.hxx create mode 100644 src/lib/hunspell/src/suggestmgr.cxx create mode 100644 src/lib/hunspell/src/suggestmgr.hxx create mode 100644 src/lib/hunspell/src/utf_info.cxx create mode 100644 src/lib/hunspell/src/w_char.hxx create mode 100644 src/lib/lib.pro create mode 100644 src/lib/markdown/examples/smartypants.c create mode 100644 src/lib/markdown/examples/sundown.c create mode 100644 src/lib/markdown/html/houdini.h create mode 100644 src/lib/markdown/html/houdini_href_e.c create mode 100644 src/lib/markdown/html/houdini_html_e.c create mode 100644 src/lib/markdown/html/html.c create mode 100644 src/lib/markdown/html/html.h create mode 100644 src/lib/markdown/html/html_smartypants.c create mode 100644 src/lib/markdown/html_block_names.txt create mode 100644 src/lib/markdown/markdown.pro create mode 100644 src/lib/markdown/src/autolink.c create mode 100644 src/lib/markdown/src/autolink.h create mode 100644 src/lib/markdown/src/buffer.c create mode 100644 src/lib/markdown/src/buffer.h create mode 100644 src/lib/markdown/src/html_blocks.h create mode 100644 src/lib/markdown/src/markdown.c create mode 100644 src/lib/markdown/src/markdown.h create mode 100644 src/lib/markdown/src/stack.c create mode 100644 src/lib/markdown/src/stack.h create mode 100644 src/lib/multimarkdown/.gitignore create mode 100644 src/lib/multimarkdown/.gitmodules create mode 100644 src/lib/multimarkdown/.travis.yml create mode 100644 src/lib/multimarkdown/LICENSE create mode 100755 src/lib/multimarkdown/README.markdown create mode 100644 src/lib/multimarkdown/TODO.txt create mode 100755 src/lib/multimarkdown/clean_dfx.sh create mode 100644 src/lib/multimarkdown/peg-multimarkdown.pro create mode 100644 src/lib/multimarkdown/peg/.gitignore create mode 100644 src/lib/multimarkdown/peg/README.md create mode 100644 src/lib/multimarkdown/peg/compile.c create mode 100644 src/lib/multimarkdown/peg/examples/accept.c create mode 100644 src/lib/multimarkdown/peg/examples/accept.peg create mode 100644 src/lib/multimarkdown/peg/examples/accept.ref create mode 100644 src/lib/multimarkdown/peg/examples/basic.leg create mode 100644 src/lib/multimarkdown/peg/examples/basic.ref create mode 100644 src/lib/multimarkdown/peg/examples/bench.bas create mode 100644 src/lib/multimarkdown/peg/examples/calc.leg create mode 100644 src/lib/multimarkdown/peg/examples/calc.ref create mode 100644 src/lib/multimarkdown/peg/examples/dc.c create mode 100644 src/lib/multimarkdown/peg/examples/dc.peg create mode 100644 src/lib/multimarkdown/peg/examples/dc.ref create mode 100644 src/lib/multimarkdown/peg/examples/dcv.c create mode 100644 src/lib/multimarkdown/peg/examples/dcv.peg create mode 100644 src/lib/multimarkdown/peg/examples/dcv.ref create mode 100644 src/lib/multimarkdown/peg/examples/fibonacci.bas create mode 100644 src/lib/multimarkdown/peg/examples/left.c create mode 100644 src/lib/multimarkdown/peg/examples/left.peg create mode 100644 src/lib/multimarkdown/peg/examples/localctx.c create mode 100644 src/lib/multimarkdown/peg/examples/localctx.ref create mode 100644 src/lib/multimarkdown/peg/examples/rule.c create mode 100644 src/lib/multimarkdown/peg/examples/rule.peg create mode 100644 src/lib/multimarkdown/peg/examples/rule.ref create mode 100644 src/lib/multimarkdown/peg/examples/username.leg create mode 100644 src/lib/multimarkdown/peg/examples/wc.leg create mode 100644 src/lib/multimarkdown/peg/examples/wc.ref create mode 100644 src/lib/multimarkdown/peg/leg.c create mode 100644 src/lib/multimarkdown/peg/leg.leg create mode 100644 src/lib/multimarkdown/peg/leg.pro create mode 100644 src/lib/multimarkdown/peg/peg.c create mode 100644 src/lib/multimarkdown/peg/peg.gyp create mode 100644 src/lib/multimarkdown/peg/peg.peg create mode 100644 src/lib/multimarkdown/peg/peg.peg-c create mode 100644 src/lib/multimarkdown/peg/peg.pro create mode 100644 src/lib/multimarkdown/peg/tree.c create mode 100644 src/lib/multimarkdown/peg/tree.h create mode 100644 src/lib/multimarkdown/peg/version.h create mode 100644 src/lib/multimarkdown/peg/win/getopt.c create mode 100644 src/lib/multimarkdown/peg/win/getopt.h create mode 100644 src/lib/multimarkdown/peg/win/libgen.h create mode 100644 src/lib/multimarkdown/peg/win/unistd.h create mode 100755 src/lib/multimarkdown/scripts/mmd create mode 100755 src/lib/multimarkdown/scripts/mmd2all create mode 100755 src/lib/multimarkdown/scripts/mmd2odf create mode 100755 src/lib/multimarkdown/scripts/mmd2opml create mode 100755 src/lib/multimarkdown/scripts/mmd2pdf create mode 100755 src/lib/multimarkdown/scripts/mmd2tex create mode 100644 src/lib/multimarkdown/src/GLibFacade.c create mode 100644 src/lib/multimarkdown/src/GLibFacade.h create mode 100644 src/lib/multimarkdown/src/MarkdownMacPrefix.h create mode 100644 src/lib/multimarkdown/src/glib.h create mode 100644 src/lib/multimarkdown/src/main.c create mode 100644 src/lib/multimarkdown/src/markdown.c create mode 100644 src/lib/multimarkdown/src/markdown_lib.c create mode 100644 src/lib/multimarkdown/src/markdown_lib.h create mode 100644 src/lib/multimarkdown/src/markdown_output.c create mode 100644 src/lib/multimarkdown/src/markdown_parser.bat create mode 100644 src/lib/multimarkdown/src/markdown_parser.leg create mode 100755 src/lib/multimarkdown/src/markdown_parser.sh create mode 100644 src/lib/multimarkdown/src/markdown_peg.h create mode 100644 src/lib/multimarkdown/src/multimarkdown.pro create mode 100644 src/lib/multimarkdown/src/multimarkdown_lib.pri create mode 100644 src/lib/multimarkdown/src/multimarkdown_lib.pro create mode 100644 src/lib/multimarkdown/src/odf.c create mode 100644 src/lib/multimarkdown/src/odf.h create mode 100644 src/lib/multimarkdown/src/parsing_functions.c create mode 100644 src/lib/multimarkdown/src/parsing_functions.h create mode 100644 src/lib/multimarkdown/src/utility_functions.c create mode 100644 src/lib/multimarkdown/src/utility_functions.h create mode 100644 src/lib/multimarkdown/src/win/getopt.c create mode 100644 src/lib/multimarkdown/src/win/getopt.h create mode 100644 src/lib/multimarkdown/src/win/libgen.h create mode 100644 src/lib/multimarkdown/src/win/stdbool.h create mode 100644 src/lib/multimarkdown/src/win/unistd.h create mode 100755 src/lib/multimarkdown/update_sm.sh create mode 100644 src/lib/pcre/AUTHORS create mode 100644 src/lib/pcre/COPYING create mode 100644 src/lib/pcre/LICENCE create mode 100644 src/lib/pcre/config.h create mode 100644 src/lib/pcre/import_from_pcre_tarball.sh create mode 100644 src/lib/pcre/pcre.h create mode 100644 src/lib/pcre/pcre.pro create mode 100644 src/lib/pcre/pcre16_byte_order.c create mode 100644 src/lib/pcre/pcre16_chartables.c create mode 100644 src/lib/pcre/pcre16_compile.c create mode 100644 src/lib/pcre/pcre16_config.c create mode 100644 src/lib/pcre/pcre16_dfa_exec.c create mode 100644 src/lib/pcre/pcre16_exec.c create mode 100644 src/lib/pcre/pcre16_fullinfo.c create mode 100644 src/lib/pcre/pcre16_get.c create mode 100644 src/lib/pcre/pcre16_globals.c create mode 100644 src/lib/pcre/pcre16_jit_compile.c create mode 100644 src/lib/pcre/pcre16_maketables.c create mode 100644 src/lib/pcre/pcre16_newline.c create mode 100644 src/lib/pcre/pcre16_ord2utf16.c create mode 100644 src/lib/pcre/pcre16_refcount.c create mode 100644 src/lib/pcre/pcre16_string_utils.c create mode 100644 src/lib/pcre/pcre16_study.c create mode 100644 src/lib/pcre/pcre16_tables.c create mode 100644 src/lib/pcre/pcre16_ucd.c create mode 100644 src/lib/pcre/pcre16_utf16_utils.c create mode 100644 src/lib/pcre/pcre16_valid_utf16.c create mode 100644 src/lib/pcre/pcre16_version.c create mode 100644 src/lib/pcre/pcre16_xclass.c create mode 100644 src/lib/pcre/pcre_byte_order.c create mode 100644 src/lib/pcre/pcre_chartables.c create mode 100644 src/lib/pcre/pcre_compile.c create mode 100644 src/lib/pcre/pcre_config.c create mode 100644 src/lib/pcre/pcre_dfa_exec.c create mode 100644 src/lib/pcre/pcre_exec.c create mode 100644 src/lib/pcre/pcre_fullinfo.c create mode 100644 src/lib/pcre/pcre_get.c create mode 100644 src/lib/pcre/pcre_globals.c create mode 100644 src/lib/pcre/pcre_internal.h create mode 100644 src/lib/pcre/pcre_jit_compile.c create mode 100644 src/lib/pcre/pcre_maketables.c create mode 100644 src/lib/pcre/pcre_newline.c create mode 100644 src/lib/pcre/pcre_ord2utf8.c create mode 100644 src/lib/pcre/pcre_refcount.c create mode 100644 src/lib/pcre/pcre_string_utils.c create mode 100644 src/lib/pcre/pcre_study.c create mode 100644 src/lib/pcre/pcre_tables.c create mode 100644 src/lib/pcre/pcre_ucd.c create mode 100644 src/lib/pcre/pcre_valid_utf8.c create mode 100644 src/lib/pcre/pcre_version.c create mode 100644 src/lib/pcre/pcre_xclass.c create mode 100644 src/lib/pcre/sljit/sljitConfig.h create mode 100644 src/lib/pcre/sljit/sljitConfigInternal.h create mode 100644 src/lib/pcre/sljit/sljitExecAllocator.c create mode 100644 src/lib/pcre/sljit/sljitLir.c create mode 100644 src/lib/pcre/sljit/sljitLir.h create mode 100644 src/lib/pcre/sljit/sljitNativeARM_Thumb2.c create mode 100644 src/lib/pcre/sljit/sljitNativeARM_v5.c create mode 100644 src/lib/pcre/sljit/sljitNativeMIPS_32.c create mode 100644 src/lib/pcre/sljit/sljitNativeMIPS_common.c create mode 100644 src/lib/pcre/sljit/sljitNativePPC_32.c create mode 100644 src/lib/pcre/sljit/sljitNativePPC_64.c create mode 100644 src/lib/pcre/sljit/sljitNativePPC_common.c create mode 100644 src/lib/pcre/sljit/sljitNativeX86_32.c create mode 100644 src/lib/pcre/sljit/sljitNativeX86_64.c create mode 100644 src/lib/pcre/sljit/sljitNativeX86_common.c create mode 100644 src/lib/pcre/sljit/sljitUtils.c create mode 100644 src/lib/pcre/ucp.h create mode 100644 src/lib/rapidxml/rapidxml.hpp create mode 100644 src/lib/rapidxml/rapidxml.pro create mode 100644 src/lib/rapidxml/rapidxml_iterators.hpp create mode 100644 src/lib/rapidxml/rapidxml_print.hpp create mode 100644 src/lib/rapidxml/rapidxml_utils.hpp create mode 100644 src/lib/zlib/README.md create mode 100644 src/lib/zlib/zlib.pro create mode 100644 src/lib/zlib/zlib/adler32.c create mode 100644 src/lib/zlib/zlib/compress.c create mode 100644 src/lib/zlib/zlib/crc32.c create mode 100644 src/lib/zlib/zlib/crc32.h create mode 100644 src/lib/zlib/zlib/deflate.c create mode 100644 src/lib/zlib/zlib/deflate.h create mode 100644 src/lib/zlib/zlib/example.c create mode 100644 src/lib/zlib/zlib/gzclose.c create mode 100644 src/lib/zlib/zlib/gzguts.h create mode 100644 src/lib/zlib/zlib/gzlib.c create mode 100644 src/lib/zlib/zlib/gzread.c create mode 100644 src/lib/zlib/zlib/gzwrite.c create mode 100644 src/lib/zlib/zlib/infback.c create mode 100644 src/lib/zlib/zlib/inffast.c create mode 100644 src/lib/zlib/zlib/inffast.h create mode 100644 src/lib/zlib/zlib/inffixed.h create mode 100644 src/lib/zlib/zlib/inflate.c create mode 100644 src/lib/zlib/zlib/inflate.h create mode 100644 src/lib/zlib/zlib/inftrees.c create mode 100644 src/lib/zlib/zlib/inftrees.h create mode 100644 src/lib/zlib/zlib/minigzip.c create mode 100644 src/lib/zlib/zlib/trees.c create mode 100644 src/lib/zlib/zlib/trees.h create mode 100644 src/lib/zlib/zlib/uncompr.c create mode 100644 src/lib/zlib/zlib/zconf.h create mode 100644 src/lib/zlib/zlib/zconf.h.cmakein create mode 100644 src/lib/zlib/zlib/zconf.h.in create mode 100644 src/lib/zlib/zlib/zlib.3 create mode 100644 src/lib/zlib/zlib/zlib.h create mode 100644 src/lib/zlib/zlib/zlib.pc.in create mode 100644 src/lib/zlib/zlib/zutil.c create mode 100644 src/lib/zlib/zlib/zutil.h create mode 100644 src/res/MdCharm.qrc create mode 100644 src/res/conf/add.png create mode 100644 src/res/conf/delete.png create mode 100644 src/res/conf/env.png create mode 100644 src/res/conf/styles.png create mode 100644 src/res/conf/text_editor.png create mode 100644 src/res/config.png create mode 100644 src/res/copy.png create mode 100644 src/res/cut.png create mode 100644 src/res/find.png create mode 100644 src/res/find_close.png create mode 100644 src/res/help.png create mode 100644 src/res/highlighter/bash.xml create mode 100644 src/res/highlighter/cpp.xml create mode 100644 src/res/highlighter/cs.xml create mode 100644 src/res/highlighter/css.xml create mode 100644 src/res/highlighter/diff.xml create mode 100644 src/res/highlighter/http.xml create mode 100644 src/res/highlighter/ini.xml create mode 100644 src/res/highlighter/java.xml create mode 100644 src/res/highlighter/javascript.xml create mode 100644 src/res/highlighter/json.xml create mode 100644 src/res/highlighter/markdown.xml create mode 100644 src/res/highlighter/perl.xml create mode 100644 src/res/highlighter/php.xml create mode 100644 src/res/highlighter/python.xml create mode 100644 src/res/highlighter/ruby.xml create mode 100644 src/res/highlighter/sql.xml create mode 100644 src/res/highlighter/xml.xml create mode 100644 src/res/jquery.js create mode 100644 src/res/markdown/bold.png create mode 100644 src/res/markdown/code.png create mode 100644 src/res/markdown/code_syntax_notice.html create mode 100644 src/res/markdown/code_syntax_patch.css create mode 100644 src/res/markdown/default.css create mode 100644 src/res/markdown/italic.png create mode 100644 src/res/markdown/link.png create mode 100644 src/res/markdown/markdown.html create mode 100644 src/res/markdown/markdown.ico create mode 100644 src/res/markdown/markdown.js create mode 100644 src/res/markdown/markdown128.png create mode 100644 src/res/markdown/markdown16.png create mode 100644 src/res/markdown/markdown32.png create mode 100644 src/res/markdown/markdown48.png create mode 100644 src/res/markdown/markdown64.png create mode 100644 src/res/markdown/markdown_cheat_sheet.md create mode 100644 src/res/markdown/picture.png create mode 100644 src/res/markdown/quote.png create mode 100644 src/res/markdown/read_mode.png create mode 100644 src/res/markdown/strike-through.png create mode 100644 src/res/markdown/tab.png create mode 100644 src/res/markdown/untab.png create mode 100644 src/res/markdown/write_mode.png create mode 100644 src/res/markdown/write_read.png create mode 100644 src/res/mdcharm-splash.png create mode 100644 src/res/mdcharm.ico create mode 100644 src/res/mdcharm.png create mode 100644 src/res/modified.png create mode 100644 src/res/new.png create mode 100644 src/res/next.png create mode 100644 src/res/open.png create mode 100644 src/res/open_dir.png create mode 100644 src/res/paste.png create mode 100644 src/res/prev.png create mode 100644 src/res/print.png create mode 100644 src/res/redo.png create mode 100644 src/res/save.png create mode 100644 src/res/saveas.png create mode 100644 src/res/spell_check.png create mode 100644 src/res/start_here_page/StartHerePage.css create mode 100644 src/res/start_here_page/StartHerePage.html create mode 100644 src/res/start_here_page/StartHerePage.js create mode 100644 src/res/start_here_page/issue.png create mode 100644 src/res/undo.png create mode 100644 src/res/unmodified.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8a9f97 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +*.user +*.exe +*.ilk +*.lib +*.pdb +*.exp +*.obj +*.manifest +*.dll +*.Debug +*.Release +*moc_*.cpp +*qrc_*.cpp +*ui_*.h +*Makefile* +*.res +*.o +*.so +*.a +*.0 +*.1 + +/src/res/mdcharm.rc +/src/src/version.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7cb8929 --- /dev/null +++ b/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2014, zhangshine0125@gmail.com +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..8c3452c --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,23 @@ +### Dependencies +#### Windows +* Qt Creator + Qt(4.7+) +* msvc2010 +* python +* git + + +#### Linux +* Qt Creator + Qt(4.7+ | Qt5) +* python +* git + +### Build +* check out + +``` +git checkout git@github.com:zhangshine/MdCharm.git +``` +* Open "MdCharm.pro" by Qt Creator and click "Build" + +### License +The BSD 3-Clause License \ No newline at end of file diff --git a/src/MdCharm.pro b/src/MdCharm.pro new file mode 100644 index 0000000..bdbd3f7 --- /dev/null +++ b/src/MdCharm.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs + +CONFIG += ordered + +SUBDIRS += lib +SUBDIRS += MdCharm + +TRANSLATIONS = lang_en.ts \ + lang_zh.ts \ + lang_zhCN.ts diff --git a/src/MdCharm/MdCharm.pro b/src/MdCharm/MdCharm.pro new file mode 100644 index 0000000..194189b --- /dev/null +++ b/src/MdCharm/MdCharm.pro @@ -0,0 +1,191 @@ +TEMPLATE = app + +lessThan(QT_MAJOR_VERSION, 5){ + QT += core gui webkit network +} else { + DEFINES += QT_V5 + QT += core gui webkit network widgets webkitwidgets printsupport + unix: { + QMAKE_RPATHDIR += /usr/local/lib/Qt-5.0.2/lib/ + } +} + +CONFIG(debug, debug|release){ #debug + DEFINES += MDCHARM_DEBUG + TARGET = MdCharm_d + DESTDIR = ../debug/ + LIBS += -L../debug -lgbreakpad_d -lcore +} else { #release + DEFINES += NDEBUG + DEFINES += QT_NO_DEBUG_OUTPUT + TARGET = MdCharm + unix:TARGET = mdcharm + DESTDIR = ../release/ + LIBS += -L../release -lgbreakpad -lcore +} +#Fix for hunspell +win32-msvc*: { + INCLUDEPATH+=../lib/hunspell/src ../lib/pcre + CONFIG(debug, debug|release){ + LIBS += -L../debug -lhunspell_d -lmdcharm_pcre + } else { + LIBS += -L../release -lhunspell -lmdcharm_pcre + } +} + +unix: { + INCLUDEPATH +=../lib/pcre + LIBS += -lhunspell + CONFIG(debug, debug|release){ + LIBS += -L../debug -lmdcharm_pcre + } else { + LIBS += -L../release -lmdcharm_pcre + } +} + +win32 { + RC_FILE = $$PWD/../res/mdcharm.rc +} + +win32-msvc*:QMAKE_CXXFLAGS_RELEASE += -Zi +win32-msvc*:QMAKE_CFLAGS_RELEASE += -Zi +win32-msvc*:QMAKE_LFLAGS_RELEASE += /DEBUG /OPT:REF /OPT:ICF + +INCLUDEPATH += ../lib/core ../lib/markdown/html \ + ../lib/markdown/src ../lib/crashdump \ + ../lib/zlib/zlib ../lib/pcre \ + ../lib/rapidxml + +CONFIG(release, debug|release){ + version_h.target = version.h + + win32:version_h.commands = python.exe ../../src/MdCharm/version_h.py git.exe ../../src/MdCharm/version.h ../../src/MdCharm/mdcharm.rc release + unix:version_h.commands = /usr/bin/python ../../src/MdCharm/version_h.py /usr/bin/git ../../src/MdCharm/version.h ../../src/res/mdcharm.rc release + + version_h.depends = version_h_nonexist + version_h.CONFIG += recursive + + version_h_nonexist.commands= @echo generating version.h + + QMAKE_EXTRA_TARGETS += version_h version_h_nonexist + + PRE_TARGETDEPS += version.h +} + +SOURCES += \ + main.cpp \ + mdcharmform.cpp \ + editareatabwidget.cpp \ + editareawidget.cpp \ + markdowneditareawidget.cpp \ + resource.cpp \ + browereditareawidget.cpp \ + utils.cpp \ + util/syntax/hightlighter.cpp \ + util/zip/zipwriter.cpp \ + util/odt/odtwriter.cpp \ + conf/configuredialog.cpp \ + configuration.cpp \ + conf/pages.cpp \ + util/test/qregularexpression.cpp \ + baseeditor/baseeditor.cpp \ + about/aboutmdcharmdialog.cpp \ + util/gui/findandreplace.cpp \ + network/checkupdates.cpp \ + dock/projectdockwidget.cpp \ + util/gui/addnewfiledialog.cpp \ + util/gui/selectencodingdialog.cpp \ + util/gui/statusbarlabel.cpp \ + util/gui/gotodialog.cpp \ + util/spellcheck/spellchecker.cpp \ + baseeditor/markdowneditor.cpp \ + util/gui/exportdialog.cpp \ + util/sessionfileparser.cpp \ + mdcharmapplication.cpp \ + util/gui/insertlinkorpicturedialog.cpp \ + util/gui/renamefiledialog.cpp \ + util/spellcheck/spellcheckselectordialog.cpp \ + util/gui/markdowncheatsheetdialog.cpp \ + util/gui/insertcodedialog.cpp \ + util/gui/noticedialog.cpp \ + util/filesystemmodel.cpp \ + util/filesystemtreeview.cpp \ + util/rotationtoolbutton.cpp \ + baseeditor/baseautocompleter.cpp \ + baseeditor/markdownautocompleter.cpp \ + editareatabwidgetmanager.cpp \ + basewebview/basewebview.cpp \ + basewebview/markdownwebview.cpp \ + util/gui/newversioninfodialog.cpp \ + util/gui/exportdirectorydialog.cpp \ + util/gui/shortcutlineedit.cpp + + +HEADERS += \ + mdcharmform.h \ + editareatabwidget.h \ + editareawidget.h \ + markdowneditareawidget.h \ + resource.h \ + browereditareawidget.h \ + utils.h \ + util/syntax/hightlighter.h \ + util/zip/zipwriter.h \ + util/odt/odtwriter.h \ + conf/configuredialog.h \ + configuration.h \ + conf/pages.h \ + util/test/qregularexpression.h \ + baseeditor/baseeditor.h \ + about/aboutmdcharmdialog.h \ + util/gui/findandreplace.h \ + network/checkupdates.h \ + dock/projectdockwidget.h \ + util/gui/addnewfiledialog.h \ + util/gui/selectencodingdialog.h \ + util/gui/statusbarlabel.h \ + util/gui/gotodialog.h \ + util/spellcheck/spellchecker.h \ + baseeditor/markdowneditor.h \ + util/gui/exportdialog.h \ + util/sessionfileparser.h \ + mdcharmapplication.h \ + util/gui/insertlinkorpicturedialog.h \ + util/gui/renamefiledialog.h \ + util/spellcheck/spellcheckselectordialog.h \ + util/gui/markdowncheatsheetdialog.h \ + util/gui/insertcodedialog.h \ + util/gui/noticedialog.h \ + util/filesystemmodel.h \ + util/filesystemtreeview.h \ + util/rotationtoolbutton.h \ + baseeditor/baseautocompleter.h \ + baseeditor/markdownautocompleter.h \ + editareatabwidgetmanager.h \ + basewebview/basewebview.h \ + basewebview/markdownwebview.h \ + util/gui/newversioninfodialog.h \ + util/gui/exportdirectorydialog.h \ + util/gui/shortcutlineedit.h + + +FORMS += \ + conf/environmentpage.ui \ + conf/texteditorpage.ui \ + about/aboutdialog.ui \ + util/gui/findandreplaceform.ui \ + dock/projectdockwidget.ui \ + util/gui/addnewfiledialog.ui \ + conf/stylespage.ui \ + util/gui/selectencodingdialog.ui \ + util/gui/gotodialog.ui \ + util/gui/exportdialog.ui \ + util/gui/insertlinkorpicturedialog.ui \ + util/gui/renamefiledialog.ui \ + util/spellcheck/spellcheckselectordialog.ui \ + util/gui/insertcodedialog.ui \ + util/gui/noticedialog.ui \ + util/gui/exportdirectorydialog.ui + +RESOURCES += \ + $$PWD/../res/MdCharm.qrc diff --git a/src/MdCharm/ReadMe.md b/src/MdCharm/ReadMe.md new file mode 100644 index 0000000..e46c731 --- /dev/null +++ b/src/MdCharm/ReadMe.md @@ -0,0 +1 @@ +Main App \ No newline at end of file diff --git a/src/MdCharm/about/aboutdialog.ui b/src/MdCharm/about/aboutdialog.ui new file mode 100644 index 0000000..7ece8c0 --- /dev/null +++ b/src/MdCharm/about/aboutdialog.ui @@ -0,0 +1,951 @@ + + + AboutMdCharmDialog + + + + 0 + 0 + 484 + 376 + + + + About MdCharm... + + + + 5 + + + 5 + + + + + 0 + + + + MdCharm + + + + + + + + + :/mdcharm-splash.png + + + true + + + + + + + TextLabel + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + License + + + + 0 + + + 0 + + + + + false + + + QFrame::NoFrame + + + false + + + true + + + PCRE LICENCE +------------ + +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Release 8 of PCRE is distributed under the terms of the "BSD" licence, as +specified below. The documentation for PCRE, supplied in the "doc" +directory, is distributed under the same terms as the software itself. + +The basic library functions are written in C and are freestanding. Also +included in the distribution is a set of C++ wrapper functions, and a +just-in-time compiler that can be used to optimize pattern matching. These +are both optional features that can be omitted when the library is built. + + +THE BASIC LIBRARY FUNCTIONS +--------------------------- + +Written by: Philip Hazel +Email local part: ph10 +Email domain: cam.ac.uk + +University of Cambridge Computing Service, +Cambridge, England. + +Copyright (c) 1997-2012 University of Cambridge +All rights reserved. + + +PCRE JUST-IN-TIME COMPILATION SUPPORT +------------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2010-2012 Zoltan Herczeg +All rights reserved. + + +STACK-LESS JUST-IN-TIME COMPILER +-------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2009-2012 Zoltan Herczeg +All rights reserved. + + +THE C++ WRAPPER FUNCTIONS +------------------------- + +Contributed by: Google Inc. + +Copyright (c) 2007-2012, Google Inc. +All rights reserved. + + +THE "BSD" LICENCE +----------------- + +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 the University of Cambridge nor the name of Google + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. + +End + + + + + +SUNDOWN LICENCE +--------------- +Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +en_GB.dic en_GB.aff +------------------- +This dictionary was initially based on a subset of the +original English wordlist created by Kevin Atkinson for +Pspell and Aspell and thus is covered by his original +LGPL licence. + +It has been extensively updated by David Bartlett, Brian Kelk +and Andrew Brown: +- numerous Americanism have been removed +- numerous American spellings have been corrected +- missing words have been added +- many errors have been corrected +- compound hyphenated words have been added where appropriate + +Valuable inputs to this process were received from many other +people - far too numerous to name. Serious thanks to you all +for your greatly appreciated help. + +This word list is intended to be a good representation of +current modern British English and thus it should be a good +basis for Commonwealth English in most countries of the world +outside North America. + +The affix file has been created completely from scratch +by David Bartlett and Andrew Brown, based on the published +rules for MySpell and is also provided under the LGPL. + +In creating the affix rules an attempt has been made to +reproduce the most general rules for English word +formation, rather than merely use it as a means to +compress the size of the dictionary. It is hoped that this +will facilitate future localisation to other variants of +English. + +Please let David Bartlett <dwb@openoffice.org> know of any +errors that you find. + +The current release is R 1.18, 11/04/05 + +en_US.dic en_US.aff +------------------- +2006-02-07 release. +-- +This dictionary is based on a subset of the original +English wordlist created by Kevin Atkinson for Pspell +and Aspell and thus is covered by his original +LGPL license. The affix file is a heavily modified +version of the original english.aff file which was +released as part of Geoff Kuenning's Ispell and as +such is covered by his BSD license. + +Thanks to both authors for there wonderful work. + +ChangeLog + +2006-02-07 nemeth AT OOo + +Issue 48060 - add ordinal numbers with COMPOUNDRULE (1st, 11th, 101st etc.) +Issue 29112, 55498 - add NOSUGGEST flags to taboo words +Issue 56755 - add sequitor (non sequitor) +Issue 50616 - add open source words (GNOME, KDE, OOo, OpenOffice.org) +Issue 56389 - add Mozilla words (Mozilla, Firefox, Thunderbird) +Issue 29110 - add okay +Issue 58468 - add advisors +Issue 58708 - add hiragana & katakana +Issue 60240 - add arginine, histidine, monovalent, polymorphism, pyroelectric, pyroelectricity + +2005-11-01 dnaber AT OOo + +Issue 25797 - add proven, advisor, etc. + +----- + +HUNSPELL LICENSE +---------------- +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Hunspell, based on MySpell. + * + * The Initial Developers of the Original Code are + * Kevin Hendricks (MySpell) and Németh László (Hunspell). + * Portions created by the Initial Developers are Copyright (C) 2002-2005 + * the Initial Developers. All Rights Reserved. + * + * Contributor(s): + * David Einstein + * Davide Prina + * Giuseppe Modugno + * Gianluca Turconi + * Simon Brouwer + * Noll János + * Bíró Árpád + * Goldman Eleonóra + * Sarlós Tamás + * Bencsáth Boldizsár + * Halácsy Péter + * Dvornik László + * Gefferth András + * Nagy Viktor + * Varga Dániel + * Chris Halls + * Rene Engelhard + * Bram Moolenaar + * Dafydd Jones + * Harri Pitkänen + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Peg-MultiMarkdown +----------------- +Title: License Agreement + +peg-multimarkdown +portions Copyright (c) 2010-2013 Fletcher T. Penney + +based on: +markdown in c, implemented using PEG grammar +Copyright (c) 2008-2011 John MacFarlane +ODF output code (c) 2011-2013 Fletcher T. Penney + +peg-markdown is released under both the GPL and MIT licenses. +You may pick the license that best fits your needs. + +Additional MultiMarkdown files +Copyright (c) 2005-2013 Fletcher T. Penney +Copyright (c) 2011 Daniel Jalkut, licensed explicitly MIT. + +Modifications to remove reliance on Glib2 +Copyright (c) 2011 Daniel Jalkut, licensed explicitly MIT. + +The MIT License + +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. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +peg-0.1.4 (included for convenience - http://piumarta.com/software/peg/) + +Copyright (c) 2007 by Ian Piumarta +All rights reserved. + +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, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, provided that the above copyright notice(s) and this +permission notice appear in all copies of the Software. Acknowledgement +of the use of this Software in supporting documentation would be +appreciated but is not required. + +THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. + + +QT +-- + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/src/MdCharm/about/aboutmdcharmdialog.cpp b/src/MdCharm/about/aboutmdcharmdialog.cpp new file mode 100644 index 0000000..e3e824f --- /dev/null +++ b/src/MdCharm/about/aboutmdcharmdialog.cpp @@ -0,0 +1,33 @@ +#include "aboutmdcharmdialog.h" +#include "ui_aboutdialog.h" +#include "version.h" + +AboutMdCharmDialog::AboutMdCharmDialog(QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::AboutMdCharmDialog) +{ + ui->setupUi(this); +// setAttribute(Qt::WA_DeleteOnClose); + closePushButton = ui->closePushButton; + versionLabel = ui->versionLabel; + + versionLabel->setOpenExternalLinks(true); + versionLabel->setText(QString("

" + "Version %2," + "Built on %1," + "From revision %3.
" + "http://www.mdcharm.com
" + "Copyright © 2012-2013. All Rights Reserved." + "

") + .arg(QString::fromLatin1(BUILT_TIME_STR)) + .arg(QString::fromLatin1(VERSION_STR)) + .arg(QString::fromLatin1(REVISION_STR).left(10))); + + QObject::connect(closePushButton, SIGNAL(clicked()), + this, SLOT(close())); +} + +AboutMdCharmDialog::~AboutMdCharmDialog() +{ + delete ui; +} diff --git a/src/MdCharm/about/aboutmdcharmdialog.h b/src/MdCharm/about/aboutmdcharmdialog.h new file mode 100644 index 0000000..c23cec2 --- /dev/null +++ b/src/MdCharm/about/aboutmdcharmdialog.h @@ -0,0 +1,26 @@ +#ifndef ABOUTMDCHARMDIALOG_H +#define ABOUTMDCHARMDIALOG_H + +#include + +class QPlainTextEdit; +class QLabel; + +namespace Ui { + class AboutMdCharmDialog; +} + +class AboutMdCharmDialog : public QDialog +{ + Q_OBJECT + +public: + AboutMdCharmDialog(QWidget *parent); + ~AboutMdCharmDialog(); +private: + Ui::AboutMdCharmDialog *ui; + QPushButton *closePushButton; + QLabel *versionLabel; +}; + +#endif // ABOUTMDCHARMDIALOG_H diff --git a/src/MdCharm/baseeditor/baseautocompleter.cpp b/src/MdCharm/baseeditor/baseautocompleter.cpp new file mode 100644 index 0000000..830c9ac --- /dev/null +++ b/src/MdCharm/baseeditor/baseautocompleter.cpp @@ -0,0 +1,34 @@ +#include "baseautocompleter.h" + +BaseAutoCompleter::BaseAutoCompleter(QObject *parent) : + QObject(parent) +{ + pairCharMap.insert('[',']'); + pairCharMap.insert('{', '}'); + pairCharMap.insert('\"','\"'); + pairCharMap.insert('\'','\''); + pairCharMap.insert('(', ')'); + pairCharMap.insert('`', '`'); +} + +int BaseAutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor) +{ + Q_UNUSED(cursor); + return 0; +} + +QString BaseAutoCompleter::autoComplete(QTextCursor &cursor, const QString &textToInsert) const +{ + if(textToInsert.isEmpty()) + return QString(); + QChar firstChar = textToInsert[0]; + QChar firstCharPairValue = pairCharMap.value(firstChar); + if(textToInsert.length()==1 && !firstCharPairValue.isNull()){ + if(cursor.hasSelection()){ + return cursor.selectedText() + firstCharPairValue; + } else { + return firstCharPairValue; + } + } + return QString(); +} diff --git a/src/MdCharm/baseeditor/baseautocompleter.h b/src/MdCharm/baseeditor/baseautocompleter.h new file mode 100644 index 0000000..196c1dd --- /dev/null +++ b/src/MdCharm/baseeditor/baseautocompleter.h @@ -0,0 +1,25 @@ +#ifndef BASEAUTOCOMPLETER_H +#define BASEAUTOCOMPLETER_H + +#include +#include + +class BaseAutoCompleter : public QObject +{ + Q_OBJECT +public: + explicit BaseAutoCompleter(QObject *parent = 0); + virtual int paragraphSeparatorAboutToBeInserted(QTextCursor &cursor); + // Returns the text to complete at the cursor position, or an empty string + virtual QString autoComplete(QTextCursor &cursor, const QString &text) const; + +signals: + +public slots: + +protected: +private: + QMap pairCharMap; +}; + +#endif // BASEAUTOCOMPLETER_H diff --git a/src/MdCharm/baseeditor/baseeditor.cpp b/src/MdCharm/baseeditor/baseeditor.cpp new file mode 100644 index 0000000..518c853 --- /dev/null +++ b/src/MdCharm/baseeditor/baseeditor.cpp @@ -0,0 +1,620 @@ +#include +#include + +#ifdef QT_V5 +#include +#endif + +#include "baseeditor.h" +#include "util/spellcheck/spellchecker.h" +#include "configuration.h" +#include "utils.h" + +BaseEditor::BaseEditor(QWidget *parent) : + QPlainTextEdit(parent) +{ + conf = Configuration::getInstance(); + mdCharmGlobal = MdCharmGlobal::getInstance(); + lineNumberArea = new LineNumberArea(this); + + displayLineNumber = false; + finded = false; + replacing = false; + if(conf->isCheckSpell()) + spellCheckLanguage=conf->getSpellCheckLanguage(); + + connect(this, SIGNAL(textChanged()), + this, SLOT(ensureAtTheLast())); +} + +BaseEditor::~BaseEditor(){} + +void BaseEditor::initSpellCheckMatter()//triggered by setDocument() +{ + if(!conf->isCheckSpell()) + return; + connect(document(), SIGNAL(contentsChange(int,int,int)), + this, SLOT(spellCheck(int,int,int))); + checkWholeContent(); +} + +void BaseEditor::enableSpellCheck() +{ + spellCheckErrorSelection.clear(); + disconnect(document(), SIGNAL(contentsChange(int,int,int)), + this, SLOT(spellCheck(int,int,int))); + updateExtraSelection(); + if(spellCheckLanguage.isEmpty()) + spellCheckLanguage=conf->getSpellCheckLanguage(); + if(mdCharmGlobal->getSpellChecker(spellCheckLanguage)==NULL){ + Q_ASSERT(0 && "This should not be happen"); + spellCheckLanguage.clear(); + return; + } + connect(document(), SIGNAL(contentsChange(int,int,int)), + this, SLOT(spellCheck(int,int,int))); + checkWholeContent(); +} + +void BaseEditor::disableSpellCheck() +{ + spellCheckErrorSelection.clear(); + spellCheckLanguage.clear(); + disconnect(document(), SIGNAL(contentsChange(int,int,int)), + this, SLOT(spellCheck(int,int,int))); + updateExtraSelection();; +} + +void BaseEditor::setDocument(QTextDocument *doc) +{ + QPlainTextEdit::setDocument(doc); + initSpellCheckMatter(); +} + +void BaseEditor::enableHighlightCurrentLine() +{ + highlightCurrentLine(); + connect(this, SIGNAL(cursorPositionChanged()), + this, SLOT(highlightCurrentLine())); +} + +void BaseEditor::disableHighlightCurrentLine() +{ + disconnect(this, SIGNAL(cursorPositionChanged()), + this, SLOT(highlightCurrentLine())); + currentLineSelection.clear(); + updateExtraSelection(); +} + +void BaseEditor::enableDisplayLineNumber() +{ + displayLineNumber = true; + connect(this, SIGNAL(blockCountChanged(int)), + this, SLOT(updateLineNumberAreaWidth(int))); + connect(this, SIGNAL(updateRequest(QRect,int)), + this, SLOT(updateLineNumberArea(QRect,int))); + updateLineNumberAreaWidth(0); + lineNumberArea->setVisible(true); +} + +void BaseEditor::disableDisplayLineNumber() +{ + displayLineNumber = false; + disconnect(this, SIGNAL(updateRequest(QRect,int)), + this, SLOT(updateLineNumberArea(QRect,int))); + disconnect(this, SIGNAL(blockCountChanged(int)), + this, SLOT(updateLineNumberAreaWidth(int))); + updateLineNumberAreaWidth(0); + lineNumberArea->setVisible(false); +} + +int BaseEditor::lineNumberAreaWidth() +{ + int digits = 1; + int max = qMax(1, blockCount()); + while(max >= 10) + { + max /= 10; + ++digits; + } + int space = 3 + QFontMetrics(document()->defaultFont()).width(QLatin1Char('9')) * digits; + return space; +} + +int BaseEditor::firstVisibleLineNumber() +{ + return firstVisibleBlock().blockNumber()+1; +} + +void BaseEditor::findAndHighlightText(const QString &text, QTextDocument::FindFlags qff, + bool isRE, bool isSetTextCursor) +{ + if(replacing) + return; + if(text.isEmpty()) + { + findTextSelection.clear(); + currentFindSelection.clear(); + updateExtraSelection(); + return; + } + if(findAllOccurrance(text, qff, isRE)) + { + finded = true; + findFirstOccurrance(text, qff, isRE, true, isSetTextCursor); + } +} +bool BaseEditor::findAllOccurrance(const QString &text, QTextDocument::FindFlags qff, bool isRE) +{ + QTextDocument *doc = document(); + + findTextSelection.clear(); + bool finded=false; + + if(text.isEmpty()) + { + prevFindCursor = QTextCursor(); + return finded; + } else { + QTextEdit::ExtraSelection es; + QTextCursor highlightCursor(doc); + + QTextCharFormat plainFormat(highlightCursor.charFormat()); + QTextCharFormat colorFormat = plainFormat; + colorFormat.setBackground(Qt::yellow); + + es.format = colorFormat; + QRegExp re(text); + + while(!highlightCursor.isNull() && !highlightCursor.atEnd()) + { + highlightCursor = isRE ? doc->find(re, highlightCursor, qff) : + doc->find(text, highlightCursor, qff); + + if(!highlightCursor.isNull()) + { + finded = true; + es.cursor = highlightCursor; + findTextSelection.append(es); + } + } + if(!finded) + { + prevFindCursor = highlightCursor; + } + return finded; + } +} + +void BaseEditor::findFirstOccurrance(const QString &text, QTextDocument::FindFlags qff, + bool isRE, bool init, bool isSetTextCusor) +{ + if (!finded) + return; + QRegExp re(text); + QTextDocument *doc = document(); + QTextCursor currentCursor = textCursor(); + QTextCursor firstCursor; + QTextEdit::ExtraSelection es; + if(!init || prevFindCursor.isNull()) + { + QTextCursor startCursor; + if(qff&QTextDocument::FindBackward && !prevFindCursor.isNull()) + { + prevFindCursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, + abs(prevFindCursor.selectionStart()-prevFindCursor.selectionEnd())); + } + if(prevFindCursor.isNull()) + startCursor = currentCursor; + else + startCursor = prevFindCursor; + firstCursor = isRE ? doc->find(re, startCursor, qff): + doc->find(text, startCursor, qff); + } else { + firstCursor = isRE ? doc->find(re, prevFindCursor.selectionStart(), qff): + doc->find(text, prevFindCursor.selectionStart(), qff); + } + if(firstCursor.isNull()) + { + QTextCursor wholeCursor(doc); + if(qff & QTextDocument::FindBackward) + wholeCursor.movePosition(QTextCursor::End); + firstCursor = isRE ? doc->find(re, wholeCursor, qff): + doc->find(text, wholeCursor, qff); + } + if(firstCursor.isNull()) + { + prevFindCursor = firstCursor; + return; + } + es.cursor = firstCursor; + QTextCharFormat f; + f.setBackground(Qt::blue); + f.setForeground(Qt::white); + es.format = f; + currentFindSelection.clear(); + currentFindSelection.append(es); + prevFindCursor = firstCursor; + firstCursor.clearSelection(); + if(isSetTextCusor) + setTextCursor(firstCursor); + ensureCursorVisible(); + updateExtraSelection(); +} + +void BaseEditor::updateLineNumberAreaWidth(int newBlockCount) +{ + Q_UNUSED(newBlockCount) + setViewportMargins(displayLineNumber ? lineNumberAreaWidth() : 0, 0, 0, 0); +} + +void BaseEditor::updateLineNumberArea(const QRect &rect, int dy) +{ + if (dy) + lineNumberArea->scroll(0, dy); + else + lineNumberArea->update(0, rect.y(), lineNumberArea->width(), rect.height()); + + if (rect.contains(viewport()->rect())) + updateLineNumberAreaWidth(0); +} + +void BaseEditor::resizeEvent(QResizeEvent *e) +{ + QPlainTextEdit::resizeEvent(e); + if(!displayLineNumber) + return; + QRect cr = contentsRect(); + lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); +} + +void BaseEditor::keyPressEvent(QKeyEvent *e) +{ + QPlainTextEdit::keyPressEvent(e); + if(!e->isAccepted()&&e->key()==Qt::Key_Insert)//FIXME: crashrpt 8135582f-6f1c-46db-b3be-85ee2702a88d + { + emit overWriteModeChanged(); + } +} + +void BaseEditor::focusInEvent(QFocusEvent *e) +{ + emit focusInSignal(); + QPlainTextEdit::focusInEvent(e); +} + +static void fillBackground(QPainter *p, const QRectF &rect, QBrush brush, QRectF gradientRect = QRectF())//copy from QPlainTextEditor from 4.8.1 +{ + p->save(); + if (brush.style() >= Qt::LinearGradientPattern && brush.style() <= Qt::ConicalGradientPattern) { + if (!gradientRect.isNull()) { + QTransform m = QTransform::fromTranslate(gradientRect.left(), gradientRect.top()); + m.scale(gradientRect.width(), gradientRect.height()); + brush.setTransform(m); + const_cast(brush.gradient())->setCoordinateMode(QGradient::LogicalMode); + } + } else { + p->setBrushOrigin(rect.topLeft()); + } + p->fillRect(rect, brush); + p->restore(); +} + +static QColor blendColors(const QColor &a, const QColor &b, int alpha)//copy from QPlainTextEditor from 4.8.1 +{ + return QColor((a.red() * (256 - alpha) + b.red() * alpha) / 256, + (a.green() * (256 - alpha) + b.green() * alpha) / 256, + (a.blue() * (256 - alpha) + b.blue() * alpha) / 256); +} + +void BaseEditor::paintEvent(QPaintEvent *e) +{ + //copy from QPlainTextEditor + QPainter painter(viewport()); + Q_ASSERT(qobject_cast(document()->documentLayout())); + + QPointF offset(contentOffset()); + + QRect er = e->rect(); + QRect viewportRect = viewport()->rect(); + + bool editable = !isReadOnly(); + + QTextBlock block = firstVisibleBlock(); + qreal maximumWidth = document()->documentLayout()->documentSize().width(); + + //margin + qreal lineX = 0; + if (conf->isDisplayRightColumnMargin()) { + // Don't use QFontMetricsF::averageCharWidth here, due to it returning + // a fractional size even when this is not supported by the platform. + lineX = QFontMetricsF(document()->defaultFont()).width(QLatin1Char('X')) * conf->getRightMarginColumn() + offset.x() + 4; + + if (lineX < viewportRect.width()) { + const QBrush background = QBrush(QColor(239, 239, 239)); + painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()), + background); + + const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white; + const QPen pen = painter.pen(); + painter.setPen(blendColors(background.isOpaque() ? background.color() : palette().base().color(), + col, 32)); + painter.drawLine(QPointF(lineX, er.top()), QPointF(lineX, er.bottom())); + painter.setPen(pen); + } + } + + // Set a brush origin so that the WaveUnderline knows where the wave started + painter.setBrushOrigin(offset); + + // keep right margin clean from full-width selection + int maxX = offset.x() + qMax((qreal)viewportRect.width(), maximumWidth) + - document()->documentMargin(); + er.setRight(qMin(er.right(), maxX)); + painter.setClipRect(er); + + + QAbstractTextDocumentLayout::PaintContext context = getPaintContext(); + + while (block.isValid()) { + + QRectF r = blockBoundingRect(block).translated(offset); + QTextLayout *layout = block.layout(); + + if (!block.isVisible()) { + offset.ry() += r.height(); + block = block.next(); + continue; + } + + if (r.bottom() >= er.top() && r.top() <= er.bottom()) { + + QTextBlockFormat blockFormat = block.blockFormat(); + + QBrush bg = blockFormat.background(); + if (bg != Qt::NoBrush) { + QRectF contentsRect = r; + contentsRect.setWidth(qMax(r.width(), maximumWidth)); + fillBackground(&painter, contentsRect, bg); + } + + + QVector selections; + int blpos = block.position(); + int bllen = block.length(); + for (int i = 0; i < context.selections.size(); ++i) { + const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i); + const int selStart = range.cursor.selectionStart() - blpos; + const int selEnd = range.cursor.selectionEnd() - blpos; + if (selStart < bllen && selEnd > 0 + && selEnd > selStart) { + QTextLayout::FormatRange o; + o.start = selStart; + o.length = selEnd - selStart; + o.format = range.format; + selections.append(o); + } else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection) + && block.contains(range.cursor.position())) { + // for full width selections we don't require an actual selection, just + // a position to specify the line. that's more convenience in usage. + QTextLayout::FormatRange o; + QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos); + o.start = l.textStart(); + o.length = l.textLength(); + if (o.start + o.length == bllen - 1) + ++o.length; // include newline + o.format = range.format; + selections.append(o); + } + } + + bool drawCursor = ((editable || (textInteractionFlags() & Qt::TextSelectableByKeyboard)) + && context.cursorPosition >= blpos + && context.cursorPosition < blpos + bllen); + + bool drawCursorAsBlock = drawCursor && overwriteMode() ; + + if (drawCursorAsBlock) { + if (context.cursorPosition == blpos + bllen - 1) { + drawCursorAsBlock = false; + } else { + QTextLayout::FormatRange o; + o.start = context.cursorPosition - blpos; + o.length = 1; + o.format.setForeground(palette().base()); + o.format.setBackground(palette().text()); + selections.append(o); + } + } + + + layout->draw(&painter, offset, selections, er); + if ((drawCursor && !drawCursorAsBlock) + || (editable && context.cursorPosition < -1 + && !layout->preeditAreaText().isEmpty())) { + int cpos = context.cursorPosition; + if (cpos < -1) + cpos = layout->preeditAreaPosition() - (cpos + 2); + else + cpos -= blpos; + layout->drawCursor(&painter, offset, cpos, cursorWidth()); + } + } + + offset.ry() += r.height(); + if (offset.y() > viewportRect.height()) + break; + block = block.next(); + } + + if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom() + && (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) { + painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background()); + } +} + +void BaseEditor::highlightCurrentLine() +{ + currentLineSelection.clear(); + if (!isReadOnly()) + { + QTextEdit::ExtraSelection selection; + QColor lineColor = QColor::fromRgb(0xC6, 0xE2, 0xFF); + + selection.format.setBackground(lineColor); + selection.format.setProperty(QTextFormat::FullWidthSelection, true); + selection.cursor = textCursor(); + selection.cursor.clearSelection();; + currentLineSelection.append(selection); + } + updateExtraSelection(); +} + +void BaseEditor::lineNumberAreaPaintEvent(QPaintEvent *event) +{ + QPainter painter(lineNumberArea); + painter.fillRect(event->rect(), QColor::fromRgb(0xEA,0xEA,0xEA)); + painter.setFont(document()->defaultFont()); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int) blockBoundingRect(block).height(); + int height = QFontMetrics(document()->defaultFont()).height(); + while(block.isValid() && top <= event->rect().bottom()) + { + if (block.isVisible() && bottom >= event->rect().top()) + { + QString number = QString::number(blockNumber+1); + painter.setPen(Qt::black); + painter.drawText(0, top, lineNumberArea->width(), height, + Qt::AlignRight, number); + } + block = block.next(); + top = bottom; + bottom = top + (int) blockBoundingRect(block).height(); + ++blockNumber; + } +} + +void BaseEditor::updateExtraSelection() +{ + setExtraSelections(findTextSelection+currentLineSelection+currentFindSelection+spellCheckErrorSelection); +} + +void BaseEditor::findFinished() +{ + findTextSelection.clear(); + currentFindSelection.clear(); + updateExtraSelection(); + prevFindCursor = QTextCursor(); + finded = false; +} + +void BaseEditor::replace(const QString &rt) +{ + if(prevFindCursor.isNull()) + return; + prevFindCursor.beginEditBlock(); + prevFindCursor.insertText(rt); + prevFindCursor.endEditBlock(); +} + +void BaseEditor::replaceAll(const QString &ft, const QString &rt, + QTextDocument::FindFlags qff, bool isRE) +{ + QTextDocument *doc = document(); + QTextCursor tc(doc); + QRegExp re(ft); + replacing = true; + while(!tc.isNull() && !tc.atEnd()) + { + tc = isRE ? doc->find(re, tc, qff) : + doc->find(ft, tc, qff); + if(!tc.isNull()) + { + tc.beginEditBlock(); + tc.insertText(rt); + tc.endEditBlock(); + } + } + replacing = false; + findAndHighlightText(ft, qff, isRE); +} + +void BaseEditor::ensureAtTheLast() +{ + QTextCursor tc = textCursor(); + if(tc.atEnd()) + verticalScrollBar()->setValue(verticalScrollBar()->maximum()); +} + +void BaseEditor::spellCheck(int start, int unused, int length) +{ + Q_UNUSED(unused) + if(length==0) + return; +// qDebug("start %d, length %d", start, length);; + int end = start+length; + bool isInSameBlock=false; + if(start==end) + isInSameBlock = true; + QTextBlock startBlock = document()->findBlock(start); + QTextBlock endBlock = document()->findBlock(end); + if(!endBlock.isValid()) + endBlock = document()->lastBlock(); +// qDebug("start block %d, end block %d", startBlock.blockNumber(), endBlock.blockNumber()); + if(startBlock.blockNumber()==endBlock.blockNumber()) + isInSameBlock = true; + spellCheckAux(startBlock); + if(!isInSameBlock){ + for(int i=0; ifindBlockByNumber(startBlock.blockNumber()+i+1)); + } + } + updateExtraSelection(); +} + +void BaseEditor::spellCheckAux(const QTextBlock &block) +{ + removeExtraSelectionInRange(spellCheckErrorSelection, block.position(), block.position()+block.length()); + SpellChecker *spellChecker = mdCharmGlobal->getSpellChecker(spellCheckLanguage); + if(spellChecker==NULL) + return; + SpellCheckResultList resultList = spellChecker->checkString(block.text()); + QTextCharFormat spellErrorCharFormat; + spellErrorCharFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline); + spellErrorCharFormat.setUnderlineColor(Qt::darkRed); + for(int i=0; ifindBlockByNumber(i)); + updateExtraSelection(); +} + +void BaseEditor::removeExtraSelectionInRange(QList &extraList, int start, int end) +{ + QStack toRemove; + for(int i=0; i=start && tc.selectionEnd()<=end) + toRemove.push(i); + } + //remove from end to begin + while(!toRemove.isEmpty()) + extraList.removeAt(toRemove.pop()); +} diff --git a/src/MdCharm/baseeditor/baseeditor.h b/src/MdCharm/baseeditor/baseeditor.h new file mode 100644 index 0000000..11f6da1 --- /dev/null +++ b/src/MdCharm/baseeditor/baseeditor.h @@ -0,0 +1,94 @@ +#ifndef BASEEDITOR_H +#define BASEEDITOR_H + +#include + +class Configuration; +class MdCharmGlobal; + +class BaseEditor : public QPlainTextEdit +{ + Q_OBJECT + +public: + explicit BaseEditor(QWidget *parent = 0); + ~BaseEditor(); + void lineNumberAreaPaintEvent(QPaintEvent *event); + int lineNumberAreaWidth(); + int firstVisibleLineNumber(); + void enableHighlightCurrentLine(); + void disableHighlightCurrentLine(); + void enableDisplayLineNumber(); + void disableDisplayLineNumber(); + void enableSpellCheck(); + void disableSpellCheck(); + void setDocument(QTextDocument *doc); +protected: + virtual void resizeEvent(QResizeEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void focusInEvent(QFocusEvent *e); + virtual void paintEvent(QPaintEvent *e); +signals: + void overWriteModeChanged(); + void focusInSignal(); + +public slots: + void findAndHighlightText(const QString &text, QTextDocument::FindFlags qff, + bool isRE, bool isSetTextCursor=true); + void findFirstOccurrance(const QString &text, QTextDocument::FindFlags qff, + bool isRE, bool init=false, bool isSetTextCusor=true); + void findFinished(); + void replace(const QString &rt); + void replaceAll(const QString &ft, const QString &rt, + QTextDocument::FindFlags qff, bool isRE); +private slots: + void updateLineNumberAreaWidth(int newBlockCount); + void highlightCurrentLine(); + void updateLineNumberArea(const QRect &, int); + void ensureAtTheLast(); + void spellCheck(int start, int unused, int length); +private: + void initSpellCheckMatter(); + void updateExtraSelection(); + bool findAllOccurrance(const QString &text, QTextDocument::FindFlags qff, bool isRE); + void spellCheckAux(const QTextBlock &block); + void checkWholeContent(); + void removeExtraSelectionInRange(QList &extraList, int start, int end); +protected: + Configuration *conf; + MdCharmGlobal *mdCharmGlobal; + QString spellCheckLanguage; +private: + QWidget *lineNumberArea; + bool displayLineNumber; + QList currentLineSelection; + QList findTextSelection; + QList currentFindSelection; + QList spellCheckErrorSelection; + QTextCursor prevFindCursor; + bool finded; + bool replacing; +}; + +class LineNumberArea : public QWidget +{ +public: + LineNumberArea(BaseEditor *editor) : QWidget(editor) + { + baseEditor = editor; + } + QSize sizeHint() const + { + return QSize(baseEditor->lineNumberAreaWidth(), 0); + } +protected: + void paintEvent(QPaintEvent *event) + { + baseEditor->lineNumberAreaPaintEvent(event); + } + +private: + BaseEditor *baseEditor; +}; + +#endif // BASEEDITOR_H diff --git a/src/MdCharm/baseeditor/markdownautocompleter.cpp b/src/MdCharm/baseeditor/markdownautocompleter.cpp new file mode 100644 index 0000000..3281840 --- /dev/null +++ b/src/MdCharm/baseeditor/markdownautocompleter.cpp @@ -0,0 +1,86 @@ +#include +#include + +#include "markdownautocompleter.h" +#include "configuration.h" + +MarkdownAutoCompleter::MarkdownAutoCompleter(QObject *parent) : + BaseAutoCompleter(parent) +{ + orderRe = new QRegExp("([0-9]*)\\.\\s"); + conf = Configuration::getInstance(); +} + +MarkdownAutoCompleter::~MarkdownAutoCompleter() +{ + delete orderRe; + orderRe = 0; +} + +int MarkdownAutoCompleter::paragraphSeparatorAboutToBeInserted(QTextCursor &cursor) +{ + if(!conf->isAutoIndentation())//This method only deal auto-indent currently, so if not enabled, just return + return 0; + QTextBlock curBlock = cursor.block(); + if(!curBlock.isValid()) + return 0; + QString blockText = curBlock.text(); + if(blockText.isEmpty()) + return 0; + int whitespaces = 0; + while(whitespacesindexIn(text, start); + if(index!=start) + return 0; + return orderRe->cap(1).toInt(); +} + +bool MarkdownAutoCompleter::isUnorderList(QChar c1, QChar c2) +{ + return ( c1==QChar('*') || c1==QChar('+') || c1==QChar('-') ) && (c2==QChar(' ') || c2==QChar('\t')); +} diff --git a/src/MdCharm/baseeditor/markdownautocompleter.h b/src/MdCharm/baseeditor/markdownautocompleter.h new file mode 100644 index 0000000..774764c --- /dev/null +++ b/src/MdCharm/baseeditor/markdownautocompleter.h @@ -0,0 +1,24 @@ +#ifndef MARKDOWNAUTOCOMPLETER_H +#define MARKDOWNAUTOCOMPLETER_H + +#include "baseautocompleter.h" + +class QRegExp; +class Configuration; + +class MarkdownAutoCompleter : public BaseAutoCompleter +{ +public: + MarkdownAutoCompleter(QObject *parent=0); + ~MarkdownAutoCompleter(); + + virtual int paragraphSeparatorAboutToBeInserted(QTextCursor &cursor); +private: + int isOrderList(QString &text, int start); + bool isUnorderList(QChar c1, QChar c2); +private: + QRegExp *orderRe; + Configuration *conf; +}; + +#endif // MARKDOWNAUTOCOMPLETER_H diff --git a/src/MdCharm/baseeditor/markdowneditor.cpp b/src/MdCharm/baseeditor/markdowneditor.cpp new file mode 100644 index 0000000..9ef01c8 --- /dev/null +++ b/src/MdCharm/baseeditor/markdowneditor.cpp @@ -0,0 +1,448 @@ +#include +#include +#include +#include + +#include "markdowneditor.h" +#include "markdowntohtml.h" +#include "configuration.h" +#include "markdownautocompleter.h" +#include "resource.h" +#include "util/spellcheck/spellchecker.h" + +MarkdownEditor::MarkdownEditor(QWidget *parent): + BaseEditor(parent) +{ + mdcharmGlobalInstance = MdCharmGlobal::getInstance(); + copyAsHtmlAction = new QAction(tr("Copy as Html"), this); + autoCompleter = new MarkdownAutoCompleter(this); + connect(copyAsHtmlAction, SIGNAL(triggered()), this, SLOT(copyAsHtmlSlot())); + setFrameShape(QTextEdit::NoFrame); +} + +void MarkdownEditor::keyPressEvent(QKeyEvent *event) +{ + if (event->key()==Qt::Key_Tab){ + tabText(); + event->accept(); + return; + } else if (event == QKeySequence::InsertParagraphSeparator) { + QTextCursor tc = textCursor(); + int newBlocks = autoCompleter->paragraphSeparatorAboutToBeInserted(tc); + if(newBlocks!=0){ + event->accept(); + ensureCursorVisible();//when at the end, cursor may not visible + return; + } + //WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + //if newBlocks==0 must not return and accept + } else if( (event->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier + && conf->isAutoPair()) { + + QTextCursor cursor = textCursor(); + QString text = event->text(); + const QString autoText = autoCompleter->autoComplete(cursor, text); + if(!autoText.isEmpty()){ + event->accept(); + cursor.beginEditBlock(); + cursor.insertText(text); + int pos = cursor.position(); + cursor.insertText(autoText); + cursor.setPosition(pos); + cursor.endEditBlock(); + setTextCursor(cursor); + return; + } + } + + BaseEditor::keyPressEvent(event); +} + +void MarkdownEditor::contextMenuEvent(QContextMenuEvent *e) +{ + // copy as Html + e->accept(); + QMenu *menu = createStandardContextMenu(); + menu->addSeparator(); + QAction* spellCheckStatusAction = new QAction(tr("Check spelling"), menu); + spellCheckStatusAction->setCheckable(true); + spellCheckStatusAction->setChecked(!spellCheckLanguage.isEmpty()); + menu->addAction(spellCheckStatusAction); + QList spellCheckLanguageActions = addSpellCheckLanguageActions(menu); + menu->addSeparator(); + copyAsHtmlAction->setEnabled(textCursor().hasSelection()); + menu->addAction(copyAsHtmlAction); + QList spellCheckActions; + if(e->reason()==QContextMenuEvent::Mouse&&!textCursor().hasSelection()){ + setTextCursor(cursorForPosition(e->pos())); + spellCheckActions = addSpellCheckActions(menu); + } + QAction *selected = menu->exec(e->globalPos()); + if(selected){ + if(spellCheckActions.indexOf(selected)!=-1){ + replaceTextInCurrentCursor(selected->text()); + } else if(selected==spellCheckStatusAction) { + if(selected->isChecked()){//enable spell check + spellCheckLanguage = conf->getSpellCheckLanguage(); + if(conf->getSpellCheckLanguage().isEmpty()) + spellCheckLanguage = conf->getAllAvailableSpellCheckDictNames().first(); + enableSpellCheck(); + } else {//disable spell check + spellCheckLanguage.clear(); + disableSpellCheck(); + } + } else if(spellCheckLanguageActions.indexOf(selected)!=-1 && + selected->data().canConvert(QVariant::String) && + !selected->data().toString().isEmpty() && + selected->data().toString()!=spellCheckLanguage){ + disableSpellCheck(); + spellCheckLanguage = selected->data().toString(); + enableSpellCheck(); + } + } + menu->deleteLater(); +} + +void MarkdownEditor::replaceTextInCurrentCursor(const QString &text) +{ + QTextCursor cursor = textCursor(); + cursor.select(QTextCursor::WordUnderCursor); + cursor.insertText(text); +} + +void MarkdownEditor::tabText() +{ + QTextCursor cursor = textCursor(); + QString text; + if(conf->isUseWhiteSpaceInsteadOfTab()) + text.append(" ");//4*space + else + text.append('\t'); + if(cursor.hasSelection()){ + int start = cursor.selectionStart(); + QTextBlock block = document()->findBlock(cursor.selectionStart()); + QTextBlock endBlock = document()->findBlock(cursor.selectionEnd()).next(); + cursor.clearSelection(); + cursor.beginEditBlock(); + cursor.setPosition(start); + cursor.movePosition(QTextCursor::StartOfBlock); + do { + cursor.insertText(text); + cursor.movePosition(QTextCursor::NextBlock); + block = block.next(); + } while(block.isValid() && block != endBlock); + cursor.endEditBlock(); + } else { + cursor.insertText(text); + } +} + +void MarkdownEditor::italicText() +{ + //italic + QTextCursor cursor = textCursor(); + if(cursor.hasSelection()) + { + int start = cursor.selectionStart(); + int currentPos = cursor.position(); + int end = cursor.selectionEnd(); + //add '*' + cursor.beginEditBlock(); + cursor.clearSelection(); + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, end-currentPos); + cursor.insertText("*"); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, abs(end-start)+1); + cursor.insertText("*"); + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, abs(end-start)); + cursor.endEditBlock(); + setTextCursor(cursor); + } else { + cursor.beginEditBlock(); + cursor.insertText("**"); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor); + cursor.endEditBlock(); + setTextCursor(cursor); + } +} + +void MarkdownEditor::boldText() +{ + //bold + QTextCursor cursor = textCursor(); + if(cursor.hasSelection()) + { + int start = cursor.selectionStart(); + int currentPos = cursor.position(); + int end = cursor.selectionEnd(); + QString text("**"); + //add "**" + cursor.beginEditBlock(); + cursor.clearSelection(); + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, end-currentPos); + cursor.insertText(text); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, abs(end-start)+text.length()); + cursor.insertText(text); + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, abs(end-start)); + cursor.endEditBlock(); + setTextCursor(cursor); + } else { + cursor.beginEditBlock(); + cursor.insertText("****"); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); + cursor.endEditBlock(); + setTextCursor(cursor); + } +} + +void MarkdownEditor::strikeThroughText() +{ + //strike through + QTextCursor cursor = textCursor(); + if(cursor.hasSelection()){ + int start = cursor.selectionStart(); + int currentPos = cursor.position(); + int end = cursor.selectionEnd(); + QString text("~~"); + //add "~~" + cursor.beginEditBlock(); + cursor.clearSelection(); + cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, end-currentPos); + cursor.insertText(text); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, abs(end-start)+text.length()); + cursor.insertText(text); + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, abs(end-start)); + cursor.endEditBlock(); + setTextCursor(cursor); + } else { + cursor.beginEditBlock(); + cursor.insertText("~~~~"); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2); + cursor.endEditBlock(); + setTextCursor(cursor); + } +} + +void MarkdownEditor::insertLinkOrPicuture(int type, const QString &text, + const QString &url, + const QString &title, + const QString &width, + const QString &height){ + QTextCursor cursor = textCursor(); + cursor.beginEditBlock(); + cursor.clearSelection(); + + if(conf->getMarkdownEngineType()==MarkdownToHtml::MultiMarkdown){ + QString insertText = QString::fromUtf8("[%1][]").arg(text); + if(type==MdCharmGlobal::ShortcutInsertPicture) + insertText.prepend("!"); + cursor.insertText(insertText); + int curBlockNum = cursor.blockNumber(); + int cursorPosition = cursor.position(); + cursor.movePosition(QTextCursor::End); + int endBlockNum = cursor.blockNumber(); + QString ref; + if(endBlockNum==curBlockNum || (endBlockNum-1)==curBlockNum) + { + ref.append("\n\n"); + } else { + QTextBlock endBlock = cursor.block(); + if(!endBlock.text().isEmpty() && endBlock.text()[0]=='['){ + ref.append("\n"); + } else { + ref.append("\n\n"); + } + } + ref.append(QString::fromUtf8("[%1]: %2").arg(text).arg(url)); + if(!title.isEmpty()) + ref.append(" \""+title+"\""); + if(!width.isEmpty()) + ref.append(" width="+width); + if(!height.isEmpty()) + ref.append(" height="+height); + cursor.insertText(ref); + cursor.setPosition(cursorPosition); + } else { + QString insertText = QString::fromUtf8("[%1](%2 \"%3\")").arg(text).arg(url).arg(title); + if(type==MdCharmGlobal::ShortcutInsertPicture) + insertText.prepend("!"); + cursor.insertText(insertText); + } + + cursor.endEditBlock(); + setTextCursor(cursor); +} + +void MarkdownEditor::insertCode(const QString &lan) +{ + QTextCursor cursor = textCursor(); + cursor.beginEditBlock(); + cursor.clearSelection(); + if(!cursor.atBlockStart()){ + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor); + cursor.insertBlock(); + } + cursor.insertText("```"+lan+"\nInsert Your Code Here.\n```"); + cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor); + cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + cursor.endEditBlock(); + setTextCursor(cursor); +} + +void MarkdownEditor::quoteText() +{ + QTextCursor cursor = textCursor(); + QString text("> "); + if(cursor.hasSelection()) + { + int start = cursor.selectionStart(); + QTextBlock block = document()->findBlock(cursor.selectionStart()); + QTextBlock endBlock = document()->findBlock(cursor.selectionEnd()).next(); + cursor.clearSelection(); + cursor.beginEditBlock(); + cursor.setPosition(start); + cursor.movePosition(QTextCursor::StartOfBlock); + do { + cursor.insertText(text); + cursor.movePosition(QTextCursor::NextBlock); + block = block.next(); + } while(block.isValid() && block != endBlock); + cursor.endEditBlock(); + } else { + cursor.beginEditBlock(); + if(!cursor.atBlockStart()) + cursor.insertBlock(); + cursor.insertText(text); + cursor.endEditBlock(); + } +} + +void MarkdownEditor::untabText() +{ + QTextCursor cursor = textCursor(); + if (cursor.hasSelection()){ + int start = cursor.selectionStart(); + QTextBlock block = document()->findBlock(cursor.selectionStart()); + QTextBlock endBlock = document()->findBlock(cursor.selectionEnd()).next(); + cursor.clearSelection(); + cursor.beginEditBlock(); + cursor.setPosition(start); + do { + QString text = block.text(); + cursor.movePosition(QTextCursor::StartOfBlock); + if(!text.isEmpty()&&!text.trimmed().isEmpty()){ + int i=0; + while(ifindBlock(cursor.position()); + QString text = block.text(); + int pos = cursor.positionInBlock(); + if(text.isEmpty()||text.trimmed().isEmpty()) + return; + int i=0; + for(i=0; igetMarkdownEngineType(), content.data(), content.length(), textResult); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(QString::fromUtf8(textResult.c_str(), textResult.length())); +} + +QList MarkdownEditor::addSpellCheckActions(QMenu *menu) +{ + QList spellCheckActions; + if(!conf->isCheckSpell()) + return spellCheckActions; + QTextCursor cursor = textCursor(); + cursor.select(QTextCursor::WordUnderCursor); + QString text = cursor.selectedText(); + SpellChecker *sc = mdCharmGlobal->getSpellChecker(spellCheckLanguage); + if(!sc) + return spellCheckActions; + if(sc->checkString(text).isEmpty()) + return spellCheckActions; + QStringList sl = sc->suggest(text); + if(sl.isEmpty()) + return spellCheckActions; + foreach (QString s, sl) { + spellCheckActions.append(new QAction(QIcon(Resource::SpellCheckIcon), s, menu)); + } + QList actions = menu->actions(); + if(actions.isEmpty()){ + menu->addActions(spellCheckActions); + } else { + menu->insertActions(actions.first(), spellCheckActions); + menu->insertSeparator(actions.first()); + } + return spellCheckActions; +} + +QList MarkdownEditor::addSpellCheckLanguageActions(QMenu *menu) +{ + if(spellCheckLanguage.isEmpty()) + return QList(); + QMenu *scl = new QMenu(tr("Languages"), menu); + QActionGroup *lanGroup = new QActionGroup(scl); + foreach (const QString s, mdCharmGlobal->getSpellCheckerLanguageList()) { + QString dictLocaleName = mdCharmGlobal->getDictLocaleName(s); + if(dictLocaleName.isEmpty()) + dictLocaleName = s; + QAction *action = new QAction(dictLocaleName, scl); + action->setCheckable(true); + action->setData(s); + scl->addAction(action); + lanGroup->addAction(action); + if(s==spellCheckLanguage) + action->setChecked(true); + } + menu->addMenu(scl); + return lanGroup->actions(); +} diff --git a/src/MdCharm/baseeditor/markdowneditor.h b/src/MdCharm/baseeditor/markdowneditor.h new file mode 100644 index 0000000..69f49f4 --- /dev/null +++ b/src/MdCharm/baseeditor/markdowneditor.h @@ -0,0 +1,43 @@ +#ifndef MARKDOWNEDITOR_H +#define MARKDOWNEDITOR_H + +#include "baseeditor.h" +#include "utils.h" + +class QAction; +class MarkdownAutoCompleter; + +class MarkdownEditor : public BaseEditor +{ + Q_OBJECT + +public: + MarkdownEditor(QWidget *parent=0); + +protected: + virtual void keyPressEvent(QKeyEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *e); +public: + void italicText(); + void boldText(); + void tabText(); + void untabText(); + void quoteText(); + void strikeThroughText(); + void insertLinkOrPicuture(int type, const QString &text, const QString &url, + const QString &title, const QString &width, const QString &height); + void insertCode(const QString &lan); +private: + void replaceTextInCurrentCursor(const QString &text); +private slots: + void copyAsHtmlSlot(); +private: + QList addSpellCheckActions(QMenu *menu); + QList addSpellCheckLanguageActions(QMenu *menu); +private: + MdCharmGlobal *mdcharmGlobalInstance; + QAction *copyAsHtmlAction; + MarkdownAutoCompleter *autoCompleter; +}; + +#endif // MARKDOWNEDITOR_H diff --git a/src/MdCharm/basewebview/basewebview.cpp b/src/MdCharm/basewebview/basewebview.cpp new file mode 100644 index 0000000..ab73995 --- /dev/null +++ b/src/MdCharm/basewebview/basewebview.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "basewebview.h" +#include "resource.h" + +BaseWebView::BaseWebView(QWidget *parent) : + QWebView(parent) +{ +} + +void BaseWebView::contextMenuEvent(QContextMenuEvent *e) +{ + e->accept(); + QMenu *menu = new QMenu(this); + QAction *copyAction = new QAction(QIcon(Resource::CopyIcon), tr("Copy"), menu); + if(page()->hasSelection() && !page()->selectedText().isEmpty()){ + menu->addAction(copyAction); + menu->addSeparator(); + } + QAction *printAction = new QAction(QIcon(Resource::PrintLargeIcon), tr("Print..."), menu); + menu->addAction(printAction); + QAction *printPreviewAction = new QAction(tr("Print Preview..."), menu); + menu->addAction(printPreviewAction); + QAction *action = menu->exec(e->globalPos()); + if(action==copyAction){ + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(page()->selectedText()); + } else if(action==printAction){ + QPrinter webViewPrinter(QPrinter::HighResolution); + QPrintDialog printDialog(&webViewPrinter, this); + printDialog.setWindowTitle(tr("Print...")); + if(printDialog.exec()==QDialog::Accepted) + print(&webViewPrinter); + } else if(action==printPreviewAction){ + QPrinter printer(QPrinter::HighResolution); + QPrintPreviewDialog ppd(&printer, this, Qt::WindowMaximizeButtonHint); + ppd.setWindowTitle(tr("Print Preview...")); + connect(&ppd, SIGNAL(paintRequested(QPrinter*)), this, SLOT(print(QPrinter*))); + ppd.showMaximized(); + ppd.exec(); + } + menu->deleteLater(); +} diff --git a/src/MdCharm/basewebview/basewebview.h b/src/MdCharm/basewebview/basewebview.h new file mode 100644 index 0000000..e2d77d6 --- /dev/null +++ b/src/MdCharm/basewebview/basewebview.h @@ -0,0 +1,18 @@ +#ifndef BASEWEBVIEW_H +#define BASEWEBVIEW_H + +#include + +class BaseWebView : public QWebView +{ + Q_OBJECT +public: + explicit BaseWebView(QWidget *parent = 0); +protected: + virtual void contextMenuEvent(QContextMenuEvent *e); +signals: + +public slots: +}; + +#endif // BASEWEBVIEW_H diff --git a/src/MdCharm/basewebview/markdownwebview.cpp b/src/MdCharm/basewebview/markdownwebview.cpp new file mode 100644 index 0000000..0bbfb1d --- /dev/null +++ b/src/MdCharm/basewebview/markdownwebview.cpp @@ -0,0 +1,11 @@ +#include "markdownwebview.h" + +MarkdownWebView::MarkdownWebView(QWidget *parent) : + BaseWebView(parent) +{ +} + +void MarkdownWebView::reload() +{ + //Do Nothing +} diff --git a/src/MdCharm/basewebview/markdownwebview.h b/src/MdCharm/basewebview/markdownwebview.h new file mode 100644 index 0000000..6398e1e --- /dev/null +++ b/src/MdCharm/basewebview/markdownwebview.h @@ -0,0 +1,18 @@ +#ifndef MARKDOWNWEBVIEW_H +#define MARKDOWNWEBVIEW_H + +#include "basewebview.h" + +class MarkdownWebView : public BaseWebView +{ + Q_OBJECT +public: + explicit MarkdownWebView(QWidget *parent = 0); + +signals: + +public slots: + void reload(); +}; + +#endif // MARKDOWNWEBVIEW_H diff --git a/src/MdCharm/browereditareawidget.cpp b/src/MdCharm/browereditareawidget.cpp new file mode 100644 index 0000000..a5e4140 --- /dev/null +++ b/src/MdCharm/browereditareawidget.cpp @@ -0,0 +1,137 @@ +#include +#include +#include + +#ifdef QT_V5 +#include +#endif + +#include + +#include "browereditareawidget.h" +#include "version.h" +#include "configuration.h" +#include "basewebview/basewebview.h" + +BrowerWebkitHandler::BrowerWebkitHandler() +{ + conf = Configuration::getInstance(); +} + +void BrowerWebkitHandler::domReady() +{ + updateRecentFiles(); +} + +void BrowerWebkitHandler::updateRecentFiles() +{ + QStringList rf = conf->getRecentFileList(); + QString strList = rf.join(QString::fromLatin1("|")); +#ifdef Q_OS_WIN + strList.replace('/','\\');//show file list in html, keep this +#endif + emit updateRecentFileList(strList); +} + +BrowerWebkitHandler::~BrowerWebkitHandler() +{ +} + +BrowerEditAreaWidget::BrowerEditAreaWidget(const QString &filePath) : + EditAreaWidget(filePath, 0) + +{ + em.setEditorType(EditorModel::BROWER); + webkitHandler = new BrowerWebkitHandler; + brower = new BaseWebView(this); + brower->setAcceptDrops(false); + brower->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); + addJavascriptObject(); + + initSignalsAndSlots(); + initContent(filePath); + +} + +BrowerEditAreaWidget::~BrowerEditAreaWidget() +{ + webkitHandler->deleteLater(); +} + +void BrowerEditAreaWidget::initContent(const QString &filePath) +{ + QString versionInfo("Version %1 revision %2 (%3)"); + versionInfo = versionInfo.arg(QString::fromLatin1(VERSION_STR)) + .arg(QString::fromLatin1(REVISION_STR).left(10)) + .arg(QString::fromLatin1(BUILT_TIME_STR)); + QFile startHereFile(filePath); + if(!startHereFile.open(QIODevice::ReadOnly|QIODevice::Text)) + { + return; + } + brower->setHtml(QString::fromUtf8(startHereFile.readAll()).arg(versionInfo)); + startHereFile.close(); +} + +void BrowerEditAreaWidget::initSignalsAndSlots() +{ + connect(brower, SIGNAL(linkClicked(QUrl)), + this, SLOT(openLinkOutside(QUrl))); + connect(brower->page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), + this, SLOT(addJavascriptObject())); +} + +EditAreaWidget* BrowerEditAreaWidget::clone() +{ + Q_ASSERT(isEditActionOptionEnabled(AllowSplit)); + return NULL; +} + +void BrowerEditAreaWidget::setHtml(const QString &html, const QUrl &baseUrl) +{ + brower->setHtml(html, baseUrl); +} + +void BrowerEditAreaWidget::setUrl(const QUrl &url) +{ + brower->setUrl(url); +} + +void BrowerEditAreaWidget::resizeEvent(QResizeEvent *event) +{ + brower->resize(event->size()); +} + +void BrowerEditAreaWidget::setText(const QString &html) +{ + setHtml(html); +} + +void BrowerEditAreaWidget::cut(){} + +void BrowerEditAreaWidget::copy(){} + +void BrowerEditAreaWidget::paste(){} + +void BrowerEditAreaWidget::redo(){} + +void BrowerEditAreaWidget::undo(){} + +void BrowerEditAreaWidget::selectAll(){} + +bool BrowerEditAreaWidget::isModified(){ return false; } +void BrowerEditAreaWidget::setModified(bool isModi){Q_UNUSED(isModi)} +bool BrowerEditAreaWidget::isUndoAvailable(){ return false; } +bool BrowerEditAreaWidget::isRedoAvailable(){ return false; } + +void BrowerEditAreaWidget::addJavascriptObject() +{ + brower->page()->mainFrame() + ->addToJavaScriptWindowObject("browerWebkitHandler", webkitHandler); +} + +void BrowerEditAreaWidget::openLinkOutside(const QUrl &url) +{ + assert(true); + QDesktopServices::openUrl(url); +} diff --git a/src/MdCharm/browereditareawidget.h b/src/MdCharm/browereditareawidget.h new file mode 100644 index 0000000..b3f56fc --- /dev/null +++ b/src/MdCharm/browereditareawidget.h @@ -0,0 +1,64 @@ +#ifndef BROWEREDITAREAWIDGET_H +#define BROWEREDITAREAWIDGET_H + +#include + +#include "editareawidget.h" + +class BaseWebView; + +class BrowerWebkitHandler : public QObject +{ + Q_OBJECT +public: + BrowerWebkitHandler(); + ~BrowerWebkitHandler(); +signals: + void updateRecentFileList(const QString list); + void openRecentFile(const QString filePath); +public slots: + void domReady(); + void updateRecentFiles(); +private: + Configuration *conf; +}; + +class BrowerEditAreaWidget : public EditAreaWidget +{ + Q_OBJECT +public: + BrowerEditAreaWidget(const QString &filePath=QString()); + ~BrowerEditAreaWidget(); + void setHtml(const QString &html, const QUrl &baseUrl= QUrl()); + void setUrl(const QUrl &url); + virtual void setText(const QString &text); + BrowerWebkitHandler* getWebkitHanlder(){return webkitHandler;} +private: + void initContent(const QString &filePath); + void initSignalsAndSlots(); +public: + virtual EditAreaWidget* clone(); +private: + BaseWebView *brower; + BrowerWebkitHandler *webkitHandler; +protected: + virtual void resizeEvent(QResizeEvent *event); + +public slots: + virtual void copy(); + virtual void cut(); + virtual void paste(); + virtual void redo(); + virtual void undo(); + virtual void selectAll(); + virtual bool isModified(); + virtual bool isUndoAvailable(); + virtual bool isRedoAvailable(); + virtual void setModified(bool isModi); + + void addJavascriptObject(); +private slots: + void openLinkOutside(const QUrl &url); +}; + +#endif // BROWEREDITAREAWIDGET_H diff --git a/src/MdCharm/conf/configuredialog.cpp b/src/MdCharm/conf/configuredialog.cpp new file mode 100644 index 0000000..97df439 --- /dev/null +++ b/src/MdCharm/conf/configuredialog.cpp @@ -0,0 +1,106 @@ +#include + +#include "configuredialog.h" +#include "pages.h" +#include "resource.h" + +ConfigureDialog::ConfigureDialog(QWidget *parent, Qt::WindowFlags f) : + QDialog(parent, f) +{ + contentsWidget = new QListWidget; + contentsWidget->setViewMode(QListView::ListMode); + contentsWidget->setIconSize(QSize(32, 32)); + contentsWidget->setMovement(QListView::Static); + contentsWidget->setMaximumWidth(132); + contentsWidget->setSpacing(2); + + pagesWidget = new QStackedWidget; + envPage = new EnvironmentPage; + pagesWidget->insertWidget(ENVIRONMENT, envPage); + textEditorPage = new TextEditorPage; + pagesWidget->insertWidget(TEXTEDITOR, textEditorPage);//ownership to QStackedWidget, no need to delete + stylesPage = new StylesPage; + pagesWidget->insertWidget(STYLES, stylesPage); + + buttons = new QDialogButtonBox(QDialogButtonBox::Ok| + QDialogButtonBox::Cancel| + QDialogButtonBox::Apply); + + QHBoxLayout *horizontalLayout = new QHBoxLayout; + horizontalLayout->addWidget(contentsWidget); + horizontalLayout->addWidget(pagesWidget, 1); + + QHBoxLayout *buttonsLayout = new QHBoxLayout; + buttonsLayout->addWidget(buttons); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(horizontalLayout); + mainLayout->addLayout(buttonsLayout); + setLayout(mainLayout); + + setWindowTitle(tr("Preference")); + initPagesIndex(); + initSignalsAndSlots(); + + setMinimumSize(540, 400); +} + +void ConfigureDialog::initPagesIndex() +{ + QListWidgetItem *envButton = new QListWidgetItem(contentsWidget); + envButton->setText(tr("Environment")); + envButton->setIcon(QIcon(Resource::EnvPageIcon)); + envButton->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter); + envButton->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + QListWidgetItem *textEditorButton = new QListWidgetItem(contentsWidget); + textEditorButton->setText(tr("Text Editor")); + textEditorButton->setIcon(QIcon(Resource::TextEditorIcon)); + textEditorButton->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter); + textEditorButton->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + + QListWidgetItem *styleButton = new QListWidgetItem(contentsWidget); + styleButton->setText(tr("Styles")); + styleButton->setIcon(QIcon(Resource::StylesIcon)); + styleButton->setTextAlignment(Qt::AlignLeft|Qt::AlignVCenter); + styleButton->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); +} + +void ConfigureDialog::initSignalsAndSlots() +{ + QObject::connect(buttons, SIGNAL(clicked(QAbstractButton*)), + this, SLOT(onButtonsClicked(QAbstractButton*))); + QObject::connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*))); + QObject::connect(envPage, SIGNAL(updateShortcut(int,QString)), + this, SIGNAL(updateShortcut(int,QString))); +} + +void ConfigureDialog::savePageConfigurations() +{ + envPage->saveConfig(); + textEditorPage->saveConfig(); + stylesPage->saveConfig(); +} + +void ConfigureDialog::onButtonsClicked(QAbstractButton *button) +{ + QDialogButtonBox::ButtonRole br = buttons->buttonRole(button); + if(br == QDialogButtonBox::AcceptRole) // OK + { + savePageConfigurations(); + accept(); + } else if (br == QDialogButtonBox::RejectRole){// Cancel + reject(); + } else if (br == QDialogButtonBox::ApplyRole){ // Apply + savePageConfigurations(); + emit updateConfiguration(); + } +} + +void ConfigureDialog::changePage(QListWidgetItem *current, QListWidgetItem *pre) +{ + if(!current) + current = pre; + pagesWidget->setCurrentIndex(contentsWidget->row(current)); +} diff --git a/src/MdCharm/conf/configuredialog.h b/src/MdCharm/conf/configuredialog.h new file mode 100644 index 0000000..1dd68b0 --- /dev/null +++ b/src/MdCharm/conf/configuredialog.h @@ -0,0 +1,49 @@ +#ifndef CONFIGUREDIALOG_H +#define CONFIGUREDIALOG_H + +#include + +class QListWidget; +class QStackedWidget; +class QDialogButtonBox; +class QAbstractButton; +class QListWidgetItem; +class StylesPage; +class EnvironmentPage; +class TextEditorPage; + +class ConfigureDialog : public QDialog +{ + Q_OBJECT +public: + enum ListIndex + { + ENVIRONMENT=0, + TEXTEDITOR, + STYLES + }; +public: + ConfigureDialog(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowTitleHint|Qt::WindowSystemMenuHint); +private: + void initPagesIndex(); + void initSignalsAndSlots(); + void savePageConfigurations(); + +signals: + void updateConfiguration(); + void updateShortcut(int s, const QString &newShortcut); +public slots: + void onButtonsClicked(QAbstractButton *button); + void changePage(QListWidgetItem *current, QListWidgetItem *pre); +private: + QListWidget *contentsWidget; + QStackedWidget *pagesWidget; + + QDialogButtonBox *buttons; + + EnvironmentPage *envPage; + TextEditorPage *textEditorPage; + StylesPage *stylesPage; +}; + +#endif // CONFIGUREDIALOG_H diff --git a/src/MdCharm/conf/environmentpage.ui b/src/MdCharm/conf/environmentpage.ui new file mode 100644 index 0000000..770b4d3 --- /dev/null +++ b/src/MdCharm/conf/environmentpage.ui @@ -0,0 +1,250 @@ + + + EnvPage + + + + 0 + 0 + 400 + 300 + + + + EnvConfig + + + 0 + + + + General + + + + + + System + + + + + + + + Show Splash + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Check for updates on startup + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Markdown Engine + + + + + + + + Markdown Engine + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + File Extension + + + + + + + + FileType: + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + + + + + + + + + Extension: + + + + + + + + + + + + Add New: + + + + + + + + + + Add + + + Add + + + + :/conf/add.png:/conf/add.png + + + + + + + Delete + + + Delete + + + + :/conf/delete.png:/conf/delete.png + + + + + + + + + + + + Keyboard + + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + + + + + + + + + diff --git a/src/MdCharm/conf/pages.cpp b/src/MdCharm/conf/pages.cpp new file mode 100644 index 0000000..ff7f719 --- /dev/null +++ b/src/MdCharm/conf/pages.cpp @@ -0,0 +1,455 @@ +#include +#include + +#include "markdowntohtml.h" +#include "pages.h" +#include "../utils.h" +#include "ui_environmentpage.h" +#include "ui_texteditorpage.h" +#include "ui_stylespage.h" +#include "util/gui/shortcutlineedit.h" +//---------------------- EnvironmentPage --------------------------------------- +EnvironmentPage::EnvironmentPage(QWidget *parent) : + QTabWidget(parent), + ui(new Ui::EnvPage) + +{ + ui->setupUi(this); + conf = Configuration::getInstance(); + showSplashCheckBox = ui->showSplashCheckBox; + showSplashCheckBox->setChecked(conf->isShowSplash()); + checkUpdatesCheckBox = ui->checkUpdatesCheckBox; + checkUpdatesCheckBox->setChecked(conf->isCheckForUpdates()); + for(int i=1; ifileTypeListWidget->addItem(conf->fileTypeToString(i)); + } + if(Configuration::FileTypeNum>=2){ + ui->fileTypeListWidget->setCurrentRow(0); + QString exts = conf->getFileExtension().at(1); + QStringList extsList = exts.split("|"); + ui->extensionListWidget->addItems(extsList); + } + ui->markdownEngineComboBox->addItem(tr("Default"), MarkdownToHtml::PHPMarkdownExtra); + ui->markdownEngineComboBox->addItem(tr("MultiMarkdown"), MarkdownToHtml::MultiMarkdown); + ui->markdownEngineComboBox->setCurrentIndex(conf->getMarkdownEngineType()==MarkdownToHtml::PHPMarkdownExtra ? 0 : 1); + + initKeyboardData(); + + connect(ui->fileTypeListWidget, SIGNAL(currentRowChanged(int)), + this, SLOT(fileTypeListWidgetCurrentRowChangedSlot(int))); + connect(ui->addToolButton, SIGNAL(clicked()), + this, SLOT(addToolBtnSlot())); + connect(ui->deleteToolButton, SIGNAL(clicked()), + this, SLOT(deleteToolBtnSlot())); +} + +EnvironmentPage::~EnvironmentPage() +{ + delete ui; +} + +void EnvironmentPage::saveConfig() +{ + conf->setShowSplash(showSplashCheckBox->isChecked()); + conf->setCheckForUpdates(checkUpdatesCheckBox->isChecked()); + int index = ui->markdownEngineComboBox->currentIndex(); + int type = ui->markdownEngineComboBox->itemData(index).toInt(); + switch (type) { + case MarkdownToHtml::MultiMarkdown: + conf->setMarkdownEngineType(MarkdownToHtml::MultiMarkdown); + break; + default: + conf->setMarkdownEngineType(MarkdownToHtml::PHPMarkdownExtra); + break; + } + +} + +void EnvironmentPage::fileTypeListWidgetCurrentRowChangedSlot(int currentRow) +{ + ui->extensionListWidget->clear(); + ui->extensionListWidget->addItems(conf->getFileExtension().at(currentRow+1).split("|")); +} + +void EnvironmentPage::addToolBtnSlot() +{ + QString target = ui->newExtensionLineEdit->text().trimmed(); + if(target.isEmpty()) + return; + if(isFileExtAlreadyExists(target)){ + //warning and return + QMessageBox::warning(this, tr("File extension already in use"), tr("File extension already in use")); + return; + } + ui->extensionListWidget->addItem(target); + reCalculateExts(); + ui->newExtensionLineEdit->clear(); +} + +void EnvironmentPage::onShortcutLineEditChanged(const QString &text) +{ + QModelIndex index = ui->shortcutTableView->currentIndex(); + if(!index.isValid()) + return; + QModelIndex keyIndex = keyboardModel->index(index.row(), 1); + if(!keyIndex.isValid()) + return; + if(keyIndex.data().toString()==text) + return; + keyboardModel->setData(keyIndex, text); + recalculateKeyMapDuplicate(); + QStandardItem* item = keyboardModel->itemFromIndex(keyIndex); + if(!item) + return; + conf->setKeyboardShortcut(item->data().toInt(), text); + emit updateShortcut( item->data().toInt(), text); +} + +void EnvironmentPage::recalculateKeyMapDuplicate() +{ + QMap checkKeyMap; + for(int i=0; irowCount(); i++){ + QModelIndex ix = keyboardModel->index(i, 1); + if(!ix.isValid()) + return; + if(checkKeyMap.contains(ix.data().toString())) + checkKeyMap[ix.data().toString()]++; + else + checkKeyMap[ix.data().toString()] = 1; + keyboardModel->setData(ix, QColor(Qt::black), Qt::ForegroundRole); + } + QMapIterator it(checkKeyMap); + while(it.hasNext()){ + it.next(); + if(it.value()>1){ + for(int i=0; irowCount(); i++){ + QModelIndex ix = keyboardModel->index(i, 1); + if(!ix.isValid()) + return; + if(ix.data().toString()==it.key()) + keyboardModel->setData(ix, QColor(Qt::red), Qt::ForegroundRole); + } + } + } +} + +void EnvironmentPage::onShortcutItemClicked(const QModelIndex &index) +{ + QModelIndex keyIndex = keyboardModel->index(index.row(), 1); + if(!keyIndex.isValid()) + return; + shortcutLineEdit->setText(keyIndex.data().toString()); + shortcutLineEdit->setEnabled(true); + resetShortcutPushButton->setEnabled(true); +} + +void EnvironmentPage::resetKeyboardShortcut() +{ + QModelIndex index = ui->shortcutTableView->currentIndex(); + if(!index.isValid()) + return; + QStandardItem* item = keyboardModel->itemFromIndex(index); + if(!item) + return; + QString key = conf->getKeyboardDefaultShortcut(item->data().toInt()); + shortcutLineEdit->setText(key); + onShortcutLineEditChanged(key); +} + +void EnvironmentPage::deleteToolBtnSlot() +{ + QListWidgetItem *r = ui->extensionListWidget->takeItem(ui->extensionListWidget->currentRow()); + delete r; + reCalculateExts(); +} + +void EnvironmentPage::reCalculateExts() +{ + QStringList exts = conf->getFileExtension(); + int type = ui->fileTypeListWidget->currentRow() + 1; + if(type>=exts.length()){ +#ifdef MDCHARM_DEBUG + Q_ASSERT(0 && "this should not be happend"); +#endif + return; + } + + QStringList newExts; + for(int i=0; iextensionListWidget->count(); i++){ + QListWidgetItem* cur = ui->extensionListWidget->item(i); + newExts << cur->text(); + } + exts[type] = newExts.join("|"); + conf->setFileExtension(exts); +} + +bool EnvironmentPage::isFileExtAlreadyExists(QString &text) +{ + for(int i=0; iextensionListWidget->count(); i++){ + if(ui->extensionListWidget->item(i)->text()==text) + return true; + } + return false; +} + +void EnvironmentPage::initKeyboardData() +{ + shortcutLabel = new QLabel(tr("Shortcut:"), this); + shortcutLineEdit = new ShortcutLineEdit(this); + shortcutLineEdit->setEnabled(false); + resetShortcutPushButton = new QPushButton(tr("reset"), this); + resetShortcutPushButton->setEnabled(false); + QHBoxLayout *shortcutLayout = new QHBoxLayout; + shortcutLayout->addWidget(shortcutLabel); + shortcutLayout->addWidget(shortcutLineEdit); + shortcutLayout->addWidget(resetShortcutPushButton); + ui->keyboardVerticalLayout->addLayout(shortcutLayout); + + keyboardModel = new QStandardItemModel(this); + keyboardModel->setColumnCount(2); + keyboardModel->setHeaderData(0, Qt::Horizontal, tr("Command")); + keyboardModel->setHeaderData(1, Qt::Horizontal, tr("Shortcut")); + + QList items; + QStandardItem *ic, *is; + MdCharmGlobal *global = MdCharmGlobal::getInstance(); + for(int i=0; igetShortDescriptionText(i)); + ic->setData(i); + is = new QStandardItem(conf->getKeyboardShortcut(i)); + is->setData(i); + items << ic << is; + keyboardModel->appendRow(items); + } + recalculateKeyMapDuplicate(); + ui->shortcutTableView->setModel(keyboardModel); + + + connect(ui->shortcutTableView, SIGNAL(clicked(QModelIndex)), this, SLOT(onShortcutItemClicked(QModelIndex))); + connect(shortcutLineEdit, SIGNAL(textEdited(QString)), this, SLOT(onShortcutLineEditChanged(QString))); + connect(resetShortcutPushButton, SIGNAL(clicked()), this, SLOT(resetKeyboardShortcut())); +} + +void EnvironmentPage::resizeEvent(QResizeEvent *e) +{ + ui->shortcutTableView->setColumnWidth(0, e->size().width()*0.64); + ui->shortcutTableView->setColumnWidth(1, e->size().width()*0.2); + QTabWidget::resizeEvent(e); +} + +//------------------------- TextEditorPage ------------------------------------- +TextEditorPage::TextEditorPage(QWidget *parent) : + QTabWidget(parent), + ui(new Ui::TextEditorPage) +{ + ui->setupUi(this); + conf = Configuration::getInstance(); + mdcharmGlobal = MdCharmGlobal::getInstance(); + fontFamilyComboBox = ui->fontComboBox; + fontSizeComboBox = ui->fontSizeComboBox; + tabSizeSpinBox = ui->tabSizeSpinBox; + enableTWCheckBox = ui->enableTWCheckBox; + displayLineNumberCheckBox = ui->displayLineNumberCheckBox; + highlightCLCheckBox = ui->highlightCLCheckBox; + defaultEncodingComboBox = ui->defaultEncodingComboBox; + utf8BOMComboBox = ui->utf8BOMComboBox; + + assert((fontSizeComboBox->count() > getFontSizeIndex(conf->getFontSize()))); + fontSizeComboBox->setCurrentIndex(getFontSizeIndex(conf->getFontSize())); + fontFamilyComboBox->setCurrentFont(QFont(conf->getFontFamily())); +// tabSizeSpinBox->setValue(conf->getTabSize()); + displayLineNumberCheckBox->setChecked(conf->isDisplayLineNumber()); + enableTWCheckBox->setChecked(conf->isEnableTextWrapping()); + highlightCLCheckBox->setChecked(conf->isHighlightCurrentLine()); + prepareTabOptionData(); + prepareDefaultEncodingData(); + prepareUtf8BOMData(); + prepareSpellCheckLanguageData(); + ui->autoIndentationCheckBox->setChecked(conf->isAutoIndentation()); + + if(conf->isDisplayRightColumnMargin()){ + ui->rightMarginCheckBox->setChecked(true); + ui->rightMarginSpinBox->setValue(conf->getRightMarginColumn()); + } else { + ui->rightMarginCheckBox->setChecked(false); + ui->rightMarginSpinBox->setEnabled(false); + ui->rightMarginSpinBox->setValue(80); + } + ui->autoPairCheckBox->setChecked(conf->isAutoPair()); + connect(ui->rightMarginCheckBox, SIGNAL(toggled(bool)), ui->rightMarginSpinBox, SLOT(setEnabled(bool))); +} + +TextEditorPage::~TextEditorPage() +{ + delete ui; +} + +void TextEditorPage::saveConfig() +{ + // Font Family + conf->setFontFamily(fontFamilyComboBox->currentFont().family()); + // Font Size + bool b; + int r = fontSizeComboBox->currentText().toInt(&b); + conf->setFontSize(b ? r : 10); + //Tab Key Width Size + conf->setTabSize(tabSizeSpinBox->value()); + conf->setUseWhiteSpaceInsteadOfTab(ui->useSpaceInsteadCheckBox->isChecked()); + //Display Line Number + conf->setDisplayLineNumber(displayLineNumberCheckBox->isChecked()); + //Highlight Current Line + conf->setHighlightCurrentLine(highlightCLCheckBox->isChecked()); + //Enable Text Wrapping + conf->setEnableTextWrapping(enableTWCheckBox->isChecked()); + //Default Encoding + conf->setDefaultEncoding(defaultEncodingComboBox->currentText()); + //UTF-8 BOM Options + MdCharmGlobal::UTF8BOM ub; + switch(utf8BOMComboBox->itemData(utf8BOMComboBox->currentIndex()).toInt()) + { + default: + case MdCharmGlobal::Keep: + ub = MdCharmGlobal::Keep; + break; + case MdCharmGlobal::Add: + ub = MdCharmGlobal::Add; + break; + case MdCharmGlobal::Delete: + ub = MdCharmGlobal::Delete; + break; + } + conf->setUtf8BOMOptions(ub); + conf->setCheckSpell(ui->spellCheckCheckBox->isChecked()); + if(conf->isCheckSpell()) + conf->setSpellCheckLanguage(ui->spellCheckComboBox->itemData(ui->spellCheckComboBox->currentIndex()).toString()); + //Auto Indentation + conf->setAutoIndentation(ui->autoIndentationCheckBox->isChecked()); + //Right margin + if(ui->rightMarginCheckBox->isChecked()){ + conf->setRightMarginColumn(ui->rightMarginSpinBox->value()); + } else { + conf->setRightMarginColumn(-1); + } + + if(ui->autoPairCheckBox->isChecked()!=conf->isAutoPair()){ + conf->setAutoPair(ui->autoPairCheckBox->isChecked()); + } +} + +int TextEditorPage::getFontSizeIndex(const int size) +{ + QList fontSizeList; + fontSizeList << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 14 << 16 << 18 << 20 << 22 << 24 << 26 << 28 << 36 << 48 << 72; + for( int i=0; i < fontSizeList.size(); i++) + { + if (fontSizeList.at(i) == size) + return i; + } + return 4;//default size 10 +} + +void TextEditorPage::prepareTabOptionData() +{ + if(conf->isUseWhiteSpaceInsteadOfTab()){ + ui->tabSizeSpinBox->setValue(4); + ui->tabSizeSpinBox->setEnabled(false); + ui->useSpaceInsteadCheckBox->setChecked(true); + } else { + ui->tabSizeSpinBox->setValue(conf->getTabSize()); + } + connect(ui->useSpaceInsteadCheckBox, SIGNAL(toggled(bool)), + this, SLOT(useWhiteSpaceInsteadOfTabSlot(bool))); +} + +void TextEditorPage::prepareDefaultEncodingData() +{ + QStringList sl = Utils::getEncodingList(); + defaultEncodingComboBox->addItems(sl); + defaultEncodingComboBox->setCurrentIndex(sl.indexOf(conf->getDefaultEncoding())); +} + +void TextEditorPage::prepareUtf8BOMData() +{ + utf8BOMComboBox->addItem(tr("Add If Encoding is UTF-8"), MdCharmGlobal::Add); + utf8BOMComboBox->addItem(tr("Keep If Already Present"), MdCharmGlobal::Keep); + utf8BOMComboBox->addItem(tr("Always Delete"), MdCharmGlobal::Delete); + utf8BOMComboBox->setCurrentIndex(conf->getUtf8BOMOptions()); +} + +void TextEditorPage::prepareSpellCheckLanguageData() +{ + ui->spellCheckCheckBox->setChecked(conf->isCheckSpell()); + QStringList lanList; + const QStringList allDicts = conf->getAllAvailableSpellCheckDictNames(); + for(int i=0; igetDictLocaleName(dictName); + if(dictLocaleName.isEmpty()) + dictLocaleName = dictName; + ui->spellCheckComboBox->addItem(dictLocaleName, dictName); + lanList << dictName; + } + if(!conf->getSpellCheckLanguage().isEmpty()) + ui->spellCheckComboBox->setCurrentIndex(lanList.indexOf(conf->getSpellCheckLanguage())); + if(ui->spellCheckComboBox->currentIndex()==-1&&ui->spellCheckComboBox->count()>=1) + ui->spellCheckComboBox->setCurrentIndex(0); + ui->spellCheckComboBox->setEnabled(conf->isCheckSpell()); + connect(ui->spellCheckCheckBox, SIGNAL(toggled(bool)), + this, SLOT(spellCheckCheckBoxSlot(bool))); +} + +void TextEditorPage::spellCheckCheckBoxSlot(bool isChecked) +{ + ui->spellCheckComboBox->setEnabled(isChecked); +} + +void TextEditorPage::useWhiteSpaceInsteadOfTabSlot(bool isChecked) +{ + if(isChecked) + ui->tabSizeSpinBox->setValue(4); + ui->tabSizeSpinBox->setEnabled(!isChecked); +} +//----------------------------- StylesPage ------------------------------------- +StylesPage::StylesPage(QWidget *parent) : + QTabWidget(parent), + ui(new Ui::StylesPage) +{ + ui->setupUi(this); + conf = Configuration::getInstance(); + useDefaultCheckBox = ui->useDefaultCheckBox; + customCSSPlainTextEdit = ui->customCSSPlainTextEdit; + customCSSPlainTextEdit->setFont(QFont(conf->getFontFamily(),conf->getFontSize())); + customCSSPlainTextEdit->setPlainText(conf->getMarkdownCSS()); + cssHighLighter = new CSSHighLighter(customCSSPlainTextEdit->document()); + if(conf->isUseMarkdownDefaultCSS()) + { + useDefaultCheckBox->setChecked(true); + customCSSPlainTextEdit->setReadOnly(true); + } + else + { + useDefaultCheckBox->setChecked(false); + customCSSPlainTextEdit->setReadOnly(false); + } + + connect(useDefaultCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(checkStateChanged())); +} + +StylesPage::~StylesPage() +{ + delete cssHighLighter; + cssHighLighter = NULL; + delete ui; +} + +void StylesPage::saveConfig() +{ + conf->setMarkdownCSS(useDefaultCheckBox->isChecked(), customCSSPlainTextEdit->toPlainText()); +} + +void StylesPage::checkStateChanged() +{ + customCSSPlainTextEdit->setReadOnly(useDefaultCheckBox->isChecked()); +} diff --git a/src/MdCharm/conf/pages.h b/src/MdCharm/conf/pages.h new file mode 100644 index 0000000..6fbbc70 --- /dev/null +++ b/src/MdCharm/conf/pages.h @@ -0,0 +1,128 @@ +#ifndef PAGES_H +#define PAGES_H + +#include +#include +#include +#include + +#include "../configuration.h" +#include "util/syntax/hightlighter.h" + +class QFontComboBox; +class QComboBox; +class QSpinBox; +class QCheckBox; +class QPlainTextEdit; +class QLabel; +class ShortcutLineEdit; +class QPushButton; + +/*---------------------------------Example-------------------------------------- + +class Pages : public QWidget +{ + Q_OBJECT +public: + explicit Pages(QWidget *parent = 0); + +signals: + +public slots: + +}; + +------------------------------------------------------------------------------*/ +namespace Ui { +class EnvPage; +class TextEditorPage; +class StylesPage; +} +class EnvironmentPage : public QTabWidget +{ + Q_OBJECT +public: + explicit EnvironmentPage(QWidget *parent = 0); + ~EnvironmentPage(); + void saveConfig(); +private: + void reCalculateExts(); + bool isFileExtAlreadyExists(QString &text); + void initKeyboardData(); + void recalculateKeyMapDuplicate(); +protected: + void resizeEvent(QResizeEvent *e); +signals: + void updateShortcut(int s, const QString &text); +private slots: + void fileTypeListWidgetCurrentRowChangedSlot(int currentRow); + void deleteToolBtnSlot(); + void addToolBtnSlot(); + void onShortcutLineEditChanged(const QString &text); + void onShortcutItemClicked(const QModelIndex &index); + void resetKeyboardShortcut(); +private: + Ui::EnvPage *ui; + Configuration *conf; + QCheckBox *showSplashCheckBox; + QCheckBox *checkUpdatesCheckBox; + QStandardItemModel *keyboardModel; + + //Shortcut + QLabel *shortcutLabel; + ShortcutLineEdit *shortcutLineEdit; + QPushButton *resetShortcutPushButton; +}; + +class TextEditorPage : public QTabWidget +{ + Q_OBJECT +public: + explicit TextEditorPage(QWidget *parent = 0); + ~TextEditorPage(); + void saveConfig(); + +private: + int getFontSizeIndex(const int size); + void prepareDefaultEncodingData(); + void prepareUtf8BOMData(); + void prepareSpellCheckLanguageData(); + void prepareTabOptionData(); +signals: + +public slots: +private slots: + void spellCheckCheckBoxSlot(bool isChecked); + void useWhiteSpaceInsteadOfTabSlot(bool isChecked); +private: + Ui::TextEditorPage *ui; + Configuration *conf; + MdCharmGlobal *mdcharmGlobal; + QFontComboBox *fontFamilyComboBox; + QComboBox *fontSizeComboBox; + QComboBox *defaultEncodingComboBox; + QComboBox *utf8BOMComboBox; + QSpinBox *tabSizeSpinBox; + QCheckBox *enableTWCheckBox; + QCheckBox *displayLineNumberCheckBox; + QCheckBox *highlightCLCheckBox; +}; + +class StylesPage : public QTabWidget +{ + Q_OBJECT +public: + explicit StylesPage(QWidget *parent = 0); + ~StylesPage(); + void saveConfig(); +private: + Ui::StylesPage *ui; + Configuration *conf; + QCheckBox *useDefaultCheckBox; + QPlainTextEdit *customCSSPlainTextEdit; + CSSHighLighter *cssHighLighter; +private slots: + void checkStateChanged(); +}; + +#endif // PAGES_H diff --git a/src/MdCharm/conf/stylespage.ui b/src/MdCharm/conf/stylespage.ui new file mode 100644 index 0000000..18d9792 --- /dev/null +++ b/src/MdCharm/conf/stylespage.ui @@ -0,0 +1,48 @@ + + + StylesPage + + + + 0 + 0 + 400 + 300 + + + + TabWidget + + + 0 + + + + Markdown + + + + + + CSS + + + + + + Use default stylesheet + + + + + + + + + + + + + + + diff --git a/src/MdCharm/conf/texteditorpage.ui b/src/MdCharm/conf/texteditorpage.ui new file mode 100644 index 0000000..d7ee13d --- /dev/null +++ b/src/MdCharm/conf/texteditorpage.ui @@ -0,0 +1,452 @@ + + + TextEditorPage + + + + 0 + 0 + 400 + 342 + + + + TabWidget + + + 0 + + + + Font && Color + + + + + + + 0 + 0 + + + + Font + + + + + + Family + + + fontComboBox + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Size: + + + fontSizeComboBox + + + + + + + + 6 + + + + + 7 + + + + + 8 + + + + + 9 + + + + + 10 + + + + + 11 + + + + + 12 + + + + + 14 + + + + + 16 + + + + + 18 + + + + + 20 + + + + + 22 + + + + + 24 + + + + + 26 + + + + + 28 + + + + + 36 + + + + + 48 + + + + + 72 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Behavior + + + + + + Tab + + + + + + Tab Size: + + + tabSizeSpinBox + + + + + + + 1 + + + 20 + + + + + + + User white space instead of tab + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + File Encodings + + + + + + Default encoding: + + + defaultEncodingComboBox + + + + + + + + + + UTF-8 BOM: + + + utf8BOMComboBox + + + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + + + + Spell Check + + + + + + Enable spellcheck + + + + + + + Language: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Typing + + + + + + Enable Automatic indentation + + + + + + + Auto pair + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Display + + + + + + Text Warpping + + + + + + Enable Text Wrapping + + + + + + + + + Right margin at column: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Display + + + + + + Display line number + + + + + + + Highlight current line + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + diff --git a/src/MdCharm/configuration.cpp b/src/MdCharm/configuration.cpp new file mode 100644 index 0000000..528de40 --- /dev/null +++ b/src/MdCharm/configuration.cpp @@ -0,0 +1,1060 @@ +#include +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "version.h" +#include "utils.h" + +namespace { +static const char *SPELL_CHECK_DIC_DIRECTORY_NAME = "spellcheckdict"; + +#if defined(Q_WS_MAC) + enum { DEFAULT_FONT_SIZE = 12 }; + static const char *DEFAULT_FONT_FAMILY = "Monaco"; +#elif defined(Q_WS_X11) + enum { DEFAULT_FONT_SIZE = 9 }; + static const char *DEFAULT_FONT_FAMILY = "Monospace"; +#else + enum { DEFAULT_FONT_SIZE = 10 }; + static const char *DEFAULT_FONT_FAMILY = "Courier"; +#endif +} + +Configuration *Configuration::instance = 0; +const QString Configuration::FONT_FAMILY = QString::fromLatin1("Common/FontFamily"); +const QString Configuration::FONT_SIZE = QString::fromLatin1("Common/FontSize"); +const QString Configuration::TAB_SIZE = QString::fromLatin1("TextEditor/TabSize"); +const QString Configuration::DISPLAY_LINE_NUMBER = QString::fromLatin1("TextEditor/DisplayLineNumber"); +const QString Configuration::HIGHLIGHT_CURRENT_LINE = QString::fromLatin1("TextEditor/HighlighCurrentLine"); +const QString Configuration::ENABLE_TEXT_WRAPPING = QString::fromLatin1("TextEditor/EnableTextWrapping"); +const QString Configuration::PREVIEW_OPTION = QString::fromLatin1("Behavior/PreviewOption"); +const QString Configuration::IS_SHOW_SPLASH = QString::fromLatin1("Common/ShowSplash"); +const QString Configuration::IS_CHECK_FOR_UPDATES = QString::fromLatin1("Common/CheckForUpdates"); +const QString Configuration::RECENT_FILE_LIST = QString::fromLatin1("Common/RecentFiles"); +const QString Configuration::MARKDOWN_USE_DEFAULT_CSS = QString::fromLatin1("Styles/MarkdownDefaultCSSPath"); +const QString Configuration::IS_PROJECT_DOCK_WIDGET_VISIBLE = QString::fromLatin1("Dock/ProjectDockWidgetVisible"); +const QString Configuration::DEFAULT_ENCODING = QString::fromLatin1("TextEditor/DefaultEncoding"); +const QString Configuration::UTF8_BOM = QString::fromLatin1("TextEditor/Utf8Bom"); +const QString Configuration::SYNC_SCROLLBAR = QString::fromLatin1("Behavior/SyncScrollbar"); +const QString Configuration::IS_ENABLE_SPELL_CHECK = QString::fromLatin1("TextEditor/SpellCheck"); +const QString Configuration::SPELL_CHECK_LANGUAGE = QString::fromLatin1("TextEditor/SpellCheckLanguage"); +const QString Configuration::USE_WHITE_SPACE_INSTEAD_OF_TAB = QString::fromLatin1("TextEditor/UseWhiteSpaceInsteadOfTab"); +const QString Configuration::GEOMETRY_STATE = QString::fromLatin1("MdCharmState/GeometeryState"); +const QString Configuration::WINDOW_STATE = QString::fromLatin1("MdCharmState/WindowState"); +const QString Configuration::APPEND_CODE_SYNTAX_CSS = QString::fromLatin1("MdCharmUpdate/AppendCodeSyntaxCss"); +const QString Configuration::LAST_OPEN_DIR = QString::fromLatin1("MdCharmState/LastOpenDir"); +const QString Configuration::LAST_FILTER_TYPE = QString::fromLatin1("MdCharmState/LastFilterType");//Same as the FileType enum +const QString Configuration::FILE_EXTENSION = QString::fromLatin1("Common/FileExtension"); +const QString Configuration::AUTO_INDENTATION = QString::fromLatin1("TextEditor/AutoIndentation"); +const QString Configuration::AUTO_PAIR = QString::fromLatin1("TextEditor/AutoPair"); +const QString Configuration::LAST_IGNORE_REVISION = QString::fromLatin1("Update/LastIgnoreRevision"); +const QString Configuration::RIGHT_MARGIN_COLUMN = QString::fromLatin1("TextEditor/RightMarginColumn"); +const QString Configuration::MARKDOWN_ENGINE = QString::fromLatin1("Common/MarkdownEngine"); +const QString Configuration::LAST_STATE_GROUP = QString::fromLatin1("LastState/"); +const QString Configuration::SHORTCUTS_GROUP = QString::fromLatin1("Shortcuts/"); + +const QString Configuration::NATIVE_LICENSE = QString::fromLatin1("License/License"); +const QString Configuration::NATIVE_LAST_CHECK_DATETIME = QString::fromLatin1("License/LastCheckDatetime"); +const QString Configuration::NATIVE_FIRST_USE_DATETIME = QString::fromLatin1("License/FirstUseDatetime"); + +Configuration::Configuration() +{ + settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, + Utils::AppName, Utils::AppName); + regSettings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, + Utils::AppName, Utils::AppName); +#if defined(Q_OS_WIN) + defaultSpellCheckRootDir = QCoreApplication::applicationDirPath(); +#elif defined(Q_OS_LINUX) + defaultSpellCheckRootDir = "/usr/share/mdcharm";//linux +#else + defaultSpellCheckRootDir = configFileDirPath();//mac +#endif + writeSystemInfo(); + + //Must Order By FileType + fileTypeString + << tr("All Files") //All + << tr("Markdown"); //Markdown + fileTypeSuffixString + << "*.*" //All + << "*.markdown|*.md|*.mkd"; //Markdown +} + +Configuration::~Configuration() +{ + settings->sync(); +} + +void Configuration::setFontFamily(const QString &family) +{ + settings->setValue(FONT_FAMILY, family); +} + +QString Configuration::getFontFamily() +{ + QVariant var = settings->value(FONT_FAMILY); + if (var.isValid() && var.canConvert(QVariant::String)) + { + return var.toString(); + } else { + QString r(DEFAULT_FONT_FAMILY); + setFontFamily(r); + return r; + } +} + +void Configuration::setFontSize(const int fontSize) +{ + settings->setValue(FONT_SIZE, fontSize); +} + +int Configuration::getFontSize() +{ + QVariant var = settings->value(FONT_SIZE); + if(var.isValid() && var.canConvert(QVariant::Int)) + { + return var.toInt(); + } else { + setFontSize(DEFAULT_FONT_SIZE); + return DEFAULT_FONT_SIZE; + } +} + +void Configuration::setTabSize(const int tabSize) +{ + settings->setValue(TAB_SIZE, tabSize); +} + +int Configuration::getTabSize() +{ + QVariant var = settings->value(TAB_SIZE); + if(var.isValid() && var.canConvert(QVariant::Int)) + { + return var.toInt(); + } else { + setTabSize(4); + return 4; + } +} + +void Configuration::setDisplayLineNumber(const bool b) +{ + settings->setValue(DISPLAY_LINE_NUMBER, b); +} + +bool Configuration::isDisplayLineNumber() +{ + QVariant var = settings->value(DISPLAY_LINE_NUMBER); + if (var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } else { + setDisplayLineNumber(false); + return false; + } +} + +void Configuration::setEnableTextWrapping(const bool b) +{ + settings->setValue(ENABLE_TEXT_WRAPPING, b); +} + +bool Configuration::isEnableTextWrapping() +{ + QVariant var = settings->value(ENABLE_TEXT_WRAPPING); + if (var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } else { + setEnableTextWrapping(true); + return true; + } +} + +void Configuration::setHighlightCurrentLine(const bool b) +{ + settings->setValue(HIGHLIGHT_CURRENT_LINE, b); +} + +bool Configuration::isHighlightCurrentLine() +{ + QVariant var = settings->value(HIGHLIGHT_CURRENT_LINE); + if (var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } else { + setHighlightCurrentLine(false); + return false; + } +} + +void Configuration::setPreviewOption(const int b) +{ + settings->setValue(PREVIEW_OPTION, b); +} + +int Configuration::getPreviewOption() +{ + QVariant var = settings->value(PREVIEW_OPTION); + if (var.isValid() && var.canConvert(QVariant::Int)) + { + return var.toInt(); + } else { + if(isShowPreview_Old()){ + setPreviewOption(MdCharmGlobal::WriteRead); + return MdCharmGlobal::WriteRead; + } else { + setPreviewOption(MdCharmGlobal::WriteMode); + return MdCharmGlobal::WriteMode; + } + } +} +//This method is used for compatible with version before 0.9.8 +//TODO: remove it someday +bool Configuration::isShowPreview_Old() +{ + QVariant var = settings->value(QString::fromLatin1("Behavior/Preview")); + if(var.isValid() && var.canConvert(QVariant::Bool)){ + return var.toBool(); + } else { + return true; + } +} + +void Configuration::changeSyncScrollbarSetting(bool sync) +{ + settings->setValue(SYNC_SCROLLBAR, sync); +} + +bool Configuration::isSyncScrollbar() +{ + QVariant var = settings->value(SYNC_SCROLLBAR); + if (var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } else { + changeSyncScrollbarSetting(true); + return true; + } +} + +void Configuration::setCheckForUpdates(const bool b) +{ + settings->setValue(IS_CHECK_FOR_UPDATES, b); +} + +bool Configuration::isCheckForUpdates() +{ + QVariant var = settings->value(IS_CHECK_FOR_UPDATES); + if (var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } else { + setCheckForUpdates(true); + return true; + } +} + +void Configuration::setShowSplash(const bool b) +{ + settings->setValue(IS_SHOW_SPLASH, b); +} + +bool Configuration::isShowSplash() +{ + QVariant var = settings->value(IS_SHOW_SPLASH); + if(var.isValid() && var.canConvert(QVariant::Bool)) + return var.toBool(); + else + { +#ifdef Q_OS_LINUX + setShowSplash(false); + return false; +#endif +#ifdef Q_OS_WIN + setShowSplash(true); + return true; +#endif + } +} + +Configuration* Configuration::getInstance() +{ + if(!instance) + instance = new Configuration(); + return instance; +} + +void Configuration::writeSystemInfo() +{ + QFile sysinfoFile(configFileDirPath()+"/sysinfo.txt"); +// if(sysinfoFile.exists()) +// return; + if(!sysinfoFile.open(QIODevice::WriteOnly)) + return; + QString osVersion; +#ifdef Q_OS_WIN + osVersion = QString::fromLatin1("Windows %1").arg(QSysInfo::windowsVersion()); +#endif + if(osVersion.isEmpty()) + { + osVersion = QString::fromLatin1("Linux"); + } + QString fileContent = QString::fromLatin1("MdCharm Version: %1\n" + "MdCharm Revision: %2\n" + "Program File Location: %3\n" + "OS Version: %4").arg(VERSION_STR) + .arg(REVISION_STR) + .arg(qApp->applicationDirPath()) + .arg(osVersion); + sysinfoFile.write(fileContent.toLocal8Bit()); + sysinfoFile.close(); +} + +QString Configuration::configFileDirPath() +{ +#ifdef MDCHARM_DEBUG + qDebug(settings->fileName().left(settings->fileName().lastIndexOf("/")).toLatin1()); +#endif + return settings->fileName().left(settings->fileName().lastIndexOf("/")); +} + +void Configuration::setRecentFileList(const QStringList sl) +{ + settings->setValue(RECENT_FILE_LIST, sl); +} + +QStringList Configuration::getRecentFileList() +{ + QVariant var = settings->value(RECENT_FILE_LIST); + if(var.isValid() && var.canConvert(QVariant::StringList)) + { + return var.toStringList(); + } else { + setRecentFileList(QStringList()); + return QStringList(); + } +} + +void Configuration::removeFromRecentFileList(const QString &toRemove) +{ + QStringList rl = getRecentFileList(); + rl.removeAll(toRemove); + setRecentFileList(rl); +} + +bool Configuration::isUseMarkdownDefaultCSS() +{ + QVariant var = settings->value(MARKDOWN_USE_DEFAULT_CSS); + if(var.isValid() && var.canConvert(QVariant::String) && !var.toString().isEmpty()) + return false;//if string is not empty or null, it means user want to use him own css + else + return true; +} +// if b==true use custom css +void Configuration::setMarkdownCSS(bool isDefault, const QString customCSS) +{ + if(!isDefault) + { + QString markdownCSSFilePath = configFileDirPath()+"/markdown.css"; + settings->setValue(MARKDOWN_USE_DEFAULT_CSS, markdownCSSFilePath); + Utils::saveFile(markdownCSSFilePath, customCSS.toUtf8()); + } + else + { + settings->setValue(MARKDOWN_USE_DEFAULT_CSS, QString()); + } +} + +QString Configuration::getMarkdownCSS() +{ + QString cssFilePath; + if(isUseMarkdownDefaultCSS()) + { + cssFilePath = QString::fromLatin1(":/markdown/default.css"); + } else { + QVariant var = settings->value(MARKDOWN_USE_DEFAULT_CSS); + if(var.isValid() && var.canConvert(QVariant::String)) + { + cssFilePath = var.toString(); + if(!QFile::exists(cssFilePath)) + cssFilePath = QString::fromLatin1(":/markdown/default.css"); + } + else + cssFilePath = QString::fromLatin1(":/markdown/default.css"); + } + if(cssFilePath.isEmpty()) + return QString(); + QFile markdownCSSFile(cssFilePath); + if(!markdownCSSFile.open(QIODevice::ReadOnly)) + return QString(); + QTextStream textStream(&markdownCSSFile); + QString result = textStream.readAll(); + markdownCSSFile.close(); + return result; +} + +void Configuration::setProjectDockWidgetVisible(bool b) +{ + settings->setValue(IS_PROJECT_DOCK_WIDGET_VISIBLE, b); +} + +bool Configuration::isProjectDockWidgetVisible() +{ + QVariant var = settings->value(IS_PROJECT_DOCK_WIDGET_VISIBLE); + if(var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } + else + { + setProjectDockWidgetVisible(true); + return true; + } +} + +void Configuration::setDefaultEncoding(const QString &defaultEncoding) +{ + settings->setValue(DEFAULT_ENCODING, defaultEncoding); +} + +QString Configuration::getDefaultEncoding() +{ + QVariant var = settings->value(DEFAULT_ENCODING); + if(var.isValid() && var.canConvert(QVariant::String)) + { + return var.toString(); + } else { + QString defaultEncoding = QString("UTF-8"); + setDefaultEncoding(defaultEncoding); + return defaultEncoding; + } +} + +void Configuration::setUtf8BOMOptions(MdCharmGlobal::UTF8BOM ub) +{ + settings->setValue(UTF8_BOM, ub); +} + +MdCharmGlobal::UTF8BOM Configuration::getUtf8BOMOptions() +{ + QVariant var = settings->value(UTF8_BOM); + if(var.isValid() && var.canConvert(QVariant::Int)) + { + switch(var.toInt()) + { + case MdCharmGlobal::Add: + return MdCharmGlobal::Add; + break; + case MdCharmGlobal::Delete: + return MdCharmGlobal::Delete; + break; + default: + case MdCharmGlobal::Keep: + return MdCharmGlobal::Keep; + break; + } + } else { + setUtf8BOMOptions(MdCharmGlobal::Keep); + return MdCharmGlobal::Keep; + } +} + +QString Configuration::getLanguageSpellCheckDictPath(const QString &lan) +{ + if(lan.isEmpty()) + return QString(); + //Check User Data Path First + QString dicPath = QString("%1/%2/%3.dic").arg(configFileDirPath()) + .arg(SPELL_CHECK_DIC_DIRECTORY_NAME) + .arg(lan); + if(QFile::exists(dicPath)) + return dicPath; + dicPath = QString("%1/%2/%3.dic").arg(defaultSpellCheckRootDir) + .arg(SPELL_CHECK_DIC_DIRECTORY_NAME) + .arg(lan); + if(QFile::exists(dicPath)) + return dicPath; + else + return QString(); +} + +QString Configuration::getLanguageSpellCheckUserDictPath() +{ + return QString("%1/%2/user.txt").arg(configFileDirPath()).arg(SPELL_CHECK_DIC_DIRECTORY_NAME); +} + +const QStringList Configuration::getAllAvailableSpellCheckDictNames() +{ + QStringList dicts; + QString dirPath = QString("%1/%2").arg(defaultSpellCheckRootDir).arg(SPELL_CHECK_DIC_DIRECTORY_NAME); + QDir dictDir(dirPath); + if(!dictDir.exists()) + return dicts; + QStringList filter; + filter << "*.aff"; + QFileInfoList fileInfoList = dictDir.entryInfoList(filter, QDir::Files, QDir::Name); + for(int i=0; ivalue(IS_ENABLE_SPELL_CHECK); + if(var.isValid() && var.canConvert(QVariant::Bool)) + { + return var.toBool(); + } else { +// QLocale locale; +// if(locale.name()=="en_US" || locale.name()=="en_GB") +// { +// setCheckSpell(true); +// return true; +// } else { +// setCheckSpell(false); +// return false; +// } + setCheckSpell(false); + return false; + } +} + +void Configuration::setCheckSpell(bool b) +{ + settings->setValue(IS_ENABLE_SPELL_CHECK, b); +} + +QString Configuration::getSpellCheckLanguage() +{ + QVariant var = settings->value(SPELL_CHECK_LANGUAGE); + if(var.isValid() && var.canConvert(QVariant::String)){ + return var.toString(); + } else { +// QLocale locale; +// QString localeName = locale.name(); +// if(localeName=="en_US" || localeName=="en_GB"){ +// setSpellCheckLanguage(localeName); +// return localeName; +// } else { +// return QString(); +// } + return QString(); + } +} + +void Configuration::setSpellCheckLanguage(const QString &lan) +{ + settings->setValue(SPELL_CHECK_LANGUAGE, lan); +} + +bool Configuration::isUseWhiteSpaceInsteadOfTab() +{ + QVariant var = settings->value(USE_WHITE_SPACE_INSTEAD_OF_TAB); + if(var.isValid() && var.canConvert(QVariant::Bool)){ + return var.toBool(); + } else { + setUseWhiteSpaceInsteadOfTab(true); + return true; + } +} + +void Configuration::setUseWhiteSpaceInsteadOfTab(bool b) +{ + settings->setValue(USE_WHITE_SPACE_INSTEAD_OF_TAB, b); +} + +const QByteArray Configuration::getGeometryState() +{ + QVariant var = settings->value(GEOMETRY_STATE); + if(var.isValid() && var.canConvert(QVariant::ByteArray)){ + return var.toByteArray(); + } else { + return QByteArray(); + } +} + +void Configuration::setGeometryState(const QByteArray &geometryState) +{ + settings->setValue(GEOMETRY_STATE, geometryState); +} + +const QByteArray Configuration::getWindowState() +{ + QVariant var = settings->value(WINDOW_STATE); + if(var.isValid() && var.canConvert(QVariant::ByteArray)){ + return var.toByteArray(); + } else { + return QByteArray(); + } +} + +void Configuration::setWindowState(const QByteArray &windowState) +{ + settings->setValue(WINDOW_STATE, windowState); +} + +bool Configuration::isHaveValidApplicationState() +{ + return (!getGeometryState().isEmpty())&&(!getWindowState().isEmpty()); +} + +const QString Configuration::getSessionFilePath() +{ + return QString("%1/session.xml").arg(configFileDirPath()); +} + +bool Configuration::isAppendCodeSyntaxCss() +{ + QVariant var = settings->value(APPEND_CODE_SYNTAX_CSS); + if(var.isValid() && var.canConvert(QVariant::Bool)){ + return var.toBool(); + } else { + return false; + } +} + +void Configuration::setAppendCodeSyntaxCss(bool b) +{ + settings->setValue(APPEND_CODE_SYNTAX_CSS, b); +} + +void Configuration::setLastOpenDir(const QString &dir) +{ + settings->setValue(LAST_OPEN_DIR, dir); +} + +const QString Configuration::getLastOpenDir() +{ + QVariant var = settings->value(LAST_OPEN_DIR); + if(var.isValid() && var.canConvert(QVariant::String)) + return var.toString(); + else + return QDir::homePath(); +} + +void Configuration::setFileExtension(const QStringList &extensions) +{ + settings->setValue(FILE_EXTENSION, extensions); +} + +int Configuration::getLastFilterType() +{ + QVariant var = settings->value(LAST_FILTER_TYPE); + if(var.isValid() && var.canConvert(QVariant::Int)) + return var.toInt(); + else + return MarkdownFile; +} + +void Configuration::setLastFilterType(int ft) +{ + settings->setValue(LAST_FILTER_TYPE, ft); +} + +QStringList Configuration::getFileExtension() +{ + QVariant var = settings->value(FILE_EXTENSION); + if(var.isValid() && var.canConvert(QVariant::StringList)){ + QStringList result = var.toStringList(); + if(result.length()=fileTypeString.length()){ +#ifdef MDCHARM_DEBUG + Q_ASSERT(0 && "this should not be happen"); +#endif + return QString(); + } + return fileTypeString.at(fileType); +} + +QStringList Configuration::getFileOpenFilters(FileType firstType) +{ + QStringList exts = getFileExtension(); + QStringList filterList; + for(int i=0; isetValue(AUTO_INDENTATION, i); +} + +bool Configuration::isAutoIndentation() +{ + QVariant var = settings->value(AUTO_INDENTATION); + if(var.isValid() && var.canConvert(QVariant::Bool)){ + return var.toBool(); + } else { + setAutoIndentation(true); + return true; + } +} + +void Configuration::setAutoPair(bool i) +{ + settings->setValue(AUTO_PAIR, i); +} + +bool Configuration::isAutoPair() +{ + QVariant var = settings->value(AUTO_PAIR); + if(var.isValid() && var.canConvert(QVariant::Bool)){ + return var.toBool(); + } else { + setAutoPair(false); + return false; + } +} + +void Configuration::setLastIgnoreRevision(const QString &rev) +{ + settings->setValue(LAST_IGNORE_REVISION, rev); +} + +QString Configuration::getLastIgnoreRevision() const +{ + QVariant var = settings->value(LAST_IGNORE_REVISION); + if(var.isValid() && var.canConvert(QVariant::String)){ + return var.toString(); + } else { + return QString(); + } +} + +bool Configuration::isDisplayRightColumnMargin() const +{ + return getRightMarginColumn() > 0; +} + +int Configuration::getRightMarginColumn() const +{ + QVariant var = settings->value(RIGHT_MARGIN_COLUMN); + if(var.isValid() && var.canConvert(QVariant::Int)){ + return var.toInt(); + } else { + return -1; + } +} + +void Configuration::setRightMarginColumn(int column) +{ + settings->setValue(RIGHT_MARGIN_COLUMN, column); +} + +void Configuration::setMarkdownEngineType(MarkdownToHtml::MarkdownType type) +{ + settings->setValue(MARKDOWN_ENGINE, type); +} + +MarkdownToHtml::MarkdownType Configuration::getMarkdownEngineType() const +{ + QVariant var = settings->value(MARKDOWN_ENGINE); + if(var.isValid() && var.canConvert(QVariant::Int)){ + int type = var.toInt(); + switch(type) + { + case MarkdownToHtml::MultiMarkdown: + return MarkdownToHtml::MultiMarkdown; + break; + default: + return MarkdownToHtml::PHPMarkdownExtra; + break; + } + } else { + return MarkdownToHtml::PHPMarkdownExtra; + } +} + +QVariant Configuration::getLastStateValue(const QString &key) const +{ + return settings->value(LAST_STATE_GROUP+key); +} + +void Configuration::setLastStateValue(const QString &key, const QVariant &value) +{ + settings->setValue(LAST_STATE_GROUP+key, value); +} + +QString Configuration::getKeyboardShortcut(int s) +{ + QVariant var = settings->value(SHORTCUTS_GROUP+getShortcutKeyName(s)); + if(var.isValid() && var.canConvert(QVariant::String)){ + return var.toString(); + } else { + QString k = getKeyboardDefaultShortcut(s); + setKeyboardShortcut(s, k); + return k; + } +} + +void Configuration::setKeyboardShortcut(int s, const QString &keys) +{ + settings->setValue(SHORTCUTS_GROUP+getShortcutKeyName(s), keys); +} + +QString Configuration::getShortcutKeyName(int s) +{ + switch (s) { + case MdCharmGlobal::ShortcutCloseTab: + return "Close_Tab"; + break; + case MdCharmGlobal::ShortcutItalic: + return "Italic_Text"; + break; + case MdCharmGlobal::ShortcutBold: + return "Bold_Text"; + break; + case MdCharmGlobal::ShortcutQuoteText: + return "Quote_Text"; + break; + case MdCharmGlobal::ShortcutShiftTab: + return "Untab_Block_Text"; + break; + case MdCharmGlobal::ShortcutStrikeThrough: + return "Strike_Through_Text"; + break; + case MdCharmGlobal::ShortcutInsertLink: + return "Insert_Link"; + break; + case MdCharmGlobal::ShortcutInsertPicture: + return "Insert_Picture"; + break; + case MdCharmGlobal::ShortcutCheatSheet: + return "Open_Cheat_Sheet"; + break; + case MdCharmGlobal::ShortcutInsertCode: + return "Insert_Code"; + break; + case MdCharmGlobal::ShortcutNew: + return "New"; + break; + case MdCharmGlobal::ShortcutOpen: + return "Open"; + break; + case MdCharmGlobal::ShortcutSave: + return "Save"; + break; + case MdCharmGlobal::ShortcutSaveAs: + return "SaveAs"; + break; + case MdCharmGlobal::ShortcutPrint: + return "Print"; + break; + case MdCharmGlobal::ShortcutQuit: + return "Quit"; + break; + case MdCharmGlobal::ShortcutUndo: + return "Undo"; + break; + case MdCharmGlobal::ShortcutRedo: + return "Redo"; + break; + case MdCharmGlobal::ShortcutCut: + return "Cut"; + break; + case MdCharmGlobal::ShortcutCopy: + return "Copy"; + break; + case MdCharmGlobal::ShortcutPaste: + return "Paste"; + break; + case MdCharmGlobal::ShortcutSelectAll: + return "Select_All"; + break; + case MdCharmGlobal::ShortcutFind: + return "Find"; + break; + case MdCharmGlobal::ShortcutFindPrevious: + return "Find_Previous"; + break; + case MdCharmGlobal::ShortcutFindNext: + return "Find_Next"; + break; + case MdCharmGlobal::ShortcutHelpContents: + return "Help"; + break; + case MdCharmGlobal::ShortcutPrintPreview: + return "Print_Preview"; + break; + case MdCharmGlobal::ShortcutHideProjectDockBar: + return "Hide_Project_Dock_Bar"; + break; + default: + Q_ASSERT(0 && "This should not be happend"); + return ""; + break; + } +} + +QString Configuration::getKeyboardDefaultShortcut(int s) +{ + switch (s) { + case MdCharmGlobal::ShortcutCloseTab: + return "Ctrl+W"; + break; + case MdCharmGlobal::ShortcutItalic: + return QKeySequence(QKeySequence::Italic).toString(); + break; + case MdCharmGlobal::ShortcutBold: + return QKeySequence(QKeySequence::Bold).toString(); + break; + case MdCharmGlobal::ShortcutQuoteText: + return "Ctrl+Q"; + break; + case MdCharmGlobal::ShortcutShiftTab: + return "Shift+Tab"; + break; + case MdCharmGlobal::ShortcutStrikeThrough: + return "Ctrl+T"; + break; + case MdCharmGlobal::ShortcutInsertLink: + return "Ctrl+L"; + break; + case MdCharmGlobal::ShortcutInsertPicture: + return "Ctrl+Shift+L"; + break; + case MdCharmGlobal::ShortcutCheatSheet: + return "Ctrl+M"; + break; + case MdCharmGlobal::ShortcutInsertCode: + return "Ctrl+K"; + break; + case MdCharmGlobal::ShortcutNew: + return QKeySequence(QKeySequence::New).toString(); + break; + case MdCharmGlobal::ShortcutOpen: + return QKeySequence(QKeySequence::Open).toString(); + break; + case MdCharmGlobal::ShortcutSave: + return QKeySequence(QKeySequence::Save).toString(); + break; + case MdCharmGlobal::ShortcutSaveAs: + return QKeySequence(QKeySequence::SaveAs).toString(); + break; + case MdCharmGlobal::ShortcutPrint: + return QKeySequence(QKeySequence::Print).toString(); + break; + case MdCharmGlobal::ShortcutQuit: + return "Alt+F4"; + break; + case MdCharmGlobal::ShortcutUndo: + return QKeySequence(QKeySequence::Undo).toString(); + break; + case MdCharmGlobal::ShortcutRedo: + return QKeySequence(QKeySequence::Redo).toString(); + break; + case MdCharmGlobal::ShortcutCut: + return QKeySequence(QKeySequence::Cut).toString(); + break; + case MdCharmGlobal::ShortcutCopy: + return QKeySequence(QKeySequence::Copy).toString(); + break; + case MdCharmGlobal::ShortcutPaste: + return QKeySequence(QKeySequence::Paste).toString(); + break; + case MdCharmGlobal::ShortcutSelectAll: + return QKeySequence(QKeySequence::SelectAll).toString(); + break; + case MdCharmGlobal::ShortcutFind: + return QKeySequence(QKeySequence::Find).toString(); + break; + case MdCharmGlobal::ShortcutFindPrevious: + return QKeySequence(QKeySequence::FindPrevious).toString(); + break; + case MdCharmGlobal::ShortcutFindNext: + return QKeySequence(QKeySequence::FindNext).toString(); + break; + case MdCharmGlobal::ShortcutHelpContents: + return QKeySequence(QKeySequence::HelpContents).toString(); + break; + case MdCharmGlobal::ShortcutPrintPreview: + return QKeySequence(Qt::CTRL+Qt::SHIFT+Qt::Key_P).toString(); + break; + case MdCharmGlobal::ShortcutHideProjectDockBar: + return QKeySequence(Qt::ALT+Qt::Key_F2).toString(); + break; + default: + Q_ASSERT(0 && "This should not be happen"); + return QString(""); + break; + } +} + +//------------------ License ------------------------- +void Configuration::setLicense(const QString &license) +{ + regSettings->setValue(NATIVE_LICENSE, license); +} + +QString Configuration::getLicense() const +{ + QVariant var = regSettings->value(NATIVE_LICENSE); + if(var.isValid() && var.canConvert(QVariant::String)){ + return var.toString(); + } else { + return QString(); + } +} + +void Configuration::setLastCheckDatetime(const QDateTime &datetime) +{ + regSettings->setValue(NATIVE_LAST_CHECK_DATETIME, datetime); +} + +QDateTime Configuration::getLastCheckDatetime() const +{ + QVariant var = regSettings->value(NATIVE_LAST_CHECK_DATETIME); + if(var.isValid() && var.canConvert(QVariant::DateTime)){ + return var.toDateTime(); + } else { + + return QDateTime(); + } +} + +QDateTime Configuration::getFirstUseDatetime() const +{ + QVariant var = regSettings->value(NATIVE_FIRST_USE_DATETIME); + if(var.isValid() && var.canConvert(QVariant::DateTime)){ + return var.toDateTime(); + } else { + regSettings->setValue(NATIVE_FIRST_USE_DATETIME, QDateTime::currentDateTime());//First use + return QDateTime::currentDateTime(); + } +} diff --git a/src/MdCharm/configuration.h b/src/MdCharm/configuration.h new file mode 100644 index 0000000..d17d13c --- /dev/null +++ b/src/MdCharm/configuration.h @@ -0,0 +1,165 @@ +#ifndef CONFIGURATION_H +#define CONFIGURATION_H + +#include +#include "utils.h" +#include "markdowntohtml.h" + +class QSettings; + +class Configuration : public QObject +{ + Q_OBJECT +public: + /*! + * \brief The FileType enum + * \warning If we support new file type + * 1) must add file type in this + * 2) must append file type desc string to fileTypeString + * 3) must append file type suffix string to fileTypeSuffixString + */ + enum FileType + { + AllFile=0, + MarkdownFile=1, + FileTypeNum + }; +public: + static Configuration *getInstance(); + + void setFontFamily(const QString &family); + QString getFontFamily(); + void setFontSize(const int fontSize); + int getFontSize(); + void setTabSize(const int tabSize); + int getTabSize(); + void setHighlightCurrentLine(const bool b); + bool isHighlightCurrentLine(); + void setEnableTextWrapping(const bool b); + bool isEnableTextWrapping(); + void setDisplayLineNumber(const bool b); + bool isDisplayLineNumber(); + void setPreviewOption(const int type); + bool isShowPreview_Old(); + int getPreviewOption(); + void changeSyncScrollbarSetting(bool sync); + bool isSyncScrollbar(); + void setShowSplash(const bool b); + bool isShowSplash(); + void setCheckForUpdates(const bool b); + bool isCheckForUpdates(); + void setRecentFileList(const QStringList sl); + QStringList getRecentFileList(); + void removeFromRecentFileList(const QString &toRemove); + bool isUseMarkdownDefaultCSS(); + void setMarkdownCSS(bool isDefault, const QString customCSS=QString()); + QString getMarkdownCSS(); + void setProjectDockWidgetVisible(bool b); + bool isProjectDockWidgetVisible(); + void setDefaultEncoding(const QString &defaultEncoding); + QString getDefaultEncoding(); + void setUtf8BOMOptions(MdCharmGlobal::UTF8BOM ub); + MdCharmGlobal::UTF8BOM getUtf8BOMOptions(); + QString getLanguageSpellCheckDictPath(const QString &lan); + QString getLanguageSpellCheckUserDictPath(); + const QStringList getAllAvailableSpellCheckDictNames(); + bool isCheckSpell(); + void setCheckSpell(bool b); + QString getSpellCheckLanguage(); + void setSpellCheckLanguage(const QString &lan); + bool isUseWhiteSpaceInsteadOfTab(); + void setUseWhiteSpaceInsteadOfTab(bool b); + const QByteArray getGeometryState(); + void setGeometryState(const QByteArray &geometryState); + const QByteArray getWindowState(); + void setWindowState(const QByteArray &windowState); + bool isHaveValidApplicationState(); + const QString getSessionFilePath(); + bool isAppendCodeSyntaxCss(); + void setAppendCodeSyntaxCss(bool b); + void setLastOpenDir(const QString& dir); + const QString getLastOpenDir(); + void setFileExtension(const QStringList& extensions); + int getLastFilterType(); + void setLastFilterType(int ft); + QStringList getFileExtension(); + QString fileTypeToString(int order); + QStringList getFileOpenFilters(FileType firstType=AllFile); + QStringList getFileFilter(FileType fy); + void setAutoIndentation(bool i); + bool isAutoIndentation(); + void setAutoPair(bool i); + bool isAutoPair(); + void setLastIgnoreRevision(const QString &rev); + QString getLastIgnoreRevision() const; + bool isDisplayRightColumnMargin() const; + int getRightMarginColumn() const; + void setRightMarginColumn(int column); + void setMarkdownEngineType(MarkdownToHtml::MarkdownType type); + MarkdownToHtml::MarkdownType getMarkdownEngineType() const; + QVariant getLastStateValue(const QString &key) const; + void setLastStateValue(const QString &key, const QVariant &value); + QString getKeyboardShortcut(int s); + void setKeyboardShortcut(int s, const QString &keys); + QString getKeyboardDefaultShortcut(int s); + QString getShortcutKeyName(int s); + + QString getLicense() const; + void setLicense(const QString &license); + QDateTime getLastCheckDatetime() const; + void setLastCheckDatetime(const QDateTime &datetime); + QDateTime getFirstUseDatetime() const; + + QString configFileDirPath(); + void writeSystemInfo(); + + ~Configuration(); +private: + Configuration(); +private: + static Configuration *instance; + QString defaultSpellCheckRootDir; + QSettings *settings; + QSettings *regSettings; + + static const QString FONT_FAMILY; + static const QString FONT_SIZE; + static const QString TAB_SIZE; + static const QString DISPLAY_LINE_NUMBER; + static const QString HIGHLIGHT_CURRENT_LINE; + static const QString ENABLE_TEXT_WRAPPING; + static const QString PREVIEW_OPTION; + static const QString IS_SHOW_SPLASH; + static const QString IS_CHECK_FOR_UPDATES; + static const QString RECENT_FILE_LIST; + static const QString MARKDOWN_USE_DEFAULT_CSS; + static const QString IS_PROJECT_DOCK_WIDGET_VISIBLE; + static const QString DEFAULT_ENCODING; + static const QString UTF8_BOM; + static const QString SYNC_SCROLLBAR; + static const QString IS_ENABLE_SPELL_CHECK; + static const QString SPELL_CHECK_LANGUAGE; + static const QString USE_WHITE_SPACE_INSTEAD_OF_TAB; + static const QString GEOMETRY_STATE; + static const QString WINDOW_STATE; + static const QString APPEND_CODE_SYNTAX_CSS; + static const QString LAST_OPEN_DIR; + static const QString FILE_EXTENSION; + static const QString LAST_FILTER_TYPE; + static const QString AUTO_INDENTATION; + static const QString AUTO_PAIR; + static const QString LAST_IGNORE_REVISION; + static const QString RIGHT_MARGIN_COLUMN; + static const QString MARKDOWN_ENGINE; + static const QString LAST_STATE_GROUP; + static const QString SHORTCUTS_GROUP; + + static const QString NATIVE_LICENSE; + static const QString NATIVE_LAST_CHECK_DATETIME; + static const QString NATIVE_FIRST_USE_DATETIME; + + QStringList fileTypeString; + QStringList fileTypeSuffixString; +}; + +#endif // CONFIGURATION_H diff --git a/src/MdCharm/dock/projectdockwidget.cpp b/src/MdCharm/dock/projectdockwidget.cpp new file mode 100644 index 0000000..2840121 --- /dev/null +++ b/src/MdCharm/dock/projectdockwidget.cpp @@ -0,0 +1,226 @@ +#include "projectdockwidget.h" +#include "ui_projectdockwidget.h" +#include "utils.h" +#include "util/gui/renamefiledialog.h" +#include "util/filesystemmodel.h" +#include "util/filesystemtreeview.h" +#include "configuration.h" + +ProjectDockWidget::ProjectDockWidget(QWidget *parent) : + QDockWidget(parent), + ui(new Ui::ProjectDockWidget) +{ + ui->setupUi(this); + conf = Configuration::getInstance(); + initGui(); + initMenuAndAction(); + initSignalsAndSlots(); +} + +void ProjectDockWidget::initGui() +{ + setAcceptDrops(true); + fileSystemModel = new FileSystemModel(this); + fileSystemModel->setFilter(QDir::AllEntries|QDir::NoDotAndDotDot); + projectTreeView = new FileSystemTreeView(this); + ui->verticalLayout->addWidget(projectTreeView); + projectTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + projectTreeView->setModel(fileSystemModel); + + projectTreeView->setRootIsDecorated(true); + projectTreeView->setUniformRowHeights(true); + projectTreeView->setTextElideMode(Qt::ElideNone); + projectTreeView->setAttribute(Qt::WA_MacShowFocusRect); + + projectTreeView->setHeaderHidden(true); + projectTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + +#ifdef QT_V5 + projectTreeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#else + projectTreeView->header()->setResizeMode(QHeaderView::ResizeToContents); +#endif + projectTreeView->header()->setStretchLastSection(false); + +} + +void ProjectDockWidget::initMenuAndAction() +{ + fileMenu = new QMenu(this); + directoryMenu = new QMenu(this); + addNewAction = new QAction(tr("Add New..."), this); + showInExplorerAction = new QAction(tr("Show in Explorer"), this); + openFileAction = new QAction(tr("Open file"), this); + renameFileAction = new QAction(tr("Rename..."), this); + deleteFileAction = new QAction(tr("Delete..."), this); +} + +void ProjectDockWidget::initSignalsAndSlots() +{ + connect(projectTreeView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(showContextMenu(QPoint))); + connect(openFileAction, SIGNAL(triggered()), + this, SLOT(openFile())); + connect(showInExplorerAction, SIGNAL(triggered()), + this, SLOT(showInExplorer())); + connect(addNewAction, SIGNAL(triggered()), + this, SLOT(createNewAux())); + connect(renameFileAction, SIGNAL(triggered()), + this, SLOT(renameFile())); + connect(deleteFileAction, SIGNAL(triggered()), + this, SLOT(deleteFile())); + connect(projectTreeView, SIGNAL(doubleClicked(QModelIndex)), + this, SLOT(doubleClickedSlot(QModelIndex))); + connect(this, SIGNAL(visibilityChanged(bool)), + this, SLOT(visibleChange(bool))); + connect(fileSystemModel->getFileWatcher(), SIGNAL(directoryChanged(QString)), + this, SLOT(directoryChanged(QString))); +} + +void ProjectDockWidget::showContextMenu(const QPoint &point) +{ + QModelIndex modelIndex = projectTreeView->indexAt(point); + if(!modelIndex.isValid()) + return; + QFileInfo fileInfo = fileSystemModel->fileInfo(modelIndex); + if(fileInfo.isDir()) + { + prepareDirectoryMenu(fileInfo.absoluteFilePath()); + directoryMenu->exec(projectTreeView->mapToGlobal(point)); + } else if(fileInfo.isFile()&&Utils::isMarkdownFile(fileInfo.absoluteFilePath())){//markdown + prepareFileMenu(fileInfo.absoluteFilePath()); + fileMenu->exec(projectTreeView->mapToGlobal(point)); + } +} + +void ProjectDockWidget::setProjectDir(const QString &dirString) +{ + if(dirString.isEmpty() || dirString.trimmed().isEmpty()) + return; + projectDir = dirString; + + fileSystemModel->setRootPath(dirString); +} + +QString ProjectDockWidget::getProjectDir() +{ + return projectDir; +} + +void ProjectDockWidget::closeEvent(QCloseEvent *event) +{ + conf->setProjectDockWidgetVisible(false); + QDockWidget::closeEvent(event); +} + +void ProjectDockWidget::keyPressEvent(QKeyEvent *event) +{ + if(event->key()==Qt::Key_Delete){ + QModelIndex mi = projectTreeView->currentIndex(); + if(!mi.isValid()) + return; + QFileInfo fileInfo = fileSystemModel->fileInfo(mi); + if(fileInfo.isFile()){ + event->accept(); + QString filePath = fileInfo.absoluteFilePath(); + emit deleteFileSignal(filePath); + } + } +} + +ProjectDockWidget::~ProjectDockWidget() +{ + delete ui; +} + +void ProjectDockWidget::prepareDirectoryMenu(const QString &dirPath) +{ + directoryMenu->clear(); + addNewAction->setData(dirPath);; + directoryMenu->addAction(addNewAction); + showInExplorerAction->setData(dirPath); + directoryMenu->addAction(showInExplorerAction); +} + +void ProjectDockWidget::prepareFileMenu(const QString &filePath) +{ + fileMenu->clear(); + openFileAction->setData(filePath); + fileMenu->addAction(openFileAction); + showInExplorerAction->setData(filePath); + fileMenu->addAction(showInExplorerAction); + renameFileAction->setData(filePath);; + fileMenu->addAction(renameFileAction); + deleteFileAction->setData(filePath); + fileMenu->addAction(deleteFileAction); +} + +void ProjectDockWidget::openFile() +{ + emit openTheFile(openFileAction->data().toString()); +} + +void ProjectDockWidget::createNewAux() +{ + emit createNewFile(addNewAction->data().toString()); +} + +void ProjectDockWidget::showInExplorer() +{ + Utils::showInGraphicalShell(showInExplorerAction->data().toString()); +} + +void ProjectDockWidget::doubleClickedSlot(const QModelIndex &index) +{ + QFileInfo fileInfo = fileSystemModel->fileInfo(index); + if(fileInfo.isFile()&&Utils::isMarkdownFile(fileInfo.absoluteFilePath())) + { + emit openTheFile(fileInfo.absoluteFilePath()); + } +} + +void ProjectDockWidget::visibleChange(bool visible) +{ + if(visible) + conf->setProjectDockWidgetVisible(true); +} + +void ProjectDockWidget::directoryChanged(QString dir) +{ + FileSystemTreeViewState state; + projectTreeView->saveState(state); + fileSystemModel->directoryChanged(dir); + projectTreeView->loadState(state); +} + +void ProjectDockWidget::deleteFile() +{ + emit deleteFileSignal(deleteFileAction->data().toString()); +} + +void ProjectDockWidget::renameFile() +{ + RenameFileDialog rfd(renameFileAction->data().toString()); + if(QDialog::Accepted==rfd.exec()){ + emit renameFileSignal(renameFileAction->data().toString(), rfd.getNewFilePath()); + } +} + +//------------------- Drag and Drop ---------------------------------------- +void ProjectDockWidget::dragEnterEvent(QDragEnterEvent *e) +{ + if(e->mimeData()->hasUrls()) + e->acceptProposedAction(); +} + +void ProjectDockWidget::dropEvent(QDropEvent *e) +{ + if(e->mimeData()->hasUrls()){ + QUrl url = e->mimeData()->urls().first(); + if(!url.isLocalFile()) + return; + QFileInfo fileInfo(url.toLocalFile()); + if(fileInfo.isDir()) + setProjectDir(fileInfo.absoluteFilePath()); + } +} diff --git a/src/MdCharm/dock/projectdockwidget.h b/src/MdCharm/dock/projectdockwidget.h new file mode 100644 index 0000000..d1b1018 --- /dev/null +++ b/src/MdCharm/dock/projectdockwidget.h @@ -0,0 +1,76 @@ +#ifndef PROJECTDOCKWIDGET_H +#define PROJECTDOCKWIDGET_H + +#include +#include +#include + +#ifdef QT_V5 +#include +#endif + +class FileSystemModel; +class FileSystemTreeView; +class Configuration; + +namespace Ui { +class ProjectDockWidget; +} + +class ProjectDockWidget : public QDockWidget +{ + Q_OBJECT + +public: + explicit ProjectDockWidget(QWidget *parent = 0); + void initGui(); + void initMenuAndAction(); + void initSignalsAndSlots(); + + void setProjectDir(const QString &dirString); + QString getProjectDir(); + + ~ProjectDockWidget(); +protected: + virtual void closeEvent(QCloseEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *e); + virtual void dropEvent(QDropEvent *e); +private: + void prepareDirectoryMenu(const QString &dirPath); + void prepareFileMenu(const QString &filePath); +signals: + void openTheFile(const QString &filePath); + void createNewFile(const QString &fileDir); + void deleteFileSignal(const QString &filePath); + void renameFileSignal(const QString &original, const QString ¤t); + +private slots: + void showContextMenu(const QPoint &point); + void openFile(); + void showInExplorer(); + void createNewAux(); + void doubleClickedSlot(const QModelIndex &index); + void deleteFile(); + void renameFile(); + void visibleChange(bool b); + void directoryChanged(QString dir); +private: + Ui::ProjectDockWidget *ui; + FileSystemModel* fileSystemModel; + FileSystemTreeView *projectTreeView; + + Configuration *conf; + + QMenu* fileMenu; + QMenu* directoryMenu; + QAction* addNewAction; + QAction* showInExplorerAction; + QAction* openFileAction; + QAction* renameFileAction; + QAction* deleteFileAction; + + QString projectDir; +}; + +#endif // PROJECTDOCKWIDGET_H diff --git a/src/MdCharm/dock/projectdockwidget.ui b/src/MdCharm/dock/projectdockwidget.ui new file mode 100644 index 0000000..7a08864 --- /dev/null +++ b/src/MdCharm/dock/projectdockwidget.ui @@ -0,0 +1,29 @@ + + + ProjectDockWidget + + + + 0 + 0 + 176 + 513 + + + + Project + + + + + 0 + + + 0 + + + + + + + diff --git a/src/MdCharm/editareatabwidget.cpp b/src/MdCharm/editareatabwidget.cpp new file mode 100644 index 0000000..dac9399 --- /dev/null +++ b/src/MdCharm/editareatabwidget.cpp @@ -0,0 +1,312 @@ +#include +#include +#include + +#include "editareatabwidget.h" +#include "editareatabwidgetmanager.h" +#include "markdowneditareawidget.h" +#include "resource.h" +//---------------- EditAreaTabBar ---------------------------------------------- +EditAreaTabBar::EditAreaTabBar(EditAreaTabWidget *parent) : + QTabBar(parent) +{ + tabIndex = -1; + tabWidget = parent; + menu = new QMenu(this); + closeAction = new QAction(tr("Close"), this); + closeOthersAction = new QAction(tr("Close Others"), this); + closeAllAction = new QAction(tr("Close All"), this); + moveToOtherViewAction = new QAction(tr("Move to other view"), this); + cloneToOtherViewAction = new QAction(tr("Clone to other view"), this); + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(contextMenuSlot(QPoint))); + connect(closeAction, SIGNAL(triggered()), this, SLOT(closeActionSlot())); + connect(closeOthersAction, SIGNAL(triggered()), + this, SLOT(closeOtherActionSlot())); + connect(closeAllAction, SIGNAL(triggered()), + this, SLOT(closeAllActionSlot())); + connect(moveToOtherViewAction, SIGNAL(triggered()), + this, SLOT(moveToOtherViewSlot())); + connect(cloneToOtherViewAction, SIGNAL(triggered()), + this, SLOT(cloneToOtherViewSlot())); +} + +void EditAreaTabBar::mouseReleaseEvent(QMouseEvent *event) +{ + if(event->button()==Qt::MiddleButton){ + tabIndex = tabAt(event->pos()); + closeActionSlot(); + event->accept(); + } else { + QTabBar::mouseReleaseEvent(event); + } +} + +void EditAreaTabBar::contextMenuSlot(const QPoint &pos) +{ + tabIndex = tabAt(pos); + EditAreaWidget *eaw = qobject_cast(tabWidget->widget(tabIndex)); + if(!eaw) + return; + menu->clear(); + menu->addAction(closeAction); + menu->addAction(closeOthersAction); + menu->addAction(closeAllAction); + + menu->addSeparator(); + + if(eaw->isEditActionOptionEnabled(EditAreaWidget::AllowSplit) && !eaw->getFileModel().isUntitled()){ + menu->addAction(cloneToOtherViewAction); + } + menu->addAction(moveToOtherViewAction); + menu->exec(mapToGlobal(pos)); +} + +void EditAreaTabBar::closeActionSlot() +{ + emit closeTab(tabIndex); +} + +void EditAreaTabBar::closeOtherActionSlot() +{ + emit closeOtherTabs(tabIndex); +} + +void EditAreaTabBar::closeAllActionSlot() +{ + emit closeAllTabs(); +} + +void EditAreaTabBar::cloneToOtherViewSlot() +{ + emit cloneToOtherView(tabIndex); +} + +void EditAreaTabBar::moveToOtherViewSlot() +{ + emit moveToOtherView(tabIndex); +} + +//---------------- EditAreaTabWidget ------------------------------------------- +EditAreaTabWidget::EditAreaTabWidget(MdCharmForm *mainForm, EditAreaTabWidgetManager *parent) : + QTabWidget(parent), + mainForm(mainForm), + manager(parent) +{ + //Custom tabbar + tabBar = new EditAreaTabBar(this); + setTabBar(tabBar); + // Style + setTabsClosable(true); + setMovable(true); + + initSignalsAndSlots(); +} + +void EditAreaTabWidget::initSignalsAndSlots() +{ + //--------- TabBar Signals and Slots ----------------------------- + connect(tabBar, SIGNAL(closeTab(int)), this, SLOT(closeOneTab(int))); + connect(tabBar, SIGNAL(closeOtherTabs(int)), this, SLOT(closeOtherTabs(int))); + connect(tabBar, SIGNAL(closeAllTabs()), this, SLOT(closeAllTabs())); + connect(tabBar, SIGNAL(cloneToOtherView(int)), this, SIGNAL(cloneToOtherView(int))); + connect(tabBar, SIGNAL(moveToOtherView(int)), this, SIGNAL(moveToOtherView(int))); + + connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeOneTab(int))); +} + +int EditAreaTabWidget::addMarkdownTab(MarkdownEditAreaWidget *tab, const QIcon &icon, const QString &label) +{ + connect(tab, SIGNAL(showStatusMessage(QString)), manager, SIGNAL(showStatusMessage(QString))); + if(!tab){ + Q_ASSERT(0 && "this should not be happen"); + return -1; + } + connect(tab, SIGNAL(focusInSignal()), this, SIGNAL(focused())); + return addTab(tab, icon, label); +} + +int EditAreaTabWidget::addEditAreaTab(EditAreaWidget *tab, const QIcon &icon, const QString &label) +{ + EditorModel em = tab->getEditorModel(); + if(em.getEditorType()==EditorModel::MARKDOWN){ + return addMarkdownTab(qobject_cast(tab), icon, label); + } else { + Q_ASSERT(0 && "this should not be happen"); + return addTab(tab, icon, label); + } +} + +int EditAreaTabWidget::addTab(QWidget *widget, const QIcon &icon, const QString &label) +{ + return QTabWidget::addTab(widget, icon, label); +} + +int EditAreaTabWidget::addTab(QWidget *widget, const QString &label) +{ + return QTabWidget::addTab(widget, label); +} + +void EditAreaTabWidget::closeCurrentTab() +{ + closeOneTab(currentIndex()); +} + +bool EditAreaTabWidget::closeOneTab(int index) +{ + if(index<0||index>=count()) // -1 means no tab + return false; + QWidget *tab = widget(index); + EditAreaWidget *editArea = dynamic_cast(tab); + assert(editArea); + if(editArea) + { + if(MdCharmGlobal::Cancel==saveBeforeClose(editArea, tabText(index))) + return false; + FileModel fm = editArea->getFileModel(); + if(!fm.getFileFullPath().isEmpty()) + manager->removeFromFileWatcher(fm.getFileFullPath()); + } + removeTab(index); + delete tab; + return true; +} + +void EditAreaTabWidget::closeOtherTabs(int remain) +{ + if(remain>=count()) + return; + QList widgets = listTabWidgets(); + EditAreaWidget* target = qobject_cast(widget(remain)); + if(target) + widgets.removeOne(target); + closeTabWidgets(widgets); +} + +void EditAreaTabWidget::closeAllTabs() +{ + closeTabWidgets(listTabWidgets()); +} + +QList EditAreaTabWidget::listTabWidgets() +{ + QList widgets; + for(int i=0; i(widget(i)); + if(eaw) + widgets.append(eaw); + } + return widgets; +} + +void EditAreaTabWidget::closeTabWidgets(const QList& widgets) +{ + foreach (QWidget *cur, widgets) { + int tabIndex = indexOf(cur); + if(tabIndex<0) + continue; + setCurrentIndex(tabIndex); + if(!closeOneTab(tabIndex)) + break; + } +} + +void EditAreaTabWidget::closeIfExist(const QString &filePath) +{ + QString fp = filePath; + for(int i=0; i(widget(i)); + FileModel fm = editArea->getFileModel(); +#ifdef Q_OS_WIN + if(fp==fm.getFileFullPath() && fp.toLower()==fm.getFileFullPath().toLower()) +#else + if(fp==fm.getFileFullPath()) +#endif + { + closeOneTab(i); + break; + } + } +} + +void EditAreaTabWidget::renameFile(const QString &original, const QString ¤t) +{ + int target = -1; + EditAreaWidget *targetEditArea = NULL; + for(int i=0; i(widget(i)); + if(!editArea) + continue; + FileModel fm = editArea->getFileModel(); + //Since we share FileModel, the file path may be alreay changed in other view so we compare `original` and `current` +#ifdef Q_OS_WIN + if( (original==fm.getFileFullPath() && original.toLower()==fm.getFileFullPath().toLower()) + || (current==fm.getFileFullPath() && current.toLower()==fm.getFileFullPath().toLower()) ) +#else + if(original==fm.getFileFullPath() || current==fm.getFileFullPath()) +#endif + { + target = i; + targetEditArea = editArea; + break; + } + } + if(targetEditArea && target>=0){ + targetEditArea->changeFilePath(current); + setTabText(target, targetEditArea->getFileModel().getFileName()); + } +} + +MdCharmGlobal::SaveFileOptions EditAreaTabWidget::saveBeforeClose(EditAreaWidget *editArea, const QString name)//True - you can close it, false don't close the tab +{ + assert(editArea); + EditorModel em = editArea->getEditorModel(); + if(em.getEditorType()>EditorModel::EDITABLE && editArea->isModified()) + { + QMessageBox::StandardButton sb = QMessageBox::question(this, tr("Save"), + tr("Save File %1 ?").arg(name), + QMessageBox::Save|QMessageBox::Discard|QMessageBox::Cancel); + if(sb==QMessageBox::Save) + { + if(!saveFile(editArea)) + return MdCharmGlobal::Cancel; //not saved, don't close the tab + } + else if(sb==QMessageBox::Cancel) + { + return MdCharmGlobal::Cancel; + } + else + { + return MdCharmGlobal::DontSave; + } + return MdCharmGlobal::Save; + } + return MdCharmGlobal::None; +} +bool EditAreaTabWidget::saveFile(EditAreaWidget *editArea) +{ + if(!editArea || !editArea->getEditorModel().isEditable()) + return false; + FileModel fm = editArea->getFileModel(); + if(!fm.getFileFullPath().isEmpty()) + manager->removeFromFileWatcher(fm.getFileFullPath()); + bool result = editArea->saveFile(); + fm = editArea->getFileModel(); + if(!fm.getFileFullPath().isEmpty()) + manager->addToFileWatcher(fm.getFileFullPath()); + return result; +} + +void EditAreaTabWidget::saveFileAs(EditAreaWidget *editArea) +{ + if(!editArea || !editArea->getEditorModel().isEditable()) + return; + FileModel fm = editArea->getFileModel(); + if(!fm.getFileFullPath().isEmpty()) + manager->removeFromFileWatcher(fm.getFileFullPath()); + editArea->saveFileAs(); + fm = editArea->getFileModel(); + if(!fm.getFileFullPath().isEmpty()) + manager->addToFileWatcher(fm.getFileFullPath()); +} diff --git a/src/MdCharm/editareatabwidget.h b/src/MdCharm/editareatabwidget.h new file mode 100644 index 0000000..72f9526 --- /dev/null +++ b/src/MdCharm/editareatabwidget.h @@ -0,0 +1,88 @@ +#ifndef EDITAREATABWIDGET_H +#define EDITAREATABWIDGET_H + +#include +#include +#include +#include + +#include "utils.h" + +class MarkdownEditAreaWidget; +class EditAreaWidget; +class MdCharmForm; +class EditAreaTabWidget; +class EditAreaTabWidgetManager; + +class EditAreaTabBar : public QTabBar +{ + Q_OBJECT +public: + explicit EditAreaTabBar(EditAreaTabWidget *parent=0); +signals: + void closeTab(int index); + void closeOtherTabs(int remainIndex); + void closeAllTabs(); + void cloneToOtherView(int index); + void moveToOtherView(int index); + +protected: + virtual void mouseReleaseEvent(QMouseEvent *event); +private slots: + void contextMenuSlot(const QPoint &pos); + void closeActionSlot(); + void closeAllActionSlot(); + void closeOtherActionSlot(); + void cloneToOtherViewSlot(); + void moveToOtherViewSlot(); +private: + QMenu *menu; + QAction *closeAction; + QAction *closeAllAction; + QAction *closeOthersAction; + QAction *cloneToOtherViewAction; + QAction *moveToOtherViewAction; + EditAreaTabWidget *tabWidget; + + int tabIndex; +}; + +class EditAreaTabWidget : public QTabWidget +{ + Q_OBJECT +public: + explicit EditAreaTabWidget(MdCharmForm *mainForm, EditAreaTabWidgetManager *parent = 0); + bool saveFile(EditAreaWidget *editArea); + void saveFileAs(EditAreaWidget *editArea); + MdCharmGlobal::SaveFileOptions saveBeforeClose(EditAreaWidget *editArea, const QString name); + void closeIfExist(const QString &filePath); + void renameFile(const QString &original, const QString ¤t); + int addMarkdownTab(MarkdownEditAreaWidget* tab, const QIcon &icon, const QString &label); + int addEditAreaTab(EditAreaWidget *tab, const QIcon &icon, const QString &label); +private: + void initSignalsAndSlots(); + int addTab(QWidget *widget, const QIcon &icon, const QString &label); + int addTab(QWidget *widget, const QString &label); +protected: + +private: + QStringList fileChangedPendingList; + EditAreaTabBar *tabBar; + MdCharmForm *mainForm; + EditAreaTabWidgetManager *manager; +signals: + void focused(); + void cloneToOtherView(int index); + void moveToOtherView(int index); +private slots: + void closeTabWidgets(const QList &widgets); + +public slots: + QList listTabWidgets(); + bool closeOneTab(int index); + void closeAllTabs(); + void closeOtherTabs(int remain); + void closeCurrentTab(); +}; + +#endif // EDITAREATABWIDGET_H diff --git a/src/MdCharm/editareatabwidgetmanager.cpp b/src/MdCharm/editareatabwidgetmanager.cpp new file mode 100644 index 0000000..fb20c29 --- /dev/null +++ b/src/MdCharm/editareatabwidgetmanager.cpp @@ -0,0 +1,742 @@ +#include "editareatabwidgetmanager.h" +#include "editareatabwidget.h" +#include "editareawidget.h" +#include "markdowneditareawidget.h" +#include "dock/projectdockwidget.h" +#include "mdcharmform.h" +#include "resource.h" +#include "configuration.h" + +#include + +EditAreaTabWidgetManager::EditAreaTabWidgetManager(MdCharmForm *mainForm) : + QWidget(mainForm), mainForm(mainForm) +{ + conf = Configuration::getInstance(); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::TabWidget)); + layout = new QHBoxLayout(this); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + bgLabel = new QLabel(this); + bgLabel->setText("No files are open
  • Open File with Ctrl+O
  • Create New File with Ctrl+N
"); + bgLabel->setFont(QFont(conf->getFontFamily(), conf->getFontSize())); + QSize size = bgLabel->sizeHint(); + bgLabel->setGeometry(geometry().width()/2,geometry().height()/2,size.width(), size.height()); + + mainSplitter = new QSplitter(Qt::Horizontal, this); + layout->addWidget(mainSplitter); + + views.append(new EditAreaTabWidget(mainForm, this)); + views.append(new EditAreaTabWidget(mainForm, this)); + foreach (EditAreaTabWidget *view, views) { + view->setVisible(false); + connect(view, SIGNAL(currentChanged(int)), this, SIGNAL(currentChanged())); + connect(view, SIGNAL(currentChanged(int)), this, SLOT(checkViewStatus())); + connect(view, SIGNAL(currentChanged(int)), this, SLOT(updateCurrentTabWidget())); + connect(view, SIGNAL(moveToOtherView(int)), this, SLOT(moveToOtherViewSlot(int))); + connect(view, SIGNAL(cloneToOtherView(int)), this, SLOT(cloneToOtherViewSlot(int))); + connect(view, SIGNAL(focused()), this, SLOT(updateCurrentTabWidget())); + mainSplitter->addWidget(view); + } + + fileWatcher = new QFileSystemWatcher(this); + connect(fileWatcher, SIGNAL(fileChanged(QString)), this, SLOT(fileChangedSlot(QString))); + + newFileId = 0; + currentTabWidget = NULL; +} + +MarkdownEditAreaWidget* EditAreaTabWidgetManager::addMarkdownEditAreaWidget( + const QString &filePath, const QUrl &baseUrl, int id) +{ + return addMarkdownEditAreaWidget(getCurrentTabWidget(), filePath, baseUrl, id); +} +MarkdownEditAreaWidget* EditAreaTabWidgetManager::addMarkdownEditAreaWidget( + EditAreaTabWidget *view, const QString &filePath, const QUrl &baseUrl, int id) +{ + //we assume file is exist or file path is empty(new file) + MarkdownEditAreaWidget *newMEAW = new MarkdownEditAreaWidget(mainForm, filePath, baseUrl); + QString title=tr("Untitled %1").arg(id); + if(!filePath.isEmpty()) + { + FileModel fm = newMEAW->getFileModel(); + title = fm.getFileName(); + fileWatcher->addPath(fm.getFileFullPath()); + } + int index = view->addMarkdownTab(newMEAW, QIcon(Resource::UnmodifiedIconStr), title); + view->setVisible(true); + view->setCurrentIndex(index); + newMEAW->setFocusEditor(); + connect(newMEAW, SIGNAL(updateActions()), this, SLOT(updateStatus())); + connect(newMEAW, SIGNAL(addToRecentFileList(QString)), + this, SIGNAL(addToRecentFileList(QString))); + return newMEAW; +} + +void EditAreaTabWidgetManager::checkFileStatusWhenMainWindowActived() +{ + while (!fileChangedPendingList.isEmpty()) { + fileChangedNotify(fileChangedPendingList.takeFirst()); + } +} + +bool EditAreaTabWidgetManager::saveFile() +{ + return saveFile(getCurrentWidget()); +} + +bool EditAreaTabWidgetManager::saveFile(EditAreaWidget *editArea) +{ + Q_ASSERT(editArea); + if(!editArea || !editArea->getEditorModel().isEditable()) + return false; + foreach (EditAreaTabWidget *view, views) { + if(view->indexOf(editArea)!=-1){ + view->saveFile(editArea); + //update tab text + FileModel fm = editArea->getFileModel(); + QList find = findEditAreaWidgetByFilePath(fm.getFileFullPath()); + foreach (EditAreaWidget *eaw, find) { + updateTabText(eaw, fm.getFileName()); + } + } + } + return false; +} + +void EditAreaTabWidgetManager::saveFileAs() +{ + EditAreaWidget *editArea = getCurrentWidget(); + Q_ASSERT(editArea); + if(!editArea || !editArea->getEditorModel().isEditable()) + return; + foreach (EditAreaTabWidget *view, views) { + if(view->indexOf(editArea)!=-1){ + view->saveFileAs(editArea); + //update tab text + FileModel fm = editArea->getFileModel(); + QList find = findEditAreaWidgetByFilePath(fm.getFileFullPath()); + foreach (EditAreaWidget *eaw, find) { + updateTabText(eaw, fm.getFileName()); + } + } + } + return; +} + +void EditAreaTabWidgetManager::changeSyncScrollBarSetting(bool sync) +{ + conf->changeSyncScrollbarSetting(sync); + QList tabs = getAllEditAreaWidgets(); + foreach (EditAreaWidget *eaw, tabs) { + eaw->changeSyncScrollbarSetting(sync); + } +} + +void EditAreaTabWidgetManager::deleteFileAndTab(const QString &filePath) +{ + foreach (EditAreaTabWidget *view, views) { + view->closeIfExist(filePath); + } + if(!QFile::remove(filePath)){ + QMessageBox::warning(this, tr("Failed to delete this file"), tr("Failed to delete this file")); + } +} + +void EditAreaTabWidgetManager::renameFileAndTab(const QString &original, const QString ¤t) +{ + fileWatcher->removePath(original); + if(!QFile::rename(original, current)){//try to rename file + QMessageBox::warning(this, tr("Failed to rename this file"), tr("Failed to rename this file")); + fileWatcher->addPath(original); + return; + } + foreach (EditAreaTabWidget *view, views) { + view->renameFile(original, current); + } + fileWatcher->addPath(current); +} + +EditAreaWidget* EditAreaTabWidgetManager::addNewTabWidget(const QString &filePath) +{ + return addNewTabWidget(getCurrentTabWidget(), filePath); +} + +void EditAreaTabWidgetManager::showFind() +{ + EditAreaWidget *editArea = getCurrentWidget(); + Q_ASSERT(editArea); + if(!editArea) + return; + EditorModel em = editArea->getEditorModel(); + if(em.getEditorType()==EditorModel::MARKDOWN){ + MarkdownEditAreaWidget *mea = qobject_cast(editArea); + Q_ASSERT(mea); + if(!mea) + return; + mea->showFind(); + mea->setFocusFinder(); + } +} + +EditAreaWidget* EditAreaTabWidgetManager::addNewTabWidget(EditAreaTabWidget *view, const QString &filePath) +{ + //find if file is already open by mdcharm + QList find = findEditAreaWidgetByFilePath(filePath); + if(!find.isEmpty()){ + setCurrentEditAreaWidget(find.first()); + return find.first(); + } + //file not opened yet, open it + QString formatFilePath = filePath; + formatFilePath.replace("\\", "/"); + QString projectDir = mainForm->getProjectDockWidget()->getProjectDir(); + QUrl baseUrl; + if(!filePath.isEmpty()) { + //check file existence + if(!QFile::exists(filePath)){ // file not exist + QMessageBox::StandardButton sb = QMessageBox::question(this, + tr("Create new file"), + tr("%1 doesn't exist. Create it?").arg(filePath), + QMessageBox::Yes|QMessageBox::No, + QMessageBox::Yes); + if(sb == QMessageBox::No) + return NULL; + //else, try create the file + QFile newFile(filePath); + if( !newFile.open(QIODevice::WriteOnly|QIODevice::Text) ){ + QMessageBox::information(this, tr("Create new file"), + tr("Cannot create the file \"%1\"").arg(filePath)); + return NULL; + } + newFile.close(); + } + if(!projectDir.isEmpty() && formatFilePath.startsWith(projectDir)) + baseUrl = QUrl::fromLocalFile(projectDir+"/"); + else + baseUrl = QUrl::fromLocalFile(filePath); + } + MarkdownEditAreaWidget *ret = addMarkdownEditAreaWidget(view, formatFilePath, baseUrl, formatFilePath.isEmpty() ? ++newFileId:0); + emit updateActions(); + return ret; +} + +void EditAreaTabWidgetManager::updateStatus() +{ + EditAreaWidget *editArea = qobject_cast(sender()); + Q_ASSERT(editArea); + if(editArea){ + EditAreaTabWidget *view = findEditAreaTabWidget(editArea); + if(view){ + view->setTabIcon(view->indexOf(editArea), editArea->isModified() ? QIcon(Resource::ModifiedIconStr) : QIcon(Resource::UnmodifiedIconStr)); + } + } + emit updateActions(); +} + +void EditAreaTabWidgetManager::fileChangedSlot(const QString filePath) +{ + if(!mainForm->isActiveWindow()){ + addToFileChangedPendingList(filePath); + return; + } + //if the main form is active + fileChangedNotify(filePath); +} + +void EditAreaTabWidgetManager::addToFileChangedPendingList(const QString &path) +{ + if(path.isEmpty()) + return; + if(fileChangedPendingList.isEmpty()||fileChangedPendingList.indexOf(path)==-1) + fileChangedPendingList.append(path); +} + +void EditAreaTabWidgetManager::fileChangedNotify(const QString &path) +{ + QList result = findEditAreaWidgetByFilePath(path); + if(result.isEmpty()){//not found + fileWatcher->removePath(path); + return; + } + if(QFile::exists(path)){//file content changed + QMessageBox::StandardButton sb = + QMessageBox::question(this, + tr("Reload"), + tr("%1\nThis file has been modified by another program.\nDo you want to reload it?").arg(path), + QMessageBox::Yes|QMessageBox::No); + if(sb==QMessageBox::Yes){ + //reload file + result.first()->reloadFile(); + } + } else {//file is renamed or removed + QMessageBox::StandardButton sb = + QMessageBox::question(this, + tr("Keep non existing file"), + tr("The file \"%1\" doesn't exist anymore.\n Keep this file in editor?").arg(path), + QMessageBox::Yes|QMessageBox::No); + if(sb==QMessageBox::No){ + removeTabs(result); + } else { + foreach (EditAreaWidget *eaw, result) { + eaw->setModified(true); + } + } + } +} +QList EditAreaTabWidgetManager::findEditAreaWidgetByFilePath(const QString &path) +{ + QList result; + if(path.isEmpty()) + return result; + foreach (EditAreaTabWidget *view, views) { + EditAreaWidget *find = findInEditAreaTabWidget(view, path); + if(find) + result.append(find); + } + return result; +} + +EditAreaTabWidget* EditAreaTabWidgetManager::findEditAreaTabWidget(EditAreaWidget *eaw) +{ + if(!eaw) + return NULL; + foreach (EditAreaTabWidget *view, views) { + int index = view->indexOf(eaw); + if(index!=-1){ + return view; + } + } + Q_ASSERT(0 && "can't be null"); + return NULL; +} + +void EditAreaTabWidgetManager::updateConfiguration() +{ + QList tabs = getAllEditAreaWidgets(); + foreach (EditAreaWidget *eaw, tabs) { + eaw->updateConfiguration(); + } +} + +void EditAreaTabWidgetManager::switchPreview(int type) +{ + QList tabs = getAllEditAreaWidgets(); + foreach (EditAreaWidget *eaw, tabs) { + eaw->switchPreview(type); + } +} + +QString EditAreaTabWidgetManager::getCurrentTabTabText() +{ + return getTabTabText(getCurrentWidget()); +} + +QString EditAreaTabWidgetManager::getTabTabText(EditAreaWidget *tab) +{ + Q_ASSERT(tab); + EditAreaTabWidget *tabWidget = getCurrentTabWidget(); + Q_ASSERT(tabWidget); + if(!tab || !tabWidget) + return QString(); + int index = tabWidget->indexOf(tab); + if(index!=-1){ + return tabWidget->tabText(index); + } + return QString(); +} + +void EditAreaTabWidgetManager::updateSpellCheckOption(bool isCheck) +{ + QList tabs = getAllEditAreaWidgets(); + foreach (EditAreaWidget *tab, tabs) { + if(tab->getEditorModel().getEditorType()==EditorModel::MARKDOWN){ + MarkdownEditAreaWidget *meaw = qobject_cast(tab); + if(!meaw) + continue; + if(isCheck) + meaw->enableSpellCheck(); + else + meaw->disableSpellCheck(); + } + } +} + +bool EditAreaTabWidgetManager::saveAllBeforeClose() +{ + QStringList sl; + foreach (EditAreaTabWidget *editAreaTabWidget, views) { + for(int i=0; icount(); i++){ + EditAreaWidget *eaw = qobject_cast(editAreaTabWidget->widget(i)); + if(!eaw || (!eaw->getFileModel().isUntitled() && sl.indexOf(eaw->getFileModel().getFileFullPath())!=-1))//deal clone tab + continue; + if(MdCharmGlobal::Cancel==editAreaTabWidget->saveBeforeClose(eaw, editAreaTabWidget->tabText(i))) + return false; + sl.append(eaw->getFileModel().getFileFullPath()); + } + } + return true; +} + +void EditAreaTabWidgetManager::closeCurrentTab() +{ + EditAreaTabWidget *tabWidget = getCurrentTabWidget(); + if(!tabWidget) + return; + tabWidget->closeCurrentTab(); +} + +void EditAreaTabWidgetManager::removeTabs(QList tabs)//delete +{ + foreach (EditAreaWidget *eaw, tabs) { + removeOneTab(eaw); + } + checkViewStatus();//refresh views +} + +void EditAreaTabWidgetManager::removeOneTab(EditAreaWidget *target) +{ + Q_ASSERT(target); + if(!target) + return; + EditAreaTabWidget *view = findEditAreaTabWidget(target); + Q_ASSERT(view); + if(view){ + view->removeTab(view->indexOf(target)); + target->deleteLater(); + } +} + +void EditAreaTabWidgetManager::checkViewStatus() +{ + bool allHide = true; + foreach (EditAreaTabWidget *view, views) { + if(view->count()==0&&view->isVisible()) + view->hide(); + if(view->count()>0&&!view->isVisible()) + view->show(); + if(view->count()>0) + allHide = false; + } + bgLabel->setVisible(allHide); +} + +void EditAreaTabWidgetManager::updateCurrentTabWidget() +{ + currentTabWidget = qobject_cast(sender()); + if(currentTabWidget && currentTabWidget->count()<=0) + currentTabWidget = NULL; + emit updateActions(); + emit currentChanged(); +} + +void EditAreaTabWidgetManager::moveToOtherViewSlot(int index) +{ + EditAreaTabWidget *view = qobject_cast(sender()); + Q_ASSERT(view); + if(!view) + return; + if(index>=view->count()) + return; + EditAreaWidget *eaw = qobject_cast(view->widget(index)); + if(!eaw) + return; + EditAreaTabWidget *otherView = NULL; + foreach (EditAreaTabWidget *v, views) { + if(v!=view){ + otherView = v; + break; + } + } + if(!otherView) + return; + //find if it exist + FileModel fm = eaw->getFileModel(); + EditAreaWidget *exist = findInEditAreaTabWidget(otherView, fm.getFileFullPath()); + if(exist){//remove current if exist in other view and return + view->removeTab(index); + otherView->setCurrentIndex(otherView->indexOf(exist)); + checkViewStatus(); + return; + } + QString tabText = view->tabText(index); + view->removeTab(index); + int newIndex = otherView->addEditAreaTab(eaw, eaw->isModified() ? QIcon(Resource::ModifiedIconStr) : QIcon(Resource::UnmodifiedIconStr), tabText); + otherView->setVisible(true); + otherView->setCurrentIndex(newIndex); + currentTabWidget = otherView; + checkViewStatus(); +} + +void EditAreaTabWidgetManager::cloneToOtherViewSlot(int index) +{ + EditAreaTabWidget *view = qobject_cast(sender()); + Q_ASSERT(view); + if(!view) + return; + if(index>=view->count()) + return; + EditAreaWidget *eaw = qobject_cast(view->widget(index)); + if(!eaw) + return; + EditAreaTabWidget *otherView = NULL; + foreach (EditAreaTabWidget *v, views) { + if(v!=view){ + otherView = v; + break; + } + } + if(!otherView) + return; + //find if it exist + FileModel fm = eaw->getFileModel(); + EditAreaWidget* exist = findInEditAreaTabWidget(otherView, fm.getFileFullPath()); + if(exist){ + otherView->setCurrentIndex(otherView->indexOf(exist)); + return; + } + EditAreaWidget *copy = eaw->clone(); + if(!copy) + return; + EditorModel em = copy->getEditorModel(); + if(em.getEditorType()==EditorModel::MARKDOWN){ + MarkdownEditAreaWidget *meaw = qobject_cast(copy); + if(meaw){ + connect(meaw, SIGNAL(updateActions()), this, SLOT(updateStatus())); + connect(meaw, SIGNAL(addToRecentFileList(QString)), + this, SIGNAL(addToRecentFileList(QString))); + } + } + int newIndex = otherView->addEditAreaTab(copy, copy->isModified() ? QIcon(Resource::ModifiedIconStr) : QIcon(Resource::UnmodifiedIconStr), view->tabText(index)); + otherView->setVisible(true); + otherView->setCurrentIndex(newIndex); + currentTabWidget = otherView; + checkViewStatus(); +} + +EditAreaTabWidget* EditAreaTabWidgetManager::getCurrentTabWidget() +{ + if(!currentTabWidget || currentTabWidget->count()<=0)//Notice: if we support delete view make sure it is not null + return views.first(); + return currentTabWidget; +} + +EditAreaWidget* EditAreaTabWidgetManager::getCurrentWidget() +{ + EditAreaTabWidget *currentTabWidget = getCurrentTabWidget(); + if(!currentTabWidget) + return NULL; + return qobject_cast(currentTabWidget->currentWidget()); +} + +void EditAreaTabWidgetManager::setCurrentEditAreaWidget(EditAreaWidget *eaw) +{ + Q_ASSERT(eaw); + if(!eaw) + return; + foreach (EditAreaTabWidget *view, views) { + int index = view->indexOf(eaw); + if(index!=-1){ + view->setVisible(true); + view->setFocus(); + view->setCurrentIndex(index); + MarkdownEditAreaWidget *meaw = qobject_cast(eaw); + if(meaw) + meaw->setFocusEditor(); + break; + } + } +} + +QList EditAreaTabWidgetManager::getAllEditAreaWidgets() +{ + QList widgets; + foreach (EditAreaTabWidget* eatw, views) { + widgets.append(eatw->listTabWidgets()); + } + return widgets; +} + +void EditAreaTabWidgetManager::updateTabText(EditAreaWidget *editArea, const QString &fileName) +{ + Q_ASSERT(editArea); + if(!editArea) + return; + foreach (EditAreaTabWidget *view, views) { + int index = view->indexOf(editArea); + if(index!=-1){ + view->setTabText(index, fileName); + break; + } + } +} + +EditAreaWidget* EditAreaTabWidgetManager::findInEditAreaTabWidget(EditAreaTabWidget *view, const QString &filePath) +{ + if(filePath.isEmpty() || view->count()<=0) + return NULL; + QString formatStr = QDir::fromNativeSeparators(filePath); +#ifdef Q_OS_WIN + formatStr = formatStr.toLower(); +#endif + QList tabs = view->listTabWidgets(); + foreach (EditAreaWidget *tab, tabs) { + FileModel fm = tab->getFileModel(); +#ifdef Q_OS_WIN + if(fm.getFileFullPath().toLower()==formatStr){ +#else + if(fm.getFileFullPath()==formatStr){ +#endif + return tab; + } + } + return NULL; +} + +void EditAreaTabWidgetManager::saveTabsState() +{ + const QString sessionFilePath = conf->getSessionFilePath(); + QFile sessionFile(sessionFilePath); + if(!sessionFile.open(QIODevice::ReadWrite|QIODevice::Text|QIODevice::Truncate)) + return; + + EditAreaTabWidget *eatw = getCurrentTabWidget(); + int activeViewIndex = -1; + foreach(EditAreaTabWidget *view, views){ + if(view->count()>0) + activeViewIndex++; + if(view==eatw) + break; + } + if(activeViewIndex==-1) + activeViewIndex = 0; + QXmlStreamWriter xmlStreamWriter(&sessionFile); + //set pretty formating + xmlStreamWriter.setAutoFormatting(true); + //write content + xmlStreamWriter.writeStartDocument(); + xmlStreamWriter.writeStartElement(Utils::AppName); + xmlStreamWriter.writeStartElement("Session"); + xmlStreamWriter.writeAttribute("activeView", QString::number(activeViewIndex)); + foreach (EditAreaTabWidget *editAreaTabWidget, views) { + if(editAreaTabWidget->count()<=0) + continue; + xmlStreamWriter.writeStartElement("MainView"); + for(int i=0; icount(); i++){ + EditAreaWidget *edit = qobject_cast(editAreaTabWidget->widget(i)); + if(edit){ + EditorModel em = edit->getEditorModel(); + StateModel sm = edit->getState(); + int isCurrentTab = editAreaTabWidget->currentIndex()==i ? 1 : 0; + if(em.isEditable()&&sm.isValid()){ + xmlStreamWriter.writeStartElement("File"); + xmlStreamWriter.writeAttribute("isCurrentTab", QString::number(isCurrentTab)); + xmlStreamWriter.writeAttribute("firstVisibleLine", QString::number(sm.getFirstVisibleLine())); + xmlStreamWriter.writeAttribute("selectionStart", QString::number(sm.getSelectionStart())); + xmlStreamWriter.writeAttribute("selectionEnd", QString::number(sm.getSelectionEnd())); + xmlStreamWriter.writeAttribute("verticalScrollBarCurrentValue", + QString::number(sm.getVerticalScorllBarCurrentValue())); + xmlStreamWriter.writeAttribute("verticalScrollBarMaxValue", QString::number(sm.getVerticalScrollBarMaxValue())); + xmlStreamWriter.writeAttribute("filename", sm.getFileFullPath()); + xmlStreamWriter.writeEndElement();//end File + } + } + } + + xmlStreamWriter.writeEndElement();//end MainView + } + xmlStreamWriter.writeStartElement("DockWidget"); + xmlStreamWriter.writeStartElement("Project"); + xmlStreamWriter.writeAttribute("dirPath", mainForm->getProjectDockWidget()->getProjectDir()); + xmlStreamWriter.writeEndElement();//end project + xmlStreamWriter.writeEndElement();//end dock widget + + xmlStreamWriter.writeEndElement();//end Session + xmlStreamWriter.writeEndElement();//end MdCharm + xmlStreamWriter.writeEndDocument();//end document + + sessionFile.close(); +} + +void EditAreaTabWidgetManager::restoreTabsState(const QList &sml) +{ + if(sml.isEmpty()) + return; + int willIndex = views.first()->count(); + int currentView = 0; + int count = 0; + foreach (StateModel sm, sml) { + if(sm.getViewNum()!=currentView){ + EditAreaTabWidget *sumView = getIndexView(currentView); + if(sumView && sumView->count()>willIndex) + sumView->setCurrentIndex(willIndex); + currentView = sm.getViewNum(); + count = 0; + willIndex = 0; + } + if(!QFile::exists(sm.getFileFullPath())) + continue; + QList exist = findEditAreaWidgetByFilePath(sm.getFileFullPath()); + EditAreaTabWidget *view = getIndexView(currentView); + if(exist.isEmpty()){ + EditAreaWidget* eaw = addNewTabWidget(view, sm.getFileFullPath()); + if(eaw){ + eaw->restoreFileState(sm); + } + } else { + EditAreaWidget *src = exist.first(); + EditAreaTabWidget *srcView = findEditAreaTabWidget(src); + Q_ASSERT(srcView && srcView!=view); + if(srcView){ + EditAreaWidget *copy = src->clone(); + if(!copy) + continue; + if(copy->getEditorModel().getEditorType()==EditorModel::MARKDOWN){ + MarkdownEditAreaWidget *meaw = qobject_cast(copy); + if(meaw){ + connect(meaw, SIGNAL(updateActions()), this, SLOT(updateStatus())); + connect(meaw, SIGNAL(addToRecentFileList(QString)), + this, SIGNAL(addToRecentFileList(QString))); + } + } + view->addEditAreaTab(copy, + copy->isModified()?QIcon(Resource::ModifiedIconStr):QIcon(Resource::UnmodifiedIconStr), + srcView->tabText(srcView->indexOf(src))); + copy->restoreFileState(sm); + } + } + if(sm.isCurrentTab()) + willIndex += count; + count++; + } + EditAreaTabWidget *last = getIndexView(currentView); + if(last && last->count()>willIndex) + last->setCurrentIndex(willIndex); + checkViewStatus(); +} + +EditAreaTabWidget* EditAreaTabWidgetManager::getIndexView(int index) +{ + if(index>=views.count()) + return views.last(); + return views.value(index); +} + +void EditAreaTabWidgetManager::removeFromFileWatcher(const QString &filePath) +{ + fileWatcher->removePath(filePath); +} + +void EditAreaTabWidgetManager::addToFileWatcher(const QString &filePath) +{ + fileWatcher->addPath(filePath); +} + +void EditAreaTabWidgetManager::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); + QRect gRect = geometry(); + QSize lSize = bgLabel->sizeHint(); + bgLabel->setGeometry((gRect.width()-lSize.width())/2, (gRect.height()-lSize.height())/2, lSize.width(), lSize.height()); +} diff --git a/src/MdCharm/editareatabwidgetmanager.h b/src/MdCharm/editareatabwidgetmanager.h new file mode 100644 index 0000000..1c85807 --- /dev/null +++ b/src/MdCharm/editareatabwidgetmanager.h @@ -0,0 +1,99 @@ +#ifndef EDITAREATABWIDGETMANAGER_H +#define EDITAREATABWIDGETMANAGER_H + +#include +#include +#include +#include +#include +#include +#include + +#include "editareawidget.h" + +class EditAreaTabWidget; +class EditAreaWidget; +class MarkdownEditAreaWidget; +class MdCharmForm; +class Configuration; + +class EditAreaTabWidgetManager : public QWidget +{ + Q_OBJECT +public: + explicit EditAreaTabWidgetManager(MdCharmForm *mainForm); + MarkdownEditAreaWidget* addMarkdownEditAreaWidget(const QString &filePath, + const QUrl &baseUrl, + int id); + EditAreaTabWidget* getCurrentTabWidget(); + EditAreaWidget* getCurrentWidget(); + void setCurrentEditAreaWidget(EditAreaWidget *eaw); + QList findEditAreaWidgetByFilePath(const QString &path); + EditAreaTabWidget* findEditAreaTabWidget(EditAreaWidget *eaw); + void updateConfiguration(); + void switchPreview(int type); + QString getCurrentTabTabText(); + QString getTabTabText(EditAreaWidget *tab); + void updateSpellCheckOption(bool isCheck); + bool saveAllBeforeClose(); + void saveTabsState(); + void restoreTabsState(const QList &sml); + void removeFromFileWatcher(const QString &filePath); + void addToFileWatcher(const QString &filePath); +protected: + virtual void resizeEvent(QResizeEvent *e); +signals: + void currentChanged(); + void addToRecentFileList(QString); + void updateActions(); + void showStatusMessage(const QString &msg); +public slots: + void closeCurrentTab(); + void checkFileStatusWhenMainWindowActived(); + bool saveFile(); + bool saveFile(EditAreaWidget *editArea); + void saveFileAs(); + void changeSyncScrollBarSetting(bool sync); + void deleteFileAndTab(const QString &filePath); + void renameFileAndTab(const QString &original, const QString ¤t); + EditAreaWidget* addNewTabWidget(const QString &filePath); + void showFind(); +private slots: + void updateStatus(); + void fileChangedSlot(const QString filePath); + void addToFileChangedPendingList(const QString &path); + void fileChangedNotify(const QString &path); + void checkViewStatus(); + void updateCurrentTabWidget(); + void moveToOtherViewSlot(int index); + void cloneToOtherViewSlot(int index); +private: + void removeTabs(QList tabs); + void removeOneTab(EditAreaWidget* target); + QList getAllEditAreaWidgets(); + void updateTabText(EditAreaWidget *editArea, const QString &fileName); + EditAreaWidget* findInEditAreaTabWidget(EditAreaTabWidget *view, const QString &filePath); + EditAreaTabWidget* getIndexView(int index); + EditAreaWidget* addNewTabWidget(EditAreaTabWidget *view, const QString &filePath); + MarkdownEditAreaWidget* addMarkdownEditAreaWidget(EditAreaTabWidget *view, + const QString &filePath, + const QUrl &baseUrl, + int id); +private: + QFileSystemWatcher *fileWatcher; + QSplitter *mainSplitter; + MdCharmForm *mainForm; + QLabel *bgLabel; + + QStringList fileChangedPendingList; + QList views; + QHBoxLayout *layout; + + Configuration *conf; + + EditAreaTabWidget *currentTabWidget; + + int newFileId; +}; + +#endif // EDITAREATABWIDGETMANAGER_H diff --git a/src/MdCharm/editareawidget.cpp b/src/MdCharm/editareawidget.cpp new file mode 100644 index 0000000..6ac8566 --- /dev/null +++ b/src/MdCharm/editareawidget.cpp @@ -0,0 +1,205 @@ +#include +#include + +#include "editareawidget.h" +#include "configuration.h" + +/************************* FileModel ******************************************/ +FileModel::FileModel() +{ + hasBom = false; +} + +QString FileModel::getFileName() +{ + if (fileFullPath.isEmpty()) + return QString(); + return QFileInfo(fileFullPath).fileName(); +} + +void FileModel::setFileFullPath(const QString filePath){ + fileFullPath = filePath; +} +void FileModel::setHasBom(bool b) { hasBom = b; } +void FileModel::setEncodingFormatName(const QString c){ encodingFormatName = c; } +QString FileModel::getEncodingFormatName() { return encodingFormatName; } + +bool FileModel::isHasBom() { return hasBom; } +QString FileModel::getFileFullPath(){ return fileFullPath; } +bool FileModel::isUntitled(){ return fileFullPath.isEmpty(); } + +/************************* EditorModel ****************************************/ +void EditorModel::setFindVisible(bool f){ findVisible=f; } +bool EditorModel::isFindVisible(){ return findVisible; } +void EditorModel::setCanCopy(bool c){ canCopy = c; } +bool EditorModel::isCopyAvailable(){ return canCopy; } +void EditorModel::setCurrentLineNumber(int l) { currentLineNumber = l; } +int EditorModel::getCurrentLineNumber() { return currentLineNumber; } +void EditorModel::setCurrentColumnNumber(int c) { currentColumnNumber = c; } +int EditorModel::getCurrentColumnNumber() { return currentColumnNumber; } +void EditorModel::overWriteChanged() { overWrite = !overWrite; } +bool EditorModel::isOverWrite() { return overWrite; } +void EditorModel::setEditorType(EditorType t) { editorType = t; } +EditorModel::EditorType EditorModel::getEditorType() { return editorType; } +bool EditorModel::isEditable() +{ + if(editorType>EDITABLE) + return true; + return false; +} + +/************************* StateModel *****************************************/ +bool StateModel::isValid(){return !fileFullPath.isEmpty();} +bool StateModel::isCurrentTab(){return currentTab; } +void StateModel::setIsCurrentTab(bool b){currentTab=b;} +int StateModel::getSelectionStart()const{return selectionStart;} +void StateModel::setSelectionStart(int start){selectionStart = start;} +int StateModel::getSelectionEnd()const{return selectionEnd;} +void StateModel::setSelectionEnd(int end){selectionEnd = end;} +int StateModel::getFirstVisibleLine()const{return firstVisibleLine;} +void StateModel::setFirstVisibleLine(int line){firstVisibleLine = line;} +const QString StateModel::getFileFullPath()const{return fileFullPath;} +void StateModel::setFileFullPath(const QString &ffp){fileFullPath=ffp;} +int StateModel::getVerticalScorllBarCurrentValue()const {return verticalScrollBarCurrentValue;} +void StateModel::setVerticalScrllBarCurrentValue(int value){verticalScrollBarCurrentValue=value;} +int StateModel::getVerticalScrollBarMaxValue()const {return verticalScrollBarMaxValue;} +void StateModel::setVerticalScrollBarMaxValue(int value) {verticalScrollBarMaxValue = value;} +int StateModel::getViewNum(){ return viewNum; } +void StateModel::setViewNum(int v){ viewNum = v; } + +EditAreaWidget::EditAreaWidget(const QString &filePath, EditActionOptions op) : + QWidget(0), + options(op) +{ + fm = QSharedPointer(new FileModel()); + fm->setFileFullPath(filePath); + conf = Configuration::getInstance(); +} + +EditAreaWidget::EditAreaWidget(EditAreaWidget &e) +{ + fm = QSharedPointer(e.fm); + conf = Configuration::getInstance(); + options = e.options; + em = e.em; +} + +void EditAreaWidget::setText(const QString &text) +{ + Q_UNUSED(text); +} + +QString EditAreaWidget::getText() +{ + return QString(); +} + +void EditAreaWidget::exportToPdf(const QString &filePath) +{ + Q_UNUSED(filePath) +} + +void EditAreaWidget::exportToODT(const QString &filePath) +{ + Q_UNUSED(filePath) +} + +void EditAreaWidget::exportToHtml(const QString &filePath) +{ + Q_UNUSED(filePath) +} + +void EditAreaWidget::printContent() +{ + assert("this method must not be called!"); +} + +void EditAreaWidget::printPreview() +{ + assert("this method must not be called!"); +} + +bool EditAreaWidget::saveFile() +{ + return false;//saved? +} + +void EditAreaWidget::saveFileAs() +{ + +} + +void EditAreaWidget::switchPreview(int isShowPreview) +{ + Q_UNUSED(isShowPreview); +} + +void EditAreaWidget::changeSyncScrollbarSetting(bool sync) +{ + Q_UNUSED(sync); +} + +void EditAreaWidget::reloadFile() +{ +} + +int EditAreaWidget::getCurrentMaxBlockCount() +{ + return 1; +} + +void EditAreaWidget::gotoLine(int line) +{ + Q_UNUSED(line) +} + +const StateModel EditAreaWidget::getState() +{ + return StateModel(); +} + +void EditAreaWidget::restoreFileState(const StateModel &sm) +{ + Q_UNUSED(sm) +} + +void EditAreaWidget::changeFilePath(const QString &newFilePath) +{ + fm->setFileFullPath(newFilePath); +} + +void EditAreaWidget::setCopyAvaliable(bool a) +{ + em.setCanCopy(a); + emit updateActions(); +} + +FileModel EditAreaWidget::getFileModel() +{ + return *fm; +} + +EditorModel EditAreaWidget::getEditorModel() +{ + return em; +} + +void EditAreaWidget::setFileEncoding(const QString en) +{ + fm->setEncodingFormatName(en); +} + +void EditAreaWidget::updateConfiguration() +{ + +} + +void EditAreaWidget::findNext() +{} +void EditAreaWidget::findPrevious() +{} + +bool EditAreaWidget::isEditActionOptionEnabled(EditActionOption op) +{ + return options.testFlag(op); +} diff --git a/src/MdCharm/editareawidget.h b/src/MdCharm/editareawidget.h new file mode 100644 index 0000000..7cfb1e4 --- /dev/null +++ b/src/MdCharm/editareawidget.h @@ -0,0 +1,173 @@ +#ifndef EDITAREAWIDGET_H +#define EDITAREAWIDGET_H + +#include + +class Configuration; + +class FileModel +{ +private: + bool hasBom; + QString fileFullPath; + QString encodingFormatName; +public: + FileModel(); + void setFileFullPath(const QString filePath); + void setHasBom(bool b); + void setEncodingFormatName(const QString c); + QString getEncodingFormatName(); + + bool isHasBom(); + QString getFileFullPath(); + QString getFileName(); + bool isUntitled(); +}; + +class EditorModel +{ +public: + enum EditorType + { + NONE, + BROWER, + EDITABLE,//seperator + MARKDOWN + }; + + EditorModel() + { + currentLineNumber = 1; + currentColumnNumber = 1; + overWrite = false; + canCopy = false; + findVisible = false; + editorType = NONE; + } + +private: + int currentLineNumber; + int currentColumnNumber; + bool overWrite; + bool canCopy; + bool findVisible; + EditorType editorType; +public: + void setFindVisible(bool f); + bool isFindVisible(); + void setCanCopy(bool c); + bool isCopyAvailable(); + void setCurrentLineNumber(int l); + int getCurrentLineNumber(); + void setCurrentColumnNumber(int c); + int getCurrentColumnNumber(); + void overWriteChanged(); + bool isOverWrite(); + void setEditorType(EditorType t); + EditorType getEditorType(); + bool isEditable(); +}; + +class StateModel +{ +public: + bool isValid(); + bool isCurrentTab(); + void setIsCurrentTab(bool b); + int getSelectionStart()const; + void setSelectionStart(int start); + int getSelectionEnd()const; + void setSelectionEnd(int end); + int getFirstVisibleLine()const; + void setFirstVisibleLine(int line); + const QString getFileFullPath()const; + void setFileFullPath(const QString &ffp); + int getVerticalScorllBarCurrentValue()const; + void setVerticalScrllBarCurrentValue(int value); + int getVerticalScrollBarMaxValue()const; + void setVerticalScrollBarMaxValue(int value); + int getViewNum(); + void setViewNum(int v); +private: + bool currentTab; + int selectionStart; + int selectionEnd; + int firstVisibleLine; + int verticalScrollBarCurrentValue; + int verticalScrollBarMaxValue; + int viewNum; + QString fileFullPath; +}; + +class EditAreaWidget : public QWidget +{ + Q_OBJECT +public: + enum EditActionOption{ + AllowSelectAll = 1 << 0, + AllowSaveAs = 1 << 1, + AllowExportToHtml = 1 << 2, + AllowExportToPdf = 1 << 3, + AllowExportToODT = 1 << 4, + AllowPrint = 1 << 5, + AllowPreview = 1 << 6, + AllowFind = 1 << 7, + AllowSplit = 1 << 8 + }; + Q_DECLARE_FLAGS(EditActionOptions, EditActionOption) +private: +public: + explicit EditAreaWidget(const QString &filePath=QString(), EditActionOptions op=0); + explicit EditAreaWidget(EditAreaWidget &e); + virtual void setText(const QString &text); + virtual QString getText(); + virtual void exportToPdf(const QString &filePath); + virtual void exportToODT(const QString &filePath); + virtual void exportToHtml(const QString &filePath); + virtual void printContent(); + virtual void printPreview(); + virtual bool saveFile(); + virtual void saveFileAs(); + virtual void switchPreview(int isShowPreview); + virtual void changeSyncScrollbarSetting(bool sync); + virtual void reloadFile(); + virtual int getCurrentMaxBlockCount(); + virtual void gotoLine(int line); + virtual const StateModel getState(); + virtual void restoreFileState(const StateModel &sm); + virtual void changeFilePath(const QString &newFilePath); + virtual EditAreaWidget* clone() = 0; + FileModel getFileModel(); + EditorModel getEditorModel(); + bool isEditActionOptionEnabled(EditActionOption op); +protected: + QSharedPointer fm; + EditorModel em; + Configuration *conf; + EditActionOptions options; +signals: + void editorContentModifiedSignal(bool b); + void updateActions(); + void updateStatusBar(); + void showStatusMessage(const QString &msg); +public slots: + virtual void copy() = 0; + virtual void cut() = 0; + virtual void paste() = 0; + virtual void redo() = 0; + virtual void undo() = 0; + virtual void selectAll() = 0; + virtual bool isModified() = 0; + virtual void setModified(bool isModi) = 0; + virtual bool isUndoAvailable() = 0; + virtual bool isRedoAvailable() = 0; + virtual void findNext(); + virtual void findPrevious(); + void setCopyAvaliable(bool a); + void setFileEncoding(const QString en); + virtual void updateConfiguration(); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(EditAreaWidget::EditActionOptions) + +#endif // EDITAREAWIDGET_H diff --git a/src/MdCharm/main.cpp b/src/MdCharm/main.cpp new file mode 100644 index 0000000..5066e55 --- /dev/null +++ b/src/MdCharm/main.cpp @@ -0,0 +1,133 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "version.h" +#include "mdcharmapplication.h" +#include "mdcharmform.h" +#include "configuration.h" +#include "util/spellcheck/spellchecker.h" +#include "codesyntaxhighlighter.h" +#include + +void delayForShowSplash(unsigned int msec); +void initHilighter(); + +int main(int argc, char** argv) +{ +#ifdef QT_V5 + qApp->addLibraryPath("/usr/local/lib/Qt-5.0.2/plugins"); + qApp->addLibraryPath("/usr/local/lib/Qt-5.0.2/"); +#endif + + MdCharmApplication app(argc, argv); + if(app.isRunning()) + return 0; + app.setApplicationName(Utils::AppName); + app.setApplicationVersion(QLatin1String(VERSION_STR)); + app.setOrganizationName(Utils::AppName); + app.setOrganizationDomain(QLatin1String("mdcharm.com")); + + Configuration *conf = Configuration::getInstance(); + bool isShowSplash = conf->isShowSplash(); + if(argc>1) + isShowSplash = false; + QTime startTime; + QSplashScreen *splash = NULL; + if (isShowSplash) + { + splash = new QSplashScreen(QPixmap(":/mdcharm-splash.png")); + splash->show(); + app.processEvents(); + startTime = QTime::currentTime(); + } + + BreakpadQt::GlobalHandler *handler = BreakpadQt::GlobalHandler::instance(); + handler->setDumpPath(QDir::tempPath()); +#ifdef Q_OS_LINUX + handler->setReporter(qApp->applicationDirPath().append("/mdcharm_crashrpt")); +#else + handler->setReporter(qApp->applicationDirPath().append("/crashrpt")); +#endif + handler->appendArgument(conf->configFileDirPath().append("/sysinfo.txt")); + if(conf->isCheckSpell()) + MdCharmGlobal::getInstance(); + QStringList argsList = app.arguments(); + argsList.removeFirst(); + + initHilighter(); + + MdCharmForm mcf; + mcf.connect(&app, SIGNAL(openFiles(QStringList)), SLOT(openFilesAndShow(QStringList))); + if(isShowSplash) + { + QTime endTime = QTime::currentTime(); + + if((endTime.msec()-startTime.msec())<750) + { + delayForShowSplash(750-(endTime.msec()-startTime.msec())); + } + } + QApplication::setOverrideCursor(Qt::BusyCursor); + if(!conf->isHaveValidApplicationState()) + mcf.showMaximized(); + else{ + mcf.show(); + mcf.restoreMdCharmState(); + } + mcf.openArgFiles(argsList); + QApplication::restoreOverrideCursor(); + mcf.checkCodeSyntaxCss(); + + if(isShowSplash) + { + splash->finish(&mcf); + delete splash; + } + return app.exec();//FIXME crashrpt 2364fed2-bfb0-451b-b171-cc574f249e77 689c4c7f-7d04-49a8-9e81-3d794ecda3cc +} + +void delayForShowSplash(unsigned int msec) +{ + QTime dieTime = QTime::currentTime().addMSecs(msec); + while(QTime::currentTime() xmlMap; + xmlMap.insert("bash", ":/highlighter/highlighter/bash.xml"); + xmlMap.insert("cpp", ":/highlighter/highlighter/cpp.xml"); + xmlMap.insert("cs", ":/highlighter/highlighter/cs.xml"); + xmlMap.insert("css", ":/highlighter/highlighter/css.xml"); + xmlMap.insert("diff", ":/highlighter/highlighter/diff.xml"); + xmlMap.insert("http", ":/highlighter/highlighter/http.xml"); + xmlMap.insert("ini", ":/highlighter/highlighter/ini.xml"); + xmlMap.insert("java", ":/highlighter/highlighter/java.xml"); + xmlMap.insert("javascript", ":/highlighter/highlighter/javascript.xml"); + xmlMap.insert("json", ":/highlighter/highlighter/json.xml"); + xmlMap.insert("markdown", ":/highlighter/highlighter/markdown.xml"); + xmlMap.insert("perl", ":/highlighter/highlighter/perl.xml"); + xmlMap.insert("php", ":/highlighter/highlighter/php.xml"); + xmlMap.insert("python", ":/highlighter/highlighter/python.xml"); + xmlMap.insert("ruby", ":/highlighter/highlighter/ruby.xml"); + xmlMap.insert("sql", ":/highlighter/highlighter/sql.xml"); + xmlMap.insert("xml", ":/highlighter/highlighter/xml.xml"); + QMapIterator it(xmlMap); + while(it.hasNext()){ + it.next(); + QFile file(it.value()); + if(!file.open(QFile::ReadOnly)) + continue; + languageManager->addLanguage(it.key().toStdString(), file.readAll().data()); + file.close(); + } +} diff --git a/src/MdCharm/markdowneditareawidget.cpp b/src/MdCharm/markdowneditareawidget.cpp new file mode 100644 index 0000000..64e20db --- /dev/null +++ b/src/MdCharm/markdowneditareawidget.cpp @@ -0,0 +1,812 @@ +#include + +#ifdef QT_V5 +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "markdowneditareawidget.h" +#include "markdowntohtml.h" +#include "resource.h" +#include "utils.h" +#include "util/zip/zipwriter.h" +#include "util/odt/odtwriter.h" +#include "baseeditor/markdowneditor.h" +#include "configuration.h" +#include "util/gui/findandreplace.h" +#include "util/gui/insertlinkorpicturedialog.h" +#include "util/gui/insertcodedialog.h" +#include "util/syntax/hightlighter.h" +#include "mdcharmform.h" +#include "dock/projectdockwidget.h" +#include "basewebview/markdownwebview.h" + +//------------------MarkdownWebkitHandler--------------------------------------- + +MarkdownWebkitHandler::MarkdownWebkitHandler() +{ + m_domReady = false; +} + +void MarkdownWebkitHandler::domReady() +{ + m_domReady = true; + emit initContent(); +} + +bool MarkdownWebkitHandler::isDomReady() +{ + return m_domReady; +} + +MarkdownWebkitHandler::~MarkdownWebkitHandler() +{ +} + +//------------------MarkdownEditAreaWidget-------------------------------------- + +MarkdownEditAreaWidget::MarkdownEditAreaWidget(MdCharmForm *mainForm, const QString &filePath, const QUrl &baseUrl) : + EditAreaWidget(filePath, + AllowSaveAs|AllowSelectAll|AllowExportToHtml|AllowExportToODT + |AllowExportToPdf|AllowPrint|AllowPreview|AllowFind|AllowSplit), + mainForm(mainForm) +{ + inited = false; +// lastRevision = -2; + this->baseUrl = baseUrl; + em.setEditorType(EditorModel::MARKDOWN); + + initGui(); + initContent(filePath); + initConfiguration(); + initPreviewerMatter(); + initSignalsAndSlots(); +} + +void MarkdownEditAreaWidget::initPreviewerMatter() +{ + connect(previewer, SIGNAL(linkClicked(QUrl)), + this, SLOT(openUrl(QUrl))); + markdownWebkitHandler = new MarkdownWebkitHandler(); + addJavascriptObject();//warning:add before setHtml!!!!!!!!!!!!!!!!!!!!!!!!!! + QObject::connect(previewer->page()->mainFrame(),SIGNAL(javaScriptWindowObjectCleared()), + this, SLOT(addJavascriptObject())); + QObject::connect(previewer->page(), SIGNAL(linkHovered(QString,QString,QString)), + this, SIGNAL(showStatusMessage(QString))); + initHtmlEngine(); +} + +void MarkdownEditAreaWidget::initHtmlEngine() +{ + QFile htmlTemplate(":/markdown/markdown.html"); + if(!htmlTemplate.open(QIODevice::ReadOnly)) + { + Utils::showFileError(htmlTemplate.error(), ":/markdown/markdown.html"); + return; + } + QString htmlContent = htmlTemplate.readAll(); + htmlTemplate.close(); + + std::string textResult = convertMarkdownToHtml(); + + previewer->setHtml(htmlContent.arg(conf->getMarkdownCSS()) + .arg("") + .arg("") + .arg(QString::fromUtf8(textResult.c_str(), textResult.length())), + baseUrl); +} + +void MarkdownEditAreaWidget::initGui() +{ + splitter = new QSplitter(Qt::Horizontal, this); +#ifdef Q_OS_WIN + splitter->setStyleSheet("QSplitter::handle{background-color: gray;}"); + splitter->setHandleWidth(1); +#endif + editor = new MarkdownEditor(this); + editorScrollBar = editor->verticalScrollBar(); + splitter->addWidget(editor); + previewer = new MarkdownWebView(this); + previewer->setAcceptDrops(false); + previewer->setBaseSize(editor->baseSize().width(),previewer->baseSize().height()); + splitter->addWidget(previewer); + findAndReplaceWidget = new FindAndReplace(this); + findAndReplaceWidget->hide(); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0,0,0,0); + mainLayout->setSpacing(0); + mainLayout->addWidget(splitter); + mainLayout->addWidget(findAndReplaceWidget); + setLayout(mainLayout); +} + +void MarkdownEditAreaWidget::initConfiguration() +{ + previewer->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);//make qwebkit not load the link + + if (conf->isDisplayLineNumber()) + editor->enableDisplayLineNumber(); + else + editor->disableDisplayLineNumber(); + if (conf->isHighlightCurrentLine()) + editor->enableHighlightCurrentLine(); + else + editor->disableHighlightCurrentLine(); + editor->setWordWrapMode(conf->isEnableTextWrapping() ? QTextOption::WrapAtWordBoundaryOrAnywhere:QTextOption::NoWrap); + QFont font(conf->getFontFamily(), conf->getFontSize()); + editor->document()->setDefaultFont(font); + + editor->setTabStopWidth(conf->getTabSize()*QFontMetrics(editor->document()->defaultFont()).width(" ")); + switch(conf->getPreviewOption()){ + case MdCharmGlobal::WriteMode: + previewer->setHidden(true); + break; + case MdCharmGlobal::ReadMode: + editor->setHidden(true); + break; + default: + break; + } +} + +void MarkdownEditAreaWidget::initContent(const QString &filePath) +{ + doc = QSharedPointer(new QTextDocument(NULL)); + QAbstractTextDocumentLayout *layout = new QPlainTextDocumentLayout(doc.data()); + doc->setDocumentLayout(layout); + editor->setDocument(doc.data()); + highlighter = new MarkdownHighLighter(doc.data()); + //deal file content + if(filePath.isEmpty()) + { + fm->setEncodingFormatName(conf->getDefaultEncoding());//default encoding + setModified(false); + return; + } + QFile openFile(filePath); + if (!openFile.open(QIODevice::ReadOnly)) + { + Utils::showFileError(openFile.error(), filePath); + return; + } + QTextStream openFileStream(&openFile); + //autodetected + QString fileContent = openFileStream.readAll(); + QByteArray cn = openFileStream.codec()->name(); + fm->setEncodingFormatName(cn); + if(cn.startsWith("UTF"))//UTF8 UTF16LE UTF16BE + fm->setHasBom(true); + if(cn==QByteArray("System")) + {//check if it is utf8 without bom + openFile.seek(0); + if(Utils::isUtf8WithoutBom(openFile.readAll())) + { + openFileStream.setCodec("UTF-8"); + fm->setEncodingFormatName("UTF-8"); + fm->setHasBom(false); + openFileStream.seek(0); + fileContent = openFileStream.readAll(); + } + } + if(cn.isEmpty()) + { + fm->setEncodingFormatName(conf->getDefaultEncoding()); + fm->setHasBom(true); + } + doc->setPlainText(fileContent); + doc->setModified(false); + openFile.close(); +} + +void MarkdownEditAreaWidget::initSignalsAndSlots() +{ + if(conf->getPreviewOption()==MdCharmGlobal::WriteRead) + connect(editor, SIGNAL(textChanged()), + this, SLOT(parseMarkdown())); + if(conf->isSyncScrollbar()){ + connect(editorScrollBar, SIGNAL(valueChanged(int)), + this, SLOT(scrollPreviewTo(int))); + connect(editor, SIGNAL(textChanged()), + this, SLOT(scrollPreviewTo())); + } + connect(editor, SIGNAL(modificationChanged(bool)), + this, SIGNAL(updateActions())); + + connect(editor, SIGNAL(cursorPositionChanged()), + this, SLOT(cursorPositionChanged())); + + connect(editor, SIGNAL(copyAvailable(bool)), + this, SLOT(setCopyAvaliable(bool))); + connect(editor, SIGNAL(undoAvailable(bool)), + this, SIGNAL(updateActions())); + connect(editor, SIGNAL(redoAvailable(bool)), + this, SIGNAL(updateActions())); + connect(editor, SIGNAL(overWriteModeChanged()), + this, SLOT(overWriteModeChanged())); + connect(findAndReplaceWidget, SIGNAL(findText(QString,QTextDocument::FindFlags,bool, bool)), + editor, SLOT(findAndHighlightText(QString, QTextDocument::FindFlags,bool, bool))); + connect(findAndReplaceWidget, SIGNAL(findHide()), + editor, SLOT(findFinished())); + connect(findAndReplaceWidget, SIGNAL(findHide()), + this, SLOT(setFocusEditor())); + connect(findAndReplaceWidget, SIGNAL(findHide()), + this, SLOT(hideFind())); + connect(findAndReplaceWidget, SIGNAL(findNext(QString,QTextDocument::FindFlags,bool)), + editor, SLOT(findFirstOccurrance(QString,QTextDocument::FindFlags,bool))); + connect(findAndReplaceWidget, SIGNAL(findPrevious(QString,QTextDocument::FindFlags,bool)), + editor, SLOT(findFirstOccurrance(QString,QTextDocument::FindFlags,bool))); + connect(findAndReplaceWidget, SIGNAL(replace(QString)), + editor, SLOT(replace(QString))); + connect(findAndReplaceWidget, SIGNAL(replaceAll(QString,QString,QTextDocument::FindFlags,bool)), + editor, SLOT(replaceAll(QString,QString,QTextDocument::FindFlags,bool))); + connect(editor, SIGNAL(focusInSignal()), this, SIGNAL(focusInSignal())); +} + +void MarkdownEditAreaWidget::parseMarkdown() +{ +// if (lastRevision == editor->document()->revision()) +// return; +// lastRevision = editor->document()->revision(); + std::string textResult = convertMarkdownToHtml(); + previewer->page()->mainFrame()->findFirstElement("body").setInnerXml(QString::fromUtf8(textResult.c_str(), textResult.length())); +} + +void MarkdownEditAreaWidget::reFind() +{ + findAndReplaceWidget->resendFindTextSignal(); +} + +void MarkdownEditAreaWidget::addJavascriptObject() +{ + previewer->page()->mainFrame() + ->addToJavaScriptWindowObject("markdownWebkitHandler", markdownWebkitHandler); +} + +void MarkdownEditAreaWidget::setText(const QString &text) +{ + editor->setPlainText(text); +} + +QString MarkdownEditAreaWidget::getText() +{ + return editor->toPlainText().toUtf8(); +} + +void MarkdownEditAreaWidget::resizeEvent(QResizeEvent *event) +{ + const QSize size = event->size(); + if(!inited && size.width()>0) + { + const int halfSize = size.width()/2; + QList widgetSize; + widgetSize.append(halfSize); + widgetSize.append(halfSize); + splitter->setSizes(widgetSize); + inited = true; + } + EditAreaWidget::resizeEvent(event); +} + +void MarkdownEditAreaWidget::exportToPdf(const QString &filePath) +{ + parseMarkdown(); + QPrinter printer; + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setOutputFileName(filePath); + printer.setCreator("MdCharm(http://www.mdcharm.com/)"); + previewer->print(&printer); +} + +void MarkdownEditAreaWidget::exportToODT(const QString &filePath) +{ + std::string textResult = convertMarkdownToHtml(); + QTextDocument textDocument; + Configuration *conf = Configuration::getInstance(); + QFont font(conf->getFontFamily(), conf->getFontSize()); + textDocument.setDefaultFont(font); + textDocument.setHtml(QString::fromUtf8(textResult.c_str(), textResult.length())); +// QTextDocumentWriter writer(filePath, QByteArray("odf")); +// writer.write(&textDocument); + ODTWriter odtWriter(textDocument, filePath); + odtWriter.writeAll(); +} + +void MarkdownEditAreaWidget::exportToHtml(const QString &filePath) +{ + QFile htmlTemplate(":/markdown/markdown.html"); + if(!htmlTemplate.open(QIODevice::ReadOnly)) + { + Utils::showFileError(htmlTemplate.error(), ":/markdown/markdown.html"); + return; + } + QString htmlContent = htmlTemplate.readAll(); + htmlTemplate.close(); + + std::string textResult = convertMarkdownToHtml(); + + htmlContent = htmlContent.arg(conf->getMarkdownCSS()) + .arg("") + .arg("") + .arg(QString::fromUtf8(textResult.c_str(), textResult.length())); + Utils::saveFile(filePath, htmlContent.toUtf8()); +} + +void MarkdownEditAreaWidget::printContent() +{ + QPrinter printer(QPrinter::HighResolution); + printer.setDocName(fm->getFileName()); + + QPrintDialog dialog(&printer, this); + dialog.setWindowTitle(tr("Print Document")); + if(dialog.exec() == QDialog::Accepted) + { + editor->print(&printer); + } +} + +void MarkdownEditAreaWidget::printPreview() +{ + QPrinter printer(QPrinter::HighResolution); + printer.setDocName(fm->getFileName()); + QPrintPreviewDialog ppd(&printer, this, Qt::WindowMaximizeButtonHint); + connect(&ppd, SIGNAL(paintRequested(QPrinter*)), this, SLOT(paintRequestSlot(QPrinter*))); + ppd.exec(); +} + +bool MarkdownEditAreaWidget::saveFile() +{ + QString fileFullPath = fm->getFileFullPath(); + if (fileFullPath.isEmpty()) + { + QStringList filterList = conf->getFileOpenFilters(Configuration::MarkdownFile); + QString dir; + QVariant var = conf->getLastStateValue("MarkdownEditArea_SaveFile"); + if(var.isValid() && var.canConvert(QVariant::String)) + dir = var.toString(); + fileFullPath = Utils::getSaveFileName(QString::fromLatin1(".md"), this, QString::fromLatin1("Save"), //TODO: use recommand suffix instead of .md + dir.isEmpty() ? QDir::homePath() : dir, filterList.join(";;")); + if (fileFullPath.isEmpty()) + return false; + conf->setLastStateValue("MarkdownEditArea_SaveFile", QFileInfo(fileFullPath).absoluteDir().absolutePath()); + fm->setFileFullPath(fileFullPath); + emit addToRecentFileList(fileFullPath); + } + bool bom = Utils::calculateBom(fm->isHasBom(), fm->getEncodingFormatName(), + conf->getUtf8BOMOptions()); + bool isSaveSuccess = Utils::saveFile(fileFullPath, + Utils::encodeString(editor->toPlainText(), fm->getEncodingFormatName(), bom)); + if(isSaveSuccess) + setModified(false); + return isSaveSuccess; +} + +void MarkdownEditAreaWidget::saveFileAs() +{ + QStringList filterList = conf->getFileOpenFilters(Configuration::MarkdownFile); + QString dir; + QVariant var = conf->getLastStateValue("MarkdownEditArea_SaveFileAs"); + if(var.isValid() && var.canConvert(QVariant::String)) + dir = var.toString(); + QString fileFullPath = Utils::getSaveFileName(QString::fromLatin1(".md"), this, QString::fromLatin1("Save as"), //TODO: use recommand suffix instead of .md + dir.isEmpty() ? QDir::homePath() : dir, filterList.join(";;")); + if (fileFullPath.isEmpty()) + return; + conf->setLastStateValue("MarkdownEditArea_SaveFileAs", QFileInfo(fileFullPath).absoluteDir().absolutePath()); + bool bom = Utils::calculateBom(fm->isHasBom(), fm->getEncodingFormatName(), + conf->getUtf8BOMOptions()); + Utils::saveFile(fileFullPath, Utils::encodeString(editor->toPlainText(), fm->getEncodingFormatName(), bom)); + fm->setFileFullPath(fileFullPath); + setModified(false); +} + +void MarkdownEditAreaWidget::switchPreview(int type) +{ + disconnect(editor, SIGNAL(textChanged()), this, SLOT(parseMarkdown())); + switch(type){ + case MdCharmGlobal::WriteMode: + previewer->setVisible(false); + editor->setVisible(true); + break; + case MdCharmGlobal::WriteRead: + previewer->setVisible(true); + editor->setVisible(true); + connect(editor, SIGNAL(textChanged()), this, SLOT(parseMarkdown())); + parseMarkdown(); + break; + case MdCharmGlobal::ReadMode: + editor->setVisible(false); + previewer->setVisible(true); + parseMarkdown(); + break; + default: + break; + } +} + +void MarkdownEditAreaWidget::changeSyncScrollbarSetting(bool sync) +{ + disconnect(editorScrollBar, SIGNAL(valueChanged(int)), + this, SLOT(scrollPreviewTo(int))); + disconnect(editor, SIGNAL(cursorPositionChanged()), + this, SLOT(scrollPreviewTo())); + if(sync) + { + connect(editorScrollBar, SIGNAL(valueChanged(int)), + this, SLOT(scrollPreviewTo(int))); + connect(editor, SIGNAL(cursorPositionChanged()), + this, SLOT(scrollPreviewTo())); + } +} + +void MarkdownEditAreaWidget::reloadFile() +{ + FileModel fm = getFileModel(); + QFile openFile(fm.getFileFullPath()); + if(!openFile.open(QIODevice::ReadOnly)) + { + Utils::showFileError(openFile.error(), fm.getFileFullPath()); + return; + } + QTextStream openFileStream(&openFile); + openFileStream.setCodec(QTextCodec::codecForName(fm.getEncodingFormatName().toLatin1())); + setText(openFileStream.readAll()); + openFile.close(); + setModified(false);//after reload file, the file status is not modified +} + +int MarkdownEditAreaWidget::getCurrentMaxBlockCount() +{ + return editor->blockCount(); +} + +void MarkdownEditAreaWidget::gotoLine(int line) +{ + QTextCursor textCursor = editor->textCursor(); + int current = textCursor.blockNumber()+1; + QTextCursor::MoveOperation mo; + if(current>line) + { + mo = QTextCursor::PreviousBlock; + } else if (currentsetTextCursor(textCursor); +} + +const StateModel MarkdownEditAreaWidget::getState() +{ + StateModel sm; + sm.setFirstVisibleLine(editor->firstVisibleLineNumber()); + QTextCursor cursor = editor->textCursor(); + sm.setSelectionStart(cursor.selectionStart()); + sm.setSelectionEnd(cursor.selectionEnd()); + sm.setVerticalScrllBarCurrentValue(editorScrollBar->value()); + sm.setVerticalScrollBarMaxValue(editorScrollBar->maximum()); + sm.setFileFullPath(fm->getFileFullPath()); + return sm; +} + +void MarkdownEditAreaWidget::restoreFileState(const StateModel &sm) +{ + QTextCursor cursor = editor->textCursor(); + cursor.setPosition(sm.getSelectionStart()); + if(sm.getSelectionStart()!=sm.getSelectionEnd()){ + cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, sm.getSelectionEnd()-sm.getSelectionStart()); + } + editor->setTextCursor(cursor); + + int firstVisibleLine = sm.getFirstVisibleLine(); + if(firstVisibleLine > 1 && editorScrollBar->isVisible() && + firstVisibleLine<=editor->blockCount()){ + if((sm.getVerticalScrollBarMaxValue()==editorScrollBar->maximum() || sm.getVerticalScrollBarMaxValue()==(editorScrollBar->maximum()+1))&& + sm.getVerticalScorllBarCurrentValue()<=editorScrollBar->maximum()) + { + editorScrollBar->setValue(sm.getVerticalScorllBarCurrentValue()); + } else { + gotoLine(firstVisibleLine); + QTextBlock block = editor->textCursor().block(); + while(editor->firstVisibleLineNumber()cut(); +} + +void MarkdownEditAreaWidget::copy() +{ + editor->copy(); +} + +void MarkdownEditAreaWidget::paste() +{ + editor->paste(); +} + +void MarkdownEditAreaWidget::selectAll() +{ + editor->selectAll(); +} + +bool MarkdownEditAreaWidget::isModified() +{ + return document()->isModified(); +} + +bool MarkdownEditAreaWidget::isUndoAvailable() +{ + return document()->isUndoAvailable(); +} + +bool MarkdownEditAreaWidget::isRedoAvailable() +{ + return document()->isRedoAvailable(); +} + +void MarkdownEditAreaWidget::setModified(bool isModi) +{ + document()->setModified(isModi); +} + +void MarkdownEditAreaWidget::redo() +{ + editor->redo(); +} + +void MarkdownEditAreaWidget::undo() +{ + editor->undo(); +} + +void MarkdownEditAreaWidget::updateConfiguration() +{ + previewer->page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);//make qwebkit not load the link + + if (conf->isDisplayLineNumber()) + editor->enableDisplayLineNumber(); + else + editor->disableDisplayLineNumber(); + if (conf->isHighlightCurrentLine()) + editor->enableHighlightCurrentLine(); + else + editor->disableHighlightCurrentLine(); + if (conf->isCheckSpell()) + editor->enableSpellCheck(); + else + editor->disableSpellCheck(); + editor->setWordWrapMode(conf->isEnableTextWrapping() ? QTextOption::WrapAtWordBoundaryOrAnywhere:QTextOption::NoWrap); + QFont font(conf->getFontFamily(), conf->getFontSize()); + editor->document()->setDefaultFont(font); + + editor->setTabStopWidth(conf->getTabSize()*QFontMetrics(editor->document()->defaultFont()).width(" ")); +// switchPreview(conf->getPreviewOption()); + changeSyncScrollbarSetting(conf->isSyncScrollbar()); + initHtmlEngine(); +} + +void MarkdownEditAreaWidget::setFocusEditor() +{ + editor->setFocus(); +} + +void MarkdownEditAreaWidget::setFocusFinder() +{ + findAndReplaceWidget->setFocusTextEdit(); +} + +void MarkdownEditAreaWidget::cursorPositionChanged() +{ + QTextCursor tc = editor->textCursor(); + em.setCurrentLineNumber(tc.blockNumber()+1); + em.setCurrentColumnNumber(tc.columnNumber()+1); + emit updateStatusBar(); +} + +void MarkdownEditAreaWidget::overWriteModeChanged() +{ + em.overWriteChanged(); + editor->setOverwriteMode(em.isOverWrite()); + emit updateStatusBar(); +} + +void MarkdownEditAreaWidget::showFind() +{ + findAndReplaceWidget->show(); + em.setFindVisible(true); + disconnect(editor, SIGNAL(textChanged()), + this, SLOT(reFind())); + connect(editor, SIGNAL(textChanged()), + this, SLOT(reFind())); + QTextCursor cursor = editor->textCursor(); + if(cursor.hasSelection()){ + findAndReplaceWidget->setFindText(cursor.selectedText()); + } else { + QClipboard *clipboard = QApplication::clipboard(); + const QString clipText = clipboard->text(); + if(!clipText.trimmed().isEmpty()) + findAndReplaceWidget->setFindText(clipText); + } + emit updateActions(); +} + +void MarkdownEditAreaWidget::dealMarkdownMenuAction(int type) +{ + switch(type) + { + case MdCharmGlobal::ShortcutBold: + editor->boldText(); + break; + case MdCharmGlobal::ShortcutItalic: + editor->italicText(); + break; + case MdCharmGlobal::ShortcutQuoteText: + editor->quoteText(); + break; + case MdCharmGlobal::ShortcutStrikeThrough: + editor->strikeThroughText(); + break; + case MdCharmGlobal::ShortcutTabBlockText: + editor->tabText(); + break; + case MdCharmGlobal::ShortcutShiftTab: + editor->untabText(); + break; + case MdCharmGlobal::ShortcutInsertLink: + case MdCharmGlobal::ShortcutInsertPicture: + insertLinkOrPicture(type); + break; + case MdCharmGlobal::ShortcutInsertCode: + insertCode(); + break; + default: + break; + } +} + +void MarkdownEditAreaWidget::disableSpellCheck() +{ + editor->disableSpellCheck(); +} + +void MarkdownEditAreaWidget::enableSpellCheck() +{ + editor->enableSpellCheck(); +} + +QTextDocument* MarkdownEditAreaWidget::document() +{ + return editor->document(); +} + +void MarkdownEditAreaWidget::insertLinkOrPicture(int type) +{ + QString proDir = mainForm->getProjectDockWidget()->getProjectDir(); + if(!fm->getFileFullPath().startsWith(proDir)) + proDir = QFileInfo(fm->getFileFullPath()).absolutePath(); + InsertLinkOrPictureDialog insertPLDialog(type, proDir, this); + if(QDialog::Accepted==insertPLDialog.exec()){ + editor->insertLinkOrPicuture(type, insertPLDialog.getText(), + insertPLDialog.getUrl(), + insertPLDialog.getTitle(), + insertPLDialog.getWidth(), + insertPLDialog.getHeight()); + } +} + +void MarkdownEditAreaWidget::insertCode() +{ + if(conf->getMarkdownEngineType()==MarkdownToHtml::MultiMarkdown){ + editor->insertCode(QString()); + } else { + InsertCodeDialog insertCodeDialog(this); + if(insertCodeDialog.exec()==QDialog::Accepted && !insertCodeDialog.getCodeType().isEmpty()){ + editor->insertCode(insertCodeDialog.getCodeType()); + } + } +} + +void MarkdownEditAreaWidget::hideFind() +{ + findAndReplaceWidget->hide(); + disconnect(editor, SIGNAL(textChanged()), + this, SLOT(reFind())); + em.setFindVisible(false); + emit updateActions(); +} + +void MarkdownEditAreaWidget::findNext() +{ + findAndReplaceWidget->resendFindNextSignal(); +} + +void MarkdownEditAreaWidget::findPrevious() +{ + findAndReplaceWidget->resendFindPreviousSignal(); +} + +void MarkdownEditAreaWidget::scrollPreviewTo(int value)//Vertical scrollbar visible=true +{ + float maxEdit = editorScrollBar->maximum(); + int previewMax = previewer->page()->mainFrame()->scrollBarMaximum(Qt::Vertical); + previewer->page()->mainFrame()->setScrollBarValue(Qt::Vertical, value/maxEdit*previewMax); +} + +void MarkdownEditAreaWidget::scrollPreviewTo()//Vertical scrollbar visible=false +{ + int blockCount = editor->blockCount(); + int curBlock = editor->textCursor().blockNumber()+1;//FIXME: crashrpt 60e48fe9-6bfc-43cd-afac-002f89817ead + int previewMax = previewer->page()->mainFrame()->scrollBarMaximum(Qt::Vertical); + if(!editorScrollBar->isVisible() || blockCount==curBlock) + previewer->page()->mainFrame()->setScrollBarValue(Qt::Vertical, curBlock/blockCount*previewMax); + +} + +void MarkdownEditAreaWidget::openUrl(const QUrl &url) +{ + if(url.toString(QUrl::RemoveQuery|QUrl::RemoveFragment)==previewer->url().toString(QUrl::RemoveQuery|QUrl::RemoveFragment)){ + if(!url.fragment().isEmpty()) + previewer->page()->mainFrame()->scrollToAnchor(url.fragment()); + return; + } + QDesktopServices::openUrl(url); +} + +void MarkdownEditAreaWidget::paintRequestSlot(QPrinter *printer) +{ + editor->print(printer); +} + +MarkdownEditAreaWidget::~MarkdownEditAreaWidget() +{ + markdownWebkitHandler->deleteLater(); +} + +std::string MarkdownEditAreaWidget::convertMarkdownToHtml() +{ + QByteArray content = editor->toPlainText().toUtf8(); + std::string textResult; + MarkdownToHtml::translateMarkdownToHtml(conf->getMarkdownEngineType(), content.data(), content.length(), textResult); + return textResult; +} +//-------------------------------- Clone --------------------------------------- +MarkdownEditAreaWidget::MarkdownEditAreaWidget(MarkdownEditAreaWidget &src) : + EditAreaWidget(src) +{ + mainForm = src.mainForm; + inited = false; + baseUrl = src.baseUrl; + doc = QSharedPointer(src.doc); + em.setEditorType(EditorModel::MARKDOWN); + + initGui(); + editor->setDocument(doc.data()); + initConfiguration(); + initPreviewerMatter(); + initSignalsAndSlots(); +} + +EditAreaWidget* MarkdownEditAreaWidget::clone() +{ + Q_ASSERT(isEditActionOptionEnabled(AllowSplit)); + return new MarkdownEditAreaWidget(*(this)); +} diff --git a/src/MdCharm/markdowneditareawidget.h b/src/MdCharm/markdowneditareawidget.h new file mode 100644 index 0000000..cdf57b1 --- /dev/null +++ b/src/MdCharm/markdowneditareawidget.h @@ -0,0 +1,132 @@ +#ifndef MARKDOWNEDITAREAWIDGET_H +#define MARKDOWNEDITAREAWIDGET_H + +#include "editareawidget.h" + +#include +#include + +#ifdef QT_V5 +#include +#endif + +class QSplitter; +class QPlainTextEdit; +class MarkdownWebView; +class BaseEditor; +class MarkdownEditor; +class FindAndReplace; +class QScrollBar; +class HighLighter; +class MdCharmForm; + +//This class is useless +class MarkdownWebkitHandler : public QObject +{ + Q_OBJECT +public: + MarkdownWebkitHandler(); + ~MarkdownWebkitHandler(); + bool isDomReady(); +signals: + void updateContent(const QString&); + void initContent(); +public slots: + void domReady(); +private: + bool m_domReady; +}; + +class MarkdownEditAreaWidget : public EditAreaWidget +{ + Q_OBJECT +public: + explicit MarkdownEditAreaWidget(MdCharmForm *mainForm, const QString &filePath=QString(), + const QUrl &baseUrl=QUrl()); + ~MarkdownEditAreaWidget(); + virtual void setText(const QString &text); + virtual QString getText(); + virtual void exportToPdf(const QString &filePath); + virtual void exportToODT(const QString &filePath); + virtual void exportToHtml(const QString &filePath); + virtual void printContent(); + virtual void printPreview(); + virtual bool saveFile(); + virtual void saveFileAs(); + virtual void switchPreview(int type); + virtual void changeSyncScrollbarSetting(bool sync); + virtual void reloadFile(); + virtual int getCurrentMaxBlockCount(); + virtual void gotoLine(int line); + virtual const StateModel getState(); + virtual void restoreFileState(const StateModel &sm); + void showFind(); + void dealMarkdownMenuAction(int type); + void disableSpellCheck(); + void enableSpellCheck(); + QTextDocument* document(); +private: + explicit MarkdownEditAreaWidget(MarkdownEditAreaWidget &src); + void initPreviewerMatter(); + void initHtmlEngine(); + void initGui(); + void initConfiguration(); + void initContent(const QString &filePath); + void initSignalsAndSlots(); + void insertLinkOrPicture(int type); + void insertCode(); + std::string convertMarkdownToHtml(); +public: + virtual EditAreaWidget* clone(); +private: + MdCharmForm *mainForm; + QSplitter *splitter; + QScrollBar *editorScrollBar; + MarkdownEditor *editor; + MarkdownWebView *previewer; + MarkdownWebkitHandler *markdownWebkitHandler; + HighLighter *highlighter; + FindAndReplace *findAndReplaceWidget; + QSharedPointer doc; +// int lastRevision; + + bool inited; + QUrl baseUrl; +protected: + virtual void resizeEvent(QResizeEvent *event); + +signals: + void addToRecentFileList(const QString &path); + void focusInSignal(); + +public slots: + void parseMarkdown(); + void reFind(); + void addJavascriptObject(); + virtual void copy(); + virtual void cut(); + virtual void paste(); + virtual void redo(); + virtual void undo(); + virtual void selectAll(); + virtual bool isModified(); + virtual bool isUndoAvailable(); + virtual bool isRedoAvailable(); + virtual void setModified(bool isModi); + virtual void findNext(); + virtual void findPrevious(); + + virtual void updateConfiguration(); + void setFocusEditor(); + void setFocusFinder(); + void hideFind(); +private slots: + void cursorPositionChanged(); + void overWriteModeChanged(); + void scrollPreviewTo(int value); + void scrollPreviewTo(); + void openUrl(const QUrl &url); + void paintRequestSlot(QPrinter *printer); +}; + +#endif // MARKDOWNEDITAREAWIDGET_H diff --git a/src/MdCharm/mdcharmapplication.cpp b/src/MdCharm/mdcharmapplication.cpp new file mode 100644 index 0000000..bdca03b --- /dev/null +++ b/src/MdCharm/mdcharmapplication.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +#include "mdcharmapplication.h" + +MdCharmApplication::MdCharmApplication(int &argc, char **argv) : + QApplication(argc, argv) +{ + _isRunning = false; + QString serverName ="MdCharm-Server"; + QLocalSocket socket; + socket.connectToServer(serverName); + if(socket.waitForConnected(500)){ + QTextStream stream(&socket); + QStringList args = arguments(); + if(args.count() > 1){ + args.removeFirst();//remove app self path + stream << args.join(","); + } + else + stream << QString(); + stream.flush(); + socket.waitForBytesWritten(); + _isRunning = true; + return; + } + localServer = new QLocalServer(this); + connect(localServer, SIGNAL(newConnection()), + this, SLOT(newConnectionSlot())); + if(!localServer->listen(serverName)){ + if(localServer->serverError()==QAbstractSocket::AddressInUseError + && QFile::exists(localServer->serverName())){ + QFile::remove(localServer->serverName()); + localServer->listen(serverName); + } + } +} + +void MdCharmApplication::newConnectionSlot() +{ + qDebug("new connection"); + QLocalSocket *socket = localServer->nextPendingConnection(); + if(!socket) + return; + socket->waitForReadyRead(1000); + QTextStream stream(socket); + QString args = stream.readAll(); + delete socket; + emit openFiles(args.split(",")); +} + +bool MdCharmApplication::isRunning() +{ + return _isRunning; +} diff --git a/src/MdCharm/mdcharmapplication.h b/src/MdCharm/mdcharmapplication.h new file mode 100644 index 0000000..547075f --- /dev/null +++ b/src/MdCharm/mdcharmapplication.h @@ -0,0 +1,25 @@ +#ifndef MDCHARMAPPLICATION_H +#define MDCHARMAPPLICATION_H + +#include +#include + +class MdCharmApplication : public QApplication +{ + Q_OBJECT +public: + explicit MdCharmApplication(int& argc, char** argv); + +signals: + void openFiles(const QStringList &sl); +public: + bool isRunning(); +public slots: +private slots: + void newConnectionSlot(); +private: + QLocalServer *localServer; + bool _isRunning; +}; + +#endif // MDCHARMAPPLICATION_H diff --git a/src/MdCharm/mdcharmform.cpp b/src/MdCharm/mdcharmform.cpp new file mode 100644 index 0000000..a02d853 --- /dev/null +++ b/src/MdCharm/mdcharmform.cpp @@ -0,0 +1,1299 @@ +#include + +#include +#include +#include + +#include "mdcharmform.h" +#include "markdowneditareawidget.h" +#include "resource.h" +#include "markdowntohtml.h" +#include "utils.h" +#include "configuration.h" +#include "conf/configuredialog.h" +#include "about/aboutmdcharmdialog.h" +#include "version.h" +#include "utils.h" +#include "editareatabwidget.h" +#include "util/gui/addnewfiledialog.h" +#include "util/gui/selectencodingdialog.h" +#include "util/gui/gotodialog.h" +#include "util/gui/exportdialog.h" +#include "util/spellcheck/spellchecker.h" +#include "util/spellcheck/spellcheckselectordialog.h" +#include "util/sessionfileparser.h" +#include "util/gui/noticedialog.h" +#include "util/gui/markdowncheatsheetdialog.h" +#include "util/gui/statusbarlabel.h" +#include "util/rotationtoolbutton.h" +#include "util/gui/newversioninfodialog.h" +#include "util/gui/exportdirectorydialog.h" +#include "network/checkupdates.h" +#include "dock/projectdockwidget.h" +#include "editareatabwidgetmanager.h" + +MdCharmForm::MdCharmForm(QWidget *parent) : + QMainWindow(parent) +{ + showCongratulation = false; + clipboard = QApplication::clipboard(); + conf = Configuration::getInstance(); + mdcharmGlobalInstance = MdCharmGlobal::getInstance(); + cu = new CheckUpdates(); + mcsd = NULL; + appTitle = QString::fromLatin1("MdCharm"); + initGui(); + initMenuContent(); + initToolBarContent(); + initStatusBarContent(); + initDockWidgets(); + initSignalsAndSlots(); + initShortcutMatters(); + + updatePasteAction(); + + connect(cu, SIGNAL(finished(QString)), + this, SLOT(checkUpdatesResult(QString))); + checkUpdatesTimer.setSingleShot(true); + connect(&checkUpdatesTimer, SIGNAL(timeout()), + cu, SLOT(check())); + if(conf->isCheckForUpdates()) + checkUpdatesTimer.start(3000);//3s +} + +MdCharmForm::~MdCharmForm() +{ + cu->deleteLater(); +} + +void MdCharmForm::initGui() +{ + setWindowIcon(QIcon(":/mdcharm.png")); + setAcceptDrops(true); + editAreaTabWidgetManager = new EditAreaTabWidgetManager(this); + editAreaTabWidgetManager->setObjectName(QString::fromLatin1("editAreaTabWidgetManager")); + setCentralWidget(editAreaTabWidgetManager); + menuBar = new QMenuBar(this); + menuBar->setObjectName(QString::fromLatin1("menuBar")); + setMenuBar(menuBar); + //Status Bar + statusBar = new QStatusBar(this); + statusBar->setObjectName(QString::fromLatin1("statusBar")); + setStatusBar(statusBar); + //Tool Bar + toolBar = new QToolBar(tr("ToolBar"), this); + toolBar->setObjectName(QString::fromLatin1("toolBar")); + toolBar->setIconSize(QSize(16, 16)); + addToolBar(toolBar); + markdownToolBar = new QToolBar(tr("Markdown ToolBar"), this); + markdownToolBar->setObjectName("markdownToolBar"); + markdownToolBar->setIconSize(QSize(16, 16)); + markdownToolBar->setEnabled(false); + addToolBar(markdownToolBar); + previewOptionToolBar = new QToolBar(tr("Preview Option ToolBar"), this); + previewOptionToolBar->setObjectName("previewOptionToolBar");; + previewOptionToolBar->setIconSize(QSize(16, 16));; + previewOptionToolBar->setEnabled(true); + addToolBar(previewOptionToolBar); + dockBar = new QToolBar(tr("Dock Bar"), this); + dockBar->setObjectName("dockBar"); + dockBar->setIconSize(QSize(16, 16)); + dockBar->setAllowedAreas(Qt::LeftToolBarArea); + dockBar->setFloatable(false); + dockBar->setMovable(false); + addToolBar(Qt::LeftToolBarArea, dockBar); + resize(800, 600); +} + +void MdCharmForm::initMenuContent() +{ + //File Menu + fileMenu = new QMenu(tr("&File"), this); + menuBar->addMenu(fileMenu); + newAction = new QAction(QIcon(Resource::NewLargeIcon), tr("&New"), this); + newAction->setData(MdCharmGlobal::ShortcutNew); + newAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_New); + shortcutActions.append(newAction); + fileMenu->addAction(newAction); + openAction = new QAction(QIcon(Resource::OpenLargeIcon), tr("&Open..."), this); + openAction->setToolTip(tr("Open a file")); + openAction->setData(MdCharmGlobal::ShortcutOpen); + openAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Open); + shortcutActions.append(openAction); + fileMenu->addAction(openAction); + openDirectoryAction = new QAction(QIcon(Resource::OpenDirIcon),tr("Open Directory..."), this); + openDirectoryAction->setToolTip(tr("Open a directory")); + fileMenu->addAction(openDirectoryAction); + saveAction = new QAction(QIcon(Resource::SaveLargeIcon), tr("&Save"), this); + saveAction->setData(MdCharmGlobal::ShortcutSave); + saveAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Save); + saveAction->setEnabled(false); + shortcutActions.append(saveAction); + fileMenu->addAction(saveAction); + saveAsAction = new QAction(QIcon(Resource::SaveAsLargeIcon), tr("Save &as..."), this); + saveAsAction->setData(MdCharmGlobal::ShortcutSaveAs); + saveAsAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_SaveAs); + saveAsAction->setEnabled(false); + shortcutActions.append(saveAsAction); + fileMenu->addAction(saveAsAction); + fileMenu->addSeparator(); + + recentFileMenu = new QMenu(tr("Recent Files"), this);//recent file list menu + fileMenu->addMenu(recentFileMenu); + for(int i=0; isetVisible(false); + connect(recentFileActions[i], SIGNAL(triggered()), + this, SLOT(openRecentFile())); + recentFileMenu->addAction(recentFileActions[i]); + } + recentFileMenu->addSeparator(); + clearRecentFilesAction = new QAction(tr("Clear Recent Files List"), this); + recentFileMenu->addAction(clearRecentFilesAction); + updateRecentFileActions(); + + fileMenu->addSeparator(); + exportAction = new QAction(tr("&Export to..."), this); + exportAction->setEnabled(false); + fileMenu->addAction(exportAction); + + fileMenu->addSeparator(); + printPreviewAction = new QAction(tr("Print Pre&view..."), this); + printPreviewAction->setShortcut(Qt::CTRL+Qt::SHIFT+Qt::Key_P); + printPreviewAction->setEnabled(false); + shortcutActions.append(printPreviewAction); + fileMenu->addAction(printPreviewAction); + printAction = new QAction(QIcon(Resource::PrintLargeIcon),tr("&Print..."), this); + printAction->setData(MdCharmGlobal::ShortcutPrint); + printAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Print); + printAction->setEnabled(false); + shortcutActions.append(printAction); + fileMenu->addAction(printAction); + + fileMenu->addSeparator(); + quitAction = new QAction(tr("&Quit"), this); + quitAction->setData(MdCharmGlobal::ShortcutQuit); + quitAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Quit); + shortcutActions.append(quitAction); + fileMenu->addAction(quitAction); + + //Edit Menu + editMenu = new QMenu(tr("&Edit"), this); + menuBar->addMenu(editMenu); + + undoAction = new QAction(QIcon(Resource::UndoIcon), tr("&Undo"), this); + undoAction->setData(MdCharmGlobal::ShortcutUndo); + undoAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Undo); + undoAction->setEnabled(false); + shortcutActions.append(undoAction); + editMenu->addAction(undoAction); + redoAction = new QAction(QIcon(Resource::RedoIcon), tr("&Redo"), this); + redoAction->setData(MdCharmGlobal::ShortcutRedo); + redoAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Redo); + redoAction->setEnabled(false); + shortcutActions.append(redoAction); + editMenu->addAction(redoAction); + + editMenu->addSeparator(); + + cutAction = new QAction(QIcon(Resource::CutIcon), tr("Cu&t"), this); + cutAction->setData(MdCharmGlobal::ShortcutCut); + cutAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Cut); + cutAction->setEnabled(false); + shortcutActions.append(cutAction); + editMenu->addAction(cutAction); + copyAction = new QAction(QIcon(Resource::CopyIcon), tr("&Copy"), this); + copyAction->setData(MdCharmGlobal::ShortcutCopy); + copyAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Copy); + copyAction->setEnabled(false); + shortcutActions.append(copyAction); + editMenu->addAction(copyAction); + pasteAction = new QAction(QIcon(Resource::PasteIcon), tr("&Paste"), this); + pasteAction->setData(MdCharmGlobal::ShortcutPaste); + pasteAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Paste); + pasteAction->setEnabled(false); + shortcutActions.append(pasteAction); + editMenu->addAction(pasteAction); + + editMenu->addSeparator(); + + selectAllAction = new QAction(tr("&Select All"), this); + editMenu->addAction(selectAllAction); + selectAllAction->setData(MdCharmGlobal::ShortcutSelectAll); + selectAllAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Select_All); + selectAllAction->setEnabled(false); + shortcutActions.append(selectAllAction); + findMenu = new QMenu(tr("&Find"), this); + editMenu->addMenu(findMenu); + findAction = new QAction(QIcon(Resource::FindIcon), tr("Find..."), this); + findAction->setData(MdCharmGlobal::ShortcutFind); + findAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Find); + shortcutActions.append(findAction); + findMenu->addAction(findAction); + findPreviousAction = new QAction(tr("Find Previous"), this); + findPreviousAction->setData(MdCharmGlobal::ShortcutFindPrevious); + findPreviousAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Find_Previous); + shortcutActions.append(findPreviousAction); + findMenu->addAction(findPreviousAction); + findNextAction = new QAction(tr("Find Next"), this); + findNextAction->setData(MdCharmGlobal::ShortcutFindNext); + findNextAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Find_Next); + shortcutActions.append(findNextAction); + findMenu->addAction(findNextAction); + //View Menu + viewMenu = new QMenu(tr("&View"), this); + menuBar->addMenu(viewMenu); + //Tools Menu + toolsMenu = new QMenu(tr("&Tools"), this); +// menuBar->addMenu(toolsMenu); //TODO: + exportDirAction = new QAction(tr("&Export Directory"), this); +// toolsMenu->addAction(exportDirAction); //TODO: + //Settings Menu + settingsMenu = new QMenu(tr("&Settings"), this); + menuBar->addMenu(settingsMenu); + preferencesAction = new QAction(QIcon(Resource::ConfigIcon), tr("&Preferences..."), this); + settingsMenu->addAction(preferencesAction); + + settingsMenu->addSeparator(); + + writeModeAction = new QAction(QIcon(Resource::WriteModeIcon), tr("Show &Editor Only"), this); + writeModeAction->setCheckable(true); + writeModeAction->setData(MdCharmGlobal::WriteMode); + writeModeAction->setIconVisibleInMenu(false); + connect(writeModeAction, SIGNAL(triggered()), this, SLOT(previewOptionToolBarSlot())); + settingsMenu->addAction(writeModeAction); + writeReadAction = new QAction(QIcon(Resource::WriteReadIcon), tr("&Live Preview"), this); + writeReadAction->setCheckable(true); + writeReadAction->setData(MdCharmGlobal::WriteRead); + writeReadAction->setIconVisibleInMenu(false); + connect(writeReadAction, SIGNAL(triggered()), this, SLOT(previewOptionToolBarSlot())); + settingsMenu->addAction(writeReadAction); + readModeAction = new QAction(QIcon(Resource::ReadModeIcon), tr("Show Preview Panel &Only"), this); + readModeAction->setCheckable(true); + readModeAction->setData(MdCharmGlobal::ReadMode); + readModeAction->setIconVisibleInMenu(false); + connect(readModeAction, SIGNAL(triggered()), this, SLOT(previewOptionToolBarSlot())); + settingsMenu->addAction(readModeAction); + previewOptionActionGroup = new QActionGroup(this); + previewOptionActionGroup->addAction(writeModeAction); + previewOptionActionGroup->addAction(writeReadAction); + previewOptionActionGroup->addAction(readModeAction); + updatePreviewOptionActions(conf->getPreviewOption()); + + settingsMenu->addSeparator(); + + syncScrollbarAction = new QAction(tr("&Sync Scrollbar"), this); + syncScrollbarAction->setCheckable(true); + syncScrollbarAction->setChecked(conf->isSyncScrollbar()); + settingsMenu->addAction(syncScrollbarAction); + //Help Menu + helpMenu = new QMenu(tr("&Help"), this); + menuBar->addMenu(helpMenu); +// helpAction = new QAction(QIcon(Resource::HelpIcon), tr("Documentation"), this); +// helpAction->setShortcut(QKeySequence::HelpContents); +// helpMenu->addAction(helpAction); +// helpMenu->addSeparator(); + markdownCheatSheetAction = new QAction(tr("Markdown Cheat Sheet"), this); + markdownCheatSheetAction->setData(MdCharmGlobal::ShortcutCheatSheet); + markdownCheatSheetAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Cheat_Sheet); + shortcutActions.append(markdownCheatSheetAction); + helpMenu->addAction(markdownCheatSheetAction); + helpMenu->addSeparator(); + checkUpdatesAction = new QAction(tr("Check for Updates"), this); + helpMenu->addAction(checkUpdatesAction); + aboutAction = new QAction(tr("&About"), this); + helpMenu->addAction(aboutAction); + + //Spell Check Action + spellCheckAction = new QAction(QIcon(Resource::SpellCheckIcon), tr("Spell Check"), this); + spellCheckAction->setCheckable(true); + spellCheckAction->setChecked(conf->isCheckSpell()); +} + +void MdCharmForm::initStatusBarContent() +{ + lineAndColumnInfoLabel = new StatusBarLabel(statusBar); + lineAndColumnInfoLabel->setToolTip(tr("Current line number and column number")); + lineAndColumnInfoLabel->setFixedWidth(lineAndColumnInfoLabel->fontMetrics().width("-Line: XXXX Column: XXXX-")); + connect(lineAndColumnInfoLabel, SIGNAL(labelClicked()), + this, SLOT(showGotoDialog())); + statusBar->addPermanentWidget(lineAndColumnInfoLabel); + overWriteInfoLabel = new QLabel(statusBar); + overWriteInfoLabel->setFixedWidth(overWriteInfoLabel->fontMetrics().width("-XXXXXX-"));//insert or over + overWriteInfoLabel->setToolTip(tr("Editor insert mode")); + statusBar->addPermanentWidget(overWriteInfoLabel); + encodingInfoLabel = new StatusBarLabel(statusBar); + encodingInfoLabel->setFixedWidth(encodingInfoLabel->fontMetrics().width("-XXXXXXX-XXXXXXX-"));//windows 1520 + encodingInfoLabel->setToolTip(tr("Current file encoding")); + connect(encodingInfoLabel, SIGNAL(labelClicked()), + this, SLOT(showSelectEncodingDialog())); + statusBar->addPermanentWidget(encodingInfoLabel); +} + +void MdCharmForm::initToolBarContent() +{ + viewMenu->addAction(toolBar->toggleViewAction()); + toolBar->addAction(newAction); + toolBar->addAction(openAction); + toolBar->addAction(saveAction); + toolBar->addAction(saveAsAction); + + toolBar->addSeparator(); + + toolBar->addAction(cutAction); + toolBar->addAction(copyAction); + toolBar->addAction(pasteAction); + + toolBar->addSeparator(); + + toolBar->addAction(undoAction); + toolBar->addAction(redoAction); + + toolBar->addSeparator(); + + toolBar->addAction(spellCheckAction); + //Markdown toolbar + viewMenu->addAction(markdownToolBar->toggleViewAction()); + QAction *action; + action = markdownToolBar->addAction(QIcon(Resource::ItalicIcon), + tr("Italic(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Italic.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutItalic); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Italic); + shortcutActions.append(action); + + action = markdownToolBar->addAction(QIcon(Resource::BoldIcon), + tr("Bold(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Bold.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutBold); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Bold); + shortcutActions.append(action); + + action = markdownToolBar->addAction(QIcon(Resource::QuoteIcon), + tr("Quote(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Quote_Text.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutQuoteText); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Quote_Text); + shortcutActions.append(action); + + strikeThoughAction = markdownToolBar->addAction(QIcon(Resource::StrikeThroughIcon), + tr("Strike through(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Strike_Through.toString()), + this, SLOT(markdownToolBarSlot())); + strikeThoughAction->setData(MdCharmGlobal::ShortcutStrikeThrough); + strikeThoughAction->setEnabled(conf->getMarkdownEngineType()!=MarkdownToHtml::MultiMarkdown); + strikeThoughAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Strike_Through); + shortcutActions.append(action); + + action = markdownToolBar->addAction(QIcon(Resource::TabIcon), tr("Tab(Tab)"), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutTabBlockText); + + action = markdownToolBar->addAction(QIcon(Resource::UntabIcon), + tr("Untab(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Shift_Tab.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutShiftTab); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Shift_Tab); + shortcutActions.append(action); + + action = markdownToolBar->addAction(QIcon(Resource::LinkIcon), + tr("Insert Link(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Insert_Link.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutInsertLink); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Insert_Link); + shortcutActions.append(action); + + action = markdownToolBar->addAction(QIcon(Resource::PictureIcon), + tr("Insert Picture(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Insert_Picture.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutInsertPicture); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Insert_Picture); + shortcutActions.append(action); + + action = markdownToolBar->addAction(QIcon(Resource::CodeIcon), + tr("Insert Code(%1)").arg(mdcharmGlobalInstance->MdCharm_ShortCut_Insert_Code.toString()), + this, SLOT(markdownToolBarSlot())); + action->setData(MdCharmGlobal::ShortcutInsertCode); + action->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Insert_Code); + shortcutActions.append(action); + + //Preview Option ToolBar + viewMenu->addAction(previewOptionToolBar->toggleViewAction()); + previewOptionToolBar->addAction(writeModeAction); + previewOptionToolBar->addAction(writeReadAction); + previewOptionToolBar->addAction(readModeAction); + + viewMenu->addAction(dockBar->toggleViewAction()); +} + +void MdCharmForm::initDockWidgets() +{ + projectDockWidget = new ProjectDockWidget(this); + addDockWidget(Qt::LeftDockWidgetArea, projectDockWidget); + QAction *projectDockAction = projectDockWidget->toggleViewAction(); + projectDockAction->setShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Hide_Project_DockBar); + projectDockAction->setData(MdCharmGlobal::ShortcutHideProjectDockBar); + shortcutActions.append(projectDockAction); + viewMenu->addSeparator(); + viewMenu->addAction(projectDockAction); + if(!conf->isProjectDockWidgetVisible()) + projectDockWidget->setVisible(false); + RotationToolButton *btn = new RotationToolButton(dockBar); + btn->setDefaultAction(projectDockAction); + btn->setRotation(RotationToolButton::CounterClockwise); + btn->setAutoRaise(true); + dockBar->addWidget(btn); +} + +void MdCharmForm::initSignalsAndSlots() +{ + connect(newAction, SIGNAL(triggered()), this, SLOT(addNewTabWidget())); + connect(openAction, SIGNAL(triggered()), this, SLOT(openFiles())); + connect(openDirectoryAction, SIGNAL(triggered()), this, SLOT(openDirectory())); + connect(exportAction, SIGNAL(triggered()), this, SLOT(exportsActionSlot())); + connect(saveAction, SIGNAL(triggered()), editAreaTabWidgetManager, SLOT(saveFile())); + connect(saveAsAction, SIGNAL(triggered()), editAreaTabWidgetManager, SLOT(saveFileAs())); + connect(this, SIGNAL(updateRecentFileList()), this, SLOT(updateRecentFileActions())); + connect(printAction, SIGNAL(triggered()), this, SLOT(printFile())); + connect(printPreviewAction, SIGNAL(triggered()), this, SLOT(printPreviewSlot())); + connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); + connect(checkUpdatesAction, SIGNAL(triggered()), cu, SLOT(check())); + connect(checkUpdatesAction, SIGNAL(triggered()), this, SLOT(enableShowCongratulation())); + connect(aboutAction, SIGNAL(triggered()), this, SLOT(showAboutMdCharmDialog())); + connect(editAreaTabWidgetManager, SIGNAL(currentChanged()), this, SLOT(tabChange())); + connect(this, SIGNAL(mainWindowActived()), editAreaTabWidgetManager, SLOT(checkFileStatusWhenMainWindowActived())); + connect(preferencesAction, SIGNAL(triggered()), this, SLOT(showPreferencesDialog())); + if (clipboard) + connect(clipboard, SIGNAL(dataChanged()), this, SLOT(updatePasteAction())); + connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste())); + connect(cutAction, SIGNAL(triggered()), this, SLOT(cut())); + connect(copyAction, SIGNAL(triggered()), this, SLOT(copy())); + connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll())); + connect(findAction, SIGNAL(triggered()), editAreaTabWidgetManager, SLOT(showFind())); + connect(undoAction, SIGNAL(triggered()), this, SLOT(undo())); + connect(redoAction, SIGNAL(triggered()), this, SLOT(redo())); + connect(syncScrollbarAction, SIGNAL(toggled(bool)), + editAreaTabWidgetManager, SLOT(changeSyncScrollBarSetting(bool))); + connect(clearRecentFilesAction, SIGNAL(triggered()), + this, SLOT(clearRecentFilesList())); + connect(projectDockWidget, SIGNAL(openTheFile(QString)), this, SLOT(openTheFile(QString))); + connect(projectDockWidget, SIGNAL(createNewFile(QString)), this, SLOT(crateNewFile(QString))); + connect(projectDockWidget, SIGNAL(deleteFileSignal(QString)), + editAreaTabWidgetManager, SLOT(deleteFileAndTab(QString))); + connect(projectDockWidget, SIGNAL(renameFileSignal(QString,QString)), + editAreaTabWidgetManager, SLOT(renameFileAndTab(QString,QString))); + connect(findNextAction, SIGNAL(triggered()), this, SLOT(findNextActionSlot())); + connect(findPreviousAction, SIGNAL(triggered()), this, SLOT(findPreviousActionSlot())); + connect(spellCheckAction, SIGNAL(triggered(bool)), this, SLOT(spellCheckActionSlot(bool))); + connect(markdownCheatSheetAction, SIGNAL(triggered()), this, SLOT(showMarkdownCheatSheet())); + connect(editAreaTabWidgetManager, SIGNAL(addToRecentFileList(QString)), this, SLOT(addToRecentFileList(QString))); + connect(editAreaTabWidgetManager, SIGNAL(updateActions()), this, SLOT(updateActions())); + connect(editAreaTabWidgetManager, SIGNAL(showStatusMessage(QString)), statusBar, SLOT(showMessage(QString))); + connect(exportDirAction, SIGNAL(triggered()), this, SLOT(exportDirSlot())); +} + +void MdCharmForm::initShortcutMatters() +{ + //FIXME: when use QKeySequence::Close, only Ctrl+F4 but not Ctrl+W works on + //windows with Qt 4.8.1, so we use Ctrl+W instead of QKeySequence::Close. + //When we developing for Mac platform, fix this + closeTabShortcut = new QShortcut(mdcharmGlobalInstance->MdCharm_ShortCut_Close_Tab, this); + closeTabShortcut->setContext(Qt::ApplicationShortcut); + connect(closeTabShortcut, SIGNAL(activated()), + editAreaTabWidgetManager, SLOT(closeCurrentTab())); +} + +void MdCharmForm::setStatusMessage(QString &msg) +{ + statusBar->showMessage(msg); +} + +EditAreaWidget* MdCharmForm::addNewTabWidget(const QString &filePath) +{ + return editAreaTabWidgetManager->addNewTabWidget(filePath); +} + +void MdCharmForm::openFiles() +{ + QString selectedFilter; + QStringList filterList = conf->getFileOpenFilters(); + if(conf->getLastFilterType()getLastFilterType()); + QStringList filePaths = QFileDialog::getOpenFileNames(this, + tr("Select a file to open"), + conf->getLastOpenDir(), + filterList.join(";;"), + &selectedFilter); + int currentIndex = filterList.indexOf(selectedFilter); + if(currentIndex!=-1) + conf->setLastFilterType(currentIndex); + if (filePaths.isEmpty()) + return; + QString path = filePaths.first(); + QFileInfo fileInfo(path); + conf->setLastOpenDir(fileInfo.absolutePath()); + foreach (QString filePath, filePaths) {//open all + addToRecentFileList(filePath); + addNewTabWidget(filePath); + } +} + +void MdCharmForm::openDirectory() +{ + QString dir; + QVariant var = conf->getLastStateValue("MdCharmForm_OpenDirectory"); + if(var.isValid() && var.canConvert(QVariant::String)){ + dir = var.toString(); + } + QString dirPath = Utils::getExistingDirectory(this, tr("Select a directory to open"), dir); + if(dirPath.isEmpty()) + return; + conf->setLastStateValue("MdCharmForm_OpenDirectory", dirPath); + projectDockWidget->setVisible(true); + projectDockWidget->setProjectDir(dirPath); +} + +void MdCharmForm::exportToHtml(const QString &filePath) +{ + if(filePath.isEmpty()){ + emit exportToOneFileFinished(false); + return; + } + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + assert(editArea); + if(!editArea){ + emit exportToOneFileFinished(false); + return; + } + editArea->exportToHtml(filePath); + emit exportToOneFileFinished(true); +} + +void MdCharmForm::exportToPdf(const QString &filePath) +{ + if (filePath.isEmpty()){ + emit exportToOneFileFinished(false); + return; + } + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + assert(editArea); + if(!editArea){ + emit exportToOneFileFinished(false); + return; + } + editArea->exportToPdf(filePath); + emit exportToOneFileFinished(true); +} + +void MdCharmForm::exportToODT(const QString &filePath) +{ + if (filePath.isEmpty()){ + emit exportToOneFileFinished(false); + return; + } + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + assert(editArea); + if(!editArea){//null + emit exportToOneFileFinished(false); + return; + } + assert(editArea); + editArea->exportToODT(filePath); + emit exportToOneFileFinished(true); +} + +void MdCharmForm::printFile() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + assert(editArea); + if(!editArea) + { + return; + } + editArea->printContent(); +} + +void MdCharmForm::printPreviewSlot() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + assert(editArea); + if(!editArea) + return; + editArea->printPreview(); +} + +// Settings Menu + +void MdCharmForm::showPreferencesDialog() +{ + ConfigureDialog confDialog(this); + QObject::connect(&confDialog, SIGNAL(updateConfiguration()), + this, SLOT(updateConfiguration())); + QObject::connect(&confDialog, SIGNAL(updateShortcut(int,QString)), + this, SLOT(updateShortcut(int,QString))); + if(confDialog.exec()==QDialog::Accepted) + updateConfiguration(); +} +// About Menu +void MdCharmForm::showAboutMdCharmDialog() +{ + AboutMdCharmDialog dialog(this); + dialog.exec(); +} + +void MdCharmForm::updateActions() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) //null + { + cutAction->setEnabled(false); + copyAction->setEnabled(false); + pasteAction->setEnabled(false); + undoAction->setEnabled(false); + redoAction->setEnabled(false); + selectAllAction->setEnabled(false); + findAction->setEnabled(false); + saveAction->setEnabled(false); + saveAsAction->setEnabled(false); + markdownToolBar->setEnabled(false); + exportAction->setEnabled(false); + printAction->setEnabled(false); + printPreviewAction->setEnabled(false); + setWindowTitle(appTitle); + return; + } + assert(editArea); + FileModel fm = editArea->getFileModel(); + EditorModel em = editArea->getEditorModel(); + if( em.getEditorType()==EditorModel::BROWER) + updateWindowTitle(editAreaTabWidgetManager->getCurrentTabTabText(), false); + else + updateWindowTitle(fm.getFileFullPath(), editArea->isModified()); + if(em.getEditorType()==EditorModel::MARKDOWN) + markdownToolBar->setEnabled(true); + else + markdownToolBar->setEnabled(false); + //update actions + updatePasteAction(); + updatePreviewOptionActions(conf->getPreviewOption()); + cutAction->setEnabled(em.isCopyAvailable()); + copyAction->setEnabled(em.isCopyAvailable()); + selectAllAction->setEnabled(editArea->isEditActionOptionEnabled(EditAreaWidget::AllowSelectAll)); + undoAction->setEnabled(editArea->isUndoAvailable()); + redoAction->setEnabled(editArea->isRedoAvailable()); + saveAsAction->setEnabled(editArea->isEditActionOptionEnabled(EditAreaWidget::AllowSaveAs)); + saveAction->setEnabled(editArea->isModified()); + exportAction->setEnabled( editArea->isEditActionOptionEnabled(EditAreaWidget::AllowExportToHtml) || + editArea->isEditActionOptionEnabled(EditAreaWidget::AllowExportToODT) || + editArea->isEditActionOptionEnabled(EditAreaWidget::AllowExportToPdf)); + printAction->setEnabled(editArea->isEditActionOptionEnabled(EditAreaWidget::AllowPrint)); + printPreviewAction->setEnabled(editArea->isEditActionOptionEnabled(EditAreaWidget::AllowPreview)); + if (syncScrollbarAction->isChecked() != conf->isSyncScrollbar()) + syncScrollbarAction->setChecked(conf->isSyncScrollbar()); + findAction->setEnabled(editArea->isEditActionOptionEnabled(EditAreaWidget::AllowFind)); + findNextAction->setEnabled(em.isFindVisible()); + findPreviousAction->setEnabled(em.isFindVisible()); +} + +void MdCharmForm::updatePreviewOptionActions(int type) +{ + switch(type) + { + case MdCharmGlobal::WriteMode: + if(!writeModeAction->isChecked()) + writeModeAction->setChecked(true); + break; + case MdCharmGlobal::WriteRead: + if(!writeReadAction->isChecked()) + writeReadAction->setChecked(true); + break; + case MdCharmGlobal::ReadMode: + if(!readModeAction->isChecked()) + readModeAction->setChecked(true); + break; + default: + break; + } +} + +void MdCharmForm::updateStatusBar() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea){ + lineAndColumnInfoLabel->clear(); + overWriteInfoLabel->clear(); + encodingInfoLabel->clear(); + return; + } + FileModel fm = editArea->getFileModel(); + EditorModel em = editArea->getEditorModel(); + if(!em.isEditable())// not editable + { + lineAndColumnInfoLabel->clear(); + overWriteInfoLabel->clear(); + encodingInfoLabel->clear(); + return; + } + lineAndColumnInfoLabel->setText(tr("Line: %1 Column: %2") + .arg(em.getCurrentLineNumber()) + .arg(em.getCurrentColumnNumber())); + overWriteInfoLabel->setText(em.isOverWrite()? tr("OVER") : tr("INSERT")); + encodingInfoLabel->setText(fm.getEncodingFormatName() + + (fm.getEncodingFormatName().startsWith("UTF")? (fm.isHasBom() ? "" : " (No Bom)"):"")); + +} + +void MdCharmForm::updateWindowTitle(const QString &title, bool isModified) +{ + QString formatTitle = title; +#ifdef Q_OS_WIN + formatTitle.replace("/", "\\"); +#endif + setWindowModified(isModified); + setWindowTitle(QString("%1[*] - %2") + .arg(title.isEmpty() ? editAreaTabWidgetManager->getCurrentTabTabText() : formatTitle) + .arg(appTitle)); +} + +void MdCharmForm::addToRecentFileList(const QString &fileFullPath) +{ + QString path = QFileInfo(fileFullPath).absoluteFilePath(); + QStringList rf = conf->getRecentFileList(); + int index = rf.indexOf(path); + if(index!=-1) // not find + { + rf.removeAt(index); + } + rf.prepend(path); + if(rf.length()>MaxRecentFiles) + { + rf.removeLast(); + } + conf->setRecentFileList(rf); + emit updateRecentFileList(); +} + +void MdCharmForm::updatePasteAction() +{ + if(clipboard) + { + QString text = clipboard->text(); + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + EditorModel em = editArea->getEditorModel(); + pasteAction->setEnabled(!text.isEmpty() && em.getEditorType()==EditorModel::MARKDOWN); + } +} + +void MdCharmForm::updateConfiguration() +{ + if(spellCheckAction->isChecked()!=conf->isCheckSpell()) + spellCheckAction->setChecked(conf->isCheckSpell()); + editAreaTabWidgetManager->updateConfiguration(); + if(conf->getMarkdownEngineType()==MarkdownToHtml::MultiMarkdown) + strikeThoughAction->setEnabled(false); + else + strikeThoughAction->setEnabled(true); +} + +void MdCharmForm::updateShortcut(int s, const QString &newShortcut) +{ + foreach (QAction *action, shortcutActions) { + if(action->data().toInt()==s){ + action->setShortcut(newShortcut); + QString actionText = action->text(); + int index = actionText.indexOf("("); + if(index>0){ + actionText = actionText.left(index); + action->setText(actionText+"("+newShortcut+")"); + } + break; + } + } +} + +void MdCharmForm::openFilesAndShow(const QStringList &sl) +{ + for(int i=0; igetCurrentWidget(); + if(!editArea) + return; + assert(editArea); + editArea->cut(); +} + +void MdCharmForm::copy() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + assert(editArea); + editArea->copy(); +} + +void MdCharmForm::paste() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + assert(editArea); + editArea->paste(); +} + +void MdCharmForm::undo() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + assert(editArea); + editArea->undo(); +} + +void MdCharmForm::redo() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + assert(editArea); + editArea->redo(); +} + +void MdCharmForm::selectAll() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + assert(editArea); + editArea->selectAll(); +} + +void MdCharmForm::tabChange() +{ + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea)//all tab closed + { + //disable copy cut paste selectAll + updateActions(); + updateStatusBar(); + return; + } + disconnect(editArea, SIGNAL(updateActions()), + this, SLOT(updateActions())); + connect(editArea, SIGNAL(updateActions()), + this, SLOT(updateActions())); + updateActions(); + disconnect(editArea, SIGNAL(updateStatusBar()), + this, SLOT(updateStatusBar())); + connect(editArea, SIGNAL(updateStatusBar()), + this, SLOT(updateStatusBar())); + updateStatusBar(); +} + +void MdCharmForm::closeEvent(QCloseEvent *event) +{ + if(!editAreaTabWidgetManager->saveAllBeforeClose()){ + event->ignore(); + return; + } + //save window state + saveMdCharmState(); + if(mcsd){ + mcsd->hide(); + mcsd->deleteLater(); + } + QMainWindow::closeEvent(event); +} + +void MdCharmForm::showSelectEncodingDialog() +{ + QString oldEncoding = encodingInfoLabel->text(); + if(oldEncoding.isEmpty()) + return; + EditAreaWidget *eaw = editAreaTabWidgetManager->getCurrentWidget(); + if(!eaw) + return; + EditorModel em = eaw->getEditorModel(); + FileModel fm = eaw->getFileModel(); + SelectEncodingDialog sed(eaw->isModified(), fm.getEncodingFormatName(), this); + if(sed.exec()==QDialog::Accepted && oldEncoding!=sed.getSelectedEncoding() + && em.getEditorType()>=EditorModel::EDITABLE) + { + eaw->setFileEncoding(sed.getSelectedEncoding()); + encodingInfoLabel->setText(sed.getSelectedEncoding()); + if(sed.isReloadFile()) + eaw->reloadFile(); + } +} + +void MdCharmForm::showGotoDialog() +{ + EditAreaWidget *eaw = editAreaTabWidgetManager->getCurrentWidget(); + if(!eaw) + return; + EditorModel em = eaw->getEditorModel(); + if(em.getEditorType()getCurrentMaxBlockCount(), 1, this); + if(gd.exec()==QDialog::Accepted) + { + eaw->gotoLine(gd.getLineNumber()); + } +} + +void MdCharmForm::checkUpdatesResult(const QString &info) +{ + QStringList versionInfoList = info.split('|'); + if(versionInfoList.length()<5) + return; + bool isNew = false; + bool isRecommand = false; + QString warning; + QString version; + QString revision; + for( int i=0; i(sender()); + if(act) + { + openTheFile(act->data().toString()); + } +} + +void MdCharmForm::openTheFile(const QString &filePath) +{ + QFileInfo fileInfo(filePath); + if(fileInfo.exists()) + addToRecentFileList(fileInfo.absoluteFilePath()); + else //file not exist + { + if(QMessageBox::warning(this, tr("File not exist"), + tr("%1 doesn't' exist, do you want to create it?").arg(fileInfo.absoluteFilePath()), + QMessageBox::Yes|QMessageBox::No) + ==QMessageBox::Yes) + { + fileInfo.absoluteDir().mkpath(fileInfo.absoluteDir().absolutePath()); + QFile tryCreate(fileInfo.absoluteFilePath()); + if(!tryCreate.open(QIODevice::WriteOnly)) + { + Utils::showFileError(tryCreate.error(), fileInfo.absoluteFilePath()); + return; + } + tryCreate.close(); + addToRecentFileList(fileInfo.absoluteFilePath()); + } else { + conf->removeFromRecentFileList(fileInfo.absoluteFilePath().replace("\\","/")); + updateRecentFileList(); + return; + } + } + addNewTabWidget(fileInfo.absoluteFilePath()); +} + +void MdCharmForm::crateNewFile(const QString &dir) +{ + AddNewFileDialog anfd(this); + anfd.setParentDir(dir); + int res = anfd.exec(); + if(res==QDialog::Accepted) + { + QString realName = Utils::checkOrAppendDefaultSuffix(MdCharmGlobal::Markdown, anfd.getFileName()); + QString realPath = dir + "/" + realName; + QFile newFile(realPath); + if(!newFile.open(QIODevice::WriteOnly)) + return Utils::showFileError(newFile.error(), realPath); + newFile.close(); + openTheFile(realPath); + } +} + +void MdCharmForm::updateRecentFileActions() +{ + QStringList rf = conf->getRecentFileList(); + if(rf.isEmpty()) + { + recentFileMenu->setEnabled(false); + return; + } else { + recentFileMenu->setEnabled(true); + } + int len = rf.length()<=MaxRecentFiles ? rf.length() : MaxRecentFiles; + for (int i=0; isetData(path); +#ifdef Q_OS_WIN + path.replace('/','\\');//keep this +#endif + act->setText(path);//replace method will change the string + act->setVisible(true); + } + for(int i=len; isetVisible(false); + } +} + +void MdCharmForm::restoreMdCharmState() +{ + restoreGeometry(conf->getGeometryState()); + restoreState(conf->getWindowState()); + SessionFileParser sfp; + if( !sfp.startParse(conf->getSessionFilePath()) ){ + return; + } + projectDockWidget->setProjectDir(sfp.getProjectDockWidgetDirPath());//must restor project dir first + editAreaTabWidgetManager->restoreTabsState(sfp.getFileStateModelList()); +} + +void MdCharmForm::saveMdCharmState() +{ + conf->setGeometryState(saveGeometry()); + conf->setWindowState(saveState()); + editAreaTabWidgetManager->saveTabsState(); +} + +void MdCharmForm::openArgFiles(QStringList &fileList) +{ + if(fileList.isEmpty()) + return; + for(int i=0; iisUseMarkdownDefaultCSS()){ + conf->setAppendCodeSyntaxCss(true); + } else if(!conf->isAppendCodeSyntaxCss()) { //user custom css + QFile codeSyntaxPatchFile(":/markdown/code_syntax_patch.css"); + if(!codeSyntaxPatchFile.open(QFile::ReadOnly)) + return; + QTextStream stream(&codeSyntaxPatchFile); + conf->setMarkdownCSS(false, conf->getMarkdownCSS()+stream.readAll()); + codeSyntaxPatchFile.close(); + conf->setAppendCodeSyntaxCss(true); + QFile codeSyntaxNoticeFile(":/markdown/code_syntax_notice.html"); + if(!codeSyntaxNoticeFile.open(QFile::ReadOnly)) + return; + QString notice = codeSyntaxNoticeFile.readAll(); + codeSyntaxNoticeFile.close(); + showNotice(notice); + } +} + +ProjectDockWidget* MdCharmForm::getProjectDockWidget() +{ + return projectDockWidget; +} + +void MdCharmForm::showNotice(const QString ¬ice) +{ + QMessageBox::information(this, tr("Notice"), notice, QMessageBox::Ok); +} + +void MdCharmForm::clearRecentFilesList() +{ + conf->setRecentFileList(QStringList()); + updateRecentFileActions(); + emit updateRecentFileList(); +} + +void MdCharmForm::findNextActionSlot() +{ + EditAreaWidget *eaw = editAreaTabWidgetManager->getCurrentWidget(); + if(!eaw) + return; + eaw->findNext(); +} + +void MdCharmForm::findPreviousActionSlot() +{ + EditAreaWidget *eaw = editAreaTabWidgetManager->getCurrentWidget(); + if(!eaw) + return; + eaw->findPrevious(); +} + +void MdCharmForm::exportsActionSlot() +{ + ExportDialog ed(this); + connect(&ed, SIGNAL(exportToHtml(QString)), + this, SLOT(exportToHtml(QString))); + connect(&ed, SIGNAL(exportToOdt(QString)), + this, SLOT(exportToODT(QString))); + connect(&ed, SIGNAL(exportToPdf(QString)), + this, SLOT(exportToPdf(QString))); + connect(this, SIGNAL(exportToOneFileFinished(bool)), + &ed, SLOT(exportOneFileFinished(bool))); + ed.exec(); +} + +void MdCharmForm::spellCheckActionSlot(bool checked) +{ + conf->setCheckSpell(checked); + if(conf->getSpellCheckLanguage().isEmpty()){ + SpellCheckSelectorDialog scsd(this); + if(scsd.exec()==QDialog::Accepted) + conf->setSpellCheckLanguage(scsd.getSpellCheckLanguageName()); + } + if(conf->getSpellCheckLanguage().isEmpty()){ + conf->setCheckSpell(false); + } + editAreaTabWidgetManager->updateSpellCheckOption(checked); + if(conf->isCheckSpell()!=spellCheckAction->isChecked()){ + disconnect(spellCheckAction, SIGNAL(triggered(bool)), this, SLOT(spellCheckActionSlot(bool))); + spellCheckAction->setChecked(conf->isCheckSpell()); + connect(spellCheckAction, SIGNAL(triggered(bool)), this, SLOT(spellCheckActionSlot(bool))); + } +} + +void MdCharmForm::showMarkdownCheatSheet() +{ + if(!mcsd) + mcsd = new MarkdownCheatSheetDialog; + mcsd->showAndPopup(); +} + +//---------------------------- Drag and Drop ----------------------------------- +void MdCharmForm::dragEnterEvent(QDragEnterEvent *e) +{ + if(e->mimeData()->hasFormat("text/plain")|| + e->mimeData()->hasFormat("text/uri-list")) + { + e->acceptProposedAction(); + } +} + +void MdCharmForm::dragLeaveEvent(QDragLeaveEvent *e) +{ + Q_UNUSED(e) +} + +void MdCharmForm::dropEvent(QDropEvent *e) +{ + const QMimeData* mimeData = e->mimeData(); + if (mimeData->hasUrls()) + { + QList urlList = mimeData->urls(); + for(int i=0; ihasText()){ + //TODO: current it only deal text as markdown + EditAreaWidget * eaw = addNewTabWidget(); + if(eaw) + eaw->setText(mimeData->text()); + } +} + +//------------------- Change Event --------------------------------------------- +void MdCharmForm::changeEvent(QEvent *e) +{ + QMainWindow::changeEvent(e); + if (e->type() == QEvent::ActivationChange) { + if (isActiveWindow()) { + emit mainWindowActived(); + } + } +} + +//------------------- Markdown ToolBar ----------------------------------------- +void MdCharmForm::markdownToolBarSlot() +{ + if(conf->getPreviewOption()==MdCharmGlobal::ReadMode)//in reader mode, ignore markdown menu actions + return; + QAction *action = qobject_cast(sender()); + if(!action) + return; + EditAreaWidget *editArea = editAreaTabWidgetManager->getCurrentWidget(); + if(!editArea) + return; + EditorModel em = editArea->getEditorModel(); + if(em.getEditorType()!=EditorModel::MARKDOWN) + return; + MarkdownEditAreaWidget *meaw = qobject_cast(editArea); + if(!meaw) + return; + meaw->dealMarkdownMenuAction(action->data().toInt()); +} + +//-----------------------Preview Option ToolBar--------------------------------- +void MdCharmForm::previewOptionToolBarSlot() +{ + QAction *action = qobject_cast(sender()); + if(!action) + return; + conf->setPreviewOption(action->data().toInt()); + editAreaTabWidgetManager->switchPreview(action->data().toInt()); +} diff --git a/src/MdCharm/mdcharmform.h b/src/MdCharm/mdcharmform.h new file mode 100644 index 0000000..1ea173c --- /dev/null +++ b/src/MdCharm/mdcharmform.h @@ -0,0 +1,208 @@ +#ifndef MDCHARMFORM_H +#define MDCHARMFORM_H + +#include +#include +#include +#include +#include + +class EditAreaWidget; +class EditAreaTabWidgetManager; +class CheckUpdates; +class ProjectDockWidget; +class StatusBarLabel; +class MarkdownCheatSheetDialog; +class Configuration; +class QLabel; +class MdCharmGlobal; + +class MdCharmForm : public QMainWindow +{ + Q_OBJECT + +public: + explicit MdCharmForm(QWidget *parent = 0); + ~MdCharmForm(); + +private: + void initGui(); + void initMenuContent(); + void initStatusBarContent(); + void initToolBarContent(); + void initDockWidgets(); + void initSignalsAndSlots(); + void initShortcutMatters(); + void setStatusMessage(QString &msg); + void updateWindowTitle(const QString &title, bool isModified); + void addStartHerePage(); + void saveMdCharmState(); + void showNotice(const QString ¬ice); +public: + void restoreMdCharmState(); + void openArgFiles(QStringList &fileList); + void checkCodeSyntaxCss(); + ProjectDockWidget* getProjectDockWidget(); +protected: + void dragEnterEvent(QDragEnterEvent *e); + void dragLeaveEvent(QDragLeaveEvent *e); + void dropEvent(QDropEvent *e); + void changeEvent(QEvent *e); + void closeEvent(QCloseEvent *event); + +private: + //Menu + QMenuBar *menuBar; + // File Menu + QMenu *fileMenu; + // File Menu Action + QAction *newAction; + QAction *openAction; + QAction *openDirectoryAction; + QAction *saveAction; + QAction *saveAsAction; + QAction *exportAction; + + QMenu *recentFileMenu; + + QAction *printAction; + QAction *printPreviewAction; + + QAction *quitAction; + // Edit Menu + QMenu *editMenu; + + QAction *undoAction; + QAction *redoAction; + + QAction *cutAction; + QAction *copyAction; + QAction *pasteAction; + + QAction *selectAllAction; + QMenu *findMenu; + QAction *findAction; + QAction *findPreviousAction; + QAction *findNextAction; + + QAction *spellCheckAction; + // View Menu + QMenu *viewMenu; + // Tools Menu + QMenu *toolsMenu; + QAction *exportDirAction; + // Setting Menu + QMenu *settingsMenu; + QAction *preferencesAction; + QActionGroup *previewOptionActionGroup; + QAction *writeModeAction; + QAction *writeReadAction; + QAction *readModeAction; + QAction *syncScrollbarAction; + // Help Menu + QMenu *helpMenu; + // Help Menu Action +// QAction *helpAction; + QAction *markdownCheatSheetAction; + QAction *checkUpdatesAction; + QAction *aboutAction; + // Tool Bar + QToolBar *toolBar; + QToolBar *markdownToolBar; + QToolBar *previewOptionToolBar; + //Dock Tool Bar + QToolBar *dockBar; + //Markdown Toolbar + QAction *strikeThoughAction; + // Main Widget + EditAreaTabWidgetManager *editAreaTabWidgetManager; + // Status Bar + StatusBarLabel *lineAndColumnInfoLabel; + QLabel *overWriteInfoLabel; + StatusBarLabel *encodingInfoLabel; + QStatusBar *statusBar; + + //Dock Widgets + ProjectDockWidget *projectDockWidget; + + //other + QClipboard *clipboard; + Configuration *conf; + QTimer checkUpdatesTimer; + CheckUpdates *cu; + + //var area + bool showCongratulation; + + //Recent File Actions + enum { MaxRecentFiles = 10 }; + QAction *recentFileActions[MaxRecentFiles];//max=10 + QAction *clearRecentFilesAction; + + //Global Shortcut + QShortcut *closeTabShortcut; + + MarkdownCheatSheetDialog *mcsd; + + //Window title + QString appTitle; + + MdCharmGlobal *mdcharmGlobalInstance; + QList shortcutActions; + +public slots: + EditAreaWidget *addNewTabWidget(const QString &filePath=QString()); + void updateActions(); + void openFiles(); + void openDirectory(); + void exportToHtml(const QString &filePath); + void exportToPdf(const QString &filePath); + void exportToODT(const QString &filePath); + + void cut(); + void copy(); + void paste(); + void undo(); + void redo(); + void selectAll(); + + void updatePasteAction(); + void updateConfiguration(); + void updateShortcut(int s, const QString &newShortcut); + + void openFilesAndShow(const QStringList &sl); + + void exportDirSlot(); +private slots: + void printFile(); + void printPreviewSlot(); + void showPreferencesDialog(); + void showAboutMdCharmDialog(); + void showSelectEncodingDialog(); + void showGotoDialog(); + void tabChange(); + void updatePreviewOptionActions(int type); + void updateStatusBar(); + void checkUpdatesResult(const QString &info); + void enableShowCongratulation(); + void openRecentFile(); + void openTheFile(const QString &filePath); + void crateNewFile(const QString &dir); + void updateRecentFileActions(); + void clearRecentFilesList(); + void findNextActionSlot(); + void findPreviousActionSlot(); + void addToRecentFileList(const QString &fileFullPath); + void exportsActionSlot(); + void spellCheckActionSlot(bool checked); + void showMarkdownCheatSheet(); + + void markdownToolBarSlot(); + void previewOptionToolBarSlot(); +signals: + void updateRecentFileList(); + void mainWindowActived(); + void exportToOneFileFinished(bool isSuccess); +}; + +#endif // MDCHARMFORM_H diff --git a/src/MdCharm/network/checkupdates.cpp b/src/MdCharm/network/checkupdates.cpp new file mode 100644 index 0000000..30db509 --- /dev/null +++ b/src/MdCharm/network/checkupdates.cpp @@ -0,0 +1,66 @@ +#include "checkupdates.h" +#include "version.h" + +CheckUpdates::CheckUpdates(QObject *parent) : + QObject(parent) +{ + hasError = false; + reply = NULL; +} + +void CheckUpdates::error(QNetworkReply::NetworkError err) +{ + if(err!=QNetworkReply::NoError) + { + hasError = true; + qDebug("network error is %d", err); + } +} + +bool CheckUpdates::isHasError() +{ + return hasError; +} + +void CheckUpdates::check() +{ + QNetworkRequest request; + QUrl url = QUrl::fromUserInput("http://www.mdcharm.com/checkupdate"); +#ifdef QT_V5 + QUrlQuery query; + query.addQueryItem(QString::fromLatin1("current_version"), QString::fromLatin1(VERSION_STR)); +#else + url.addQueryItem(QString::fromLatin1("current_version"), QString::fromAscii(VERSION_STR)); +#endif +#ifdef Q_OS_LINUX + QString type("Linux"); +#elif defined Q_OS_WIN + QString type("Windows"); +#else + QString type("Unknow"); +#endif + +#ifdef QT_V5 + query.addQueryItem(QString::fromLatin1("type"), type); + url.setQuery(query); +#else + url.addQueryItem(QString::fromLatin1("type"), type); +#endif + request.setUrl(url); + reply = manager.get(request); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(error(QNetworkReply::NetworkError))); + connect(reply, SIGNAL(finished()), + this, SLOT(httpFinished())); +} + +void CheckUpdates::httpFinished() +{ + if(isHasError()) + return; + if(reply==NULL) + return; + QString contents = reply->readAll(); + qDebug("%s",contents.toLatin1().constData()); + emit finished(contents); +} diff --git a/src/MdCharm/network/checkupdates.h b/src/MdCharm/network/checkupdates.h new file mode 100644 index 0000000..36f0353 --- /dev/null +++ b/src/MdCharm/network/checkupdates.h @@ -0,0 +1,28 @@ +#ifndef CHECKUPDATES_H +#define CHECKUPDATES_H + +#include +#include + +class CheckUpdates : public QObject +{ + Q_OBJECT +public: + explicit CheckUpdates(QObject *parent = 0); + bool isHasError(); + + +signals: + void finished(const QString &info); + +public slots: + void error(QNetworkReply::NetworkError err); + void httpFinished(); + void check(); +private: + QNetworkAccessManager manager; + QNetworkReply *reply; + bool hasError; +}; + +#endif // CHECKUPDATES_H diff --git a/src/MdCharm/resource.cpp b/src/MdCharm/resource.cpp new file mode 100644 index 0000000..01d7df6 --- /dev/null +++ b/src/MdCharm/resource.cpp @@ -0,0 +1,48 @@ +#include "resource.h" + +const QString Resource::NewLargeIcon = QString::fromLatin1(":/new.png"); +const QString Resource::OpenLargeIcon = QString::fromLatin1(":/open.png"); +const QString Resource::OpenDirIcon = QString::fromLatin1(":/open_dir.png"); +const QString Resource::PrintLargeIcon = QString::fromLatin1(":/print.png"); +const QString Resource::SaveAsLargeIcon = QString::fromLatin1(":/saveas.png"); +const QString Resource::SaveLargeIcon = QString::fromLatin1(":/save.png"); +const QString Resource::RedoIcon = QString::fromLatin1(":/redo.png"); +const QString Resource::UndoIcon = QString::fromLatin1(":/undo.png"); +const QString Resource::CutIcon = QString::fromLatin1(":/cut.png"); +const QString Resource::CopyIcon = QString::fromLatin1(":/copy.png"); +const QString Resource::PasteIcon = QString::fromLatin1(":/paste.png"); +const QString Resource::HelpIcon = QString::fromLatin1(":/help.png"); +const QString Resource::FindIcon = QString::fromLatin1(":/find.png"); +const QString Resource::ConfigIcon = QString::fromLatin1(":/config.png"); +const QString Resource::ModifiedIconStr = QString::fromLatin1(":/modified.png"); +const QString Resource::UnmodifiedIconStr = QString::fromLatin1(":/unmodified.png"); +const QString Resource::SpellCheckIcon = QString::fromLatin1(":/spell_check.png"); + +/* Markdown Editor */ +const QString Resource::BoldIcon = QString::fromLatin1(":/markdown/bold.png"); +const QString Resource::ItalicIcon = QString::fromLatin1(":/markdown/italic.png"); +const QString Resource::StrikeThroughIcon = QString::fromLatin1(":/markdown/strike-through.png"); +const QString Resource::QuoteIcon = QString::fromLatin1(":/markdown/quote.png"); +const QString Resource::TabIcon = QString::fromLatin1(":/markdown/tab.png"); +const QString Resource::UntabIcon = QString::fromLatin1(":/markdown/untab.png"); +const QString Resource::LinkIcon = QString::fromLatin1(":/markdown/link.png"); +const QString Resource::PictureIcon = QString::fromLatin1(":/markdown/picture.png"); +const QString Resource::WriteModeIcon = QString::fromLatin1(":/markdown/write_mode.png"); +const QString Resource::ReadModeIcon = QString::fromLatin1(":/markdown/read_mode.png"); +const QString Resource::WriteReadIcon = QString::fromLatin1(":/markdown/write_read.png"); +const QString Resource::CodeIcon = QString::fromLatin1(":/markdown/code.png"); + +const QString Resource::EnvPageIcon = QString::fromLatin1(":/conf/env.png"); +const QString Resource::TextEditorIcon = QString::fromLatin1(":/conf/text_editor.png"); +const QString Resource::StylesIcon = QString::fromLatin1(":/conf/styles.png"); + +const QString Resource::Utf8HtmlTemplate = QString::fromLatin1( + "" + "" + "" + "" + "" + "" + "%1" + "" + ""); diff --git a/src/MdCharm/resource.h b/src/MdCharm/resource.h new file mode 100644 index 0000000..507245a --- /dev/null +++ b/src/MdCharm/resource.h @@ -0,0 +1,50 @@ +#ifndef RESOURCE_H +#define RESOURCE_H + +#include +#include + +class Resource +{ +public: + static const QString NewLargeIcon; + static const QString OpenLargeIcon; + static const QString OpenDirIcon; + static const QString SaveLargeIcon; + static const QString SaveAsLargeIcon; + static const QString PrintLargeIcon; + static const QString Utf8HtmlTemplate; + static const QString RedoIcon; + static const QString UndoIcon; + static const QString CutIcon; + static const QString CopyIcon; + static const QString PasteIcon; + static const QString HelpIcon; + static const QString FindIcon; + static const QString ConfigIcon; + static const QString ModifiedIconStr; + static const QString UnmodifiedIconStr; + static const QString SpellCheckIcon; + //Markdown Toolbar + static const QString ItalicIcon; + static const QString BoldIcon; + static const QString QuoteIcon; + static const QString StrikeThroughIcon; + static const QString TabIcon; + static const QString UntabIcon; + static const QString LinkIcon; + static const QString PictureIcon; + static const QString WriteModeIcon; + static const QString ReadModeIcon; + static const QString WriteReadIcon; + static const QString CodeIcon; + + static const QString EnvPageIcon; + static const QString TextEditorIcon; + static const QString StylesIcon; + +// static const QIcon ModifiedIcon; +// static const QIcon UnmodifiedIcon; +}; + +#endif // RESOURCE_H diff --git a/src/MdCharm/util/filesystemmodel.cpp b/src/MdCharm/util/filesystemmodel.cpp new file mode 100644 index 0000000..7af5626 --- /dev/null +++ b/src/MdCharm/util/filesystemmodel.cpp @@ -0,0 +1,330 @@ +#include "filesystemmodel.h" + +#include + +FileNode::FileNode(FileSystemModel *model): + model(model), + parent(0), + children(0) +{ + +} + +FileNode::FileNode(FileSystemModel *model, const QString &path, FileNode *parent) : + model(model), + parent(parent), + children(0), + path(path) +{ + QFileInfo info(path); + if(parent && parent->getParent() == NULL){ + text = info.absoluteFilePath(); + } else { + text = info.fileName(); + } + if(info.isDir() && !path.isEmpty()){ + model->getFileWatcher()->addPath(path); + } +} + +FileNode::~FileNode() +{ + if(isDir() && !path.isEmpty()){ + model->getFileWatcher()->removePath(path); + } + if(children){ + qDeleteAll(children->begin(), children->end()); + delete children; + } +} + +QList* FileNode::getChildren() +{ + if(children==NULL){ + children = new QList(); + if(!path.isEmpty()){ + QFileInfo info(path); + if(info.isDir()){ + QDir dir(path); + foreach(QFileInfo childInfo, dir.entryInfoList(model->getFilter(), model->getSort())){ + children->append(new FileNode(model, childInfo.absoluteFilePath(), this)); + } + } + } + } + return children; +} + +FileNode* FileNode::getParent() +{ + return parent; +} + +FileNode* FileNode::child(int row) +{ + return children->at(row); +} + +int FileNode::row() const +{ + if(parent) + parent->getChildren()->indexOf(const_cast(this)); + return 0; +} + +QString FileNode::getPath() const +{ + return path; +} + +QString FileNode::getText() const +{ + return text; +} + +bool FileNode::isDir() const +{ + return QFileInfo(path).isDir(); +} + +bool FileNode::isFile() const +{ + return QFileInfo(path).isFile(); +} + +QFileInfo FileNode::fileInfo() const +{ + return QFileInfo(path); +} + +void FileNode::clear() +{ + if(children){ + qDeleteAll(children->begin(), children->end()); + children->clear(); + } +} + +void FileNode::reload() +{ + clear(); + if(children==NULL) + children = new QList(); + if(!path.isEmpty()){ + QFileInfo info(path); + if(info.isDir()){ + QDir dir(path); + foreach(QFileInfo childInfo, dir.entryInfoList(model->getFilter(), model->getSort())){ + children->append(new FileNode(model, childInfo.absoluteFilePath(), this)); + } + } + } +} + +FileNode *FileNode::findPath(const QString &target) +{ + if(!target.startsWith(path)) + return NULL; + if(path==target) + return this; + + QStringList nameList = target.right(target.length()-path.length()).split("/", QString::SkipEmptyParts); + FileNode *curParent = this; + bool find = false; + foreach(QString name, nameList){ + find = false; + QList* curChildren = curParent->getChildren(); + for(int i=0; ilength(); i++){ + FileNode *node = curChildren->at(i); + if(!node->isDir()) + continue; + if(node->getText()==name){ + curParent = node; + find=true; + break; + } + } + if(!find) + return NULL; + } + return curParent; +} + +FileSystemModel::FileSystemModel(QObject *parent) : + QAbstractItemModel(parent), + rootNode(new FileNode(this)), + iconProvider(new QFileIconProvider), + fileWatcher(new QFileSystemWatcher(this)) +{ + filters = QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot; + sorts = QDir::DirsFirst | QDir::Name |QDir::Type; +} + +FileSystemModel::~FileSystemModel() +{ + delete rootNode; + delete iconProvider; + delete fileWatcher; +} + +QFileSystemWatcher* FileSystemModel::getFileWatcher() const +{ + return fileWatcher; +} + +void FileSystemModel::setFilter(QDir::Filters f) +{ + filters = f; +} + +QDir::Filters FileSystemModel::getFilter() const +{ + return filters; +} + +void FileSystemModel::setSortFlags(QDir::SortFlags flags) +{ + sorts = flags; +} + +QDir::SortFlags FileSystemModel::getSort() const +{ + return sorts; +} + +void FileSystemModel::setRootPath(const QString &path) +{ + startPath = path; + beginResetModel(); + rootNode->clear(); + rootNode->getChildren()->append(new FileNode(this, path, rootNode)); + endResetModel(); +} + +QList FileSystemModel::findPaths(const QString &path) const +{ + QList list; + QString curPath = QDir::fromNativeSeparators(QDir::cleanPath(path)); + for(int i=0; iindex(i, 0)); + if(find.isValid()) + list.append(find); + } + return list; +} + +void FileSystemModel::directoryChanged(const QString &path) +{ + beginResetModel(); + QDir dir(path); + bool b = dir.exists(); + if(!b) + fileWatcher->removePath(path); + foreach(QModelIndex index, findPaths(path)){ + FileNode *node = nodeFromIndex(index); + if(b) + node->reload(); + else if(node->getParent()) + node->getParent()->reload(); + } + endResetModel(); +} + +FileNode* FileSystemModel::nodeFromIndex(const QModelIndex &index) const +{ + if(index.isValid()) + return static_cast(index.internalPointer()); + return rootNode; +} + +QFileInfo FileSystemModel::fileInfo(const QModelIndex &index) const +{ + return nodeFromIndex(index)->fileInfo(); +} + +int FileSystemModel::rowCount(const QModelIndex &parent) const +{ + FileNode *node = nodeFromIndex(parent); + return node->getChildren()->length(); +} + +int FileSystemModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +QModelIndex FileSystemModel::parent(const QModelIndex &child) const +{ + FileNode *node = nodeFromIndex(child); + FileNode *parent = node->getParent(); + if(parent == rootNode) + return QModelIndex(); + return createIndex(parent->row(), 0, parent); +} + +QModelIndex FileSystemModel::index(int row, int column, const QModelIndex &parent) const +{ + if(!hasIndex(row, column, parent)) + return QModelIndex(); + FileNode *node = nodeFromIndex(parent); + return createIndex(row, column, node->child(row)); +} + +QVariant FileSystemModel::data(const QModelIndex &index, int role) const +{ + FileNode *node = nodeFromIndex(index); + if(!node) + return QVariant(); + switch (role) { + case Qt::DisplayRole: +#ifdef Q_OS_WIN + return node->getText().replace("/", "\\"); +#else + return node->getText(); +#endif + break; + case Qt::DecorationRole: + return iconProvider->icon(node->fileInfo()); + break; + case Qt::FontRole: + { + QFont font; + if(node->getPath()==startPath) + font.setBold(true); + return font; + } + break; + default: + return QVariant(); + break; + } + return QVariant(); +} + +QModelIndex FileSystemModel::findPathHelper(const QString &path, const QModelIndex &parentIndex) const +{ + FileNode *node = nodeFromIndex(parentIndex); + if(!path.startsWith(node->getPath())) + return QModelIndex(); + if(path==node->getPath()) + return parentIndex; + QStringList nameList = path.right(path.length()-node->getPath().length()).split("/", QString::SkipEmptyParts); + QModelIndex parent = parentIndex; + bool find = false; + int count = nameList.count(); + for(int i=0; iindex(j, 0, parent); + FileNode *node = nodeFromIndex(index); + if( ( ( i == count-1) || node->isDir()) && node->getText()==nameList.at(i)){ + parent = index; + find = true; + break; + } + } + if(!find) + return QModelIndex(); + } + return parent; +} diff --git a/src/MdCharm/util/filesystemmodel.h b/src/MdCharm/util/filesystemmodel.h new file mode 100644 index 0000000..cb460cd --- /dev/null +++ b/src/MdCharm/util/filesystemmodel.h @@ -0,0 +1,75 @@ +#ifndef FILESYSTEMMODEL_H +#define FILESYSTEMMODEL_H + +#include +#include +#include + +#ifdef QT_V5 +#include +#endif + +class FileSystemModel; +class FileNode +{ +public: + FileNode(FileSystemModel *model); + FileNode(FileSystemModel *model, const QString &path, FileNode *parent); + ~FileNode(); + FileNode* getParent(); + FileNode* child(int row); + int row() const; + QList* getChildren(); + QString getPath() const; + QString getText() const; + QFileInfo fileInfo() const; + bool isDir() const; + bool isFile() const; + void clear(); + void reload(); + FileNode* findPath(const QString &path); +private: + FileSystemModel *model; + FileNode *parent; + QList *children; + QString path; + QString text; +}; + +class FileSystemModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit FileSystemModel(QObject *parent = 0); + ~FileSystemModel(); + QFileSystemWatcher* getFileWatcher() const; + void setFilter(QDir::Filters f); + QDir::Filters getFilter() const; + void setSortFlags(QDir::SortFlags flags); + QDir::SortFlags getSort() const; + void setRootPath(const QString &path); + QList findPaths(const QString &path) const; + FileNode* nodeFromIndex(const QModelIndex &index) const; + QFileInfo fileInfo(const QModelIndex &index) const; +protected: + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent=QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const; + virtual QVariant data(const QModelIndex &index, int role) const; +private: + QModelIndex findPathHelper(const QString &path, const QModelIndex &parentIndex) const; +signals: + +public slots: + void directoryChanged(const QString& path); +private: + FileNode *rootNode; + QString startPath; + QFileIconProvider *iconProvider; + QFileSystemWatcher *fileWatcher; + QDir::Filters filters; + QDir::SortFlags sorts; +}; + +#endif // FILESYSTEMMODEL_H diff --git a/src/MdCharm/util/filesystemtreeview.cpp b/src/MdCharm/util/filesystemtreeview.cpp new file mode 100644 index 0000000..694a6fc --- /dev/null +++ b/src/MdCharm/util/filesystemtreeview.cpp @@ -0,0 +1,140 @@ +#include "filesystemtreeview.h" + +#include +#include + +#ifdef QT_V5 +#include +#endif + +/***************************** FileSystemTreeViewState ******************/ +QList& FileSystemTreeViewState::getExpands() +{ + return expands; +} + +QStringList& FileSystemTreeViewState::getCurrentIndexStringList() +{ + return currentIndexStringList; +} + +void FileSystemTreeViewState::setCurrentIndexStringList(const QStringList &sl) +{ + currentIndexStringList = sl; +} + +int FileSystemTreeViewState::getVerticalBarState() +{ + return vbar; +} + +void FileSystemTreeViewState::setVertialBarState(int v) +{ + vbar = v; +} + +int FileSystemTreeViewState::getHorizontalBarState() +{ + return hbar; +} + +void FileSystemTreeViewState::setHorizontalBarState(int h) +{ + hbar = h; +} + +/***************************** Helper Function **************************/ +static QStringList stringListFromIndex(const QModelIndex &index) +{ + QStringList list; + if (!index.isValid()) + return list; + list.append(stringListFromIndex(index.parent())); + list.append(index.data().toString()); + return list; +} + +static QModelIndex indexFromStringList(QAbstractItemModel *model, QStringList &list, const QModelIndex & parent = QModelIndex()) +{ + if (list.isEmpty()) + return QModelIndex(); + QString text = list.front(); + for (int i = 0; i < model->rowCount(parent); i++) { + QModelIndex child = model->index(i,0,parent); + if (child.data().toString() == text) { + list.pop_front(); + if (list.isEmpty()) { + return child; + } else { + QModelIndex next = indexFromStringList(model,list,child); + if (next.isValid()) + return next; + else + return child; + } + } + } + return QModelIndex(); +} + +/*************** FileSystemTreeView **************************************/ +FileSystemTreeView::FileSystemTreeView(QWidget *parent) : + QTreeView(parent) +{ + connect(this, SIGNAL(expanded(QModelIndex)), + this, SLOT(expandedTree(QModelIndex))); + connect(this, SIGNAL(collapsed(QModelIndex)), + this, SLOT(collapsedTree(QModelIndex))); +} + +void FileSystemTreeView::reset() +{ + QTreeView::reset(); + expandsIndexs.clear(); +} + +void FileSystemTreeView::saveState(FileSystemTreeViewState &state) +{ + state.getExpands().clear(); + QSetIterator i(getExpandsIndexs()); + while(i.hasNext()) + state.getExpands().append(stringListFromIndex(i.next())); + state.setCurrentIndexStringList(stringListFromIndex(currentIndex())); + state.setVertialBarState(verticalScrollBar()->value()); + state.setHorizontalBarState(horizontalScrollBar()->value()); +} + +void FileSystemTreeView::loadState(FileSystemTreeViewState &state) +{ + expandToDepth(0); + foreach (QStringList sl, state.getExpands()) { + QModelIndex expandIndex = indexFromStringList(model(), sl); + if(expandIndex.isValid()) + setExpanded(expandIndex, true); + } + QModelIndex curIndex = indexFromStringList(model(), state.getCurrentIndexStringList()); + if(curIndex.isValid()) + setCurrentIndex(curIndex); + verticalScrollBar()->setValue(state.getVerticalBarState()); + horizontalScrollBar()->setValue(state.getHorizontalBarState()); +} + +const QSet& FileSystemTreeView::getExpandsIndexs() const +{ + return expandsIndexs; +} + +QSet& FileSystemTreeView::getExpandsIndexs() +{ + return expandsIndexs; +} + +void FileSystemTreeView::expandedTree(const QModelIndex &index) +{ + expandsIndexs.insert(index); +} + +void FileSystemTreeView::collapsedTree(const QModelIndex &index) +{ + expandsIndexs.remove(index); +} diff --git a/src/MdCharm/util/filesystemtreeview.h b/src/MdCharm/util/filesystemtreeview.h new file mode 100644 index 0000000..b8bb483 --- /dev/null +++ b/src/MdCharm/util/filesystemtreeview.h @@ -0,0 +1,45 @@ +#ifndef FILESYSTEMTREEVIEW_H +#define FILESYSTEMTREEVIEW_H + +#include + +class FileSystemTreeViewState +{ +public: + QList& getExpands(); + QStringList& getCurrentIndexStringList(); + void setCurrentIndexStringList(const QStringList &sl); + int getVerticalBarState(); + void setVertialBarState(int v); + int getHorizontalBarState(); + void setHorizontalBarState(int h); +private: + QList expands; + QStringList currentIndexStringList; + int vbar; + int hbar; +}; + +class FileSystemTreeView : public QTreeView +{ + Q_OBJECT +public: + explicit FileSystemTreeView(QWidget *parent = 0); + const QSet& getExpandsIndexs() const; + QSet& getExpandsIndexs(); + void saveState(FileSystemTreeViewState &state); + void loadState(FileSystemTreeViewState &state); +protected: + virtual void reset(); +signals: + +public slots: + void expandedTree(const QModelIndex &index); + void collapsedTree(const QModelIndex &index); + +private: + QSet expandsIndexs; + +}; + +#endif // FILESYSTEMTREEVIEW_H diff --git a/src/MdCharm/util/gui/addnewfiledialog.cpp b/src/MdCharm/util/gui/addnewfiledialog.cpp new file mode 100644 index 0000000..c01b8d8 --- /dev/null +++ b/src/MdCharm/util/gui/addnewfiledialog.cpp @@ -0,0 +1,37 @@ +#include "addnewfiledialog.h" +#include "ui_addnewfiledialog.h" + +AddNewFileDialog::AddNewFileDialog(QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::AddNewFileDialog) +{ + ui->setupUi(this); + parentDirLabel = ui->parentDirLabel; + fileNameLineEdit = ui->fileNameLineEdit; + buttonBox = ui->buttonBox; + + connect(buttonBox, SIGNAL(accepted()), + this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), + this, SLOT(reject())); +} + +void AddNewFileDialog::setParentDir(const QString &dirPath) +{ + QString dir = dirPath; + dir.append("/"); +#ifdef Q_OS_WIN + dir.replace("/","\\");//keep this +#endif + parentDirLabel->setText(dir); +} + +QString AddNewFileDialog::getFileName() +{ + return fileNameLineEdit->text(); +} + +AddNewFileDialog::~AddNewFileDialog() +{ + delete ui; +} diff --git a/src/MdCharm/util/gui/addnewfiledialog.h b/src/MdCharm/util/gui/addnewfiledialog.h new file mode 100644 index 0000000..49e7db7 --- /dev/null +++ b/src/MdCharm/util/gui/addnewfiledialog.h @@ -0,0 +1,31 @@ +#ifndef ADDNEWFILEDIALOG_H +#define ADDNEWFILEDIALOG_H + +#include + +class QLabel; +class QLineEdit; +class QDialogButtonBox; + +namespace Ui { +class AddNewFileDialog; +} + +class AddNewFileDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddNewFileDialog(QWidget *parent = 0); + void setParentDir(const QString &dirPath); + QString getFileName(); + ~AddNewFileDialog(); + +private: + Ui::AddNewFileDialog *ui; + QLabel *parentDirLabel; + QLineEdit *fileNameLineEdit; + QDialogButtonBox *buttonBox; +}; + +#endif // ADDNEWFILEDIALOG_H diff --git a/src/MdCharm/util/gui/addnewfiledialog.ui b/src/MdCharm/util/gui/addnewfiledialog.ui new file mode 100644 index 0000000..c3e4fb7 --- /dev/null +++ b/src/MdCharm/util/gui/addnewfiledialog.ui @@ -0,0 +1,71 @@ + + + AddNewFileDialog + + + + 0 + 0 + 400 + 91 + + + + Add new file + + + + 3 + + + 6 + + + + + Enter new markdown file name: + + + + + + + 0 + + + 0 + + + 0 + + + + + path + + + + + + + + 160 + 0 + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/MdCharm/util/gui/exportdialog.cpp b/src/MdCharm/util/gui/exportdialog.cpp new file mode 100644 index 0000000..4f57aa5 --- /dev/null +++ b/src/MdCharm/util/gui/exportdialog.cpp @@ -0,0 +1,294 @@ +#include +#include +#include + +#include "exportdialog.h" +#include "ui_exportdialog.h" +#include "utils.h" +#include "configuration.h" + +ExportDialog::ExportDialog(QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::ExportDialog) +{ + ui->setupUi(this); + + conf = Configuration::getInstance(); + restoreState(); + + connect(ui->htmlCheckBox, SIGNAL(toggled(bool)), + this, SLOT(htmlCheckBoxActionSlot(bool))); + connect(ui->htmlPushButton, SIGNAL(clicked()), + this, SLOT(htmlPushButtonSlot())); + connect(ui->pdfCheckBox, SIGNAL(toggled(bool)), + this, SLOT(pdfCheckBoxActionSlot(bool))); + connect(ui->pdfPushButton, SIGNAL(clicked()), + this, SLOT(pdfPushButtonSlot())); + connect(ui->odtCheckBox, SIGNAL(toggled(bool)), + this, SLOT(odtCheckBoxActionSlot(bool))); + connect(ui->odtPushButton, SIGNAL(clicked()), + this, SLOT(odtPushButtonSlot())); + connect(ui->selectAllCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(selectAllCheckBoxSlot(int))); + connect(ui->exportButtonBox, SIGNAL(rejected()), + this, SLOT(close())); + connect(ui->exportButtonBox, SIGNAL(accepted()), + this, SLOT(okButtonSlot())); +} + + +void ExportDialog::restoreState() +{ + //Load last operation + int checked = 0; + QVariant var = conf->getLastStateValue("ExportDialog_HtmlCheckBox"); + if(var.isValid() && var.canConvert(QVariant::Bool) && var.toBool()){ + checked++; + ui->htmlCheckBox->setChecked(true); + var = conf->getLastStateValue("ExportDialog_HtmlLineEdit"); + if(var.isValid() && var.canConvert(QVariant::String)) + ui->htmlLineEdit->setText(var.toString()); + } else { + ui->htmlLineEdit->setEnabled(false); + ui->htmlPushButton->setEnabled(false); + } + + var = conf->getLastStateValue("ExportDialog_PdfCheckBox"); + if(var.isValid() && var.canConvert(QVariant::Bool) && var.toBool()){ + checked++; + ui->pdfCheckBox->setChecked(true); + var = conf->getLastStateValue("ExportDialog_PdfLineEdit"); + if(var.isValid() && var.canConvert(QVariant::String)) + ui->pdfLineEdit->setText(var.toString()); + + } else { + ui->pdfLineEdit->setEnabled(false); + ui->pdfPushButton->setEnabled(false); + } + + var = conf->getLastStateValue("ExportDialog_OdtCheckBox"); + if(var.isValid() && var.canConvert(QVariant::Bool) && var.toBool()){ + checked++; + ui->odtCheckBox->setChecked(true); + var = conf->getLastStateValue("ExportDialog_OdtLineEdit"); + if(var.isValid() && var.canConvert(QVariant::String)) + ui->odtLineEdit->setText(var.toString()); + } else { + ui->odtLineEdit->setEnabled(false); + ui->odtPushButton->setEnabled(false); + } + + ui->selectAllCheckBox->setTristate(true); + if(checked==0) + ui->selectAllCheckBox->setCheckState(Qt::Unchecked); + else if(checked==3) + ui->selectAllCheckBox->setCheckState(Qt::Checked); + else + ui->selectAllCheckBox->setCheckState(Qt::PartiallyChecked); +} + +ExportDialog::~ExportDialog() +{ + delete ui; +} + +void ExportDialog::saveState() +{ + conf->setLastStateValue("ExportDialog_HtmlCheckBox", ui->htmlCheckBox->isChecked()); + conf->setLastStateValue("ExportDialog_HtmlLineEdit", ui->htmlLineEdit->text()); + conf->setLastStateValue("ExportDialog_PdfCheckBox", ui->pdfCheckBox->isChecked()); + conf->setLastStateValue("ExportDialog_PdfLineEdit", ui->pdfLineEdit->text()); + conf->setLastStateValue("ExportDialog_OdtCheckBox", ui->odtCheckBox->isChecked()); + conf->setLastStateValue("ExportDialog_OdtLineEdit", ui->odtLineEdit->text()); +} + +void ExportDialog::htmlCheckBoxActionSlot(bool b) +{ + ui->htmlLineEdit->setEnabled(b); + ui->htmlPushButton->setEnabled(b); + updateSelectAllCheckBoxStatus(); +} + +void ExportDialog::pdfCheckBoxActionSlot(bool b) +{ + ui->pdfLineEdit->setEnabled(b); + ui->pdfPushButton->setEnabled(b); + updateSelectAllCheckBoxStatus(); +} + +void ExportDialog::odtCheckBoxActionSlot(bool b) +{ + ui->odtLineEdit->setEnabled(b); + ui->odtPushButton->setEnabled(b); + updateSelectAllCheckBoxStatus(); +} + +void ExportDialog::selectAllCheckBoxSlot(int state) +{ + switch(state) + { + case Qt::Unchecked: + ui->htmlCheckBox->setChecked(false); + ui->pdfCheckBox->setChecked(false); + ui->odtCheckBox->setChecked(false); + break; + case Qt::Checked: + ui->htmlCheckBox->setChecked(true); + ui->pdfCheckBox->setChecked(true); + ui->odtCheckBox->setChecked(true); + break; + case Qt::PartiallyChecked: + break; + default: + break; + } +} + +void ExportDialog::exportOneFileFinished(bool isSuccess, bool addPer) +{ + if(addPer){ + ui->exportProgressBar->setValue(ui->exportProgressBar->value()+per); + if(isSuccess) + log.append("OK\n"); + else + log.append("Failed\n"); + } + if(stack.isEmpty()){ + ui->exportProgressBar->setValue(100); + QMessageBox::information(this, tr("Export Result"), log); + close(); + } else { + ExportType et = stack.pop(); + if(et==HTML){ + log.append(tr("Export to Html file: ")); + emit exportToHtml(ui->htmlLineEdit->text()); + }else if(et==PDF){ + log.append(tr("Export to Pdf file: ")); + emit exportToPdf(ui->pdfLineEdit->text()); + }else if (et==ODT){ + log.append(tr("Export to Odt file: ")); + emit exportToOdt(ui->odtLineEdit->text()); + }else{ + + } + } +} + +void ExportDialog::updateSelectAllCheckBoxStatus() +{ + if(ui->htmlCheckBox->isChecked()&&ui->pdfCheckBox->isChecked()&&ui->odtCheckBox->isChecked()){ + ui->selectAllCheckBox->setCheckState(Qt::Checked); + } else if (ui->htmlCheckBox->isChecked()==false&&ui->pdfCheckBox->isChecked()==false&&ui->odtCheckBox->isChecked()==false){ + ui->selectAllCheckBox->setCheckState(Qt::Unchecked); + } else{ + ui->selectAllCheckBox->setCheckState(Qt::PartiallyChecked); + } +} + +void ExportDialog::htmlPushButtonSlot() +{ + QString suffix = QString::fromLatin1(".html"); + QString filePath = Utils::getSaveFileName(suffix, this, tr("Save File"), + ui->htmlLineEdit->text(), tr("Html files(*.html)")); + if(!filePath.isEmpty()){ + ui->htmlLineEdit->setText(filePath); + updateOtherLineEdits(filePath); + } +} + +void ExportDialog::pdfPushButtonSlot() +{ + QString suffix = QString::fromLatin1(".pdf"); + QString filePath = Utils::getSaveFileName(suffix, this, tr("Save File"), + ui->pdfLineEdit->text(), tr("Pdf files(*.pdf)")); + if(!filePath.isEmpty()){ + ui->pdfLineEdit->setText(filePath); + updateOtherLineEdits(filePath); + } +} + +void ExportDialog::odtPushButtonSlot() +{ + QString suffix = QString::fromLatin1(".odt");//multi suffixs + QString filePath = Utils::getSaveFileName(suffix, this, tr("Save File"), + ui->odtLineEdit->text(), tr("ODT files(*.odt)")); + if(!filePath.isEmpty()){ + ui->odtLineEdit->setText(filePath); + updateOtherLineEdits(filePath); + } +} + +void ExportDialog::updateOtherLineEdits(const QString &filePath) +{ + QFileInfo fileInfo(filePath); + QString filePathWithoutSuffix = filePath.left(filePath.length()-fileInfo.suffix().length()); + if(ui->htmlLineEdit->text().isEmpty()) + ui->htmlLineEdit->setText(filePathWithoutSuffix+QString::fromLatin1("html")); + if(ui->pdfLineEdit->text().isEmpty()) + ui->pdfLineEdit->setText(filePathWithoutSuffix+QString::fromLatin1("pdf")); + if(ui->odtLineEdit->text().isEmpty()) + ui->odtLineEdit->setText(filePathWithoutSuffix+QString::fromLatin1("odt")); +} + +void ExportDialog::okButtonSlot() +{ + if(ui->selectAllCheckBox->checkState()==Qt::Unchecked) + return; + //assert line edit is not empty + if(ui->htmlCheckBox->isChecked() && + (ui->htmlLineEdit->text().isEmpty()|| + ui->htmlLineEdit->text().trimmed().isEmpty())){ + QMessageBox::warning(this, tr("Warning"), tr("Html file path can't be empty!")); + return; + } + if(ui->odtCheckBox->isChecked() && + (ui->odtLineEdit->text().isEmpty()|| + ui->odtLineEdit->text().trimmed().isEmpty())){ + QMessageBox::warning(this, tr("Warning"), tr("Odt file path can't be empty!")); + return; + } + if(ui->pdfCheckBox->isChecked() && + (ui->pdfLineEdit->text().isEmpty()|| + ui->pdfLineEdit->text().trimmed().isEmpty())){ + QMessageBox::warning(this, tr("Warning"), tr("Pdf file path can't be empty!")); + return; + } + + ui->selectAllCheckBox->setEnabled(false); + ui->htmlCheckBox->setEnabled(false); + ui->htmlLineEdit->setEnabled(false); + ui->htmlPushButton->setEnabled(false); + ui->pdfCheckBox->setEnabled(false); + ui->pdfLineEdit->setEnabled(false); + ui->pdfPushButton->setEnabled(false); + ui->odtCheckBox->setEnabled(false); + ui->odtLineEdit->setEnabled(false); + ui->odtPushButton->setEnabled(false); + ui->exportButtonBox->setEnabled(false); + int i=0; + stack.clear(); + if(ui->htmlCheckBox->isChecked()){ + i++; + stack.push(HTML); + } + if(ui->pdfCheckBox->isChecked()){ + i++; + stack.push(PDF); + } + if(ui->odtCheckBox->isChecked()){ + i++; + stack.push(ODT); + } + if(i==0){ + close(); + return; + } + per = 100 / i; + saveState(); + QTimer::singleShot(50, this, SLOT(startExport())); +} + +void ExportDialog::startExport() +{ + exportOneFileFinished(false, false); +} diff --git a/src/MdCharm/util/gui/exportdialog.h b/src/MdCharm/util/gui/exportdialog.h new file mode 100644 index 0000000..18908f9 --- /dev/null +++ b/src/MdCharm/util/gui/exportdialog.h @@ -0,0 +1,55 @@ +#ifndef EXPORTDIALOG_H +#define EXPORTDIALOG_H + +#include +#include + +namespace Ui { +class ExportDialog; +} + +class Configuration; + +class ExportDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ExportDialog(QWidget *parent = 0); + ~ExportDialog(); + void updateSelectAllCheckBoxStatus(); + void updateOtherLineEdits(const QString &filePath); + +signals: + void exportToHtml(const QString &filePath); + void exportToPdf(const QString &filePath); + void exportToOdt(const QString &filePath); +public slots: + void exportOneFileFinished(bool isSuccess, bool addPer=true);//Export to one file finished +private slots: + void htmlCheckBoxActionSlot(bool b); + void pdfCheckBoxActionSlot(bool b); + void odtCheckBoxActionSlot(bool b); + void selectAllCheckBoxSlot(int state); + void htmlPushButtonSlot(); + void pdfPushButtonSlot(); + void odtPushButtonSlot(); + void okButtonSlot(); + void startExport(); +private: + void restoreState(); + void saveState(); +private: + enum ExportType{ + PDF, ODT, HTML + }; + +private: + Ui::ExportDialog *ui; + Configuration *conf; + int per; + QStack stack; + QString log; +}; + +#endif // EXPORTDIALOG_H diff --git a/src/MdCharm/util/gui/exportdialog.ui b/src/MdCharm/util/gui/exportdialog.ui new file mode 100644 index 0000000..0fcab5b --- /dev/null +++ b/src/MdCharm/util/gui/exportdialog.ui @@ -0,0 +1,119 @@ + + + ExportDialog + + + + 0 + 0 + 400 + 182 + + + + Export... + + + + + + Select all + + + + + + + + + HTML + + + + + + + + + + + 0 + 0 + + + + Browse... + + + + + + + ODT + + + + + + + + + + + 0 + 0 + + + + Browse... + + + + + + + PDF + + + + + + + + + + + 0 + 0 + + + + Browse... + + + + + + + + + + + 0 + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/MdCharm/util/gui/exportdirectorydialog.cpp b/src/MdCharm/util/gui/exportdirectorydialog.cpp new file mode 100644 index 0000000..d5d6029 --- /dev/null +++ b/src/MdCharm/util/gui/exportdirectorydialog.cpp @@ -0,0 +1,252 @@ +#include "exportdirectorydialog.h" +#include "ui_exportdirectorydialog.h" + +#include +#include +#include +#include +#include + +#include "utils.h" +#include "configuration.h" +#include "markdowntohtml.h" + +ExportDirectoryDialog::ExportDirectoryDialog(QWidget *parent, const QString &dirPath) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::ExportDirectoryDialog) +{ + ui->setupUi(this); + webView = new QWebView; + timer = new QTimer(this); + timer->setSingleShot(true); + if(!dirPath.isEmpty()){ + ui->dirPathLineEdit->setText(dirPath); + ui->browerPushButton->setEnabled(false); + } + ui->dirPathLineEdit->setReadOnly(true); + ui->seperateHtmlRadioButton->setChecked(true); + ui->seperateCssAndHtmlcheckBox->setEnabled(false); + ui->keepDirCheckBox->setChecked(true); + ui->exportPathWidget->setHidden(true); + m_model = new QStandardItemModel(this); + clearModel(); + ui->filesTreeView->setModel(m_model); + ui->filesTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); + m_conf = Configuration::getInstance(); + + connect(ui->browerPushButton, SIGNAL(clicked()), this, SLOT(browerSourceDirSlot())); + connect(ui->upPushButton, SIGNAL(clicked()), this, SLOT(moveUp())); + connect(ui->downPushButton, SIGNAL(clicked()), this, SLOT(moveDown())); + connect(ui->removePushButton, SIGNAL(clicked()), this, SLOT(removeOne())); + connect(ui->suddirCheckBox, SIGNAL(clicked()), this, SLOT(fillData())); + connect(ui->seperatePDFRadioButton, SIGNAL(toggled(bool)), ui->seperateCssAndHtmlcheckBox, SLOT(setDisabled(bool))); + connect(ui->singlePDFRadioButton, SIGNAL(toggled(bool)), ui->seperateCssAndHtmlcheckBox, SLOT(setDisabled(bool))); + connect(ui->keepDirCheckBox, SIGNAL(toggled(bool)), ui->exportPathWidget, SLOT(setHidden(bool))); + connect(ui->exportPushButton, SIGNAL(clicked()), this, SLOT(exportBtnSlot())); + connect(ui->exportPathBrowerPushButton, SIGNAL(clicked()), this, SLOT(exportPathBrowerSlot())); + connect(this, SIGNAL(exportFinish()), this, SLOT(exportFinishSlot())); + connect(this, SIGNAL(exportNext()), this, SLOT(exportOneByOne())); +} + +ExportDirectoryDialog::~ExportDirectoryDialog() +{ + delete ui; + webView->deleteLater(); +} + +void ExportDirectoryDialog::browerSourceDirSlot() +{ + QString dirStr = Utils::getExistingDirectory(this, tr("Select the directory")); + if(dirStr.isEmpty()) + return; + ui->dirPathLineEdit->setText(dirStr); + fillData(); +} + +void ExportDirectoryDialog::exportPathBrowerSlot() +{ + QString dirStr = Utils::getExistingDirectory(this, tr("Select the directory")); + if(dirStr.isEmpty()) + return; + ui->exportLineEdit->setText(dirStr); +} + +void ExportDirectoryDialog::fillData() +{ + QString dirStr = ui->dirPathLineEdit->text(); + if(dirStr.isEmpty() || dirStr.trimmed().isEmpty()) + return; + clearModel(); + QFileInfoList fileInfoList = Utils::listAllFileInDir(dirStr, + m_conf->getFileFilter(Configuration::MarkdownFile), + QDir::NoFilter, + ui->suddirCheckBox->isChecked() + ); + foreach (QFileInfo fi, fileInfoList) { + addFile(fi.filePath()); + } +} + +void ExportDirectoryDialog::moveDown() +{ + QModelIndex index = ui->filesTreeView->currentIndex(); + int row = index.row(); + if(!index.isValid() || row>=m_model->rowCount()-1) + return; + m_model->insertRow(row+1, m_model->takeRow(row)); + ui->filesTreeView->setCurrentIndex(m_model->index(row+1, 0)); +} + +void ExportDirectoryDialog::removeOne() +{ + QModelIndex index = ui->filesTreeView->currentIndex(); + if(!index.isValid()) + return; + m_model->removeRow(index.row()); +} + +void ExportDirectoryDialog::exportBtnSlot() +{ + if(!ui->keepDirCheckBox->isChecked() && ui->exportLineEdit->text().isEmpty()){ + QMessageBox::warning(this, tr("Select export path"), tr("Please select an export path or check \"Keep Directory Struct\" checkbox")); + return; + } + ui->exportPushButton->setEnabled(false); + timer->singleShot(50, this, SLOT(startExport())); +} + +void ExportDirectoryDialog::startExport() +{ + if(htmlTemplate.isEmpty() || htmlTemplate.trimmed().isEmpty()){ + htmlTemplate = Utils::getHtmlTemplate(); + cssTemplate = m_conf->getMarkdownCSS(); + } + pendingFile = getFiles(); + if(ui->seperateHtmlRadioButton->isChecked()){ + while(!pendingFile.isEmpty()){ + QFileInfo fi(pendingFile.takeFirst()); + QString fileSavePath = ui->keepDirCheckBox->isChecked() + ? fi.path()+"/"+fi.baseName()+".html" + : ui->exportLineEdit->text()+"/"+fi.baseName()+".html"; + QString fileContent = htmlTemplate.arg(cssTemplate, + "", + "", + Utils::translateMarkdown2Html(m_conf->getMarkdownEngineType(), Utils::readFile(fi.absoluteFilePath()))); + Utils::saveFile(fileSavePath, fileContent.toUtf8()); + } + + } else if(ui->singleHtmlRadioButton->isChecked()){ + QStringList fileContentList; + while(!pendingFile.isEmpty()){ + QFileInfo fi(pendingFile.takeFirst()); + fileContentList.append(Utils::translateMarkdown2Html(m_conf->getMarkdownEngineType(), Utils::readFile(fi.absoluteFilePath()))); + } + QString sumAll = fileContentList.join("
"); + QString fileSavePath = Utils::getSaveFileName("*.html", this, tr("Select export path"), ui->dirPathLineEdit->text(), tr("Html files(*.html)")); + if(fileSavePath.isEmpty()) + return; + QString fileContent = htmlTemplate.arg(cssTemplate, + "", + "", + sumAll); + Utils::saveFile(fileSavePath, fileContent.toUtf8()); + } else if(ui->seperatePDFRadioButton->isChecked()){ + exportOneByOne(); + } else if(ui->singlePDFRadioButton->isChecked()){ + QStringList fileContentList; + while(!pendingFile.isEmpty()){ + QFileInfo fi(pendingFile.takeFirst()); + fileContentList.append(Utils::translateMarkdown2Html(m_conf->getMarkdownEngineType(), Utils::readFile(fi.absoluteFilePath()))); + } + QString sumAll = fileContentList.join("
"); + QString fileSavePath = Utils::getSaveFileName("*.pdf", this, tr("Select export path"), ui->dirPathLineEdit->text(), tr("PDF files(*.pdf)")); + if(fileSavePath.isEmpty()) + return; + pdfOutputFilPath = fileSavePath; + QString fileContent = htmlTemplate.arg(cssTemplate, + "", + "", + sumAll); + exportOneByOne(fileContent); + } +} + +void ExportDirectoryDialog::exportOneByOne(const QString &content) +{ + if(content.isEmpty()){ + if(pendingFile.isEmpty()){ + emit exportFinish(); + return; + } + QFileInfo fi(pendingFile.takeFirst()); + QString fileSavePath = ui->keepDirCheckBox->isChecked() + ? fi.path() + "/" + fi.baseName() + ".pdf" + : ui->exportLineEdit->text()+"/"+fi.baseName()+".pdf"; + QString fileContent = htmlTemplate.arg(cssTemplate, + "", + "", + Utils::translateMarkdown2Html(m_conf->getMarkdownEngineType(), Utils::readFile(fi.absoluteFilePath()))); + pdfOutputFilPath = fileSavePath; + exportOne(fileContent); + } else { + exportOne(content); + } +} + +void ExportDirectoryDialog::exportOne(const QString &content) +{ + if(pdfOutputFilPath.isEmpty()) + return; + connect(webView, SIGNAL(loadFinished(bool)), this, SLOT(loadFinish()), Qt::UniqueConnection); + webView->setHtml(content, QUrl::fromUserInput(ui->dirPathLineEdit->text())); +} + +void ExportDirectoryDialog::loadFinish() +{ + disconnect(webView, SIGNAL(loadFinished(bool)), this, SLOT(loadFinish())); + QPrinter printer; + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setOutputFileName(pdfOutputFilPath); + printer.setCreator("MdCharm (http://www.mdcharm.com/)"); + webView->print(&printer); + pdfOutputFilPath.clear(); + emit exportNext(); +} + +void ExportDirectoryDialog::exportFinishSlot() +{ + ui->exportPushButton->setEnabled(true); +} + +void ExportDirectoryDialog::moveUp() +{ + QModelIndex index = ui->filesTreeView->currentIndex(); + int row = index.row(); + if(!index.isValid()||row<=0) + return; + m_model->insertRow(row-1, m_model->takeRow(row)); + ui->filesTreeView->setCurrentIndex(m_model->index(row-1, 0)); +} + +void ExportDirectoryDialog::clearModel() +{ + m_model->clear(); + m_model->setHorizontalHeaderLabels(QStringList()<rowCount(); i++){ + QModelIndex index = m_model->index(i, 0); + if(index.isValid()){ + files << index.data().toString(); + } + } + return files; +} + +void ExportDirectoryDialog::addFile(const QString &filePath) +{ + m_model->appendRow(new QStandardItem(filePath)); +} diff --git a/src/MdCharm/util/gui/exportdirectorydialog.h b/src/MdCharm/util/gui/exportdirectorydialog.h new file mode 100644 index 0000000..2ea2e49 --- /dev/null +++ b/src/MdCharm/util/gui/exportdirectorydialog.h @@ -0,0 +1,56 @@ +#ifndef EXPORTDIRECTORYDIALOG_H +#define EXPORTDIRECTORYDIALOG_H + +#include +#include + +class QStandardItemModel; +class Configuration; + +namespace Ui { +class ExportDirectoryDialog; +} + +class ExportDirectoryDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ExportDirectoryDialog(QWidget *parent = 0, const QString &dirPath=QString()); + ~ExportDirectoryDialog(); + void addFile(const QString &filePath); +private slots: + void browerSourceDirSlot(); + void exportPathBrowerSlot(); + void fillData(); + void moveUp(); + void moveDown(); + void removeOne(); + void exportBtnSlot(); + void startExport(); + void exportOneByOne(const QString &content = QString()); + void exportOne(const QString &content); + void loadFinish(); + void exportFinishSlot(); +signals: + void exportNext(); + void exportFinish(); +private: + void clearModel(); + QStringList getFiles() const; +private: + Ui::ExportDirectoryDialog *ui; + Configuration *m_conf; + QStandardItemModel *m_model; + QWebView *webView; + + QStringList pendingFile; + QString htmlTemplate; + QString cssTemplate; + + QString pdfOutputFilPath; + + QTimer *timer; +}; + +#endif // EXPORTDIRECTORYDIALOG_H diff --git a/src/MdCharm/util/gui/exportdirectorydialog.ui b/src/MdCharm/util/gui/exportdirectorydialog.ui new file mode 100644 index 0000000..8ae4564 --- /dev/null +++ b/src/MdCharm/util/gui/exportdirectorydialog.ui @@ -0,0 +1,229 @@ + + + ExportDirectoryDialog + + + + 0 + 0 + 686 + 475 + + + + Export Whole Directory... + + + + + + + + Directory: + + + + + + + + + + include suddir + + + + + + + Brower... + + + + + + + + + + + + + + + + Move up + + + Up + + + + :/prev.png:/prev.png + + + + + + + Move down + + + Down + + + + :/next.png:/next.png + + + + + + + Remove + + + + :/conf/delete.png:/conf/delete.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Export Option + + + + + + + + Seperate Html + + + + + + + Single Html + + + + + + + Seperate PDF + + + + + + + Single PDF + + + + + + + + + Seperate css and Html + + + + + + + Keep directory struct + + + + + + + + + + Export Path: + + + + + + + + + + Brower... + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Export + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + diff --git a/src/MdCharm/util/gui/findandreplace.cpp b/src/MdCharm/util/gui/findandreplace.cpp new file mode 100644 index 0000000..f8f87ad --- /dev/null +++ b/src/MdCharm/util/gui/findandreplace.cpp @@ -0,0 +1,158 @@ +#include "findandreplace.h" + +#include "ui_findandreplaceform.h" + +FindAndReplace::FindAndReplace(QWidget *parent) : + QWidget(parent), ui(new Ui::FindAndReplaceForm) +{ + ui->setupUi(this); + closeFindButton = ui->closeFindToolButton; + prevFindButton = ui->prevFindToolButton; + nextFindButton = ui->nextFindToolButton; + findTextLineEdit = ui->findTextLineEdit; + + connect(findTextLineEdit, SIGNAL(textChanged(QString)), + this, SLOT(find(QString))); + connect(closeFindButton, SIGNAL(clicked()), + this, SLOT(hideFind())); + connect(prevFindButton, SIGNAL(clicked()), + this, SLOT(previous())); + connect(nextFindButton, SIGNAL(clicked()), + this, SLOT(next())); + connect(findTextLineEdit, SIGNAL(returnPressed()), + this, SLOT(next())); + connect(ui->reCheckBox, SIGNAL(clicked(bool)), + this, SLOT(regularExpressionCheckedSlot(bool))); + connect(ui->excludePushButton, SIGNAL(clicked()), + this, SLOT(next())); + connect(ui->replacePushButton, SIGNAL(clicked()), + this, SLOT(replaceButtonSlot())); + connect(ui->replaceAllPushButton, SIGNAL(clicked()), + this, SLOT(replaceAllButtonSlot())); +} + +FindAndReplace::~FindAndReplace() +{ + delete ui; + ui = NULL; +} + +void FindAndReplace::regularExpressionCheckedSlot(bool b) +{ + if(b) + { + ui->wholeWordCheckBox->setChecked(false); + ui->wholeWordCheckBox->setEnabled(false); + } else { + ui->wholeWordCheckBox->setEnabled(true); + } +} + +void FindAndReplace::find(const QString &ft, bool isSetCursor) +{ + QTextDocument::FindFlags ff; + if(ui->caseSensitiveCheckBox->isChecked()) + ff |= QTextDocument::FindCaseSensitively; + if(ui->wholeWordCheckBox->isChecked()) + ff |= QTextDocument::FindWholeWords; + emit findText(ft, ff, ui->reCheckBox->isChecked(), isSetCursor); +} + +void FindAndReplace::replaceButtonSlot() +{ + QTextDocument::FindFlags ff; + if(ui->caseSensitiveCheckBox->isChecked()) + ff |= QTextDocument::FindCaseSensitively; + if(ui->wholeWordCheckBox->isChecked()) + ff |= QTextDocument::FindWholeWords; + emit replace(ui->replaceLineEdit->text()); +} + +void FindAndReplace::replaceAllButtonSlot() +{ + QTextDocument::FindFlags ff; + if(ui->caseSensitiveCheckBox->isChecked()) + ff |= QTextDocument::FindCaseSensitively; + if(ui->wholeWordCheckBox->isChecked()) + ff |= QTextDocument::FindWholeWords; + emit replaceAll(ui->findTextLineEdit->text(), ui->replaceLineEdit->text(), + ff, ui->reCheckBox->isChecked()); +} + +void FindAndReplace::resendFindTextSignal() +{ + if(isVisible()) + find(ui->findTextLineEdit->text(), false); +} + +void FindAndReplace::resendFindNextSignal() +{ + next(); +} + +void FindAndReplace::resendFindPreviousSignal() +{ + previous(); +} + +void FindAndReplace::setFindText(const QString &txt) +{ + if(txt.isEmpty()) + return; + if(!findTextLineEdit->text().trimmed().isEmpty()) + return; + findTextLineEdit->setText(txt); +} + +void FindAndReplace::hideFind() +{ + findTextLineEdit->clear(); + emit findHide(); +} + +void FindAndReplace::previous() +{ + QTextDocument::FindFlags ff=QTextDocument::FindBackward; + if(ui->reCheckBox->isChecked()) + { + if(ui->caseSensitiveCheckBox->isChecked()) + ff = ff | QTextDocument::FindCaseSensitively; + emit findPrevious(findTextLineEdit->text(), + ff, true); + return; + } + //else + if(ui->caseSensitiveCheckBox->isChecked()) + { + ff = ff | QTextDocument::FindCaseSensitively; + } + if(ui->wholeWordCheckBox->isChecked()) + { + ff = ff | QTextDocument::FindWholeWords; + } + emit findPrevious(findTextLineEdit->text(), ff, false); +} + +void FindAndReplace::next() +{ + QTextDocument::FindFlags ff; + if(ui->reCheckBox->isChecked()) + { + if(ui->caseSensitiveCheckBox->isChecked()) + ff = ff | QTextDocument::FindCaseSensitively; + emit findNext(findTextLineEdit->text(), + ff, true); + return; + } + //else + if(ui->caseSensitiveCheckBox->isChecked()) + ff = ff | QTextDocument::FindCaseSensitively; + if(ui->wholeWordCheckBox->isChecked()) + ff = ff | QTextDocument::FindWholeWords; + emit findNext(findTextLineEdit->text(), ff, false); +} + +void FindAndReplace::setFocusTextEdit() +{ + findTextLineEdit->setFocus(); +} diff --git a/src/MdCharm/util/gui/findandreplace.h b/src/MdCharm/util/gui/findandreplace.h new file mode 100644 index 0000000..44062fc --- /dev/null +++ b/src/MdCharm/util/gui/findandreplace.h @@ -0,0 +1,52 @@ +#ifndef FINDANDREPLACE_H +#define FINDANDREPLACE_H + +#include +#include + +class QLineEdit; +class QToolButton; + +namespace Ui { + class FindAndReplaceForm; +} + +class FindAndReplace : public QWidget +{ + Q_OBJECT +public: + explicit FindAndReplace(QWidget *parent = 0); + ~FindAndReplace(); + void setFocusTextEdit(); + void resendFindTextSignal(); + void resendFindNextSignal(); + void resendFindPreviousSignal(); + void setFindText(const QString &txt); + +signals: + void findPrevious(const QString &ft, QTextDocument::FindFlags qff, bool isRE); + void findNext(const QString &ft, QTextDocument::FindFlags qff, bool isRE); + void findText(const QString &ft, QTextDocument::FindFlags qff, bool isRE, bool isSetCursor=true); + void replace(const QString &rt); + void replaceAll(const QString &ft, const QString &rt, QTextDocument::FindFlags qff, bool isRE); + void findHide(); +private: + Ui::FindAndReplaceForm *ui; + QToolButton *closeFindButton; + QToolButton *prevFindButton; + QToolButton *nextFindButton; + QLineEdit *findTextLineEdit; + +private slots: + void regularExpressionCheckedSlot(bool b); + void find(const QString &ft, bool isSetCursor=true); + void replaceButtonSlot(); + void replaceAllButtonSlot(); + +public slots: + void hideFind(); + void previous(); + void next(); +}; + +#endif // FINDANDREPLACE_H diff --git a/src/MdCharm/util/gui/findandreplaceform.ui b/src/MdCharm/util/gui/findandreplaceform.ui new file mode 100644 index 0000000..0a1447c --- /dev/null +++ b/src/MdCharm/util/gui/findandreplaceform.ui @@ -0,0 +1,208 @@ + + + FindAndReplaceForm + + + + 0 + 0 + 734 + 55 + + + + + 0 + 0 + + + + FindAndReplace + + + + 3 + + + 1 + + + 1 + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 22 + 22 + + + + + 22 + 22 + + + + + + + :/find.png + + + true + + + + + + + Find: + + + + + + + + + + Previous + + + Previous + + + + :/prev.png:/prev.png + + + false + + + Qt::ToolButtonTextBesideIcon + + + + + + + Next + + + Next + + + + :/next.png:/next.png + + + Qt::ToolButtonTextBesideIcon + + + + + + + Case Sensitive + + + + + + + Regular Expression + + + + + + + Whole Words + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + close + + + + :/find_close.png:/find_close.png + + + true + + + + + + + + + + 3 + + + + + Replace + + + + + + + Replace All + + + + + + + Exclude + + + + + + + + + + + + diff --git a/src/MdCharm/util/gui/gotodialog.cpp b/src/MdCharm/util/gui/gotodialog.cpp new file mode 100644 index 0000000..3f3a219 --- /dev/null +++ b/src/MdCharm/util/gui/gotodialog.cpp @@ -0,0 +1,42 @@ +#include "gotodialog.h" +#include "ui_gotodialog.h" + +#include + +GotoDialog::GotoDialog(int max, int min, QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::GotoDialog) +{ + ui->setupUi(this); + this->max = max; + this->min = min; + ui->gotoLabel->setText(tr("Line(%1 - %2)").arg(min).arg(max)); + connect(ui->buttonBox, SIGNAL(rejected()), + this, SLOT(reject())); + connect(ui->buttonBox, SIGNAL(accepted()), + this, SLOT(checkLineNumber())); +} + +void GotoDialog::checkLineNumber() +{ + QString text = ui->gotoLineEdit->text(); + togo = text.toInt(); + if(togo>max || togo + +namespace Ui { +class GotoDialog; +} + +class GotoDialog : public QDialog +{ + Q_OBJECT + +public: + explicit GotoDialog(int max, int min=1, QWidget *parent = 0); + ~GotoDialog(); + int getLineNumber(); +private slots: + void checkLineNumber(); +private: + Ui::GotoDialog *ui; + int max; + int min; + int togo; +}; + +#endif // GOTODIALOG_H diff --git a/src/MdCharm/util/gui/gotodialog.ui b/src/MdCharm/util/gui/gotodialog.ui new file mode 100644 index 0000000..60db655 --- /dev/null +++ b/src/MdCharm/util/gui/gotodialog.ui @@ -0,0 +1,38 @@ + + + GotoDialog + + + + 0 + 0 + 233 + 85 + + + + Goto + + + + + + Line(1 - 100) + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/MdCharm/util/gui/insertcodedialog.cpp b/src/MdCharm/util/gui/insertcodedialog.cpp new file mode 100644 index 0000000..53e5a8f --- /dev/null +++ b/src/MdCharm/util/gui/insertcodedialog.cpp @@ -0,0 +1,30 @@ +#include "insertcodedialog.h" +#include "ui_insertcodedialog.h" + +InsertCodeDialog::InsertCodeDialog(QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::InsertCodeDialog) +{ + ui->setupUi(this); + QStringList supportLan; + supportLan << "bash" << "c" << "cpp" << "cs" + << "css" << "diff" << "http" << "html" << "ini" + << "java" << "javascript" << "json" + << "markdown" << "perl" << "php" + << "python" << "ruby" << "sql" << "xml"; + ui->codeTypeComboBox->addItems(supportLan); + connect(ui->buttonBox, SIGNAL(rejected()), + this, SLOT(close())); + connect(ui->buttonBox, SIGNAL(accepted()), + this, SLOT(accept())); +} + +QString InsertCodeDialog::getCodeType() +{ + return ui->codeTypeComboBox->currentText(); +} + +InsertCodeDialog::~InsertCodeDialog() +{ + delete ui; +} diff --git a/src/MdCharm/util/gui/insertcodedialog.h b/src/MdCharm/util/gui/insertcodedialog.h new file mode 100644 index 0000000..5be20fa --- /dev/null +++ b/src/MdCharm/util/gui/insertcodedialog.h @@ -0,0 +1,23 @@ +#ifndef INSERTCODEDIALOG_H +#define INSERTCODEDIALOG_H + +#include + +namespace Ui { +class InsertCodeDialog; +} + +class InsertCodeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InsertCodeDialog(QWidget *parent = 0); + QString getCodeType(); + ~InsertCodeDialog(); + +private: + Ui::InsertCodeDialog *ui; +}; + +#endif // INSERTCODEDIALOG_H diff --git a/src/MdCharm/util/gui/insertcodedialog.ui b/src/MdCharm/util/gui/insertcodedialog.ui new file mode 100644 index 0000000..455a0fc --- /dev/null +++ b/src/MdCharm/util/gui/insertcodedialog.ui @@ -0,0 +1,45 @@ + + + InsertCodeDialog + + + + 0 + 0 + 193 + 69 + + + + Select code type + + + + + + + + Code type: + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + diff --git a/src/MdCharm/util/gui/insertlinkorpicturedialog.cpp b/src/MdCharm/util/gui/insertlinkorpicturedialog.cpp new file mode 100644 index 0000000..9c24af0 --- /dev/null +++ b/src/MdCharm/util/gui/insertlinkorpicturedialog.cpp @@ -0,0 +1,112 @@ +#include +#include +#include + +#include "insertlinkorpicturedialog.h" +#include "ui_insertlinkorpicturedialog.h" +#include "utils.h" +#include "configuration.h" +#include "markdowntohtml.h" + +InsertLinkOrPictureDialog::InsertLinkOrPictureDialog(int type, QString proDir, QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::InsertLinkOrPictureDialog), + proDir(proDir) +{ + ui->setupUi(this); + ui->urlLineEdit->setFocus(); + conf = Configuration::getInstance(); + if(type==MdCharmGlobal::ShortcutInsertPicture){ + ui->textLabel->setText(tr("Alt Text:")); + ui->urlLabel->setText(tr("Picture Url:")); + if(conf->getMarkdownEngineType()!=MarkdownToHtml::MultiMarkdown){ + ui->widthLineEdit->hide(); + ui->widthLabel->hide(); + ui->heightLineEdit->hide(); + ui->heightLabel->hide(); + } + setWindowTitle(tr("Insert Picture")); + } else { + ui->insertLocalPushButton->hide(); + ui->textLabel->setText(tr("Link Text:")); + ui->urlLabel->setText(tr("Link Url:")); + ui->widthLineEdit->hide(); + ui->widthLabel->hide(); + ui->heightLineEdit->hide(); + ui->heightLabel->hide(); + setWindowTitle(tr("Insert Link")); + } + connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(close())); + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(checkAndAccept())); + connect(ui->insertLocalPushButton, SIGNAL(clicked()), this, SLOT(insertLocalFileSlot())); + this->type = type; + setFixedHeight(sizeHint().height()); +} + +InsertLinkOrPictureDialog::~InsertLinkOrPictureDialog() +{ + delete ui; +} + +void InsertLinkOrPictureDialog::checkAndAccept() +{ + QString text = ui->textLineEdit->text(); + if(text.isEmpty() && text.trimmed().isEmpty()){ + showWarning(tr("Alt Text can't be empty!"), tr("Link Text cann't be empty!")); + return; + } + QString url = ui->urlLineEdit->text(); + if(url.isEmpty() && url.trimmed().isEmpty()){ + showWarning(tr("Picture Url can't be empty!"), tr("Link Url cann't be empty!")); + return; + } + accept(); +} + +void InsertLinkOrPictureDialog::showWarning(const QString &pw, const QString &lw) +{ + if(type==MdCharmGlobal::ShortcutInsertPicture) + QMessageBox::warning(this, pw, pw); + else + QMessageBox::warning(this, lw, lw); +} + +void InsertLinkOrPictureDialog::insertLocalFileSlot() +{ + QString filePath = QFileDialog::getOpenFileName(this, tr("Select a image"), proDir, tr("All(*.*)")); + if(filePath.isEmpty()) + return; + QFileInfo fileInfo(filePath); + ui->textLineEdit->setText(fileInfo.fileName()); + filePath = fileInfo.absoluteFilePath(); + if(!proDir.isEmpty()){ + QDir current(proDir); + filePath = current.relativeFilePath(filePath); + } + ui->urlLineEdit->setText(filePath); +} + +const QString InsertLinkOrPictureDialog::getText() const +{ + return ui->textLineEdit->text(); +} + +const QString InsertLinkOrPictureDialog::getUrl() const +{ + return ui->urlLineEdit->text(); +} + +const QString InsertLinkOrPictureDialog::getTitle() const +{ + return ui->titleLineEdit->text(); +} + +const QString InsertLinkOrPictureDialog::getWidth() const +{ + return ui->widthLineEdit->text(); +} + +const QString InsertLinkOrPictureDialog::getHeight() const +{ + return ui->heightLineEdit->text(); +} diff --git a/src/MdCharm/util/gui/insertlinkorpicturedialog.h b/src/MdCharm/util/gui/insertlinkorpicturedialog.h new file mode 100644 index 0000000..761766d --- /dev/null +++ b/src/MdCharm/util/gui/insertlinkorpicturedialog.h @@ -0,0 +1,36 @@ +#ifndef INSERTLINKORPICTUREDIALOG_H +#define INSERTLINKORPICTUREDIALOG_H + +#include + +namespace Ui { +class InsertLinkOrPictureDialog; +} + +class Configuration; + +class InsertLinkOrPictureDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InsertLinkOrPictureDialog(int type, QString proDir=QString(), QWidget *parent = 0); + ~InsertLinkOrPictureDialog(); + const QString getText() const; + const QString getUrl() const; + const QString getTitle() const; + const QString getWidth() const; + const QString getHeight() const; + +private: + Ui::InsertLinkOrPictureDialog *ui; + int type; + QString proDir; + Configuration *conf; +private slots: + void checkAndAccept(); + void showWarning(const QString &pw, const QString &lw); + void insertLocalFileSlot(); +}; + +#endif // INSERTLINKORPICTUREDIALOG_H diff --git a/src/MdCharm/util/gui/insertlinkorpicturedialog.ui b/src/MdCharm/util/gui/insertlinkorpicturedialog.ui new file mode 100644 index 0000000..3a86805 --- /dev/null +++ b/src/MdCharm/util/gui/insertlinkorpicturedialog.ui @@ -0,0 +1,121 @@ + + + InsertLinkOrPictureDialog + + + + 0 + 0 + 427 + 174 + + + + Dialog + + + + + + Width: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Height: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + optional + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + optional + + + + + + + optional + + + + + + + Browse... + + + + + + + Link Text: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Title: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Link Url: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + urlLineEdit + textLineEdit + titleLineEdit + buttonBox + + + + diff --git a/src/MdCharm/util/gui/markdowncheatsheetdialog.cpp b/src/MdCharm/util/gui/markdowncheatsheetdialog.cpp new file mode 100644 index 0000000..17983ef --- /dev/null +++ b/src/MdCharm/util/gui/markdowncheatsheetdialog.cpp @@ -0,0 +1,50 @@ +#include + +#include "markdowncheatsheetdialog.h" +#include "configuration.h" +#include "markdowntohtml.h" + +MarkdownCheatSheetDialog::MarkdownCheatSheetDialog(QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint|Qt::WindowMinMaxButtonsHint|Qt::WindowCloseButtonHint) +{ + webView = new MarkdownWebView(this); + QHBoxLayout *mainLayout = new QHBoxLayout(this); + mainLayout->setMargin(0); + setLayout(mainLayout); + mainLayout->addWidget(webView); + resize(800, 600); + Configuration *conf = Configuration::getInstance(); + QFile htmlTemplate(":/markdown/markdown.html"); + if(!htmlTemplate.open(QIODevice::ReadOnly)) + { + Utils::showFileError(htmlTemplate.error(), ":/markdown/markdown.html"); + return; + } + QString htmlContent = htmlTemplate.readAll(); + htmlTemplate.close(); + + QFile cheatSheetMdFile(":/markdown/markdown_cheat_sheet.md"); + cheatSheetMdFile.open(QFile::ReadOnly); + QByteArray content = cheatSheetMdFile.readAll(); + std::string textResult; + MarkdownToHtml::translateMarkdownToHtml(MarkdownToHtml::PHPMarkdownExtra, content.data(), content.length(), textResult); + + htmlContent = htmlContent.arg(conf->getMarkdownCSS()) + .arg("") + .arg("") + .arg(QString::fromUtf8(textResult.c_str(), textResult.length())); + webView->setHtml(htmlContent); + cheatSheetMdFile.close(); +} + +MarkdownCheatSheetDialog::~MarkdownCheatSheetDialog() +{ +} + +void MarkdownCheatSheetDialog::showAndPopup() +{ + show(); + raise(); + activateWindow(); + setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); +} diff --git a/src/MdCharm/util/gui/markdowncheatsheetdialog.h b/src/MdCharm/util/gui/markdowncheatsheetdialog.h new file mode 100644 index 0000000..f8bef65 --- /dev/null +++ b/src/MdCharm/util/gui/markdowncheatsheetdialog.h @@ -0,0 +1,20 @@ +#ifndef MARKDOWNCHEATSHEETDIALOG_H +#define MARKDOWNCHEATSHEETDIALOG_H + +#include + +#include "basewebview/markdownwebview.h" + +class MarkdownCheatSheetDialog : public QDialog +{ + Q_OBJECT +public: + explicit MarkdownCheatSheetDialog(QWidget *parent = 0); + ~MarkdownCheatSheetDialog(); + void showAndPopup(); +protected: +private: + MarkdownWebView *webView; +}; + +#endif // MARKDOWNCHEATSHEETDIALOG_H diff --git a/src/MdCharm/util/gui/newversioninfodialog.cpp b/src/MdCharm/util/gui/newversioninfodialog.cpp new file mode 100644 index 0000000..de239f6 --- /dev/null +++ b/src/MdCharm/util/gui/newversioninfodialog.cpp @@ -0,0 +1,51 @@ +#include "newversioninfodialog.h" +#include "markdowntohtml.h" + +#include + +NewVersionInfoDialog::NewVersionInfoDialog(const QString &version, const QString &revision, const QString &infoMarkdown, QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), rev(revision) +{ + setWindowTitle(tr("MdCharm %1").arg(version)); + QGridLayout *layout = new QGridLayout; + layout->setMargin(0); + layout->setVerticalSpacing(8); + layout->setHorizontalSpacing(0); + layout->setSizeConstraint(QLayout::SetNoConstraint); + + std::string html; + QByteArray content = infoMarkdown.toUtf8(); + MarkdownToHtml::translateMarkdownToHtml(MarkdownToHtml::PHPMarkdownExtra, content.data(), content.length(), html); + infoBrower = new QTextBrowser(this); + infoBrower->setFrameShape(QFrame::NoFrame); + infoBrower->setHtml(QString::fromUtf8(html.c_str(), html.length())); + layout->addWidget(infoBrower, 0, 0); + + buttonBox = new QDialogButtonBox(this); + ignoreThisVersionButton = buttonBox->addButton(tr("Ignore This Version"), QDialogButtonBox::RejectRole); + remindMeLaterPushButton = buttonBox->addButton(tr("Remind Me Later"), QDialogButtonBox::RejectRole); + downloadPushButton = buttonBox->addButton(tr("Download Now"), QDialogButtonBox::AcceptRole); + QVBoxLayout *buttonBoxLayout = new QVBoxLayout; + buttonBoxLayout->setMargin(6); + buttonBoxLayout->addWidget(buttonBox); + layout->addLayout(buttonBoxLayout, 1, 0); + setLayout(layout); + setModal(true); + + connect(buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*))); + + conf = Configuration::getInstance(); +} + +void NewVersionInfoDialog::buttonClicked(QAbstractButton *btn) +{ + if(btn==ignoreThisVersionButton){ + conf->setLastIgnoreRevision(rev); + reject(); + } else if(btn==remindMeLaterPushButton){ + reject(); + } else if(btn==downloadPushButton){ + QDesktopServices::openUrl(QUrl::fromUserInput("http://www.mdcharm.com/download.html")); + accept(); + } +} diff --git a/src/MdCharm/util/gui/newversioninfodialog.h b/src/MdCharm/util/gui/newversioninfodialog.h new file mode 100644 index 0000000..7d2954b --- /dev/null +++ b/src/MdCharm/util/gui/newversioninfodialog.h @@ -0,0 +1,34 @@ +#ifndef NEWVERSIONINFODIALOG_H +#define NEWVERSIONINFODIALOG_H + +#include +#include +#include +#include +#include + +#include "configuration.h" + +class NewVersionInfoDialog : public QDialog +{ + Q_OBJECT +public: + explicit NewVersionInfoDialog(const QString &version, const QString &revision, const QString &infoMarkdown, QWidget *parent = 0); + +signals: + +public slots: +private slots: + void buttonClicked(QAbstractButton *btn); +private: + QTextBrowser *infoBrower; + QDialogButtonBox *buttonBox; + QPushButton *remindMeLaterPushButton; + QPushButton *ignoreThisVersionButton; + QPushButton *downloadPushButton; + + Configuration *conf; + QString rev; +}; + +#endif // NEWVERSIONINFODIALOG_H diff --git a/src/MdCharm/util/gui/noticedialog.cpp b/src/MdCharm/util/gui/noticedialog.cpp new file mode 100644 index 0000000..84a45bc --- /dev/null +++ b/src/MdCharm/util/gui/noticedialog.cpp @@ -0,0 +1,21 @@ +#include "noticedialog.h" +#include "ui_noticedialog.h" + +NoticeDialog::NoticeDialog(QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::NoticeDialog) +{ + ui->setupUi(this); + connect(ui->buttonBox, SIGNAL(accepted()), + this, SLOT(accept())); +} + +void NoticeDialog::setNoticeContent(const QString &content) +{ + ui->noticeLabel->setText(content); +} + +NoticeDialog::~NoticeDialog() +{ + delete ui; +} diff --git a/src/MdCharm/util/gui/noticedialog.h b/src/MdCharm/util/gui/noticedialog.h new file mode 100644 index 0000000..9ddede7 --- /dev/null +++ b/src/MdCharm/util/gui/noticedialog.h @@ -0,0 +1,23 @@ +#ifndef NOTICEDIALOG_H +#define NOTICEDIALOG_H + +#include + +namespace Ui { +class NoticeDialog; +} + +class NoticeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit NoticeDialog(QWidget *parent = 0); + void setNoticeContent(const QString &content); + ~NoticeDialog(); + +private: + Ui::NoticeDialog *ui; +}; + +#endif // NOTICEDIALOG_H diff --git a/src/MdCharm/util/gui/noticedialog.ui b/src/MdCharm/util/gui/noticedialog.ui new file mode 100644 index 0000000..ca16b69 --- /dev/null +++ b/src/MdCharm/util/gui/noticedialog.ui @@ -0,0 +1,57 @@ + + + NoticeDialog + + + + 0 + 0 + 400 + 142 + + + + Notice + + + + + + Notice + + + true + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QDialogButtonBox::Ok + + + true + + + + + + + + diff --git a/src/MdCharm/util/gui/renamefiledialog.cpp b/src/MdCharm/util/gui/renamefiledialog.cpp new file mode 100644 index 0000000..6a07fef --- /dev/null +++ b/src/MdCharm/util/gui/renamefiledialog.cpp @@ -0,0 +1,52 @@ +#include +#include + +#include "renamefiledialog.h" +#include "ui_renamefiledialog.h" +#include "utils.h" + +RenameFileDialog::RenameFileDialog(const QString &fileFullPath, QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::RenameFileDialog) +{ + ui->setupUi(this); + oldFilePath = fileFullPath; + QString filename = QFileInfo(fileFullPath).fileName(); + ui->infoLabel->setText(tr("Rename file %1 to:").arg(filename)); + ui->renameLineEdit->setText(filename); + connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(close())); + connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(acceptSlot())); +} + +RenameFileDialog::~RenameFileDialog() +{ + delete ui; +} + +QString RenameFileDialog::getNewFilePath() +{ + return newFP; +} + +void RenameFileDialog::acceptSlot() +{ + if(ui->renameLineEdit->text().isEmpty() || + ui->renameLineEdit->text().trimmed().isEmpty()){ + QMessageBox::warning(this, tr("File name can't be empty!"), tr("File name can't be empty!")); + return; + } + QFileInfo fi(oldFilePath); + QString filename = fi.fileName(); + QString suffix = fi.suffix(); + QString newFilePath = oldFilePath.left(oldFilePath.length()-filename.length())+ui->renameLineEdit->text(); + if(!Utils::isMarkdownFile(newFilePath)){//TODO: currently only support markdown suffix + newFilePath.append("."); + newFilePath.append(suffix); + } + if(QFile::exists(newFilePath)){ + QMessageBox::warning(this, tr("This file is already exists!"), tr("This file is already exists!")); + return; + } + newFP = newFilePath; + accept(); +} diff --git a/src/MdCharm/util/gui/renamefiledialog.h b/src/MdCharm/util/gui/renamefiledialog.h new file mode 100644 index 0000000..7c782a2 --- /dev/null +++ b/src/MdCharm/util/gui/renamefiledialog.h @@ -0,0 +1,28 @@ +#ifndef RENAMEFILEDIALOG_H +#define RENAMEFILEDIALOG_H + +#include + +namespace Ui { +class RenameFileDialog; +} + +class RenameFileDialog : public QDialog +{ + Q_OBJECT + +public: + explicit RenameFileDialog(const QString &fileFullPath, QWidget *parent = 0); + QString getNewFilePath(); + ~RenameFileDialog(); + +private slots: + void acceptSlot(); + +private: + Ui::RenameFileDialog *ui; + QString oldFilePath; + QString newFP; +}; + +#endif // RENAMEFILEDIALOG_H diff --git a/src/MdCharm/util/gui/renamefiledialog.ui b/src/MdCharm/util/gui/renamefiledialog.ui new file mode 100644 index 0000000..915a1c6 --- /dev/null +++ b/src/MdCharm/util/gui/renamefiledialog.ui @@ -0,0 +1,41 @@ + + + RenameFileDialog + + + + 0 + 0 + 327 + 85 + + + + Rename + + + + + + Rename file %1 to: + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + diff --git a/src/MdCharm/util/gui/selectencodingdialog.cpp b/src/MdCharm/util/gui/selectencodingdialog.cpp new file mode 100644 index 0000000..63d98d8 --- /dev/null +++ b/src/MdCharm/util/gui/selectencodingdialog.cpp @@ -0,0 +1,61 @@ +#include "selectencodingdialog.h" +#include "ui_selectencodingdialog.h" +#include "../../utils.h" +#include "../../configuration.h" + +#include + +#ifdef QT_V5 +#include +#endif + +SelectEncodingDialog::SelectEncodingDialog(bool modified, QString currentEncoding, QWidget *parent) : + QDialog(parent, Qt::WindowTitleHint|Qt::WindowSystemMenuHint), + ui(new Ui::SelectEncodingDialog) +{ + ui->setupUi(this); + this->modified = modified; + conf = Configuration::getInstance(); + encodingComboBox = ui->encodingComboBox; + reloadFileCheckBox = ui->reloadFileCheckBox; + buttonBox = ui->buttonBox; + connect(buttonBox, SIGNAL(accepted()), + this, SLOT(reloadFileWarning())); + connect(buttonBox, SIGNAL(rejected()), + this, SLOT(reject())); + QStringList sl = Utils::getEncodingList(); + int index = sl.indexOf(currentEncoding); + encodingComboBox->addItems(sl); + encodingComboBox->setCurrentIndex(index); + reloadFileCheckBox->setChecked(true); +} + +SelectEncodingDialog::~SelectEncodingDialog() +{ + delete ui; +} + +QString SelectEncodingDialog::getSelectedEncoding() +{ + return encodingComboBox->currentText(); +} + +bool SelectEncodingDialog::isReloadFile() +{ + return reloadFileCheckBox->isChecked(); +} + +void SelectEncodingDialog::reloadFileWarning() +{ + if(isReloadFile()&&modified) + { + if(QMessageBox::No == QMessageBox::warning(this, tr("Reload File"), + tr("File changes will lost if reload this file." + " Do you really want to reload this file?"), + QMessageBox::Yes|QMessageBox::No)) + { + return; + } + } + accept(); +} diff --git a/src/MdCharm/util/gui/selectencodingdialog.h b/src/MdCharm/util/gui/selectencodingdialog.h new file mode 100644 index 0000000..001258a --- /dev/null +++ b/src/MdCharm/util/gui/selectencodingdialog.h @@ -0,0 +1,38 @@ +#ifndef SELECTENCODINGDIALOG_H +#define SELECTENCODINGDIALOG_H + +#include + +class Configuration; + +namespace Ui { +class SelectEncodingDialog; +} + +class QComboBox; +class QDialogButtonBox; +class QCheckBox; + +class SelectEncodingDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SelectEncodingDialog(bool modified, QString currentEncoding, QWidget *parent = 0); + ~SelectEncodingDialog(); + QString getSelectedEncoding(); + bool isReloadFile(); + +private slots: + void reloadFileWarning(); + +private: + Ui::SelectEncodingDialog *ui; + QComboBox *encodingComboBox; + QDialogButtonBox *buttonBox; + QCheckBox *reloadFileCheckBox; + Configuration *conf; + bool modified; +}; + +#endif // SELECTENCODINGDIALOG_H diff --git a/src/MdCharm/util/gui/selectencodingdialog.ui b/src/MdCharm/util/gui/selectencodingdialog.ui new file mode 100644 index 0000000..41a320b --- /dev/null +++ b/src/MdCharm/util/gui/selectencodingdialog.ui @@ -0,0 +1,69 @@ + + + SelectEncodingDialog + + + + 0 + 0 + 185 + 85 + + + + Change File Encoding + + + + + + + + + + + File Encoding: + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Reload File + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff --git a/src/MdCharm/util/gui/shortcutlineedit.cpp b/src/MdCharm/util/gui/shortcutlineedit.cpp new file mode 100644 index 0000000..10bfe1b --- /dev/null +++ b/src/MdCharm/util/gui/shortcutlineedit.cpp @@ -0,0 +1,29 @@ +#include + +#include "shortcutlineedit.h" + +ShortcutLineEdit::ShortcutLineEdit(QWidget *parent) : + QLineEdit(parent) +{ +} + +void ShortcutLineEdit::keyPressEvent(QKeyEvent *e) +{ + QStringList keys; + Qt::KeyboardModifiers modifiers = e->modifiers(); + if(modifiers!=Qt::NoModifier){ + if(modifiers.testFlag(Qt::ShiftModifier)) + keys.append("Shift"); + if(modifiers.testFlag(Qt::ControlModifier)) + keys.append("Ctrl"); + if(modifiers.testFlag(Qt::AltModifier)) + keys.append("Alt"); + if(modifiers.testFlag(Qt::MetaModifier)) + keys.append("Meta"); + } + if(e->key()!=0 && e->key()!=Qt::Key_unknown){ + keys.append(QKeySequence(e->key()).toString()); + } + setText(keys.join("+")); + emit textEdited(keys.join("+")); +} diff --git a/src/MdCharm/util/gui/shortcutlineedit.h b/src/MdCharm/util/gui/shortcutlineedit.h new file mode 100644 index 0000000..c1e555e --- /dev/null +++ b/src/MdCharm/util/gui/shortcutlineedit.h @@ -0,0 +1,21 @@ +#ifndef SHORTCUTLINEEDIT_H +#define SHORTCUTLINEEDIT_H + +#include + +class ShortcutLineEdit : public QLineEdit +{ + Q_OBJECT +public: + explicit ShortcutLineEdit(QWidget *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *e); + +signals: + +public slots: + +}; + +#endif // SHORTCUTLINEEDIT_H diff --git a/src/MdCharm/util/gui/statusbarlabel.cpp b/src/MdCharm/util/gui/statusbarlabel.cpp new file mode 100644 index 0000000..9916631 --- /dev/null +++ b/src/MdCharm/util/gui/statusbarlabel.cpp @@ -0,0 +1,13 @@ +#include "statusbarlabel.h" +#include + +StatusBarLabel::StatusBarLabel(QWidget *parent, Qt::WindowFlags f) + : QLabel(parent, f) +{ +} + +void StatusBarLabel::mouseReleaseEvent(QMouseEvent *ev) +{ + ev->accept(); + emit labelClicked(); +} diff --git a/src/MdCharm/util/gui/statusbarlabel.h b/src/MdCharm/util/gui/statusbarlabel.h new file mode 100644 index 0000000..e2098c0 --- /dev/null +++ b/src/MdCharm/util/gui/statusbarlabel.h @@ -0,0 +1,17 @@ +#ifndef STATUSBARLABEL_H +#define STATUSBARLABEL_H + +#include + +class StatusBarLabel : public QLabel +{ + Q_OBJECT +public: + StatusBarLabel(QWidget *parent = 0, Qt::WindowFlags f = 0); +protected: + virtual void mouseReleaseEvent(QMouseEvent *ev); +signals: + void labelClicked(); +}; + +#endif // STATUSBARLABEL_H diff --git a/src/MdCharm/util/odt/odtwriter.cpp b/src/MdCharm/util/odt/odtwriter.cpp new file mode 100644 index 0000000..a7b6e62 --- /dev/null +++ b/src/MdCharm/util/odt/odtwriter.cpp @@ -0,0 +1,798 @@ +#include +#include + +#include + +#include "odtwriter.h" +#include "configuration.h" + +static QString pixelToPoint(qreal pixels) +{ + return QString::number(pixels * 72/96) + QString::fromLatin1("pt"); +} + +static QString bulletChar(QTextListFormat::Style style) +{ + switch(style) { + case QTextListFormat::ListDisc: + return QChar(0x25cf); // bullet character + case QTextListFormat::ListCircle: + return QChar(0x25cb); // white circle + case QTextListFormat::ListSquare: + return QChar(0x25a1); // white square + case QTextListFormat::ListDecimal: + return QString::fromLatin1("1"); + case QTextListFormat::ListLowerAlpha: + return QString::fromLatin1("a"); + case QTextListFormat::ListUpperAlpha: + return QString::fromLatin1("A"); + case QTextListFormat::ListLowerRoman: + return QString::fromLatin1("i"); + case QTextListFormat::ListUpperRoman: + return QString::fromLatin1("I"); + default: + case QTextListFormat::ListStyleUndefined: + return QString(); + } +} + +ODTWriter::ODTWriter(const QTextDocument &document, const QString &fileName): + document(document), manifestWriter(&manifest), contentWriter(&content), + metaWriter(&meta), stylesWriter(&styles) +{ + zip = new ZipWriter(fileName); + conf = Configuration::getInstance(); + + officeNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:office:1.0"); + metaNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:meta:1.0"); + configNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:config:1.0"); + textNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:text:1.0"); + tableNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:table:1.0"); + drawNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"); + presentationNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"); + chartNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:chart:1.0"); + formNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:form:1.0"); + scriptNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:script:1.0"); + styleNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:style:1.0"); + numberNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:data sytle:1.0"); + animNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:animation:1.0"); + dcNS = QLatin1String("http://purl.org/dc/elements/1.1/"); + xlinkNS = QLatin1String("http://www.w3.org/1999/xlink"); + mathNS = QLatin1String("http://www.w3.org/1998/Math/MathML"); + xformNS = QLatin1String("http://www.w3.org/2002/xforms"); + foNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"); + svgNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"); + smilNS = QLatin1String("urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"); + + + manifestNS= QLatin1String("urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"); + + picCounter = 0; +} + +ODTWriter::~ODTWriter() +{ + delete zip; +} + +bool ODTWriter::writeAll() +{ + assert(zip->isWritable()); + + manifestWriter.setAutoFormatting(true); + manifestWriter.setAutoFormattingIndent(1); + + manifestWriter.writeNamespace(manifestNS, QString::fromLatin1("manifest")); + manifestWriter.writeStartDocument(); + manifestWriter.writeStartElement(manifestNS, QString::fromLatin1("manifest")); + //manifestWriter.writeAttribute(manifestNS, QString::fromLatin1("version"), QString::fromLatin1("1.2")); + addFile(QString::fromLatin1("/"), QString::fromLatin1("application/vnd.oasis.opendocument.text")); + + contentWriter.setAutoFormatting(true); + contentWriter.setAutoFormattingIndent(2); + + writeNameSpaces(contentWriter); + contentWriter.writeStartDocument(); + contentWriter.writeStartElement(officeNS, QString::fromLatin1("document-content")); + contentWriter.writeAttribute(officeNS, QString::fromLatin1("version"), QString::fromLatin1("1.2")); + + // add fragments. (for character formats) +// QTextDocumentPrivate::FragmentIterator fragIt = document.docHandle()->begin(); +// QSet formats; +// while (fragIt != m_document->docHandle()->end()) { +// const QTextFragmentData * const frag = fragIt.value(); +// formats << frag->format; +// ++fragIt; +// } + +// // add blocks (for blockFormats) +// QTextDocumentPrivate::BlockMap &blocks = m_document->docHandle()->blockMap(); +// QTextDocumentPrivate::BlockMap::Iterator blockIt = blocks.begin(); +// while (blockIt != blocks.end()) { +// const QTextBlockData * const block = blockIt.value(); +// formats << block->format; +// ++blockIt; +// } + + // add objects for lists, frames and tables +// QVector allFormats = document.allFormats(); +// QList copy = formats.toList(); +// for (QList::Iterator iter = copy.begin(); iter != copy.end(); ++iter) { +// QTextObject *object = document.objectForFormat(allFormats[*iter]); +// if (object) +// formats << object->formatIndex(); +// } + + QSet formats; + + //int blockCount = document.blockCount(); + for (QTextBlock it = document.begin(); it != document.end(); it = it.next()) + { + formats << it.blockFormatIndex(); + for(QTextBlock::iterator ita = it.begin(); !(ita.atEnd()); ++ita) + { + QTextFragment currentFragment = ita.fragment(); + if(currentFragment.isValid()) + { + formats << currentFragment.charFormatIndex(); + } + } + } + + writeFormats(contentWriter, formats); + + contentWriter.writeStartElement(officeNS, QString::fromLatin1("body")); + contentWriter.writeStartElement(officeNS, QString::fromLatin1("text")); + QTextFrame *rootFrame = document.rootFrame(); + writeFrame(contentWriter, rootFrame); + contentWriter.writeEndElement(); // text + contentWriter.writeEndElement(); // body + contentWriter.writeEndElement(); // document-content + contentWriter.writeEndDocument(); + + metaWriter.setAutoFormatting(true); + metaWriter.setAutoFormattingIndent(1); + writeNameSpaces(metaWriter); + metaWriter.writeStartDocument(); + metaWriter.writeStartElement(officeNS, QString::fromLatin1("document-meta")); + metaWriter.writeStartElement(officeNS, QString::fromLatin1("meta")); + metaWriter.writeEndElement(); + metaWriter.writeEndElement(); + metaWriter.writeEndDocument(); + + stylesWriter.setAutoFormatting(true); + stylesWriter.setAutoFormattingIndent(1); + writeNameSpaces(stylesWriter); + stylesWriter.writeStartDocument(); + stylesWriter.writeStartElement(officeNS, QString::fromLatin1("document-styles")); + stylesWriter.writeStartElement(officeNS, QString::fromLatin1("styles")); + stylesWriter.writeEndElement(); + stylesWriter.writeEndElement(); + stylesWriter.writeEndDocument(); + + writeMimeType(); + + writeStyles(); + writeMeta(); + + writeContent(); + + manifestWriter.writeEndDocument(); + writeManifest(); + + zip->close(); + return true; +} + +void ODTWriter::writeBlock(QXmlStreamWriter &writer, const QTextBlock &block) +{ + if (block.textList()) { // its a list-item + const int listLevel = block.textList()->format().indent(); + if (m_listStack.isEmpty() || m_listStack.top() != block.textList()) { + // not the same list we were in. + while (m_listStack.count() >= listLevel && !m_listStack.isEmpty() && m_listStack.top() != block.textList() ) { // we need to close tags + m_listStack.pop(); + writer.writeEndElement(); // list + if (m_listStack.count()) + writer.writeEndElement(); // list-item + } + while (m_listStack.count() < listLevel) { + if (m_listStack.count()) + writer.writeStartElement(textNS, QString::fromLatin1("list-item")); + writer.writeStartElement(textNS, QString::fromLatin1("list")); + if (m_listStack.count() == listLevel - 1) { + m_listStack.push(block.textList()); + writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("L%1") + .arg(block.textList()->formatIndex())); + } + else { + m_listStack.push(0); + } + } + } + writer.writeStartElement(textNS, QString::fromLatin1("list-item")); + } + else { + while (! m_listStack.isEmpty()) { + m_listStack.pop(); + writer.writeEndElement(); // list + if (m_listStack.count()) + writer.writeEndElement(); // list-item + } + } + + if (block.length() == 1) { // only a linefeed + writer.writeEmptyElement(textNS, QString::fromLatin1("p")); + writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("p%1") + .arg(block.blockFormatIndex())); + if (block.textList()) + writer.writeEndElement(); // numbered-paragraph + return; + } + writer.writeStartElement(textNS, QString::fromLatin1("p")); + writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("p%1") + .arg(block.blockFormatIndex())); + for (QTextBlock::Iterator frag= block.begin(); !frag.atEnd(); frag++) { + writer.writeCharacters(QString()); // Trick to make sure that the span gets no linefeed in front of it. + writer.writeStartElement(textNS, QString::fromLatin1("span")); + + QString fragmentText = frag.fragment().text(); + if (fragmentText.length() == 1 && fragmentText[0] == 0xFFFC) { // its an inline character. + writeInlineCharacter(writer, frag.fragment()); + writer.writeEndElement(); // span + continue; + } + + writer.writeAttribute(textNS, QString::fromLatin1("style-name"), QString::fromLatin1("c%1") + .arg(frag.fragment().charFormatIndex())); + bool escapeNextSpace = true; + int precedingSpaces = 0; + int exportedIndex = 0; + for (int i=0; i <= fragmentText.count(); ++i) { + bool isSpace = false; + QChar character = fragmentText[i]; + isSpace = character.unicode() == ' '; + + // find more than one space. -> + if (!isSpace && escapeNextSpace && precedingSpaces > 1) { + const bool startParag = exportedIndex == 0 && i == precedingSpaces; + if (!startParag) + writer.writeCharacters(fragmentText.mid(exportedIndex, i - precedingSpaces + 1 - exportedIndex)); + writer.writeEmptyElement(textNS, QString::fromLatin1("s")); + const int count = precedingSpaces - (startParag?0:1); + if (count > 1) + writer.writeAttribute(textNS, QString::fromLatin1("c"), QString::number(count)); + precedingSpaces = 0; + exportedIndex = i; + } + + if (i < fragmentText.count()) { + if (character.unicode() == 0x2028) { // soft-return + //if (exportedIndex < i) + writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex)); + writer.writeEmptyElement(textNS, QString::fromLatin1("line-break")); + exportedIndex = i+1; + continue; + } else if (character.unicode() == '\t') { // Tab + //if (exportedIndex < i) + writer.writeCharacters(fragmentText.mid(exportedIndex, i - exportedIndex)); + writer.writeEmptyElement(textNS, QString::fromLatin1("tab")); + exportedIndex = i+1; + precedingSpaces = 0; + } else if (isSpace) { + ++precedingSpaces; + escapeNextSpace = true; + } else if (!isSpace) { + precedingSpaces = 0; + } + } + } + + writer.writeCharacters(fragmentText.mid(exportedIndex)); + writer.writeEndElement(); // span + } + writer.writeCharacters(QString()); // Trick to make sure that the span gets no linefeed behind it. + writer.writeEndElement(); // p + if (block.textList()) + writer.writeEndElement(); // list-item +} + +void ODTWriter::writeFormats(QXmlStreamWriter &writer, QSet formats) const +{ + writer.writeStartElement(officeNS, QString::fromLatin1("automatic-styles")); + QVector allStyles = document.allFormats(); + QSetIterator formatId(formats); + while(formatId.hasNext()) { + int formatIndex = formatId.next(); + QTextFormat textFormat = allStyles.at(formatIndex); + switch (textFormat.type()) { + case QTextFormat::CharFormat: + if (textFormat.isTableCellFormat()) + writeTableCellFormat(writer, textFormat.toTableCellFormat(), formatIndex); + else + writeCharacterFormat(writer, textFormat.toCharFormat(), formatIndex); + break; + case QTextFormat::BlockFormat: + writeBlockFormat(writer, textFormat.toBlockFormat(), formatIndex); + break; + case QTextFormat::ListFormat: + writeListFormat(writer, textFormat.toListFormat(), formatIndex); + break; + case QTextFormat::FrameFormat: + writeFrameFormat(writer, textFormat.toFrameFormat(), formatIndex); + break; + case QTextFormat::TableFormat: + break; + default: + break; + } + } + + writer.writeEndElement(); // automatic-styles +} + +void ODTWriter::writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat format, int formatIndex) const +{ + writer.writeStartElement(styleNS, QString::fromLatin1("style")); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("p%1").arg(formatIndex)); + writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("paragraph")); + writer.writeStartElement(styleNS, QString::fromLatin1("paragraph-properties")); + + if (format.hasProperty(QTextFormat::BlockAlignment)) { + const Qt::Alignment alignment = format.alignment() & Qt::AlignHorizontal_Mask; + QString value; + if (alignment == Qt::AlignLeading) + value = QString::fromLatin1("start"); + else if (alignment == Qt::AlignTrailing) + value = QString::fromLatin1("end"); + else if (alignment == (Qt::AlignLeft | Qt::AlignAbsolute)) + value = QString::fromLatin1("left"); + else if (alignment == (Qt::AlignRight | Qt::AlignAbsolute)) + value = QString::fromLatin1("right"); + else if (alignment == Qt::AlignHCenter) + value = QString::fromLatin1("center"); + else if (alignment == Qt::AlignJustify) + value = QString::fromLatin1("justify"); + else + qWarning() << "QTextOdfWriter: unsupported paragraph alignment; " << format.alignment(); + if (! value.isNull()) + writer.writeAttribute(foNS, QString::fromLatin1("text-align"), value); + } + + if (format.hasProperty(QTextFormat::BlockTopMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-top"), pixelToPoint(qMax(qreal(0.), format.topMargin())) ); + if (format.hasProperty(QTextFormat::BlockBottomMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-bottom"), pixelToPoint(qMax(qreal(0.), format.bottomMargin())) ); + if (format.hasProperty(QTextFormat::BlockLeftMargin) || format.hasProperty(QTextFormat::BlockIndent)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-left"), pixelToPoint(qMax(qreal(0.), + format.leftMargin() + format.indent()))); + if (format.hasProperty(QTextFormat::BlockRightMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-right"), pixelToPoint(qMax(qreal(0.), format.rightMargin())) ); + if (format.hasProperty(QTextFormat::TextIndent)) + writer.writeAttribute(foNS, QString::fromLatin1("text-indent"), pixelToPoint(format.textIndent())); + if (format.hasProperty(QTextFormat::PageBreakPolicy)) { + if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysBefore) + writer.writeAttribute(foNS, QString::fromLatin1("break-before"), QString::fromLatin1("page")); + if (format.pageBreakPolicy() & QTextFormat::PageBreak_AlwaysAfter) + writer.writeAttribute(foNS, QString::fromLatin1("break-after"), QString::fromLatin1("page")); + } + if (format.hasProperty(QTextFormat::BackgroundBrush)) { + QBrush brush = format.background(); + writer.writeAttribute(foNS, QString::fromLatin1("background-color"), brush.color().name()); + } + if (format.hasProperty(QTextFormat::BlockNonBreakableLines)) + writer.writeAttribute(foNS, QString::fromLatin1("keep-together"), + format.nonBreakableLines() ? QString::fromLatin1("true") : QString::fromLatin1("false")); + if (format.hasProperty(QTextFormat::TabPositions)) { + QList tabs = format.tabPositions(); + writer.writeStartElement(styleNS, QString::fromLatin1("tab-stops")); + QList::Iterator iterator = tabs.begin(); + while(iterator != tabs.end()) { + writer.writeEmptyElement(styleNS, QString::fromLatin1("tab-stop")); + writer.writeAttribute(styleNS, QString::fromLatin1("position"), pixelToPoint(iterator->position) ); + QString type; + switch(iterator->type) { + case QTextOption::DelimiterTab: type = QString::fromLatin1("char"); break; + case QTextOption::LeftTab: type = QString::fromLatin1("left"); break; + case QTextOption::RightTab: type = QString::fromLatin1("right"); break; + case QTextOption::CenterTab: type = QString::fromLatin1("center"); break; + } + writer.writeAttribute(styleNS, QString::fromLatin1("type"), type); + if (iterator->delimiter != 0) + writer.writeAttribute(styleNS, QString::fromLatin1("char"), iterator->delimiter); + ++iterator; + } + + writer.writeEndElement(); // tab-stops + } + + writer.writeEndElement(); // paragraph-properties + writer.writeEndElement(); // style +} + +void ODTWriter::writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFormat format, int formatIndex) const +{ + writer.writeStartElement(styleNS, QString::fromLatin1("style")); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("c%1").arg(formatIndex)); + writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("text")); + writer.writeEmptyElement(styleNS, QString::fromLatin1("text-properties")); + if (format.fontItalic()) + writer.writeAttribute(foNS, QString::fromLatin1("font-style"), QString::fromLatin1("italic")); + if (format.hasProperty(QTextFormat::FontWeight) && format.fontWeight() != QFont::Normal) { + QString value; + if (format.fontWeight() == QFont::Bold) + value = QString::fromLatin1("bold"); + else + value = QString::number(format.fontWeight() * 10); + writer.writeAttribute(foNS, QString::fromLatin1("font-weight"), value); + } + if (format.hasProperty(QTextFormat::FontFamily)) + writer.writeAttribute(foNS, QString::fromLatin1("font-family"), format.fontFamily()); + else + writer.writeAttribute(foNS, QString::fromLatin1("font-family"), conf->getFontFamily()); // default font + if (format.hasProperty(QTextFormat::FontPointSize)) + writer.writeAttribute(foNS, QString::fromLatin1("font-size"), QString::fromLatin1("%1pt").arg(format.fontPointSize())); + if (format.hasProperty(QTextFormat::FontCapitalization)) { + switch(format.fontCapitalization()) { + case QFont::MixedCase: + writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("none")); break; + case QFont::AllUppercase: + writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("uppercase")); break; + case QFont::AllLowercase: + writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("lowercase")); break; + case QFont::Capitalize: + writer.writeAttribute(foNS, QString::fromLatin1("text-transform"), QString::fromLatin1("capitalize")); break; + case QFont::SmallCaps: + writer.writeAttribute(foNS, QString::fromLatin1("font-variant"), QString::fromLatin1("small-caps")); break; + } + } + if (format.hasProperty(QTextFormat::FontLetterSpacing)) + writer.writeAttribute(foNS, QString::fromLatin1("letter-spacing"), pixelToPoint(format.fontLetterSpacing())); + if (format.hasProperty(QTextFormat::FontWordSpacing) && format.fontWordSpacing() != 0) + writer.writeAttribute(foNS, QString::fromLatin1("word-spacing"), pixelToPoint(format.fontWordSpacing())); + if (format.hasProperty(QTextFormat::FontUnderline)) + writer.writeAttribute(styleNS, QString::fromLatin1("text-underline-type"), + format.fontUnderline() ? QString::fromLatin1("single") : QString::fromLatin1("none")); + if (format.hasProperty(QTextFormat::FontOverline)) { + // bool fontOverline () const TODO + } + if (format.hasProperty(QTextFormat::FontStrikeOut)) + writer.writeAttribute(styleNS,QString::fromLatin1( "text-line-through-type"), + format.fontStrikeOut() ? QString::fromLatin1("single") : QString::fromLatin1("none")); + if (format.hasProperty(QTextFormat::TextUnderlineColor)) + writer.writeAttribute(styleNS, QString::fromLatin1("text-underline-color"), format.underlineColor().name()); + if (format.hasProperty(QTextFormat::FontFixedPitch)) { + // bool fontFixedPitch () const TODO + } + if (format.hasProperty(QTextFormat::TextUnderlineStyle)) { + QString value; + switch (format.underlineStyle()) { + case QTextCharFormat::NoUnderline: value = QString::fromLatin1("none"); break; + case QTextCharFormat::SingleUnderline: value = QString::fromLatin1("solid"); break; + case QTextCharFormat::DashUnderline: value = QString::fromLatin1("dash"); break; + case QTextCharFormat::DotLine: value = QString::fromLatin1("dotted"); break; + case QTextCharFormat::DashDotLine: value = QString::fromLatin1("dash-dot"); break; + case QTextCharFormat::DashDotDotLine: value = QString::fromLatin1("dot-dot-dash"); break; + case QTextCharFormat::WaveUnderline: value = QString::fromLatin1("wave"); break; + case QTextCharFormat::SpellCheckUnderline: value = QString::fromLatin1("none"); break; + } + writer.writeAttribute(styleNS, QString::fromLatin1("text-underline-style"), value); + } + if (format.hasProperty(QTextFormat::TextVerticalAlignment)) { + QString value; + switch (format.verticalAlignment()) { + case QTextCharFormat::AlignMiddle: + case QTextCharFormat::AlignBaseline: + case QTextCharFormat::AlignNormal: value = QString::fromLatin1("0%"); break; + case QTextCharFormat::AlignSuperScript: value = QString::fromLatin1("super"); break; + case QTextCharFormat::AlignSubScript: value = QString::fromLatin1("sub"); break; + case QTextCharFormat::AlignTop: value = QString::fromLatin1("100%"); break; + case QTextCharFormat::AlignBottom : value = QString::fromLatin1("-100%"); break; + } + writer.writeAttribute(styleNS, QString::fromLatin1("text-position"), value); + } + if (format.hasProperty(QTextFormat::TextOutline)) + writer.writeAttribute(styleNS, QString::fromLatin1("text-outline"), QString::fromLatin1("true")); + if (format.hasProperty(QTextFormat::TextToolTip)) { + // QString toolTip () const TODO + } + if (format.hasProperty(QTextFormat::IsAnchor)) { + // bool isAnchor () const TODO + } + if (format.hasProperty(QTextFormat::AnchorHref)) { + // QString anchorHref () const TODO + } + if (format.hasProperty(QTextFormat::AnchorName)) { + // QString anchorName () const TODO + } + if (format.hasProperty(QTextFormat::ForegroundBrush)) { + QBrush brush = format.foreground(); + writer.writeAttribute(foNS, QString::fromLatin1("color"), brush.color().name()); + } + if (format.hasProperty(QTextFormat::BackgroundBrush)) { + QBrush brush = format.background(); + writer.writeAttribute(foNS, QString::fromLatin1("background-color"), brush.color().name()); + } + + writer.writeEndElement(); +} + +void ODTWriter::writeListFormat(QXmlStreamWriter &writer, QTextListFormat format, int formatIndex) const +{ + writer.writeStartElement(textNS, QString::fromLatin1("list-style")); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("L%1").arg(formatIndex)); + + QTextListFormat::Style style = format.style(); + if (style == QTextListFormat::ListDecimal || style == QTextListFormat::ListLowerAlpha + || style == QTextListFormat::ListUpperAlpha + || style == QTextListFormat::ListLowerRoman + || style == QTextListFormat::ListUpperRoman) { + writer.writeStartElement(textNS, QString::fromLatin1("list-level-style-number")); + writer.writeAttribute(styleNS, QString::fromLatin1("num-format"), bulletChar(style)); + + if (format.hasProperty(QTextFormat::ListNumberSuffix)) + writer.writeAttribute(styleNS, QString::fromLatin1("num-suffix"), format.numberSuffix()); + else + writer.writeAttribute(styleNS, QString::fromLatin1("num-suffix"), QString::fromLatin1(".")); + + if (format.hasProperty(QTextFormat::ListNumberPrefix)) + writer.writeAttribute(styleNS, QString::fromLatin1("num-prefix"), format.numberPrefix()); + + } else { + writer.writeStartElement(textNS, QString::fromLatin1("list-level-style-bullet")); + writer.writeAttribute(textNS, QString::fromLatin1("bullet-char"), bulletChar(style)); + } + + writer.writeAttribute(textNS, QString::fromLatin1("level"), QString::number(format.indent())); + writer.writeEmptyElement(styleNS, QString::fromLatin1("list-level-properties")); + writer.writeAttribute(foNS, QString::fromLatin1("text-align"), QString::fromLatin1("start")); + QString spacing = QString::fromLatin1("%1mm").arg(format.indent() * 8); + writer.writeAttribute(textNS, QString::fromLatin1("space-before"), spacing); + //writer.writeAttribute(textNS, QString::fromLatin1("min-label-width"), spacing); + + writer.writeEndElement(); // list-level-style-* + writer.writeEndElement(); // list-style +} +void ODTWriter::writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat format, int formatIndex) const +{ + writer.writeStartElement(styleNS, QString::fromLatin1("style")); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("s%1").arg(formatIndex)); + writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("section")); + writer.writeEmptyElement(styleNS, QString::fromLatin1("section-properties")); + if (format.hasProperty(QTextFormat::BlockTopMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-top"), pixelToPoint(qMax(qreal(0.), format.topMargin())) ); + if (format.hasProperty(QTextFormat::BlockBottomMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-bottom"), pixelToPoint(qMax(qreal(0.), format.bottomMargin())) ); + if (format.hasProperty(QTextFormat::BlockLeftMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-left"), pixelToPoint(qMax(qreal(0.), format.leftMargin())) ); + if (format.hasProperty(QTextFormat::BlockRightMargin)) + writer.writeAttribute(foNS, QString::fromLatin1("margin-right"), pixelToPoint(qMax(qreal(0.), format.rightMargin())) ); + + writer.writeEndElement(); +} +void ODTWriter::writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat format, int formatIndex) const +{ + writer.writeStartElement(styleNS, QString::fromLatin1("style")); + writer.writeAttribute(styleNS, QString::fromLatin1("name"), QString::fromLatin1("T%1").arg(formatIndex)); + writer.writeAttribute(styleNS, QString::fromLatin1("family"), QString::fromLatin1("table")); + writer.writeEmptyElement(styleNS, QString::fromLatin1("table-properties")); + + qreal padding = format.topPadding(); + if(padding >0 && padding == format.bottomPadding() && padding == format.leftPadding() && padding == format.rightPadding()) + { + writer.writeAttribute(foNS, QString::fromLatin1("padding"), pixelToPoint(padding)); + } + else + { + if (padding > 0) + writer.writeAttribute(foNS, QString::fromLatin1("padding-top"), pixelToPoint(padding)); + if (format.bottomPadding() > 0) + writer.writeAttribute(foNS, QString::fromLatin1("padding-bottom"), pixelToPoint(format.bottomPadding())); + if (format.leftPadding() > 0) + writer.writeAttribute(foNS, QString::fromLatin1("padding-left"), pixelToPoint(format.leftPadding())); + if (format.rightPadding() > 0) + writer.writeAttribute(foNS, QString::fromLatin1("padding-right"), pixelToPoint(format.rightPadding())); + } + + if (format.hasProperty(QTextFormat::TextVerticalAlignment)) + { + QString pos; + switch(format.verticalAlignment()) + { + case QTextCharFormat::AlignMiddle: + pos = QString::fromLatin1("middle"); break; + case QTextCharFormat::AlignTop: + pos = QString::fromLatin1("top"); break; + case QTextCharFormat::AlignBottom: + pos = QString::fromLatin1("bottom"); break; + default: + pos = QString::fromLatin1("automatic"); break; + } + writer.writeAttribute(styleNS, QString::fromLatin1("vertical-align"), pos); + } + writer.writeEndElement(); +} +void ODTWriter::writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame) +{ + const QTextTable *table = qobject_cast(frame); + + if (table) + { + writer.writeStartElement(tableNS, QString::fromLatin1("table")); + writer.writeEmptyElement(tableNS, QString::fromLatin1("table-column")); + writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-repeated"), QString::number(table->columns())); + } else if (frame->document() && frame->document()->rootFrame() != frame){ + writer.writeStartElement(textNS, QString::fromLatin1("section")); + } + + QTextFrame::iterator iterator = frame->begin(); + QTextFrame *child = 0; + + int tableRow = -1; + while(!iterator.atEnd()) + { + if (iterator.currentFrame() && child!=iterator.currentFrame()) + writeFrame(writer, iterator.currentFrame()); + else + { + QTextBlock block = iterator.currentBlock(); + if(table) + { + QTextTableCell cell = table->cellAt(block.position()); + if(tableRow < cell.row()) + { + if (tableRow >= 0) + writer.writeEndElement(); + tableRow = cell.row(); + writer.writeStartElement(tableNS, QString::fromLatin1("table-row")); + } + + writer.writeStartElement(tableNS, QString::fromLatin1("table-cell")); + if(cell.columnSpan() > 1) + writer.writeAttribute(tableNS, QString::fromLatin1("number-columns-spanned"), QString::number(cell.columnSpan())); + if(cell.rowSpan()>1) + writer.writeAttribute(tableNS, QString::fromLatin1("number-rows-spanned"), QString::number(cell.rowSpan())); + if(cell.format().isTableCellFormat()) + writer.writeAttribute(tableNS, QString::fromLatin1("style-name"), QString::fromLatin1("T%1").arg(cell.tableCellFormatIndex())); + } + writeBlock(writer, block); + if(table) + writer.writeEndElement(); + } + child = iterator.currentFrame(); + ++iterator; + } + if (tableRow >= 0) + writer.writeEndElement(); + if (table || (frame->document() && frame->document()->rootFrame() != frame)) + writer.writeEndElement(); +} + +void ODTWriter::writeInlineCharacter(QXmlStreamWriter &writer, const QTextFragment &fragment) +{ + writer.writeStartElement(drawNS, QString::fromLatin1("frame")); + if (fragment.charFormat().isImageFormat()) + { + QTextImageFormat imageFormat = fragment.charFormat().toImageFormat(); + writer.writeAttribute(drawNS, QString::fromLatin1("name"), imageFormat.name()); + + QImage image; + QString name = imageFormat.name(); + if (name.startsWith(QLatin1String(":/"))) + name.prepend(QLatin1String("qrc")); + QUrl url = QUrl::fromEncoded(name.toUtf8()); + const QVariant data = document.resource(QTextDocument::ImageResource, url); + if (data.type() == QVariant::Image) + { + image = qvariant_cast(data); + } else if (data.type() == QVariant::ByteArray) + { + image.loadFromData(data.toByteArray()); + } + + if (image.isNull()) + { + name = imageFormat.name(); + image.load(name); + } + + if (!image.isNull()) + { + QBuffer imageBytes; + QImageWriter imageWriter(&imageBytes, "png"); + imageWriter.write(image); + const QString fileName = createUniquePictureName(); + addFile(fileName, QString::fromLatin1("image/png"), imageBytes.data()); + + qreal width = (imageFormat.hasProperty(QTextFormat::ImageWidth)) ? imageFormat.width() : image.width(); + writer.writeAttribute(svgNS, QString::fromLatin1("width"), pixelToPoint(width)); + qreal height = (imageFormat.hasProperty(QTextFormat::ImageHeight)) ? imageFormat.height() : image.height(); + writer.writeAttribute(svgNS, QString::fromLatin1("height"), pixelToPoint(height)); + + writer.writeStartElement(drawNS, QString::fromLatin1("image")); + writer.writeAttribute(xlinkNS, QString::fromLatin1("href"), fileName); + writer.writeEndElement(); + } + } + + writer.writeEndElement(); +} + +void ODTWriter::writeBufferToFile(QString &content, ZipWriter::CompressionPolicy cp, const QString &mimeType, const QString &fileName) +{ + zip->setCompressionPolicy(cp); + addFile(fileName, mimeType, content.toUtf8()); +} + +void ODTWriter::writeMimeType() +{ + zip->setCompressionPolicy(ZipWriter::NeverCompress); + zip->addFile(QString::fromLatin1("mimetype"), QByteArray("application/vnd.oasis.opendocument.text")); +} + +void ODTWriter::writeContent() +{ + writeBufferToFile(content, ZipWriter::AutoCompress, QString::fromLatin1("text/xml"), QString::fromLatin1("content.xml")); +} + +void ODTWriter::writeStyles() +{ + writeBufferToFile(styles, ZipWriter::AutoCompress, QString::fromLatin1("text/xml"), QString::fromLatin1("styles.xml")); +} + +void ODTWriter::writeMeta() +{ + writeBufferToFile(meta, ZipWriter::AutoCompress, QString::fromLatin1("text/xml"), QString::fromLatin1("meta.xml")); +} + +void ODTWriter::writeSettings() +{ + writeBufferToFile(settings, ZipWriter::AutoCompress, QString::fromLatin1("text/xml"), QString::fromLatin1("settings.xml")); +} + +void ODTWriter::writeManifest() +{ + writeBufferToFile(manifest, ZipWriter::AutoCompress, QString::fromLatin1("text/xml"), QString::fromLatin1("META-INF/manifest.xml")); +} + +QString ODTWriter::createUniquePictureName() +{ + return QString::fromLatin1("Pictures/Picture%1").arg(picCounter++); +} + +void ODTWriter::addFile(const QString &fileName, const QString &mimeType, const QByteArray &bytes) +{ + zip->addFile(fileName, bytes); + addFile(fileName, mimeType); +} + +void ODTWriter::addFile(const QString &fileName, const QString &mimeType) +{ + manifestWriter.writeEmptyElement(manifestNS, QString::fromLatin1("file-entry")); + manifestWriter.writeAttribute(manifestNS, QString::fromLatin1("media-type"), mimeType); + manifestWriter.writeAttribute(manifestNS, QString::fromLatin1("full-path"), fileName); +} + +void ODTWriter::writeNameSpaces(QXmlStreamWriter &writer) +{ + writer.writeNamespace(officeNS, QString::fromLatin1("office")); + writer.writeNamespace(metaNS, QString::fromLatin1("meta")); + writer.writeNamespace(configNS, QString::fromLatin1("config")); + writer.writeNamespace(textNS, QString::fromLatin1("text")); + writer.writeNamespace(tableNS, QString::fromLatin1("table")); + writer.writeNamespace(drawNS, QString::fromLatin1("draw")); + writer.writeNamespace(presentationNS, QString::fromLatin1("presentation")); + writer.writeNamespace(chartNS, QString::fromLatin1("chart")); + writer.writeNamespace(formNS, QString::fromLatin1("form")); + writer.writeNamespace(scriptNS, QString::fromLatin1("script")); + writer.writeNamespace(styleNS, QString::fromLatin1("style")); + writer.writeNamespace(numberNS, QString::fromLatin1("number")); + writer.writeNamespace(animNS, QString::fromLatin1("anim")); + writer.writeNamespace(dcNS, QString::fromLatin1("dc")); + writer.writeNamespace(xlinkNS, QString::fromLatin1("xlink")); + writer.writeNamespace(mathNS, QString::fromLatin1("math")); + writer.writeNamespace(xformNS, QString::fromLatin1("xform")); + writer.writeNamespace(foNS, QString::fromLatin1("fo")); + writer.writeNamespace(svgNS, QString::fromLatin1("svg")); + writer.writeNamespace(smilNS, QString::fromLatin1("smil")); +} diff --git a/src/MdCharm/util/odt/odtwriter.h b/src/MdCharm/util/odt/odtwriter.h new file mode 100644 index 0000000..6b02f28 --- /dev/null +++ b/src/MdCharm/util/odt/odtwriter.h @@ -0,0 +1,80 @@ +#ifndef ODTWRITER_H +#define ODTWRITER_H + +#include +#include +#include + +#include "../zip/zipwriter.h" + +class Configuration; +class QFile; +class QXmlStreamWriter; +class QTextBlock; +class QTextCharFormat; +class QTextListFormat; +class QTextFrameFormat; +class QTextTableCellFormat; +class QTextFrame; +class QTextFragment; +class QTextList; + +class ODTWriter +{ +public: + ODTWriter(const QTextDocument &document, const QString &fileName); + ~ODTWriter(); + + bool writeAll(); + + void writeBlock(QXmlStreamWriter &writer, const QTextBlock &block); + void writeFormats(QXmlStreamWriter &writer, QSet formats) const; + void writeBlockFormat(QXmlStreamWriter &writer, QTextBlockFormat format, int formatIndex) const; + void writeCharacterFormat(QXmlStreamWriter &writer, QTextCharFormat format, int formatIndex) const; + void writeListFormat(QXmlStreamWriter &writer, QTextListFormat format, int formatIndex) const; + void writeFrameFormat(QXmlStreamWriter &writer, QTextFrameFormat format, int formatIndex) const; + void writeTableCellFormat(QXmlStreamWriter &writer, QTextTableCellFormat, int formatIndex) const; + void writeFrame(QXmlStreamWriter &writer, const QTextFrame *frame); + void writeInlineCharacter(QXmlStreamWriter &writer, const QTextFragment &fragment); + + void writeMimeType(); + void writeContent(); + void writeStyles(); + void writeMeta(); + void writeSettings(); + void writeManifest(); + void writeBufferToFile(QString &content, ZipWriter::CompressionPolicy cp, const QString &mimeType, const QString &fileName); + + QString createUniquePictureName(); + void addFile(const QString &fileName, const QString &mimeType, const QByteArray &bytes); +private: + void addFile(const QString &fileName, const QString &mimeType); + void writeNameSpaces(QXmlStreamWriter &writer); +private: + QString content; + QString manifest; + QString meta; + QString styles; + QString settings; + + QXmlStreamWriter manifestWriter; + QXmlStreamWriter contentWriter; + QXmlStreamWriter metaWriter; + QXmlStreamWriter stylesWriter; + + QString officeNS, metaNS, configNS, textNS, tableNS, drawNS, presentationNS, + chartNS, formNS, scriptNS, styleNS, numberNS, animNS, dcNS, xlinkNS, + mathNS, xformNS,foNS, svgNS, smilNS; + QString manifestNS; + + ZipWriter *zip; + const QTextDocument &document; + + QStack m_listStack; + + Configuration *conf; + + int picCounter; +}; + +#endif // ODTWRITER_H diff --git a/src/MdCharm/util/rotationtoolbutton.cpp b/src/MdCharm/util/rotationtoolbutton.cpp new file mode 100644 index 0000000..7cafed0 --- /dev/null +++ b/src/MdCharm/util/rotationtoolbutton.cpp @@ -0,0 +1,79 @@ +#include "rotationtoolbutton.h" + +#include +#include + +static const int Vertical_Mask = 0x02; + +RotationToolButton::RotationToolButton(QWidget *parent) : + QToolButton(parent), rot(NoRotation) +{ +} + +void RotationToolButton::setRotation(Rotation rotation) +{ + if(rot != rotation){ + rot = rotation; + switch (rotation) { + case NoRotation: + case UpsideDown: + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case Clockwise: + case CounterClockwise: + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); + break; + default: + break; + } + updateGeometry(); + update(); + } +} + +RotationToolButton::Rotation RotationToolButton::getRotation() const +{ + return rot; +} + +QSize RotationToolButton::sizeHint() const +{ + QSize size = QToolButton::sizeHint(); + + if(rot & Vertical_Mask) + size.transpose(); + return size; +} + +QSize RotationToolButton::minimumSizeHint() const +{ + return sizeHint(); +} + +void RotationToolButton::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.rotate(rot); + switch (rot) { + case UpsideDown: + painter.translate(-width(), -height()); + break; + case Clockwise: + painter.translate(0, -width()); + break; + case CounterClockwise: + painter.translate(-height(), 0); + break; + default: + break; + } + + QStyleOptionToolButton opt; + initStyleOption(&opt); + if(rot & Vertical_Mask){ + QSize size = opt.rect.size(); + size.transpose(); + opt.rect.setSize(size); + } + painter.drawComplexControl(QStyle::CC_ToolButton, opt); +} diff --git a/src/MdCharm/util/rotationtoolbutton.h b/src/MdCharm/util/rotationtoolbutton.h new file mode 100644 index 0000000..05a690d --- /dev/null +++ b/src/MdCharm/util/rotationtoolbutton.h @@ -0,0 +1,33 @@ +#ifndef ROTATIONTOOLBUTTON_H +#define ROTATIONTOOLBUTTON_H + +#include + +class RotationToolButton : public QToolButton +{ + Q_OBJECT +public: + enum Rotation { + NoRotation = 0, + UpsideDown = 180, + Clockwise = 90, + CounterClockwise = 270 + }; + +public: + explicit RotationToolButton(QWidget *parent = 0); + void setRotation(Rotation rotation); + Rotation getRotation() const; + virtual QSize sizeHint() const; + virtual QSize minimumSizeHint() const; +protected: + void paintEvent(QPaintEvent *); +private: + Rotation rot; +signals: + +public slots: + +}; + +#endif // ROTATIONTOOLBUTTON_H diff --git a/src/MdCharm/util/sessionfileparser.cpp b/src/MdCharm/util/sessionfileparser.cpp new file mode 100644 index 0000000..316a07d --- /dev/null +++ b/src/MdCharm/util/sessionfileparser.cpp @@ -0,0 +1,122 @@ +#include "sessionfileparser.h" +#include "utils.h" + +#include +#include + +using namespace rapidxml; + +SessionFileParser::SessionFileParser() +{ + viewCount = 0; +} + +QList SessionFileParser::getFileStateModelList() +{ + return sml; +} + +const QString SessionFileParser::getProjectDockWidgetDirPath() +{ + return projectDockWidgetDirPath; +} + +bool SessionFileParser::startParse(const QString &sessionXmlFilePath) +{ + QFile file(sessionXmlFilePath); + if(!file.open(QFile::ReadOnly)) + return false; + + QByteArray fileContent = file.readAll(); + xml_document<> doc; + doc.parse<0>(fileContent.data());//TODO: Why file.readAll().data() not work + xml_node<> *node = doc.first_node(); + while(node){ + if(0==strcmp(node->name(), Utils::AppNameCStr)) + parseMdCharmElement(node); + node = node->next_sibling(); + } + file.close(); + return true; +} + +void SessionFileParser::parseMdCharmElement(xml_node<> *mdcharmNode) +{ + xml_node<> *node = mdcharmNode->first_node(); + while(node){ + if(0==strcmp(node->name(), "Session")) + parseSessionElement(node); + node = node->next_sibling(); + } +} + +void SessionFileParser::parseSessionElement(xml_node<> *sessionNode) +{ + xml_node<> *node = sessionNode->first_node(); + while(node){ + if(0==strcmp(node->name(), "MainView")){ + parseMainViewElement(node); + viewCount++; + } else if (0==strcmp(node->name(), "DockWidget")) + parseDockWidgetElement(node); + node = node->next_sibling(); + } +} + +void SessionFileParser::parseMainViewElement(xml_node<> *mainViewNode) +{ + xml_node<> *node = mainViewNode->first_node(); + while(node){ + if(0==strcmp(node->name(), "File")) + parseFileElement(node); + node = node->next_sibling(); + } +} + +void SessionFileParser::parseDockWidgetElement(xml_node<> *dockWidgetNode) +{ + xml_node<> *node = dockWidgetNode->first_node(); + while(node){ + if(0==strcmp(node->name(), "Project")) + parseProjectElement(node); + node = node->next_sibling(); + } +} + +void SessionFileParser::parseFileElement(xml_node<> *fileNode) +{ + StateModel sm; + xml_attribute<> *attr = fileNode->first_attribute(); + while(attr){ + if(0==strcmp(attr->name(), "isCurrentTab")) + if(0==strcmp(attr->value(), "1")) + sm.setIsCurrentTab(true); + else + sm.setIsCurrentTab(false); + else if(0==strcmp(attr->name(), "firstVisibleLine")) + sm.setFirstVisibleLine(atoi(attr->value())); + else if(0==strcmp(attr->name(), "selectionStart")) + sm.setSelectionStart(atoi(attr->value())); + else if(0==strcmp(attr->name(), "selectionEnd")) + sm.setSelectionEnd(atoi(attr->value())); + else if(0==strcmp(attr->name(), "verticalScrollBarCurrentValue")) + sm.setVerticalScrllBarCurrentValue(atoi(attr->value())); + else if(0==strcmp(attr->name(), "verticalScrollBarMaxValue")) + sm.setVerticalScrollBarMaxValue(atoi(attr->value())); + else if(0==strcmp(attr->name(), "filename")) + sm.setFileFullPath(attr->value()); + attr = attr->next_attribute(); + } + sm.setViewNum(viewCount); + sml.append(sm); +} + +void SessionFileParser::parseProjectElement(xml_node<> *projectNode) +{ + xml_attribute<> *attr = projectNode->first_attribute(); + while(attr){ + if(0==strcmp(attr->name(), "dirPath")) + projectDockWidgetDirPath = QString::fromUtf8(attr->value()); + attr = attr->next_attribute(); + } +} diff --git a/src/MdCharm/util/sessionfileparser.h b/src/MdCharm/util/sessionfileparser.h new file mode 100644 index 0000000..b838268 --- /dev/null +++ b/src/MdCharm/util/sessionfileparser.h @@ -0,0 +1,35 @@ +#ifndef SESSIONFILEPARSER_H +#define SESSIONFILEPARSER_H + +#include + +#include "editareawidget.h" +#include "rapidxml.hpp" + +/******************************************************************************* + * Session activeView index start from 0 + * MainView activeIndex index start from 0 + * + ******************************************************************************/ +class SessionFileParser +{ +public: + SessionFileParser(); + bool startParse(const QString &sessionXmlFilePath); + QList getFileStateModelList(); + const QString getProjectDockWidgetDirPath(); +private: + void parseMdCharmElement(rapidxml::xml_node<> *mdcharmNode); + void parseSessionElement(rapidxml::xml_node<> *sessionNode); + void parseMainViewElement(rapidxml::xml_node<> *mainViewNode); + void parseFileElement(rapidxml::xml_node<> *fileNode); + void parseDockWidgetElement(rapidxml::xml_node<> *dockWidgetNode); + void parseProjectElement(rapidxml::xml_node<> *projectNode); +private: + QList sml; + QXmlStreamReader reader; + QString projectDockWidgetDirPath; + int viewCount; +}; + +#endif // SESSIONFILEPARSER_H diff --git a/src/MdCharm/util/spellcheck/spellchecker.cpp b/src/MdCharm/util/spellcheck/spellchecker.cpp new file mode 100644 index 0000000..9e235a0 --- /dev/null +++ b/src/MdCharm/util/spellcheck/spellchecker.cpp @@ -0,0 +1,239 @@ +#include "spellchecker.h" +#include "utils.h" +#include "configuration.h" +#ifdef Q_OS_WIN +#include "hunspell.hxx" +#else +#include "hunspell/hunspell.hxx" +#endif + +#include +#include +#include +#include +#include + +SpellCheckTokenizer::SpellCheckTokenizer(const QString *text) +{ + this->text = text; + boundaryFinder = QTextBoundaryFinder(QTextBoundaryFinder::Word, *text); + position = 0; + startWord = -1; +#ifdef QT_V5 + if(boundaryFinder.boundaryReasons() & QTextBoundaryFinder::StartOfItem) +#else + if(boundaryFinder.boundaryReasons() & QTextBoundaryFinder::StartWord) +#endif + startWord = 0; +} + +bool SpellCheckTokenizer::hasNextWord() +{ + nextWord.clear(); + while(true){ + position = boundaryFinder.toNextBoundary(); + if(position==-1) + return false; + + const QTextBoundaryFinder::BoundaryReasons reasons = boundaryFinder.boundaryReasons(); +#ifdef QT_V5 + if(reasons & QTextBoundaryFinder::EndOfItem){ +#else + if(reasons & QTextBoundaryFinder::EndWord){ +#endif + if(startWord==-1) + continue; + //skip empty words + if(position==startWord) + continue; + nextWord = text->mid(startWord, position-startWord); + start=startWord; + end=position; +#ifdef QT_V5 + if(reasons & QTextBoundaryFinder::StartOfItem) +#else + if(reasons & QTextBoundaryFinder::StartWord) +#endif + startWord = position; + else + startWord = -1; + return true; + } +#ifdef QT_V5 + if(reasons & QTextBoundaryFinder::StartOfItem) +#else + if(reasons & QTextBoundaryFinder::StartWord) +#endif + startWord = position; + } + return false; +} + +QString SpellCheckTokenizer::getNextWord() const +{ + return nextWord; +} + +int SpellCheckTokenizer::nextWordStart() const +{ + return start; +} + +int SpellCheckTokenizer::nextWordEnd() const +{ + return end; +} + +//SpellChecker *SpellChecker::spellCheckInstance = NULL; + +//SpellChecker* SpellChecker::getInstance() +//{ +// Configuration *conf = Configuration::getInstance(); +// if(!spellCheckInstance && conf->isCheckSpell()){ +// QString filePath = conf->getLanguageSpellCheckDictPath(conf->getSpellCheckLanguage()); +// if(!filePath.isEmpty()){ +// QString withoutSuffixFilePath = filePath.left(filePath.lastIndexOf('.')); +// qDebug(withoutSuffixFilePath.toAscii()); +// spellCheckInstance = new SpellChecker(withoutSuffixFilePath, conf->getLanguageSpellCheckUserDictPath(), conf->getSpellCheckLanguage()); +// } else { +// conf->setCheckSpell(false); +// conf->setSpellCheckLanguage(QString()); +// spellCheckInstance = NULL; +// } +// } +// return spellCheckInstance; +//} + +//void SpellChecker::reload() +//{ +// Configuration *conf = Configuration::getInstance(); + +// if(conf->isCheckSpell()){ +// if(spellCheckInstance!=NULL&&spellCheckInstance->getLan()!=conf->getSpellCheckLanguage()){ +// delete spellCheckInstance; +// spellCheckInstance = NULL; +// } +// getInstance(); +// return; +// }else{ +// delete spellCheckInstance; +// spellCheckInstance = NULL; +// return; +// } +//} + +SpellChecker::SpellChecker(const QString &dictionaryPath, const QString &userDictionary, const QString &lan) +{ + this->lan = lan; + this->userDictionary = userDictionary; + QString dictFilePath = dictionaryPath + ".dic"; + QString affixFilePath = dictionaryPath + ".aff"; + QByteArray dictFilePathBA = dictFilePath.toLocal8Bit(); + QByteArray affixFilePathBA = affixFilePath.toLocal8Bit(); + hunspell = new Hunspell(affixFilePathBA.constData(), dictFilePathBA.constData()); + + encoding = "ISO8859-1"; + QFile affixFile(affixFilePath); + if(affixFile.open(QIODevice::ReadOnly)) { + QTextStream stream(&affixFile); + QRegExp enc_detector("^\\s*SET\\s+([A-Z0-9\\-]+)\\s*", Qt::CaseInsensitive); + for(QString line = stream.readLine(); !line.isEmpty(); line=stream.readLine()) { + if(enc_detector.indexIn(line) > -1) { + encoding = enc_detector.cap(1); + qDebug("Encoding set to %s", encoding.toLocal8Bit().constData()); + break; + } + } + affixFile.close(); + } + codec = QTextCodec::codecForName(encoding.toLatin1().constData()); + if(userDictionary.isEmpty()) { + QFile userDictionaryFile(userDictionary); + if(userDictionaryFile.open(QIODevice::ReadOnly)) { + QTextStream stream(&userDictionaryFile); + for(QString word=stream.readLine(); !word.isEmpty(); word=stream.readLine()) + put_word(word); + userDictionaryFile.close(); + } else { + qWarning("User dictionary in %s could not be opened", userDictionary.toLocal8Bit().constData()); + } + } else { + qDebug("User dictionary not set."); + } +} + +SpellChecker::~SpellChecker() +{ + delete hunspell; +} + +bool SpellChecker::spell(const QString &word) +{ + return hunspell->spell(codec->fromUnicode(word).constData()) != 0; +} + +SpellCheckResultList SpellChecker::checkString(const QString &text) +{ + SpellCheckResultList resultList; + if (text.isEmpty()) + return resultList;//Found 0 error + SpellCheckTokenizer tokenizer(&text); + while(tokenizer.hasNextWord()){ + QString word = tokenizer.getNextWord(); + if(!Utils::isLetterOrNumberString(word)) + continue; + //only accept alhpanumeric string + if(!hunspell->spell(codec->fromUnicode(word))){ + SpellCheckResult result; + result.start = tokenizer.nextWordStart(); + result.end = tokenizer.nextWordEnd(); + resultList.append(result); + } + } + return resultList; +} + +QStringList SpellChecker::suggest(const QString &word) +{ + char **suggestWordList; + + int numSuggestions = hunspell->suggest(&suggestWordList, codec->fromUnicode(word).constData()); + QStringList suggestions; + for(int i=0; itoUnicode(suggestWordList[i]); + free(suggestWordList[i]); + } + return suggestions; +} + +void SpellChecker::ignoreWord(const QString &word) +{ + put_word(word); +} + +void SpellChecker::put_word(const QString &word) +{ + hunspell->add(codec->fromUnicode(word).constData()); +} + +void SpellChecker::addToUserWordlist(const QString &word) +{ + put_word(word); + if(userDictionary.isEmpty()) { + QFile userDictionaryFile(userDictionary); + if(userDictionaryFile.open(QIODevice::Append)) { + QTextStream stream(&userDictionaryFile); + stream << word << "\n"; + userDictionaryFile.close(); + } else { + qWarning("User dictionary in %s could not be opened for appending a new word", userDictionary.toLocal8Bit().constData()); + } + } else { + qDebug("User dictionary not set."); + } +} + +QString SpellChecker::getLan() +{ + return lan; +} diff --git a/src/MdCharm/util/spellcheck/spellchecker.h b/src/MdCharm/util/spellcheck/spellchecker.h new file mode 100644 index 0000000..821cec7 --- /dev/null +++ b/src/MdCharm/util/spellcheck/spellchecker.h @@ -0,0 +1,60 @@ +#ifndef SPELLCHECKER_H +#define SPELLCHECKER_H + +#include +#include + +class Hunspell; + +struct SpellCheckResult +{ + int start; + int end; +}; + +typedef QList SpellCheckResultList; + +class SpellCheckTokenizer +{ +public: + SpellCheckTokenizer(const QString *text); + + bool hasNextWord(); + QString getNextWord() const; + int nextWordStart() const; + int nextWordEnd() const; + +private: + const QString *text; + QTextBoundaryFinder boundaryFinder; + int position; + int startWord; + int start, end; + QString nextWord; +}; + +class SpellChecker +{ +public: + SpellChecker(const QString &dictionaryPath, const QString &userDictionary, const QString &lan); + ~SpellChecker(); + + bool spell(const QString &word); + SpellCheckResultList checkString(const QString &text); + QStringList suggest(const QString &word); + void ignoreWord(const QString &word); + void addToUserWordlist(const QString &word); + QString getLan(); + +private: + +private: + void put_word(const QString &word); + Hunspell *hunspell; + QString userDictionary; + QString encoding; + QTextCodec *codec; + QString lan; +}; + +#endif diff --git a/src/MdCharm/util/spellcheck/spellcheckselectordialog.cpp b/src/MdCharm/util/spellcheck/spellcheckselectordialog.cpp new file mode 100644 index 0000000..5e31969 --- /dev/null +++ b/src/MdCharm/util/spellcheck/spellcheckselectordialog.cpp @@ -0,0 +1,53 @@ +#include + +#include "spellcheckselectordialog.h" +#include "ui_spellcheckselectordialog.h" + +#include "configuration.h" + +SpellCheckSelectorDialog::SpellCheckSelectorDialog(QWidget *parent, Qt::WindowFlags f) : + QDialog(parent, f), + ui(new Ui::SpellCheckSelectorDialog) +{ + ui->setupUi(this); + Configuration *conf = Configuration::getInstance(); + const QStringList sl = conf->getAllAvailableSpellCheckDictNames(); + int index = -1; + QLocale locale; + for(int i=0; ilanguageComboBox->addItem("English - United Kingdom", dictName); + } else if(dictName=="en_US"){ + ui->languageComboBox->addItem("English - United States", dictName); + } else { + ui->languageComboBox->addItem(dictName, dictName); + } + if(dictName==locale.name()) + index = i; + } + if(index==-1 && sl.length()>=1){ + ui->languageComboBox->setCurrentIndex(0); + } + + connect(ui->buttonBox, SIGNAL(rejected()), + this, SLOT(close())); + connect(ui->buttonBox, SIGNAL(accepted()), + this, SLOT(acceptSlot())); +} + +void SpellCheckSelectorDialog::acceptSlot() +{ + language = ui->languageComboBox->itemData(ui->languageComboBox->currentIndex()).toString(); + accept();; +} + +QString SpellCheckSelectorDialog::getSpellCheckLanguageName() +{ + return language; +} + +SpellCheckSelectorDialog::~SpellCheckSelectorDialog() +{ + delete ui; +} diff --git a/src/MdCharm/util/spellcheck/spellcheckselectordialog.h b/src/MdCharm/util/spellcheck/spellcheckselectordialog.h new file mode 100644 index 0000000..4de3cbd --- /dev/null +++ b/src/MdCharm/util/spellcheck/spellcheckselectordialog.h @@ -0,0 +1,27 @@ +#ifndef SPELLCHECKSELECTORDIALOG_H +#define SPELLCHECKSELECTORDIALOG_H + +#include + +namespace Ui { +class SpellCheckSelectorDialog; +} + +class SpellCheckSelectorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit SpellCheckSelectorDialog(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowTitleHint|Qt::WindowSystemMenuHint); + ~SpellCheckSelectorDialog(); + QString getSpellCheckLanguageName(); + +public slots: + void acceptSlot(); + +private: + Ui::SpellCheckSelectorDialog *ui; + QString language; +}; + +#endif // SPELLCHECKSELECTORDIALOG_H diff --git a/src/MdCharm/util/spellcheck/spellcheckselectordialog.ui b/src/MdCharm/util/spellcheck/spellcheckselectordialog.ui new file mode 100644 index 0000000..626abe3 --- /dev/null +++ b/src/MdCharm/util/spellcheck/spellcheckselectordialog.ui @@ -0,0 +1,81 @@ + + + SpellCheckSelectorDialog + + + + 0 + 0 + 235 + 69 + + + + Select Language + + + + + + + + Language: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + + buttonBox + accepted() + SpellCheckSelectorDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SpellCheckSelectorDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/MdCharm/util/syntax/hightlighter.cpp b/src/MdCharm/util/syntax/hightlighter.cpp new file mode 100644 index 0000000..e582715 --- /dev/null +++ b/src/MdCharm/util/syntax/hightlighter.cpp @@ -0,0 +1,139 @@ +#include + +#include + +#include "hightlighter.h" +#include "util/test/qregularexpression.h" + +HighLighter::HighLighter(QTextDocument *parent) : + QSyntaxHighlighter(parent) +{ +} + +HighLighter::HighLighter(QTextEdit *parent) : + QSyntaxHighlighter(parent) +{ + +} + +void HighLighter::addTextCharFormat(const int weight, const QColor color, bool isItalic, + const QString ®Exp) +{ + HighlightingRule rule; + QTextCharFormat format; + if(weight!=QFont::Normal) + format.setFontWeight(weight); + if(color.isValid()) + format.setForeground(color); + if(isItalic) + format.setFontItalic(isItalic); + rule.pattern = regExp; + rule.format = format; + highlightingRules.append(rule); +} + +void HighLighter::highlightBlock(const QString &text) +{ + foreach(const HighlightingRule &rule, highlightingRules) + { + QRegularExpression re(rule.pattern, QRegularExpression::DotMatchesEverythingOption); +#ifdef MDCHARM_DEBUG + if(!re.isValid()) + { + qDebug(rule.pattern.toLatin1()); + } +#endif + QRegularExpressionMatchIterator remi = re.globalMatch(text); + while(remi.hasNext()) + { + QRegularExpressionMatch rem = remi.next(); + setFormat(rem.capturedStart(), rem.capturedLength(), rule.format); + } + } + + setCurrentBlockState(0); + highlightMultiLine(text); +} + +void HighLighter::highlightMultiLine(const QString &text) +{ + Q_UNUSED(text) + return; +} + +MarkdownHighLighter::MarkdownHighLighter(QTextDocument *parent) : + HighLighter(parent) +{ + //html tags + addTextCharFormat(QFont::Bold, Qt::darkMagenta, false, QString::fromLatin1("<[^<>@]*>")); + //html symbols + addTextCharFormat(QFont::Bold, Qt::darkCyan, false, QString::fromLatin1("&[^; ]*;")); + //quote inside tag + addTextCharFormat(QFont::Bold, Qt::darkYellow, false, QString::fromLatin1("\"[^\"<]*\"(?=[^<]*>)")); + //html comment + addTextCharFormat(QFont::Bold, Qt::gray, false, QString::fromLatin1("")); + //italic + addTextCharFormat(QFont::Normal, Qt::darkCyan, true, QString::fromLatin1("(\\s|^)[\\*_]{1}[^\\s]{1}[^\\*_]+[\\*_]{1}(\\s|\\.|,|;|:|\\-|\\?|$)")); + //bold + addTextCharFormat(QFont::Bold, Qt::darkCyan, false, QString::fromLatin1("(\\s|^)[\\*_]{2}[^\\s]{1}[^\\*_]+[\\*_]{2}(\\s|\\.|,|;|:|\\-|\\?|$)")); + //bold and italic + addTextCharFormat(QFont::Bold, Qt::darkCyan, true, QString::fromLatin1("(\\s|^)[\\*_]{3}[^\\*_]+[\\*_]{3}(\\s|\\.|,|;|:|\\-|\\?|$)")); + //link and images + addTextCharFormat(QFont::Normal, Qt::blue, false, QString::fromLatin1("(?<=\\[)[^\\[\\]]*(?=\\])")); + addTextCharFormat(QFont::Normal, Qt::blue, false, QString::fromLatin1("(?<=\\]\\()[^\\(\\)]*(?=\\))")); + //blockquote + addTextCharFormat(QFont::Normal, Qt::darkGray, false, QString::fromLatin1("^\\s{0,3}>")); + //header + addTextCharFormat(QFont::Bold, Qt::darkMagenta, false, QString::fromLatin1("^#.*$")); + addTextCharFormat(QFont::Bold, Qt::darkMagenta, false, QString::fromLatin1("^==+$")); + //bullet + addTextCharFormat(QFont::Normal, Qt::darkRed, false, QString::fromLatin1("^[\\*\\+\\-]\\s")); + //code + addTextCharFormat(QFont::Normal, Qt::darkBlue, false, QString::fromLatin1("^([\\s]{4,}|\\t+).*$")); +} + +MarkdownHighLighter::~MarkdownHighLighter() +{ +} + +CSSHighLighter::CSSHighLighter(QTextDocument *parent) : + HighLighter(parent) +{ + addTextCharFormat(QFont::Normal, Qt::red, false, QString::fromLatin1("-?[A-Za-z_-]+(?=\\s*:)")); + //id + addTextCharFormat(QFont::Normal, Qt::darkCyan, false, QString::fromLatin1("#([a-zA-Z0-9\\-_]|[\\x80-\\xFF]|\\\\[0-9A-Fa-f]{1,6})*")); + //class + addTextCharFormat(QFont::Normal, Qt::darkCyan, false, QString::fromLatin1("(?<= |^)\\.([a-zA-Z0-9\\-_]|[\\x80-\\xFF]|\\\\[0-9A-Fa-f]{1,6})*")); + //number + addTextCharFormat(QFont::Normal, Qt::darkBlue, false, QString::fromLatin1("(?<=:)[ 0-9.%]*")); + //value + addTextCharFormat(QFont::Normal, Qt::darkGreen, false, QString::fromLatin1("[-+]?[0-9.]+(em|ex|ch|rem|vw|vh|vm|px|in|cm|mm|pt|pc|deg|rad|grad|turn|ms|s|Hz|kHz)\\b")); + + commentsFormat.setForeground(Qt::darkGreen); + commentsStart = QRegExp("/\\*"); + commentsEnd = QRegExp("\\*/"); +} + +void CSSHighLighter::highlightMultiLine(const QString &text) +{ + int startIndex = 0; + if(previousBlockState() != 1) + startIndex = commentsStart.indexIn(text); + while (startIndex >= 0) + { + int endIndex = commentsEnd.indexIn(text, startIndex); + int commentLength; + if (endIndex == -1) + { + setCurrentBlockState(1); + commentLength = text.length() - startIndex; + } + else + { + commentLength = endIndex - startIndex + + commentsEnd.matchedLength(); + } + setFormat(startIndex, commentLength, commentsFormat); + startIndex = commentsStart.indexIn(text, startIndex + commentLength); + } +} diff --git a/src/MdCharm/util/syntax/hightlighter.h b/src/MdCharm/util/syntax/hightlighter.h new file mode 100644 index 0000000..586801d --- /dev/null +++ b/src/MdCharm/util/syntax/hightlighter.h @@ -0,0 +1,54 @@ +#ifndef HIGHTLIGHTER_H +#define HIGHTLIGHTER_H + +#include + +#ifdef QT_V5 +#include +#endif + +class QTextDocument; + +struct HighlightingRule +{ + QString pattern; + QTextCharFormat format; +}; + +class HighLighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + HighLighter(QTextDocument *parent); + HighLighter(QTextEdit *parent); + void addTextCharFormat(const int weight, const QColor color, bool isItalic, + const QString ®Exp); +protected: + virtual void highlightBlock(const QString &text); + virtual void highlightMultiLine(const QString &text); +protected: + QVector highlightingRules; +}; + +class MarkdownHighLighter: public HighLighter +{ + Q_OBJECT +public: + MarkdownHighLighter(QTextDocument *parent = 0); + ~MarkdownHighLighter(); +}; + +class CSSHighLighter: public HighLighter +{ + Q_OBJECT +public: + CSSHighLighter(QTextDocument *parent = 0); +protected: + virtual void highlightMultiLine(const QString &text); +private: + QTextCharFormat commentsFormat; + QRegExp commentsStart; + QRegExp commentsEnd; +}; + +#endif // HIGHTLIGHTER_H diff --git a/src/MdCharm/util/test/qregularexpression.cpp b/src/MdCharm/util/test/qregularexpression.cpp new file mode 100644 index 0000000..da11734 --- /dev/null +++ b/src/MdCharm/util/test/qregularexpression.cpp @@ -0,0 +1,1620 @@ +#include "qregularexpression.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +// after how many usages we optimize the regexp +static const unsigned int qt_qregularexpression_optimize_after_use_count = 10; + +/*! + \internal +*/ +static int convertToPcreOptions(QRegularExpression::PatternOptions patternOptions) +{ + int options = 0; + + if (patternOptions & QRegularExpression::CaseInsensitiveOption) + options |= PCRE_CASELESS; + if (patternOptions & QRegularExpression::DotMatchesEverythingOption) + options |= PCRE_DOTALL; + if (patternOptions & QRegularExpression::MultilineOption) + options |= PCRE_MULTILINE; + if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) + options |= PCRE_EXTENDED; + if (patternOptions & QRegularExpression::InvertedGreedinessOption) + options |= PCRE_UNGREEDY; + if (patternOptions & QRegularExpression::DontCaptureOption) + options |= PCRE_NO_AUTO_CAPTURE; + if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) + options |= PCRE_UCP; + + return options; +} + +/*! + \internal +*/ +static int convertToPcreOptions(QRegularExpression::MatchOptions matchOptions) +{ + int options = 0; + + if (matchOptions & QRegularExpression::AnchoredMatchOption) + options |= PCRE_ANCHORED; + + return options; +} + +struct QRegularExpressionPrivate : QSharedData +{ + QRegularExpressionPrivate(); + ~QRegularExpressionPrivate(); + QRegularExpressionPrivate(const QRegularExpressionPrivate &other); + + void cleanCompiledPattern(); + void compilePattern(); + void getPatternInfo(); + pcre16_extra *optimizePattern(); + + QRegularExpressionMatchPrivate *doMatch(const QString &subject, + int offset, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + bool checkSubjectString = true, + const QRegularExpressionMatchPrivate *previous = 0) const; + + int captureIndexForName(const QString &name) const; + + QString pattern; + QRegularExpression::PatternOptions patternOptions; + + // *All* of the following members are set managed while holding this mutex, + // except for isDirty which is set to true by QRegularExpression setters + // (right after a detach happened). + // On the other hand, after the compilation and studying, + // it's safe to *use* (i.e. read) them from multiple threads at the same time. + // Therefore, doMatch doesn't need to lock this mutex. + QMutex mutex; + + // The PCRE pointers are reference-counted by the QRegularExpressionPrivate + // objects themselves; when the private is copied (i.e. a detach happened) + // they are set to 0 + pcre16 *compiledPattern; + pcre16_extra *studyData; + const char *errorString; + int errorOffset; + int capturingCount; + unsigned int usedCount; + bool usingCrLfNewlines; + bool isDirty; +}; + +struct QRegularExpressionMatchPrivate : QSharedData +{ + QRegularExpressionMatchPrivate(const QRegularExpression &re, + const QString &subject, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + int capturingCount); + + QRegularExpressionMatch nextMatch() const; + + const QRegularExpression regularExpression; + const QString subject; + // the capturedOffsets vector contains pairs of (start, end) positions + // for each captured substring + QVector capturedOffsets; + + const QRegularExpression::MatchType matchType; + const QRegularExpression::MatchOptions matchOptions; + + int capturedCount; + + bool hasMatch; + bool hasPartialMatch; + bool isValid; +}; + +struct QRegularExpressionMatchIteratorPrivate : QSharedData +{ + QRegularExpressionMatchIteratorPrivate(const QRegularExpression &re, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + const QRegularExpressionMatch &next); + + bool hasNext() const; + QRegularExpressionMatch next; + const QRegularExpression regularExpression; + const QRegularExpression::MatchType matchType; + const QRegularExpression::MatchOptions matchOptions; +}; + +/*! + \internal +*/ +QRegularExpression::QRegularExpression(QRegularExpressionPrivate &dd) + : d(&dd) +{ +} + +/*! + \internal +*/ +QRegularExpressionPrivate::QRegularExpressionPrivate() + : pattern(), patternOptions(0), + mutex(), + compiledPattern(0), studyData(0), + errorString(0), errorOffset(-1), + capturingCount(0), + usedCount(0), + usingCrLfNewlines(false), + isDirty(true) +{ +} + +/*! + \internal +*/ +QRegularExpressionPrivate::~QRegularExpressionPrivate() +{ + cleanCompiledPattern(); +} + +/*! + \internal + + Copies the private, which means copying only the pattern and the pattern + options. The compiledPattern and the studyData pointers are NOT copied (we + do not own them any more), and in general all the members set when + compiling a pattern are set to default values. isDirty is set back to true + so that the pattern has to be recompiled again. +*/ +QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPrivate &other) + : QSharedData(other), + pattern(other.pattern), patternOptions(other.patternOptions), + mutex(), + compiledPattern(0), studyData(0), + errorString(0), + errorOffset(-1), capturingCount(0), + usedCount(0), + usingCrLfNewlines(false), isDirty(true) +{ +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::cleanCompiledPattern() +{ + pcre16_free(compiledPattern); + pcre16_free_study(studyData); + usedCount = 0; + compiledPattern = 0; + studyData = 0; + usingCrLfNewlines = false; + errorOffset = -1; + capturingCount = 0; +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::compilePattern() +{ + QMutexLocker lock(&mutex); + + if (!isDirty) + return; + + isDirty = false; + cleanCompiledPattern(); + + int options = convertToPcreOptions(patternOptions); + options |= PCRE_UTF16; + + int errorCode; + compiledPattern = pcre16_compile2(pattern.utf16(), options, + &errorCode, &errorString, &errorOffset, 0); + + if (!compiledPattern) + return; + + Q_ASSERT(errorCode == 0); + Q_ASSERT(studyData == 0); // studying (=>optimizing) is always done later + errorOffset = -1; + + getPatternInfo(); +} + +/*! + \internal +*/ +void QRegularExpressionPrivate::getPatternInfo() +{ + Q_ASSERT(compiledPattern); + + pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount); + + // detect the settings for the newline + int patternNewlineSetting; + pcre16_fullinfo(compiledPattern, studyData, PCRE_INFO_OPTIONS, &patternNewlineSetting); + patternNewlineSetting &= PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF + | PCRE_NEWLINE_ANY | PCRE_NEWLINE_ANYCRLF; + if (patternNewlineSetting == 0) { + // no option was specified in the regexp, grab PCRE build defaults + int pcreNewlineSetting; + pcre16_config(PCRE_CONFIG_NEWLINE, &pcreNewlineSetting); + switch (pcreNewlineSetting) { + case 13: + patternNewlineSetting = PCRE_NEWLINE_CR; break; + case 10: + patternNewlineSetting = PCRE_NEWLINE_LF; break; + case 3338: // (13<<8 | 10) + patternNewlineSetting = PCRE_NEWLINE_CRLF; break; + case -2: + patternNewlineSetting = PCRE_NEWLINE_ANYCRLF; break; + case -1: + patternNewlineSetting = PCRE_NEWLINE_ANY; break; + default: + qWarning("QRegularExpressionPrivate::compilePattern(): " + "PCRE_CONFIG_NEWLINE returned an unknown newline"); + break; + } + } + + usingCrLfNewlines = (patternNewlineSetting == PCRE_NEWLINE_CRLF) || + (patternNewlineSetting == PCRE_NEWLINE_ANY) || + (patternNewlineSetting == PCRE_NEWLINE_ANYCRLF); +} + + +/*! + \class QPcreJitStackPointer + \internal + + Simple "smartpointer" wrapper around a pcre_jit_stack, to be used with + QThreadStorage. +*/ +class QPcreJitStackPointer +{ + Q_DISABLE_COPY(QPcreJitStackPointer); + +public: + /*! + \internal + */ + QPcreJitStackPointer() + { + // The default JIT stack size in PCRE is 32K, + // we allocate from 32K up to 512K. + stack = pcre16_jit_stack_alloc(32*1024, 512*1024); + } + /*! + \internal + */ + ~QPcreJitStackPointer() + { + if (stack) + pcre16_jit_stack_free(stack); + } + + pcre16_jit_stack *stack; +}; + +Q_GLOBAL_STATIC(QThreadStorage, jitStacks) + +/*! + \internal +*/ +static pcre16_jit_stack *qtPcreCallback(void *) +{ + if (jitStacks()->hasLocalData()) + return jitStacks()->localData()->stack; + + return 0; +} + +/*! + \internal +*/ +static bool isJitEnabled() +{ + QByteArray jitEnvironment = qgetenv("QT_ENABLE_REGEXP_JIT"); + if (!jitEnvironment.isEmpty()) { + bool ok; + int enableJit = jitEnvironment.toInt(&ok); + return ok ? (enableJit != 0) : true; + } + +#ifdef QT_DEBUG + return false; +#else + return true; +#endif +} + +/*! + \internal + + The purpose of the function is to call pcre16_study (which allows some + optimizations to be performed, including JIT-compiling the pattern), and + setting the studyData member variable to the result of the study. It gets + called by doMatch() every time a match is performed. As of now, the + optimizations on the pattern are performed after a certain number of usages + (i.e. the qt_qregularexpression_optimize_after_use_count constant). + + Notice that although the method is protected by a mutex, one thread may + invoke this function and return immediately (i.e. not study the pattern, + leaving studyData to NULL); but before calling pcre16_exec to perform the + match, another thread performs the studying and sets studyData to something + else. Although the assignment to studyData is itself atomic, the release of + the memory pointed by studyData isn't. Therefore, the current studyData + value is returned and used by doMatch. +*/ +pcre16_extra *QRegularExpressionPrivate::optimizePattern() +{ + Q_ASSERT(compiledPattern); + + QMutexLocker lock(&mutex); + + if (studyData || (++usedCount != qt_qregularexpression_optimize_after_use_count)) + return studyData; + + static const bool enableJit = isJitEnabled(); + + int studyOptions = 0; + if (enableJit) + studyOptions |= PCRE_STUDY_JIT_COMPILE; + + const char *err; + studyData = pcre16_study(compiledPattern, studyOptions, &err); + + if (studyData && studyData->flags & PCRE_EXTRA_EXECUTABLE_JIT) + pcre16_assign_jit_stack(studyData, qtPcreCallback, 0); + + if (!studyData && err) + qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err); + + return studyData; +} + +/*! + \internal + + Returns the capturing group number for the given name. Duplicated names for + capturing groups are not supported. +*/ +int QRegularExpressionPrivate::captureIndexForName(const QString &name) const +{ + Q_ASSERT(!name.isEmpty()); + + if (!compiledPattern) + return -1; + + int index = pcre16_get_stringnumber(compiledPattern, name.utf16()); + if (index >= 0) + return index; + + return -1; +} + +/*! + \internal + + This is a simple wrapper for pcre16_exec for handling the case in which the + JIT runs out of memory. In that case, we allocate a thread-local JIT stack + and re-run pcre16_exec. +*/ +static int pcre16SafeExec(const pcre16 *code, const pcre16_extra *extra, + const unsigned short *subject, int length, + int startOffset, int options, + int *ovector, int ovecsize) +{ + int result = pcre16_exec(code, extra, subject, length, + startOffset, options, ovector, ovecsize); + + if (result == PCRE_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { + QPcreJitStackPointer *p = new QPcreJitStackPointer; + jitStacks()->setLocalData(p); + + result = pcre16_exec(code, extra, subject, length, + startOffset, options, ovector, ovecsize); + } + + return result; +} + +/*! + \internal + + Performs a match of type \a matchType on the given \a subject string with + options \a matchOptions and returns the QRegularExpressionMatchPrivate of + the result. It also advances a match if a previous result is given as \a + previous. The \a subject string goes a Unicode validity check if + \a checkSubjectString is true (PCRE doesn't like illegal UTF-16 sequences). + + Advancing a match is a tricky algorithm. If the previous match matched a + non-empty string, we just do an ordinary match at the offset position. + + If the previous match matched an empty string, then an anchored, non-empty + match is attempted at the offset position. If that succeeds, then we got + the next match and we can return it. Otherwise, we advance by 1 position + (which can be one or two code units in UTF-16!) and reattempt a "normal" + match. We also have the problem of detecting the current newline format: if + the new advanced offset is pointing to the beginning of a CRLF sequence, we + must advance over it. +*/ +QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString &subject, + int offset, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + bool checkSubjectString, + const QRegularExpressionMatchPrivate *previous) const +{ + if (offset < 0) + offset += subject.length(); + + QRegularExpression re(*const_cast(this)); + + if (offset < 0 || offset > subject.length()) + return new QRegularExpressionMatchPrivate(re, subject, matchType, matchOptions, 0); + + if (!compiledPattern) { + qWarning("QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); + return new QRegularExpressionMatchPrivate(re, subject, matchType, matchOptions, 0); + } + + QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, + matchType, matchOptions, + capturingCount); + + // this is mutex protected + const pcre16_extra *currentStudyData = const_cast(this)->optimizePattern(); + + int pcreOptions = convertToPcreOptions(matchOptions); + + if (matchType == QRegularExpression::PartialPreferCompleteMatch) + pcreOptions |= PCRE_PARTIAL_SOFT; + else if (matchType == QRegularExpression::PartialPreferFirstMatch) + pcreOptions |= PCRE_PARTIAL_HARD; + + if (!checkSubjectString) + pcreOptions |= PCRE_NO_UTF16_CHECK; + + bool previousMatchWasEmpty = false; + if (previous && previous->hasMatch && + (previous->capturedOffsets.at(0) == previous->capturedOffsets.at(1))) { + previousMatchWasEmpty = true; + } + + int * const captureOffsets = priv->capturedOffsets.data(); + const int captureOffsetsCount = priv->capturedOffsets.size(); + + const unsigned short * const subjectUtf16 = subject.utf16(); + const int subjectLength = subject.length(); + + int result; + + if (!previousMatchWasEmpty) { + result = pcre16SafeExec(compiledPattern, currentStudyData, + subjectUtf16, subjectLength, + offset, pcreOptions, + captureOffsets, captureOffsetsCount); + } else { + result = pcre16SafeExec(compiledPattern, currentStudyData, + subjectUtf16, subjectLength, + offset, pcreOptions | PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED, + captureOffsets, captureOffsetsCount); + + if (result == PCRE_ERROR_NOMATCH) { + ++offset; + + if (usingCrLfNewlines + && offset < subjectLength + && subjectUtf16[offset - 1] == QLatin1Char('\r') + && subjectUtf16[offset] == QLatin1Char('\n')) { + ++offset; + } else if (offset < subjectLength + && QChar::isLowSurrogate(subjectUtf16[offset])) { + ++offset; + } + + result = pcre16SafeExec(compiledPattern, currentStudyData, + subjectUtf16, subjectLength, + offset, pcreOptions, + captureOffsets, captureOffsetsCount); + } + } + +#ifdef QREGULAREXPRESSION_DEBUG + qDebug() << "Matching" << pattern << "against" << subject + << offset << matchType << matchOptions << previousMatchWasEmpty + << "result" << result; +#endif + + // result == 0 means not enough space in captureOffsets; should never happen + Q_ASSERT(result != 0); + + if (result > 0) { + // full match + priv->isValid = true; + priv->hasMatch = true; + priv->capturedCount = result; + priv->capturedOffsets.resize(result * 2); + } else { + // no match, partial match or error + priv->hasPartialMatch = (result == PCRE_ERROR_PARTIAL); + priv->isValid = (result == PCRE_ERROR_NOMATCH || result == PCRE_ERROR_PARTIAL); + + if (result == PCRE_ERROR_PARTIAL) { + // partial match: + // leave the start and end capture offsets (i.e. cap(0)) + priv->capturedCount = 1; + priv->capturedOffsets.resize(2); + } else { + // no match or error + priv->capturedCount = 0; + priv->capturedOffsets.clear(); + } + } + + return priv; +} + +/*! + \internal +*/ +QRegularExpressionMatchPrivate::QRegularExpressionMatchPrivate(const QRegularExpression &re, + const QString &subject, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + int capturingCount) + : regularExpression(re), subject(subject), + matchType(matchType), matchOptions(matchOptions), + capturedCount(0), + hasMatch(false), hasPartialMatch(false), isValid(false) +{ + Q_ASSERT(capturingCount >= 0); + const int captureOffsetsCount = (capturingCount + 1) * 3; + capturedOffsets.resize(captureOffsetsCount); +} + + +/*! + \internal +*/ +QRegularExpressionMatch QRegularExpressionMatchPrivate::nextMatch() const +{ + Q_ASSERT(isValid); + Q_ASSERT(hasMatch || hasPartialMatch); + + // Note the "false" passed for the check of the subject string: + // if we're advancing a match on the same subject, + // then that subject was already checked at least once (when this object + // was created, or when the object that created this one was created, etc.) + QRegularExpressionMatchPrivate *nextPrivate = regularExpression.d->doMatch(subject, + capturedOffsets.at(1), + matchType, + matchOptions, + false, + this); + return QRegularExpressionMatch(*nextPrivate); +} + +/*! + \internal +*/ +QRegularExpressionMatchIteratorPrivate::QRegularExpressionMatchIteratorPrivate(const QRegularExpression &re, + QRegularExpression::MatchType matchType, + QRegularExpression::MatchOptions matchOptions, + const QRegularExpressionMatch &next) + : next(next), + regularExpression(re), + matchType(matchType), matchOptions(matchOptions) +{ +} + +/*! + \internal +*/ +bool QRegularExpressionMatchIteratorPrivate::hasNext() const +{ + return next.isValid() && (next.hasMatch() || next.hasPartialMatch()); +} + +// PUBLIC API + +/*! + Constructs a QRegularExpression object with an empty pattern and no pattern + options. + + \sa setPattern(), setPatternOptions() +*/ +QRegularExpression::QRegularExpression() + : d(new QRegularExpressionPrivate) +{ +} + +/*! + Constructs a QRegularExpression object using the given \a pattern as + pattern and the \a options as the pattern options. + + \sa setPattern(), setPatternOptions() +*/ +QRegularExpression::QRegularExpression(const QString &pattern, PatternOptions options) + : d(new QRegularExpressionPrivate) +{ + d->pattern = pattern; + d->patternOptions = options; +} + +/*! + Constructs a QRegularExpression object as a copy of \a re. + + \sa operator=() +*/ +QRegularExpression::QRegularExpression(const QRegularExpression &re) + : d(re.d) +{ +} + +/*! + Destroys the QRegularExpression object. +*/ +QRegularExpression::~QRegularExpression() +{ +} + +/*! + Assigns the regular expression \a re to this object, and returns a reference + to the copy. Both the pattern and the pattern options are copied. +*/ +QRegularExpression &QRegularExpression::operator=(const QRegularExpression &re) +{ + d = re.d; + return *this; +} + +/*! + \fn void QRegularExpression::swap(QRegularExpression &other) + + Swaps the regular expression \a other with this regular expression. This + operation is very fast and never fails. +*/ + +/*! + Returns the pattern string of the regular expression. + + \sa setPattern(), patternOptions() +*/ +QString QRegularExpression::pattern() const +{ + return d->pattern; +} + +/*! + Sets the pattern string of the regular expression to \a pattern. The + pattern options are left unchanged. + + \sa pattern(), setPatternOptions() +*/ +void QRegularExpression::setPattern(const QString &pattern) +{ + d.detach(); + d->isDirty = true; + d->pattern = pattern; +} + +/*! + Returns the pattern options for the regular expression. + + \sa setPatternOptions(), pattern() +*/ +QRegularExpression::PatternOptions QRegularExpression::patternOptions() const +{ + return d->patternOptions; +} + +/*! + Sets the given \a options as the pattern options of the regular expression. + The pattern string is left unchanged. + + \sa patternOptions(), setPattern() +*/ +void QRegularExpression::setPatternOptions(PatternOptions options) +{ + d.detach(); + d->isDirty = true; + d->patternOptions = options; +} + +/*! + Returns the number of capturing groups inside the pattern string, + or -1 if the regular expression is not valid. + + \sa isValid() +*/ +int QRegularExpression::captureCount() const +{ + if (!isValid()) // will compile the pattern + return -1; + return d->capturingCount; +} + +/*! + Returns true if the regular expression is a valid regular expression (that + is, it contains no syntax errors, etc.), or false otherwise. Use + errorString() to obtain a textual description of the error. + + \sa errorString(), patternErrorOffset() +*/ +bool QRegularExpression::isValid() const +{ + d.data()->compilePattern(); + return d->compiledPattern; +} + +/*! + Returns a textual description of the error found when checking the validity + of the regular expression, or "no error" if no error was found. + + \sa isValid(), patternErrorOffset() +*/ +QString QRegularExpression::errorString() const +{ + d.data()->compilePattern(); + if (d->errorString) + return QCoreApplication::translate("QRegularExpression", d->errorString); + return QCoreApplication::translate("QRegularExpression", "no error"); +} + +/*! + Returns the offset, inside the pattern string, at which an error was found + when checking the validity of the regular expression. If no error was + found, then -1 is returned. + + \sa pattern(), isValid(), errorString() +*/ +int QRegularExpression::patternErrorOffset() const +{ + d.data()->compilePattern(); + return d->errorOffset; +} + +/*! + Attempts to match the regular expression against the given \a subject + string, starting at the position \a offset inside the subject, using a + match of type \a matchType and honoring the given \a matchOptions. + + The returned QRegularExpressionMatch object contains the results of the + match. + + \sa QRegularExpressionMatch, {normal matching} +*/ +QRegularExpressionMatch QRegularExpression::match(const QString &subject, + int offset, + MatchType matchType, + MatchOptions matchOptions) const +{ + d.data()->compilePattern(); + + QRegularExpressionMatchPrivate *priv = d->doMatch(subject, offset, matchType, matchOptions); + return QRegularExpressionMatch(*priv); +} + +/*! + Attempts to perform a global match of the regular expression against the + given \a subject string, starting at the position \a offset inside the + subject, using a match of type \a matchType and honoring the given \a + matchOptions. + + The returned QRegularExpressionMatchIterator is positioned before the + first match result (if any). + + \sa QRegularExpressionMatchIterator, {global matching} +*/ +QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &subject, + int offset, + MatchType matchType, + MatchOptions matchOptions) const +{ + QRegularExpressionMatchIteratorPrivate *priv = + new QRegularExpressionMatchIteratorPrivate(*this, + matchType, + matchOptions, + match(subject, offset, matchType, matchOptions)); + + return QRegularExpressionMatchIterator(*priv); +} + +/*! + Returns true if the regular expression is equal to \a re, or false + otherwise. Two QRegularExpression objects are equal if they have + the same pattern string and the same pattern options. + + \sa operator!=() +*/ +bool QRegularExpression::operator==(const QRegularExpression &re) const +{ + return (d == re.d) || + (d->pattern == re.d->pattern && d->patternOptions == re.d->patternOptions); +} + +/*! + \fn bool QRegularExpression::operator!=(const QRegularExpression &re) const + + Returns true if the regular expression is different from \a re, or + false otherwise. + + \sa operator==() +*/ + +/*! + Escapes all characters of \a str so that they no longer have any special + meaning when used as a regular expression pattern string, and returns + the escaped string. For instance: + + \snippet code/src_corelib_tools_qregularexpression.cpp 26 + + This is very convenient in order to build patterns from arbitrary strings: + + \snippet code/src_corelib_tools_qregularexpression.cpp 27 + + \note This function implements Perl's quotemeta algorithm and escapes with + a backslash all characters in \a str, except for the characters in the + \c{[A-Z]}, \c{[a-z]} and \c{[0-9]} ranges, as well as the underscore + (\c{_}) character. The only difference with Perl is that a literal NUL + inside \a str is escaped with the sequence \c{"\\0"} (backslash + + \c{'0'}), instead of \c{"\\\0"} (backslash + \c{NUL}). +*/ +QString QRegularExpression::escape(const QString &str) +{ + QString result; + const int count = str.size(); + result.reserve(count * 2); + + // everything but [a-zA-Z0-9_] gets escaped, + // cf. perldoc -f quotemeta + for (int i = 0; i < count; ++i) { + const QChar current = str.at(i); + + if (current == QChar::Null) { + // unlike Perl, a literal NUL must be escaped with + // "\\0" (backslash + 0) and not "\\\0" (backslash + NUL), + // because pcre16_compile uses a NUL-terminated string + result.append(QLatin1Char('\\')); + result.append(QLatin1Char('0')); + } else if ( (current < QLatin1Char('a') || current > QLatin1Char('z')) && + (current < QLatin1Char('A') || current > QLatin1Char('Z')) && + (current < QLatin1Char('0') || current > QLatin1Char('9')) && + current != QLatin1Char('_') ) + { + result.append(QLatin1Char('\\')); + result.append(current); + if (current.isHighSurrogate() && i < (count - 1)) + result.append(str.at(++i)); + } else { + result.append(current); + } + } + + result.squeeze(); + return result; +} + +/*! + Destroys the match result. +*/ +QRegularExpressionMatch::~QRegularExpressionMatch() +{ +} + +/*! + Constructs a match result by copying the result of the given \a match. + + \sa operator=() +*/ +QRegularExpressionMatch::QRegularExpressionMatch(const QRegularExpressionMatch &match) + : d(match.d) +{ +} + +/*! + Assigns the match result \a match to this object, and returns a reference + to the copy. +*/ +QRegularExpressionMatch &QRegularExpressionMatch::operator=(const QRegularExpressionMatch &match) +{ + d = match.d; + return *this; +} + +/*! + \fn void QRegularExpressionMatch::swap(QRegularExpressionMatch &other) + + Swaps the match result \a other with this match result. This + operation is very fast and never fails. +*/ + +/*! + \internal +*/ +QRegularExpressionMatch::QRegularExpressionMatch(QRegularExpressionMatchPrivate &dd) + : d(&dd) +{ +} + +/*! + Returns the QRegularExpression object whose match() function returned this + object. + + \sa QRegularExpression::match(), matchType(), matchOptions() +*/ +QRegularExpression QRegularExpressionMatch::regularExpression() const +{ + return d->regularExpression; +} + + +/*! + Returns the match type that was used to get this QRegularExpressionMatch + object, that is, the match type that was passed to + QRegularExpression::match() or QRegularExpression::globalMatch(). + + \sa QRegularExpression::match(), regularExpression(), matchOptions() +*/ +QRegularExpression::MatchType QRegularExpressionMatch::matchType() const +{ + return d->matchType; +} + +/*! + Returns the match options that were used to get this + QRegularExpressionMatch object, that is, the match options that were passed + to QRegularExpression::match() or QRegularExpression::globalMatch(). + + \sa QRegularExpression::match(), regularExpression(), matchType() +*/ +QRegularExpression::MatchOptions QRegularExpressionMatch::matchOptions() const +{ + return d->matchOptions; +} + +/*! + Returns the index of the last capturing group that captured something, + including the implicit capturing group 0. This can be used to extract all + the substrings that were captured: + + \snippet code/src_corelib_tools_qregularexpression.cpp 28 + + Note that some of the capturing groups with an index less than + lastCapturedIndex() could have not matched, and therefore captured nothing. + + If the regular expression did not match, this function returns -1. + + \sa captured(), capturedStart(), capturedEnd(), capturedLength() +*/ +int QRegularExpressionMatch::lastCapturedIndex() const +{ + return d->capturedCount - 1; +} + +/*! + Returns the substring captured by the \a nth capturing group. If the \a nth + capturing group did not capture a string or doesn't exist, returns a null + QString. + + \sa capturedRef(), lastCapturedIndex(), capturedStart(), capturedEnd(), + capturedLength(), QString::isNull() +*/ +QString QRegularExpressionMatch::captured(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return QString(); + + int start = capturedStart(nth); + + if (start == -1) // didn't capture + return QString(); + + return d->subject.mid(start, capturedLength(nth)); +} + +/*! + Returns a reference to the substring captured by the \a nth capturing group. + If the \a nth capturing group did not capture a string or doesn't exist, + returns a null QStringRef. + + \sa captured(), lastCapturedIndex(), capturedStart(), capturedEnd(), + capturedLength(), QStringRef::isNull() +*/ +QStringRef QRegularExpressionMatch::capturedRef(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return QStringRef(); + + int start = capturedStart(nth); + + if (start == -1) // didn't capture + return QStringRef(); + + return d->subject.midRef(start, capturedLength(nth)); +} + +/*! + Returns the substring captured by the capturing group named \a name. If the + capturing group named \a name did not capture a string or doesn't exist, + returns a null QString. + + \sa capturedRef(), capturedStart(), capturedEnd(), capturedLength(), + QString::isNull() +*/ +QString QRegularExpressionMatch::captured(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::captured: empty capturing group name passed"); + return QString(); + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return QString(); + return captured(nth); +} + +/*! + Returns a reference to the string captured by the capturing group named \a + name. If the capturing group named \a name did not capture a string or + doesn't exist, returns a null QStringRef. + + \sa captured(), capturedStart(), capturedEnd(), capturedLength(), + QStringRef::isNull() +*/ +QStringRef QRegularExpressionMatch::capturedRef(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedRef: empty capturing group name passed"); + return QStringRef(); + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return QStringRef(); + return capturedRef(nth); +} + +/*! + Returns a list of all strings captured by capturing groups, in the order + the groups themselves appear in the pattern string. +*/ +QStringList QRegularExpressionMatch::capturedTexts() const +{ + QStringList texts; + for (int i = 0; i <= lastCapturedIndex(); ++i) + texts << captured(i); + return texts; +} + +/*! + Returns the offset inside the subject string corresponding to the + starting position of the substring captured by the \a nth capturing group. + If the \a nth capturing group did not capture a string or doesn't exist, + returns -1. + + \sa capturedEnd(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedStart(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return -1; + + return d->capturedOffsets.at(nth * 2); +} + +/*! + Returns the length of the substring captured by the \a nth capturing group. + + \note This function returns 0 if the \a nth capturing group did not capture + a string or doesn't exist. + + \sa capturedStart(), capturedEnd(), captured() +*/ +int QRegularExpressionMatch::capturedLength(int nth) const +{ + // bound checking performed by these two functions + return capturedEnd(nth) - capturedStart(nth); +} + +/*! + Returns the offset inside the subject string immediately after the ending + position of the substring captured by the \a nth capturing group. If the \a + nth capturing group did not capture a string or doesn't exist, returns -1. + + \sa capturedStart(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedEnd(int nth) const +{ + if (nth < 0 || nth > lastCapturedIndex()) + return -1; + + return d->capturedOffsets.at(nth * 2 + 1); +} + +/*! + Returns the offset inside the subject string corresponding to the starting + position of the substring captured by the capturing group named \a name. + If the capturing group named \a name did not capture a string or doesn't + exist, returns -1. + + \sa capturedEnd(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedStart(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedStart: empty capturing group name passed"); + return -1; + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return -1; + return capturedStart(nth); +} + +/*! + Returns the offset inside the subject string corresponding to the starting + position of the substring captured by the capturing group named \a name. + + \note This function returns 0 if the capturing group named \a name did not + capture a string or doesn't exist. + + \sa capturedStart(), capturedEnd(), captured() +*/ +int QRegularExpressionMatch::capturedLength(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedLength: empty capturing group name passed"); + return 0; + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return 0; + return capturedLength(nth); +} + +/*! + Returns the offset inside the subject string immediately after the ending + position of the substring captured by the capturing group named \a name. If + the capturing group named \a name did not capture a string or doesn't + exist, returns -1. + + \sa capturedStart(), capturedLength(), captured() +*/ +int QRegularExpressionMatch::capturedEnd(const QString &name) const +{ + if (name.isEmpty()) { + qWarning("QRegularExpressionMatch::capturedEnd: empty capturing group name passed"); + return -1; + } + int nth = d->regularExpression.d->captureIndexForName(name); + if (nth == -1) + return -1; + return capturedEnd(nth); +} + +/*! + Returns true if the regular expression matched against the subject string, + or false otherwise. + + \sa QRegularExpression::match(), hasPartialMatch() +*/ +bool QRegularExpressionMatch::hasMatch() const +{ + return d->hasMatch; +} + +/*! + Returns true if the regular expression partially matched against the + subject string, or false otherwise. + + \note Only a match that explicitely used the one of the partial match types + can yield a partial match. Still, if such a match succeeds totally, this + function will return false, while hasMatch() will return true. + + \sa QRegularExpression::match(), QRegularExpression::MatchType, hasMatch() +*/ +bool QRegularExpressionMatch::hasPartialMatch() const +{ + return d->hasPartialMatch; +} + +/*! + Returns true if the match object was obtained as a result from the + QRegularExpression::match() function invoked on a valid QRegularExpression + object; returns false if the QRegularExpression was invalid. + + \sa QRegularExpression::match(), QRegularExpression::isValid() +*/ +bool QRegularExpressionMatch::isValid() const +{ + return d->isValid; +} + +/*! + \internal +*/ +QRegularExpressionMatchIterator::QRegularExpressionMatchIterator(QRegularExpressionMatchIteratorPrivate &dd) + : d(&dd) +{ +} + +/*! + Destroys the QRegularExpressionMatchIterator object. +*/ +QRegularExpressionMatchIterator::~QRegularExpressionMatchIterator() +{ +} + +/*! + Constructs a QRegularExpressionMatchIterator object as a copy of \a + iterator. + + \sa operator=() +*/ +QRegularExpressionMatchIterator::QRegularExpressionMatchIterator(const QRegularExpressionMatchIterator &iterator) + : d(iterator.d) +{ +} + +/*! + Assigns the iterator \a iterator to this object, and returns a reference to + the copy. +*/ +QRegularExpressionMatchIterator &QRegularExpressionMatchIterator::operator=(const QRegularExpressionMatchIterator &iterator) +{ + d = iterator.d; + return *this; +} + +/*! + \fn void QRegularExpressionMatchIterator::swap(QRegularExpressionMatchIterator &other) + + Swaps the iterator \a other with this iterator object. This operation is + very fast and never fails. +*/ + +/*! + Returns true if the iterator object was obtained as a result from the + QRegularExpression::globalMatch() function invoked on a valid + QRegularExpression object; returns false if the QRegularExpression was + invalid. + + \sa QRegularExpression::globalMatch(), QRegularExpression::isValid() +*/ +bool QRegularExpressionMatchIterator::isValid() const +{ + return d->next.isValid(); +} + +/*! + Returns true if there is at least one match result ahead of the iterator; + otherwise it returns false. + + \sa next() +*/ +bool QRegularExpressionMatchIterator::hasNext() const +{ + return d->hasNext(); +} + +/*! + Returns the next match result without moving the iterator. + + \note Calling this function when the iterator is at the end of the result + set leads to undefined results. +*/ +QRegularExpressionMatch QRegularExpressionMatchIterator::peekNext() const +{ + if (!hasNext()) + qWarning("QRegularExpressionMatchIterator::peekNext() called on an iterator already at end"); + + return d->next; +} + +/*! + Returns the next match result and advances the iterator by one position. + + \note Calling this function when the iterator is at the end of the result + set leads to undefined results. +*/ +QRegularExpressionMatch QRegularExpressionMatchIterator::next() +{ + if (!hasNext()) { + qWarning("QRegularExpressionMatchIterator::next() called on an iterator already at end"); + return d->next; + } + + QRegularExpressionMatch current = d->next; + d->next = d->next.d.constData()->nextMatch(); + return current; +} + +/*! + Returns the QRegularExpression object whose globalMatch() function returned + this object. + + \sa QRegularExpression::globalMatch(), matchType(), matchOptions() +*/ +QRegularExpression QRegularExpressionMatchIterator::regularExpression() const +{ + return d->regularExpression; +} + +/*! + Returns the match type that was used to get this + QRegularExpressionMatchIterator object, that is, the match type that was + passed to QRegularExpression::globalMatch(). + + \sa QRegularExpression::globalMatch(), regularExpression(), matchOptions() +*/ +QRegularExpression::MatchType QRegularExpressionMatchIterator::matchType() const +{ + return d->matchType; +} + +/*! + Returns the match options that were used to get this + QRegularExpressionMatchIterator object, that is, the match options that + were passed to QRegularExpression::globalMatch(). + + \sa QRegularExpression::globalMatch(), regularExpression(), matchType() +*/ +QRegularExpression::MatchOptions QRegularExpressionMatchIterator::matchOptions() const +{ + return d->matchOptions; +} + +#ifndef QT_NO_DATASTREAM +/*! + \relates QRegularExpression + + Writes the regular expression \a re to stream \a out. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator<<(QDataStream &out, const QRegularExpression &re) +{ + out << re.pattern() << quint32(re.patternOptions()); + return out; +} + +/*! + \relates QRegularExpression + + Reads a regular expression from stream \a in into \a re. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator>>(QDataStream &in, QRegularExpression &re) +{ + QString pattern; + quint32 patternOptions; + in >> pattern >> patternOptions; + re.setPattern(pattern); + re.setPatternOptions(QRegularExpression::PatternOptions(patternOptions)); + return in; +} +#endif + +#ifndef QT_NO_DEBUG_STREAM +/*! + \relates QRegularExpression + + Writes the regular expression \a re into the debug object \a debug for + debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, const QRegularExpression &re) +{ + debug.nospace() << "QRegularExpression(" << re.pattern() << ", " << re.patternOptions() << ")"; + return debug.space(); +} + +/*! + \relates QRegularExpression + + Writes the pattern options \a patternOptions into the debug object \a debug + for debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOptions) +{ + QByteArray flags; + + if (patternOptions == QRegularExpression::NoPatternOption) { + flags = "NoPatternOption"; + } else { + flags.reserve(200); // worst case... + if (patternOptions & QRegularExpression::CaseInsensitiveOption) + flags.append("CaseInsensitiveOption|"); + if (patternOptions & QRegularExpression::DotMatchesEverythingOption) + flags.append("DotMatchesEverythingOption|"); + if (patternOptions & QRegularExpression::MultilineOption) + flags.append("MultilineOption|"); + if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) + flags.append("ExtendedPatternSyntaxOption|"); + if (patternOptions & QRegularExpression::InvertedGreedinessOption) + flags.append("InvertedGreedinessOption|"); + if (patternOptions & QRegularExpression::DontCaptureOption) + flags.append("DontCaptureOption|"); + if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) + flags.append("UseUnicodePropertiesOption|"); + flags.chop(1); + } + + debug.nospace() << "QRegularExpression::PatternOptions(" << flags << ")"; + + return debug.space(); +} +/*! + \relates QRegularExpressionMatch + + Writes the match object \a match into the debug object \a debug for + debugging purposes. + + \sa {Debugging Techniques} +*/ +QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match) +{ + debug.nospace() << "QRegularExpressionMatch("; + + if (!match.isValid()) { + debug << "Invalid)"; + return debug.space(); + } + + debug << "Valid"; + + if (match.hasMatch()) { + debug << ", has match: "; + for (int i = 0; i <= match.lastCapturedIndex(); ++i) { + debug << i + << ":(" << match.capturedStart(i) << ", " << match.capturedEnd(i) + << ", " << match.captured(i) << ")"; + if (i < match.lastCapturedIndex()) + debug << ", "; + } + } else if (match.hasPartialMatch()) { + debug << ", has partial match: (" + << match.capturedStart(0) << ", " + << match.capturedEnd(0) << ", " + << match.captured(0) << ")"; + } else { + debug << ", no match"; + } + + debug << ")"; + + return debug.space(); +} +#endif + +// fool lupdate: make it extract those strings for translation, but don't put them +// inside Qt -- they're already inside libpcre (cf. man 3 pcreapi, pcre_compile.c). +#if 0 + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +static const char *pcreCompileErrorCodes[] = +{ + QT_TRANSLATE_NOOP("QRegularExpression", "no error"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\ at end of pattern"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c at end of pattern"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character follows \\"), + QT_TRANSLATE_NOOP("QRegularExpression", "numbers out of order in {} quantifier"), + QT_TRANSLATE_NOOP("QRegularExpression", "number too big in {} quantifier"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating ] for character class"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in character class"), + QT_TRANSLATE_NOOP("QRegularExpression", "range out of order in character class"), + QT_TRANSLATE_NOOP("QRegularExpression", "nothing to repeat"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unexpected repeat"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (? or (?-"), + QT_TRANSLATE_NOOP("QRegularExpression", "POSIX named classes are supported only within a class"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing )"), + QT_TRANSLATE_NOOP("QRegularExpression", "reference to non-existent subpattern"), + QT_TRANSLATE_NOOP("QRegularExpression", "erroffset passed as NULL"), + QT_TRANSLATE_NOOP("QRegularExpression", "unknown option bit(s) set"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "failed to get memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unmatched parentheses"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: code overflow"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?<"), + QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is not fixed length"), + QT_TRANSLATE_NOOP("QRegularExpression", "malformed number or name after (?("), + QT_TRANSLATE_NOOP("QRegularExpression", "conditional group contains more than two branches"), + QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?("), + QT_TRANSLATE_NOOP("QRegularExpression", "(?R or (?[+-]digits must be followed by )"), + QT_TRANSLATE_NOOP("QRegularExpression", "unknown POSIX class name"), + QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UTF8 support"), + QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\x{...} sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid condition (?(0)"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\C not allowed in lookbehind assertion"), + QT_TRANSLATE_NOOP("QRegularExpression", "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u"), + QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is > 255"), + QT_TRANSLATE_NOOP("QRegularExpression", "closing ) for (?C expected"), + QT_TRANSLATE_NOOP("QRegularExpression", "recursive call could loop indefinitely"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?P"), + QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in subpattern name (missing terminator)"), + QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-8 string"), + QT_TRANSLATE_NOOP("QRegularExpression", "support for \\P, \\p, and \\X has not been compiled"), + QT_TRANSLATE_NOOP("QRegularExpression", "malformed \\P or \\p sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "unknown property name after \\P or \\p"), + QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum 32 characters)"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum 10000)"), + QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 (not in UTF-8 mode)"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: overran compiling workspace"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: previously-checked referenced subpattern not found"), + QT_TRANSLATE_NOOP("QRegularExpression", "DEFINE group contains more than one branch"), + QT_TRANSLATE_NOOP("QRegularExpression", "repeating a DEFINE group is not allowed"), + QT_TRANSLATE_NOOP("QRegularExpression", "inconsistent NEWLINE options"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number"), + QT_TRANSLATE_NOOP("QRegularExpression", "a numbered reference must not be zero"), + QT_TRANSLATE_NOOP("QRegularExpression", "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)"), + QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized"), + QT_TRANSLATE_NOOP("QRegularExpression", "number is too big"), + QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name expected"), + QT_TRANSLATE_NOOP("QRegularExpression", "digit expected after (?+"), + QT_TRANSLATE_NOOP("QRegularExpression", "] is an invalid data character in JavaScript compatibility mode"), + QT_TRANSLATE_NOOP("QRegularExpression", "different names for subpatterns of the same number are not allowed"), + QT_TRANSLATE_NOOP("QRegularExpression", "(*MARK) must have an argument"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UCP support"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by an ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\k is not followed by a braced, angle-bracketed, or quoted name"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in find_fixedlength()"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\N is not supported in a class"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many forward references"), + QT_TRANSLATE_NOOP("QRegularExpression", "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-16 string") +}; +#endif // #if 0 diff --git a/src/MdCharm/util/test/qregularexpression.h b/src/MdCharm/util/test/qregularexpression.h new file mode 100644 index 0000000..fe90f77 --- /dev/null +++ b/src/MdCharm/util/test/qregularexpression.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Giuseppe D'Angelo . +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QREGULAREXPRESSION_H +#define QREGULAREXPRESSION_H + +#include +#include +#include + +class QRegularExpressionMatch; +class QRegularExpressionMatchIterator; +struct QRegularExpressionPrivate; + +class QRegularExpression +{ +public: + enum PatternOption { + NoPatternOption = 0x0000, + CaseInsensitiveOption = 0x0001, + DotMatchesEverythingOption = 0x0002, + MultilineOption = 0x0004, + ExtendedPatternSyntaxOption = 0x0008, + InvertedGreedinessOption = 0x0010, + DontCaptureOption = 0x0020, + UseUnicodePropertiesOption = 0x0040 + }; + Q_DECLARE_FLAGS(PatternOptions, PatternOption) + + PatternOptions patternOptions() const; + void setPatternOptions(PatternOptions options); + + QRegularExpression(); + explicit QRegularExpression(const QString &pattern, PatternOptions options = NoPatternOption); + QRegularExpression(const QRegularExpression &re); + ~QRegularExpression(); + QRegularExpression &operator=(const QRegularExpression &re); + +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegularExpression &operator=(QRegularExpression &&re) + { d.swap(re.d); return *this; } +#endif + + inline void swap(QRegularExpression &re) { d.swap(re.d); } + + QString pattern() const; + void setPattern(const QString &pattern); + + bool isValid() const; + int patternErrorOffset() const; + QString errorString() const; + + int captureCount() const; + + enum MatchType { + NormalMatch = 0, + PartialPreferCompleteMatch, + PartialPreferFirstMatch + }; + + enum MatchOption { + NoMatchOption = 0x0000, + AnchoredMatchOption = 0x0001 + }; + Q_DECLARE_FLAGS(MatchOptions, MatchOption) + + QRegularExpressionMatch match(const QString &subject, + int offset = 0, + MatchType matchType = NormalMatch, + MatchOptions matchOptions = NoMatchOption) const; + + QRegularExpressionMatchIterator globalMatch(const QString &subject, + int offset = 0, + MatchType matchType = NormalMatch, + MatchOptions matchOptions = NoMatchOption) const; + + static QString escape(const QString &str); + + bool operator==(const QRegularExpression &re) const; + inline bool operator!=(const QRegularExpression &re) const { return !operator==(re); } + +private: + friend struct QRegularExpressionPrivate; + friend class QRegularExpressionMatch; + friend struct QRegularExpressionMatchPrivate; + friend class QRegularExpressionMatchIterator; + + QRegularExpression(QRegularExpressionPrivate &dd); + QExplicitlySharedDataPointer d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::PatternOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(QRegularExpression::MatchOptions) +Q_DECLARE_TYPEINFO(QRegularExpression, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &out, const QRegularExpression &re); +QDataStream &operator>>(QDataStream &in, QRegularExpression &re); +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QRegularExpression &re); +QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOptions); +#endif + +struct QRegularExpressionMatchPrivate; + +class QRegularExpressionMatch +{ +public: + ~QRegularExpressionMatch(); + QRegularExpressionMatch(const QRegularExpressionMatch &match); + QRegularExpressionMatch &operator=(const QRegularExpressionMatch &match); + +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegularExpressionMatch &operator=(QRegularExpressionMatch &&match) + { d.swap(match.d); return *this; } +#endif + inline void swap(QRegularExpressionMatch &match) { d.swap(match.d); } + + QRegularExpression regularExpression() const; + QRegularExpression::MatchType matchType() const; + QRegularExpression::MatchOptions matchOptions() const; + + bool hasMatch() const; + bool hasPartialMatch() const; + + bool isValid() const; + + int lastCapturedIndex() const; + + QString captured(int nth = 0) const; + QStringRef capturedRef(int nth = 0) const; + + QString captured(const QString &name) const; + QStringRef capturedRef(const QString &name) const; + + QStringList capturedTexts() const; + + int capturedStart(int nth = 0) const; + int capturedLength(int nth = 0) const; + int capturedEnd(int nth = 0) const; + + int capturedStart(const QString &name) const; + int capturedLength(const QString &name) const; + int capturedEnd(const QString &name) const; + +private: + friend class QRegularExpression; + friend struct QRegularExpressionMatchPrivate; + friend class QRegularExpressionMatchIterator; + + QRegularExpressionMatch(QRegularExpressionMatchPrivate &dd); + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QRegularExpressionMatch, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match); +#endif + +struct QRegularExpressionMatchIteratorPrivate; + +class QRegularExpressionMatchIterator +{ +public: + ~QRegularExpressionMatchIterator(); + QRegularExpressionMatchIterator(const QRegularExpressionMatchIterator &iterator); + QRegularExpressionMatchIterator &operator=(const QRegularExpressionMatchIterator &iterator); +#ifdef Q_COMPILER_RVALUE_REFS + inline QRegularExpressionMatchIterator &operator=(QRegularExpressionMatchIterator &&iterator) + { d.swap(iterator.d); return *this; } +#endif + void swap(QRegularExpressionMatchIterator &iterator) { d.swap(iterator.d); } + + bool isValid() const; + + bool hasNext() const; + QRegularExpressionMatch next(); + QRegularExpressionMatch peekNext() const; + + QRegularExpression regularExpression() const; + QRegularExpression::MatchType matchType() const; + QRegularExpression::MatchOptions matchOptions() const; + +private: + friend class QRegularExpression; + + QRegularExpressionMatchIterator(QRegularExpressionMatchIteratorPrivate &dd); + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QRegularExpressionMatchIterator, Q_MOVABLE_TYPE); + +#endif // QREGULAREXPRESSION_H diff --git a/src/MdCharm/util/zip/zipwriter.cpp b/src/MdCharm/util/zip/zipwriter.cpp new file mode 100644 index 0000000..f488d43 --- /dev/null +++ b/src/MdCharm/util/zip/zipwriter.cpp @@ -0,0 +1,523 @@ +#include +#include +#include +#include +#include + +#include + +#include "zipwriter.h" + +#if defined(Q_OS_WIN) +# undef S_IFREG +# define S_IFREG 0100000 +# ifndef S_IFDIR +# define S_IFDIR 0040000 +# endif +# ifndef S_ISDIR +# define S_ISDIR(x) ((x) & S_IFDIR) > 0 +# endif +# ifndef S_ISREG +# define S_ISREG(x) ((x) & 0170000) == S_IFREG +# endif +# define S_IFLNK 020000 +# define S_ISLNK(x) ((x) & S_IFLNK) > 0 +# ifndef S_IRUSR +# define S_IRUSR 0400 +# endif +# ifndef S_IWUSR +# define S_IWUSR 0200 +# endif +# ifndef S_IXUSR +# define S_IXUSR 0100 +# endif +# define S_IRGRP 0040 +# define S_IWGRP 0020 +# define S_IXGRP 0010 +# define S_IROTH 0004 +# define S_IWOTH 0002 +# define S_IXOTH 0001 +#endif + +static inline uint readUInt(const uchar *data) +{ + return (data[0]) + (data[1]<<8) + (data[2]<<16) + (data[3]<<24); +} + +static inline ushort readUShort(const uchar *data) +{ + return (data[0]) + (data[1]<<8); +} + +static inline void writeUInt(uchar *data, uint i) +{ + data[0] = i & 0xff; + data[1] = (i>>8) & 0xff; + data[2] = (i>>16) & 0xff; + data[3] = (i>>24) & 0xff; +} + +static inline void writeUShort(uchar *data, ushort i) +{ + data[0] = i & 0xff; + data[1] = (i>>8) & 0xff; +} + +static inline void copyUInt(uchar *dest, const uchar *src) +{ + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; +} + +static inline void copyUShort(uchar *dest, const uchar *src) +{ + dest[0] = src[0]; + dest[1] = src[1]; +} + +static void writeMSDosDate(uchar *dest, const QDateTime& dt) +{ + if (dt.isValid()) { + quint16 time = + (dt.time().hour() << 11) // 5 bit hour + | (dt.time().minute() << 5) // 6 bit minute + | (dt.time().second() >> 1); // 5 bit double seconds + + dest[0] = time & 0xff; + dest[1] = time >> 8; + + quint16 date = + ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based + | (dt.date().month() << 5) // 4 bit month + | (dt.date().day()); // 5 bit day + + dest[2] = char(date); + dest[3] = char(date >> 8); + } else { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + dest[3] = 0; + } +} + +static quint32 permissionsToMode(QFile::Permissions perms) +{ + quint32 mode = 0; + if (perms & QFile::ReadOwner) + mode |= S_IRUSR; + if (perms & QFile::WriteOwner) + mode |= S_IWUSR; + if (perms & QFile::ExeOwner) + mode |= S_IXUSR; + if (perms & QFile::ReadUser) + mode |= S_IRUSR; + if (perms & QFile::WriteUser) + mode |= S_IWUSR; + if (perms & QFile::ExeUser) + mode |= S_IXUSR; + if (perms & QFile::ReadGroup) + mode |= S_IRGRP; + if (perms & QFile::WriteGroup) + mode |= S_IWGRP; + if (perms & QFile::ExeGroup) + mode |= S_IXGRP; + if (perms & QFile::ReadOther) + mode |= S_IROTH; + if (perms & QFile::WriteOther) + mode |= S_IWOTH; + if (perms & QFile::ExeOther) + mode |= S_IXOTH; + return mode; +} + +static int inflate(Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + if ((uLong)stream.avail_in != sourceLen) + return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) + return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit2(&stream, -MAX_WBITS); + if (err != Z_OK) + return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} + +static int deflate (Bytef *dest, ulong *destLen, const Bytef *source, ulong sourceLen) +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +static QFile::Permissions modeToPermissions(quint32 mode) +{ + QFile::Permissions ret; + if (mode & S_IRUSR) + ret |= QFile::ReadOwner; + if (mode & S_IWUSR) + ret |= QFile::WriteOwner; + if (mode & S_IXUSR) + ret |= QFile::ExeOwner; + if (mode & S_IRUSR) + ret |= QFile::ReadUser; + if (mode & S_IWUSR) + ret |= QFile::WriteUser; + if (mode & S_IXUSR) + ret |= QFile::ExeUser; + if (mode & S_IRGRP) + ret |= QFile::ReadGroup; + if (mode & S_IWGRP) + ret |= QFile::WriteGroup; + if (mode & S_IXGRP) + ret |= QFile::ExeGroup; + if (mode & S_IROTH) + ret |= QFile::ReadOther; + if (mode & S_IWOTH) + ret |= QFile::WriteOther; + if (mode & S_IXOTH) + ret |= QFile::ExeOther; + return ret; +} + +static QDateTime readMSDosDate(const uchar *src) +{ + uint dosDate = readUInt(src); + quint64 uDate; + uDate = (quint64)(dosDate >> 16); + uint tm_mday = (uDate & 0x1f); + uint tm_mon = ((uDate & 0x1E0) >> 5); + uint tm_year = (((uDate & 0x0FE00) >> 9) + 1980); + uint tm_hour = ((dosDate & 0xF800) >> 11); + uint tm_min = ((dosDate & 0x7E0) >> 5); + uint tm_sec = ((dosDate & 0x1f) << 1); + + return QDateTime(QDate(tm_year, tm_mon, tm_mday), QTime(tm_hour, tm_min, tm_sec)); +} + +struct LocalFileHeader +{ + uchar signature[4]; // 0x04034b50 + uchar version_needed[2]; + uchar general_purpose_bits[2]; + uchar compression_method[2]; + uchar last_mod_file[4]; + uchar crc_32[4]; + uchar compressed_size[4]; + uchar uncompressed_size[4]; + uchar file_name_length[2]; + uchar extra_field_length[2]; +}; + +struct DataDescriptor +{ + uchar crc_32[4]; + uchar compressed_size[4]; + uchar uncompressed_size[4]; +}; + +struct CentralFileHeader +{ + uchar signature[4]; // 0x02014b50 + uchar version_made[2]; + uchar version_needed[2]; + uchar general_purpose_bits[2]; + uchar compression_method[2]; + uchar last_mod_file[4]; + uchar crc_32[4]; + uchar compressed_size[4]; + uchar uncompressed_size[4]; + uchar file_name_length[2]; + uchar extra_field_length[2]; + uchar file_comment_length[2]; + uchar disk_start[2]; + uchar internal_file_attributes[2]; + uchar external_file_attributes[4]; + uchar offset_local_header[4]; + LocalFileHeader toLocalHeader() const; +}; + +struct EndOfDirectory +{ + uchar signature[4]; // 0x06054b50 + uchar this_disk[2]; + uchar start_of_directory_disk[2]; + uchar num_dir_entries_this_disk[2]; + uchar num_dir_entries[2]; + uchar directory_size[4]; + uchar dir_start_offset[4]; + uchar comment_length[2]; +}; + +struct FileHeader +{ + CentralFileHeader h; + QByteArray file_name; + QByteArray extra_field; + QByteArray file_comment; +}; + +LocalFileHeader CentralFileHeader::toLocalHeader() const +{ + LocalFileHeader h; + writeUInt(h.signature, 0x04034b50); + copyUShort(h.version_needed, version_needed); + copyUShort(h.general_purpose_bits, general_purpose_bits); + copyUShort(h.compression_method, compression_method); + copyUInt(h.last_mod_file, last_mod_file); + copyUInt(h.crc_32, crc_32); + copyUInt(h.compressed_size, compressed_size); + copyUInt(h.uncompressed_size, uncompressed_size); + copyUShort(h.file_name_length, file_name_length); + copyUShort(h.extra_field_length, extra_field_length); + return h; +} + +ZipWriter::ZipWriter(const QString &fileName) +{ + zipFile = new QFile(fileName); + zipFile->open(QIODevice::WriteOnly); + if (zipFile->error() == QFile::NoError) + status = ZipWriter::NoError; + else + { + if(zipFile->error() == QFile::WriteError) + status = ZipWriter::FileWriteError; + else if (zipFile->error() == QFile::OpenError) + status = ZipWriter::FileOpenError; + else if (zipFile->error() == QFile::PermissionsError) + status = ZipWriter::FilePermissionsError; + else + status = ZipWriter::FileError; + } + start_of_directory = 0; +} + +ZipWriter::~ZipWriter() +{ +} + +void ZipWriter::addFile(const QString &fileName, const QByteArray &data) +{ + addEntry(ZipWriter::File, fileName, data); +} + +void ZipWriter::addDirectory(const QString &dirName) +{ + QString name = dirName; + if (!name.endsWith(QDir::separator())) + name.append(QDir::separator()); + addEntry(ZipWriter::Directory, name, QByteArray()); +} + +void ZipWriter::addSymLink(const QString &fileName, const QString &destination) +{ + addEntry(ZipWriter::Symlink, fileName, QFile::encodeName(destination)); +} + +void ZipWriter::addEntry(EntryType type, const QString &fileName, const QByteArray &content) +{ + if (!(zipFile->isOpen()||zipFile->open(QIODevice::WriteOnly))) + { + status = ZipWriter::FileOpenError; + return; + } + zipFile->seek(start_of_directory); + + ZipWriter::CompressionPolicy c = cp; + if (cp == ZipWriter::AutoCompress) + { + if (content.length() < 64) + c = ZipWriter::NeverCompress; + else + c = ZipWriter::AlwaysCompress; + } + + FileHeader header; + memset(&header.h, 0, sizeof(CentralFileHeader)); + writeUInt(header.h.signature, 0x02014b50); + + writeUShort(header.h.version_needed, 0x14); + writeUInt(header.h.uncompressed_size, content.length()); + writeMSDosDate(header.h.last_mod_file, QDateTime::currentDateTime()); + QByteArray data = content; + if (c == ZipWriter::AlwaysCompress) + { + writeUShort(header.h.compression_method, 8); + + ulong len = content.length(); + len += (len >> 12) + (len >> 14) + 11; + int res; + do + { + data.resize(len); + res = deflate((uchar *)data.data(), &len, (const uchar*) content.constData(), content.length()); + + switch(res) + { + case Z_OK: + data.resize(len); + break; + case Z_MEM_ERROR: + qWarning("ZipWriter: Not Enough memory to compress the file, skipping"); + data.resize(0); + break; + case Z_BUF_ERROR: + len *= 2; + break; + } + } while(res == Z_BUF_ERROR); + } + + writeUInt(header.h.compressed_size, data.length()); + uint crc_32 = ::crc32(0, 0, 0); + crc_32 = ::crc32(crc_32, (const uchar*)content.constData(), content.length()); + writeUInt(header.h.crc_32, crc_32); + + header.file_name = fileName.toLocal8Bit(); + if(header.file_name.size() > 0xffff) + { + qWarning("Filename too long"); + header.file_name = header.file_name.left(0xffff); + } + + writeUShort(header.h.file_name_length, header.file_name.length()); + + writeUShort(header.h.version_made, 3 << 8); + + quint32 mode = permissionsToMode(permissions); + switch(type) + { + case File: mode |= S_IFREG; break; + case Directory: mode |= S_IFDIR; break; + case Symlink: mode |= S_IFLNK; break; + } + writeUInt(header.h.external_file_attributes, mode << 16); + writeUInt(header.h.offset_local_header, start_of_directory); + + fileHeaders.append(header); + + LocalFileHeader h = header.h.toLocalHeader(); + zipFile->write((const char*)&h, sizeof(LocalFileHeader)); + zipFile->write(header.file_name); + zipFile->write(data); + start_of_directory = zipFile->pos(); + +} + +bool ZipWriter::isWritable() const +{ + return zipFile->isWritable(); +} + +bool ZipWriter::exists() const +{ + return zipFile->exists(); +} + +ZipWriter::Status ZipWriter::getStatus() const +{ + return status; +} + +void ZipWriter::setCompressionPolicy(CompressionPolicy policy) +{ + cp = policy; +} + +ZipWriter::CompressionPolicy ZipWriter::compressionPolicy() const +{ + return cp; +} + +void ZipWriter::setCreationPermissions(QFile::Permissions permissions) +{ + this->permissions = permissions; +} + +QFile::Permissions ZipWriter::creationPermissions() const +{ + return permissions; +} + +void ZipWriter::close() +{ + if( !(zipFile->openMode() &QIODevice::WriteOnly) ) + { + zipFile->close(); + delete zipFile; + return; + } + + zipFile->seek(start_of_directory); + + for (int i=0; iwrite((const char*)&header.h, sizeof(CentralFileHeader)); + zipFile->write(header.file_name); + zipFile->write(header.extra_field); + zipFile->write(header.file_comment); + } + int dir_size = zipFile->pos() - start_of_directory; + EndOfDirectory eod; + memset(&eod, 0, sizeof(EndOfDirectory)); + writeUInt(eod.signature, 0x06054b50); + writeUShort(eod.num_dir_entries_this_disk, fileHeaders.size()); + writeUShort(eod.num_dir_entries, fileHeaders.size()); + writeUInt(eod.directory_size, dir_size); + writeUInt(eod.dir_start_offset, start_of_directory); + writeUShort(eod.comment_length, 0);//no comment + + zipFile->write((const char*)&eod, sizeof(EndOfDirectory)); + zipFile->write(QByteArray()); + zipFile->close(); + delete zipFile; +} diff --git a/src/MdCharm/util/zip/zipwriter.h b/src/MdCharm/util/zip/zipwriter.h new file mode 100644 index 0000000..1c3ede8 --- /dev/null +++ b/src/MdCharm/util/zip/zipwriter.h @@ -0,0 +1,64 @@ +#ifndef ZIPWRITER_H +#define ZIPWRITER_H + +#include + +struct FileHeader; + +class ZipWriter +{ +public: + ZipWriter(const QString &fileName); + ~ZipWriter(); + + bool isWritable() const; + bool exists() const; + + enum Status { + NoError, + FileWriteError, + FileOpenError, + FilePermissionsError, + FileError + }; + + Status getStatus() const; + + enum CompressionPolicy { + AlwaysCompress, + NeverCompress, + AutoCompress + }; + + enum EntryType { + Directory, + File, + Symlink + }; + + void setCompressionPolicy(CompressionPolicy policy); + CompressionPolicy compressionPolicy() const; + + void setCreationPermissions(QFile::Permissions permissions); + QFile::Permissions creationPermissions() const; + + void addEntry(EntryType type, const QString &fileName, const QByteArray &content); + + void addFile(const QString &fileName, const QByteArray &data); + + void addDirectory(const QString &dirName); + + void addSymLink(const QString &fileName, const QString &destination); + + void close(); +private: + Status status; + CompressionPolicy cp; + QFile::Permissions permissions; + QFile *zipFile; + QList fileHeaders; + uint start_of_directory; + Q_DISABLE_COPY(ZipWriter) +}; + +#endif // ZIPWRITER_H diff --git a/src/MdCharm/utils.cpp b/src/MdCharm/utils.cpp new file mode 100644 index 0000000..17d7a34 --- /dev/null +++ b/src/MdCharm/utils.cpp @@ -0,0 +1,582 @@ +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "resource.h" +#include "util/spellcheck/spellchecker.h" + +//------------------------ MdCharmGlobal --------------------------------------- + +MdCharmGlobal *MdCharmGlobal::instance = NULL; + +MdCharmGlobal* MdCharmGlobal::getInstance() +{ + if(instance==NULL) + instance = new MdCharmGlobal(); + return instance; +} + +MdCharmGlobal::MdCharmGlobal() +{ + conf = Configuration::getInstance(); + MdCharm_ShortCut_Close_Tab = QKeySequence(conf->getKeyboardShortcut(ShortcutCloseTab)); + MdCharm_ShortCut_Italic = QKeySequence(conf->getKeyboardShortcut(ShortcutItalic)); + MdCharm_ShortCut_Bold = QKeySequence(conf->getKeyboardShortcut(ShortcutBold)); + MdCharm_ShortCut_Quote_Text = QKeySequence(conf->getKeyboardShortcut(ShortcutQuoteText)); + MdCharm_ShortCut_Shift_Tab = QKeySequence(conf->getKeyboardShortcut(ShortcutShiftTab)); + MdCharm_ShortCut_Strike_Through = QKeySequence(conf->getKeyboardShortcut(ShortcutStrikeThrough)); + MdCharm_ShortCut_Insert_Link = QKeySequence(conf->getKeyboardShortcut(ShortcutInsertLink)); + MdCharm_ShortCut_Insert_Picture = QKeySequence(conf->getKeyboardShortcut(ShortcutInsertPicture)); + MdCharm_ShortCut_Cheat_Sheet = QKeySequence(conf->getKeyboardShortcut(ShortcutCheatSheet)); + MdCharm_ShortCut_Insert_Code = QKeySequence(conf->getKeyboardShortcut(ShortcutInsertCode)); + MdCharm_ShortCut_New = QKeySequence(conf->getKeyboardShortcut(ShortcutNew)); + MdCharm_ShortCut_Open = QKeySequence(conf->getKeyboardShortcut(ShortcutOpen)); + MdCharm_ShortCut_Save = QKeySequence(conf->getKeyboardShortcut(ShortcutSave)); + MdCharm_ShortCut_SaveAs = QKeySequence(conf->getKeyboardShortcut(ShortcutSaveAs)); + MdCharm_ShortCut_Print = QKeySequence(conf->getKeyboardShortcut(ShortcutPrint)); + MdCharm_ShortCut_Quit = QKeySequence(conf->getKeyboardShortcut(ShortcutQuit)); + MdCharm_ShortCut_Undo = QKeySequence(conf->getKeyboardShortcut(ShortcutUndo)); + MdCharm_ShortCut_Redo = QKeySequence(conf->getKeyboardShortcut(ShortcutRedo)); + MdCharm_ShortCut_Cut = QKeySequence(conf->getKeyboardShortcut(ShortcutCut)); + MdCharm_ShortCut_Copy = QKeySequence(conf->getKeyboardShortcut(ShortcutCopy)); + MdCharm_ShortCut_Paste = QKeySequence(conf->getKeyboardShortcut(ShortcutPaste)); + MdCharm_ShortCut_Select_All = QKeySequence(conf->getKeyboardShortcut(ShortcutSelectAll)); + MdCharm_ShortCut_Find = QKeySequence(conf->getKeyboardShortcut(ShortcutFind)); + MdCharm_ShortCut_Find_Previous = QKeySequence(conf->getKeyboardShortcut(ShortcutFindPrevious)); + MdCharm_ShortCut_Find_Next = QKeySequence(conf->getKeyboardShortcut(ShortcutFindNext)); + MdCharm_ShortCut_Help_Contents = QKeySequence(conf->getKeyboardShortcut(ShortcutHelpContents)); + MdCharm_ShortCut_Print_Preview = QKeySequence(conf->getKeyboardShortcut(ShortcutPrintPreview)); + MdCharm_ShortCut_Hide_Project_DockBar = QKeySequence(conf->getKeyboardShortcut(ShortcutHideProjectDockBar)); + + //Spell Checker + spellCheckerLanguageList = conf->getAllAvailableSpellCheckDictNames(); + foreach (QString dictName, spellCheckerLanguageList) { + QLocale dictLocale(dictName); + cacheSpellCheckDictLocaleName.insert(dictName, + QString("%1 (%2-%3)") + .arg(dictName) + .arg(dictLocale.nativeLanguageName()) + .arg(dictLocale.nativeCountryName())); + } + loadSpellCheck(conf->getSpellCheckLanguage()); +} + +MdCharmGlobal::~MdCharmGlobal() +{ + foreach (SpellChecker *sc, spellCheckerManager.values()) { + delete sc; + } +} + +bool MdCharmGlobal::loadSpellCheck(const QString &lan) +{ + if(spellCheckerManager.contains(lan)){ + return true; + } + if(conf->isCheckSpell() && + !lan.isEmpty() && + !spellCheckerLanguageList.isEmpty()&& + spellCheckerLanguageList.indexOf(lan)!=-1){ + QString filePath = conf->getLanguageSpellCheckDictPath(lan); + if(!filePath.isEmpty()){ + QString withoutSuffixFilePath = filePath.left(filePath.lastIndexOf('.')); + spellCheckerManager[lan] = new SpellChecker(withoutSuffixFilePath, conf->getLanguageSpellCheckUserDictPath(), lan); + return true; + } else { + conf->setCheckSpell(false); + conf->setSpellCheckLanguage(QString()); + return false; + } + } else { + return false; + } +} + +SpellChecker* MdCharmGlobal::getSpellChecker(const QString &lan) +{ + if(lan.isEmpty()) + return NULL; + if(spellCheckerManager.contains(lan)) + return spellCheckerManager.value(lan); + if(spellCheckerLanguageList.indexOf(lan)==-1) + return NULL; + if(!loadSpellCheck(lan)) + return NULL; + return spellCheckerManager.value(lan); +} + +QStringList MdCharmGlobal::getSpellCheckerLanguageList() +{ + return spellCheckerLanguageList; +} + +QString MdCharmGlobal::getDictLocaleName(const QString &dictName) +{ + return cacheSpellCheckDictLocaleName.value(dictName); +} + +QString MdCharmGlobal::getShortDescriptionText(int s) +{ + switch (s) { + case ShortcutCloseTab: + return tr("Close tab"); + break; + case ShortcutItalic: + return tr("Italic text"); + break; + case ShortcutBold: + return tr("Bold text"); + break; + case ShortcutQuoteText: + return tr("Quote a block of text"); + break; + case ShortcutShiftTab: + return tr("Untab a block of text"); + break; + case ShortcutStrikeThrough: + return tr("Strike through text"); + break; + case ShortcutInsertLink: + return tr("Insert link"); + break; + case ShortcutInsertPicture: + return tr("Insert picture"); + break; + case ShortcutCheatSheet: + return tr("Open cheat-sheet"); + break; + case ShortcutInsertCode: + return tr("Insert code"); + break; + case MdCharmGlobal::ShortcutNew: + return tr("New"); + break; + case MdCharmGlobal::ShortcutOpen: + return tr("Open"); + break; + case MdCharmGlobal::ShortcutSave: + return tr("Save"); + break; + case MdCharmGlobal::ShortcutSaveAs: + return tr("SaveAs"); + break; + case MdCharmGlobal::ShortcutPrint: + return tr("Print"); + break; + case MdCharmGlobal::ShortcutQuit: + return tr("Quit"); + break; + case MdCharmGlobal::ShortcutUndo: + return tr("Undo"); + break; + case MdCharmGlobal::ShortcutRedo: + return tr("Redo"); + break; + case MdCharmGlobal::ShortcutCut: + return tr("Cut"); + break; + case MdCharmGlobal::ShortcutCopy: + return tr("Copy"); + break; + case MdCharmGlobal::ShortcutPaste: + return tr("Paste"); + break; + case MdCharmGlobal::ShortcutSelectAll: + return tr("Select All"); + break; + case MdCharmGlobal::ShortcutFind: + return tr("Find"); + break; + case MdCharmGlobal::ShortcutFindPrevious: + return tr("Find Previous"); + break; + case MdCharmGlobal::ShortcutFindNext: + return tr("Find Next"); + break; + case MdCharmGlobal::ShortcutHelpContents: + return tr("Help"); + break; + case MdCharmGlobal::ShortcutPrintPreview: + return tr("Print Preview"); + break; + case MdCharmGlobal::ShortcutHideProjectDockBar: + return tr("Hide Project Dockbar"); + break; + default: + Q_ASSERT(0 && "This should not be happend"); + return ""; + break; + } +} + +//------------------ Utils ----------------------------------------------------- + +QString Utils::AppName = QString::fromLatin1("MdCharm"); +const char* Utils::AppNameCStr = "MdCharm"; + +QString Utils::getSaveFileName(const QString &suffix, QWidget *parent, + const QString &caption, const QString &dir, + const QString &filter, QString *selectedFilter) +{ + QString filePath; +#if defined(Q_OS_WIN) + Q_UNUSED(suffix) + filePath = QFileDialog::getSaveFileName(parent, + caption, + dir, + filter, + selectedFilter); +#elif defined(Q_OS_LINUX) + //TODO: multi suffix + do + { + filePath = QFileDialog::getSaveFileName(parent, + caption, + dir, + filter, + selectedFilter, + QFileDialog::DontConfirmOverwrite); + if(filePath.isEmpty()) + break; //cancel + if(QFileInfo(filePath).suffix().isEmpty()) + filePath.append(suffix); + QString fileName = QFileInfo(filePath).fileName(); + QFile confirmFile(filePath); + if(confirmFile.exists())//confirm overwrite + { + QMessageBox::StandardButton ret = QMessageBox::question(parent, + QObject::tr("Save"), + QObject::tr("A file named \"%1\" already exists.\n Do you want to replace it?").arg(fileName), + QMessageBox::Yes|QMessageBox::No); + if(ret==QMessageBox::Yes) + break; + else + filePath.clear(); + } + } while(filePath.isEmpty()); +#elif defined(Q_OS_MAC) + assert(0);//TODO: MAC OS X +#endif + return filePath; +} + +QString Utils::getExistingDirectory(QWidget *parent, const QString &caption, + const QString &dir, + QFileDialog::Options options) +{ + return QFileDialog::getExistingDirectory(parent, caption, dir, options).replace('\\','/'); +} + +bool Utils::saveFile(const QString &fileFullPath, const QString &content) +{ + if(fileFullPath.isEmpty()) + return false; + QFile file(fileFullPath); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + Utils::showFileError(file.error(), fileFullPath); + return false; + } + if(-1 == file.write(content.toLocal8Bit()) ) // is it ok ? + { + Utils::showFileError(file.error(), fileFullPath); + } + file.close(); + return true; +} + +bool Utils::saveFile(const QString &fileFullPath, const QByteArray &content) +{ + if(fileFullPath.isEmpty()) + return false; + QFile file(fileFullPath); + if( !file.open(QIODevice::WriteOnly|QIODevice::Text)) + { + Utils::showFileError(file.error(), fileFullPath); + return false; + } + if(-1 == file.write(content) ) + { + Utils::showFileError(file.error(), fileFullPath); + } + file.close(); + return true; +} + +void Utils::showFileError(QFile::FileError error, const QString &filePath) +{ + QString path = filePath; +#ifdef Q_OS_WIN + path.replace("/", "\\");//keep this +#endif + QString tip; + switch(error) + { + case QFile::NoError: + return; + break; + case QFile::ReadError: + tip = QObject::tr("Can't Read File %1").arg(path); + break; + case QFile::WriteError: + tip = QObject::tr("Can't Write File %1").arg(path); + break; + case QFile::OpenError: + tip = QObject::tr("Can't Open File %1").arg(path); + break; + case QFile::PermissionsError: + tip = QObject::tr("Can't Open/Write This File %1. Permission Denied!").arg(path); + break; + default: + tip = QObject::tr("Can't Open This File"); + break; + } + QMessageBox::critical(0, "File Error", tip); +} + +QByteArray Utils::encodeString(const QString src, const QString &codecName, bool addBom) +{ + QTextEncoder *encoder; + if(codecName.isEmpty()) + encoder = QTextCodec::codecForName("System")->makeEncoder(); + else + encoder = QTextCodec::codecForName(codecName.toStdString().c_str())->makeEncoder(); + if(addBom) + return encoder->fromUnicode(src); + QByteArray source = encoder->fromUnicode(src); + QByteArray bom; + if(codecName == QString::fromLatin1("UTF-8")) + { + bom.resize(3); + bom[0] = (unsigned char)0xEF; + bom[1] = (unsigned char)0xBB; + bom[2] = (unsigned char)0xBF; + } else if (codecName == QString::fromLatin1("UTF-16LE")) + { + bom.resize(3); + bom[0] = (unsigned char)0xFE; + bom[1] = (unsigned char)0xFF; + bom[2] = 0x00; + } else if (codecName == QString::fromLatin1("UTF-16BE")) + { + bom.resize(3); + bom[0] = (unsigned char)0xFF; + bom[1] = (unsigned char)0xFE; + bom[2] = 0x00; + } + //TODO: utf-32le and utf32-be + if(source.startsWith(bom)) + { + source = source.remove(0, bom.length());//remove bom + } + return source; +} + +bool Utils::isUtf8WithoutBom(const QByteArray &content) +{ + if(content.isEmpty()) + { + return false; + } + int start = 0; + int end = content.length(); + bool isUTF8NoBom = true; + while(start=end-1) + break; + if(content[start+1]<=QChar(0x80) || content[start+1]>=QChar(0xC0)) + { + isUTF8NoBom = false; + break; + } + start += 2; + } + else if (content[start] < QChar(0x80 + 0x40 + 0x20 + 0x10))//1111NNNN 0xF0 + { + if (start>=end-2) + break; + //TODO: is last 2 char encoding in utf8? + start += 3; + } + else + { + break; + } + } + return isUTF8NoBom; +} + +bool Utils::isMarkdownFile(const QString &fileName) +{ + QStringList exts = Configuration::getInstance()->getFileExtension(); + if(exts.length()<=Configuration::MarkdownFile) + return false; + QRegExp extRe(exts.value(Configuration::MarkdownFile).replace('.', "\\.").replace('*', ".*"), Qt::CaseInsensitive); + extRe.setMinimal(false); + return extRe.exactMatch(fileName); +} + +QFileInfoList Utils::listAllFileInDir(const QString &dirPath, const QStringList &nameFilter, QDir::Filters filters, bool isListSubdir) +{ + QFileInfoList l; + QDirIterator it(dirPath, nameFilter, filters, isListSubdir ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags); + while(it.hasNext()){ + it.next(); + l.append(it.fileInfo()); + } + return l; +} + +QString Utils::checkOrAppendDefaultSuffix(MdCharmGlobal::WikiType type, const QString &fileName) +{ + switch(type) + { + case MdCharmGlobal::Markdown: + if(Utils::isMarkdownFile(fileName)) + return fileName; + return fileName+".md"; + default: + return fileName; + } +} + +QStringList Utils::getEncodingList() +{ + QList cl = QTextCodec::availableCodecs(); + QStringList sl; + for(int i=0; iname(); + if(cn==QByteArray("System")) + {//check if it is utf8 without bom + openFile.seek(0); + if(Utils::isUtf8WithoutBom(openFile.readAll())) + { + openFileStream.setCodec("UTF-8"); + openFileStream.seek(0); + fileContent = openFileStream.readAll(); + } + } + openFile.close(); + return fileContent; +} + +QString Utils::getHtmlTemplate() +{ + return Utils::readFile(":/markdown/markdown.html"); +} diff --git a/src/MdCharm/utils.h b/src/MdCharm/utils.h new file mode 100644 index 0000000..2a82286 --- /dev/null +++ b/src/MdCharm/utils.h @@ -0,0 +1,180 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include +#include +#include +#include +#include +#include + +#include "markdowntohtml.h" + +class SpellChecker; +class Configuration; + +class MdCharmGlobal : public QWidget +{ + Q_OBJECT +public: + enum SaveFileOptions + { + Save, + DontSave, + Cancel, + None + }; + enum UpdatesInfo + { + Version=0, + Revision, + UpdateXmlUrl, + IsRecommand, + Warning + }; + enum WikiType + { + Markdown + }; + enum UTF8BOM + { + Add, + Keep, + Delete + }; + + enum PreviewOptionMenu + { + PreviewOptionMenu_WriteMode, + PreviewOptionMenu_WriteRead, + PreviewOptionMenu_ReadMode + }; + enum PreviewOption + { + WriteMode, + WriteRead, + ReadMode + }; + + enum Shortcuts + { + //Warning: Don't change the order + ShortcutCloseTab, + ShortcutItalic, + ShortcutBold, + ShortcutQuoteText, + ShortcutShiftTab, + ShortcutStrikeThrough, + ShortcutInsertLink, + ShortcutInsertPicture, + ShortcutCheatSheet, + ShortcutInsertCode, + + ShortcutNew, + ShortcutOpen, + ShortcutSave, + ShortcutSaveAs, + ShortcutPrint, + ShortcutQuit, + ShortcutUndo, + ShortcutRedo, + ShortcutCut, + ShortcutCopy, + ShortcutPaste, + ShortcutSelectAll, + ShortcutFind, + ShortcutFindPrevious, + ShortcutFindNext, + ShortcutPrintPreview, + + ShortcutHideProjectDockBar, + + ShortcutEnd, + + ShortcutHelpContents, + ShortcutTabBlockText//not real a shortcut + }; + + QKeySequence MdCharm_ShortCut_Close_Tab; + QKeySequence MdCharm_ShortCut_Italic; + QKeySequence MdCharm_ShortCut_Bold; + QKeySequence MdCharm_ShortCut_Quote_Text; + QKeySequence MdCharm_ShortCut_Shift_Tab; + QKeySequence MdCharm_ShortCut_Strike_Through; + QKeySequence MdCharm_ShortCut_Insert_Link; + QKeySequence MdCharm_ShortCut_Insert_Picture; + QKeySequence MdCharm_ShortCut_Cheat_Sheet; + QKeySequence MdCharm_ShortCut_Insert_Code; + QKeySequence MdCharm_ShortCut_New; + QKeySequence MdCharm_ShortCut_Open; + QKeySequence MdCharm_ShortCut_Save; + QKeySequence MdCharm_ShortCut_SaveAs; + QKeySequence MdCharm_ShortCut_Print; + QKeySequence MdCharm_ShortCut_Quit; + QKeySequence MdCharm_ShortCut_Undo; + QKeySequence MdCharm_ShortCut_Redo; + QKeySequence MdCharm_ShortCut_Cut; + QKeySequence MdCharm_ShortCut_Copy; + QKeySequence MdCharm_ShortCut_Paste; + QKeySequence MdCharm_ShortCut_Select_All; + QKeySequence MdCharm_ShortCut_Find; + QKeySequence MdCharm_ShortCut_Find_Previous; + QKeySequence MdCharm_ShortCut_Find_Next; + QKeySequence MdCharm_ShortCut_Help_Contents; + QKeySequence MdCharm_ShortCut_Print_Preview; + QKeySequence MdCharm_ShortCut_Hide_Project_DockBar; + +private: + static MdCharmGlobal *instance; + Configuration *conf; + QMap spellCheckerManager; + QStringList spellCheckerLanguageList; + QMap cacheSpellCheckDictLocaleName; +private: + MdCharmGlobal(); + ~MdCharmGlobal(); + bool loadSpellCheck(const QString &lan); +public: + static MdCharmGlobal *getInstance(); + SpellChecker* getSpellChecker(const QString &lan); + QStringList getSpellCheckerLanguageList(); + QString getDictLocaleName(const QString &dictName); + QString getShortDescriptionText(int s); +}; + +class Utils +{ +public: + static QString getSaveFileName(const QString &suffix, QWidget *parent=0, + const QString &caption=QString(), + const QString &dir = QString(), + const QString &filter=QString(), + QString *selectedFilter=0); + static QString getExistingDirectory(QWidget* parent=0, const QString &caption=QString(), + const QString &dir=QString(), + QFileDialog::Options options= QFileDialog::ShowDirsOnly); + static bool saveFile(const QString &fileFullPath, const QString &content); + static bool saveFile(const QString &fileFullPath, const QByteArray &content); + static void showFileError(QFile::FileError error, const QString &filePath); + static QByteArray encodeString(const QString src, const QString &codecName, bool addBom=false); + static QString checkOrAppendDefaultSuffix(MdCharmGlobal::WikiType type, const QString &fileName); + static QStringList getEncodingList(); + static bool isUtf8WithoutBom(const QByteArray &content); + static bool isMarkdownFile(const QString &fileName); + static QFileInfoList listAllFileInDir(const QString &filePath, + const QStringList &nameFilter, + QDir::Filters filters=QDir::NoFilter, + bool isListSubdir = false); + static bool calculateBom(bool hasBom, const QString &encoding, MdCharmGlobal::UTF8BOM ub); + static bool isLetterOrNumberString(const QString &str); + static void showInGraphicalShell(const QString &path); + static QString translateMarkdown2Html(MarkdownToHtml::MarkdownType type, const QString &content); + static QString readFile(const QString &filePath); + static QString getHtmlTemplate(); +public: + static QString AppName; + static const char* AppNameCStr; +}; + +#endif // UTILS_H diff --git a/src/MdCharm/version.bat b/src/MdCharm/version.bat new file mode 100644 index 0000000..5d6f893 --- /dev/null +++ b/src/MdCharm/version.bat @@ -0,0 +1 @@ +python.exe ../../src/src/version_h.py C:/Progra~1/Git/bin/git.exe ../../src/src/version.h \ No newline at end of file diff --git a/src/MdCharm/version_h.py b/src/MdCharm/version_h.py new file mode 100644 index 0000000..a4b870f --- /dev/null +++ b/src/MdCharm/version_h.py @@ -0,0 +1,110 @@ +import os +import sys +from datetime import datetime, tzinfo + +version_h = ''' +#ifndef VERSION_H +#define VERSION_H + +#define STRINGIFY_INTERNAL(x) #x +#define STRINGIFY(x) STRINGIFY_INTERNAL(x) + +#define VERSION {0} +const char* const VERSION_STR = STRINGIFY(VERSION); + +#define VERSION_MAJOR {1} +const char* const VERSION_MAJOR_STR = STRINGIFY(VERSION_MAJOR); +#define VERSION_MINOR {2} +const char* const VERSION_MINOR_STR = STRINGIFY(VERSION_MINOR); +#define VERSION_RELEASE {3} +const char* const VERSION_RELEASE_STR = STRINGIFY(VERSION_RELEASE); + +#define REVISION {4} +const char* const REVISION_STR = STRINGIFY(REVISION); + +#define BUILT_TIME {5} +const char* const BUILT_TIME_STR = STRINGIFY(BUILT_TIME); + +#undef REVISION +#undef BUILT_TIME +#undef STRINGIFY_INTERNAL +#undef STRINGIFY + +#endif +''' +mdcharm_rc = ''' +IDI_ICON1 ICON DISCARDABLE "mdcharm.ico" +IDI_ICON2 ICON DISCARDABLE "markdown\markdown.ico" + +1 VERSIONINFO +FILEVERSION {0},{1},{2},{3} +PRODUCTVERSION {0},{1},{2},{3} +FILEOS 0x4 +FILETYPE 0x1 +{{ +BLOCK "StringFileInfo" +{{ + BLOCK "040904E4" + {{ + VALUE "CompanyName", "MdCharm" + VALUE "FileDescription", "MdCharm-Wiki Editor [msvc]" + VALUE "FileVersion", "{4}" + VALUE "InternalName", "MdCharm" + VALUE "LegalCopyright", "(C) MdCharm. 2012-2013" + VALUE "LegalTrademarks", "" + VALUE "OriginalFilename", "MdCharm" + VALUE "ProductName", "MdCharm" + VALUE "ProductVersion", "{4}" + VALUE "Comments", "" + VALUE "Aditional Notes", "http://www.mdcharm.com/" + }} +}} +BLOCK "VarFileInfo" +{{ + VALUE "Translation",0x0409,0x04E4 +}} +}} +''' +''' +Usage: version_h.py git_bin_path version_file_path debug|release, like + version_h.py C:/Progra~1/Git/bin/git.exe version.h ../res/mdcharm.rc debug +''' + +if __name__ == '__main__': + if len(sys.argv)!=5: + sys.exit(-100) + if sys.argv[4]=="debug": + if os.path.isfile(sys.argv[2]): + print 'Already Exist' + sys.exit(0) + GitPath = sys.argv[1] + revisionOutput = os.popen(GitPath + ' log -1 --format="%H"') + revision = None + if revisionOutput: + revision = str(revisionOutput.read()).strip() + else: + sys.exit(-99) + tagOutput = str(os.popen(GitPath + ' tag').read()).strip() + tag = None + if tagOutput: + tag = tagOutput.replace('\r\n', '\n').split('\n')[-1] + else: + sys.exit(-98) + if len(tag.split('.')) != 3: + sys.exit(-97) + versionList = tag.split('.') + ntimeStr = datetime.now().strftime('%B %d %Y %H:%M:%S +0800') + RealVersionH = version_h.strip().format(tag, versionList[0], versionList[1], + versionList[2], revision, ntimeStr) + resultFile = file(sys.argv[2], 'w+') + resultFile.write(RealVersionH) + resultFile.close() + RealRcFile = mdcharm_rc.format(versionList[0], versionList[1], versionList[2], 0, tag) + rcFile = file(sys.argv[3], 'w+') + rcFile.write(RealRcFile) + rcFile.close() +# print RealVersionH +# print '--------------' +# print tag +# print versionList +# print revision diff --git a/src/lib/core.pro b/src/lib/core.pro new file mode 100644 index 0000000..3a86c8f --- /dev/null +++ b/src/lib/core.pro @@ -0,0 +1,57 @@ +TEMPLATE = lib + +TARGET = core + +win32: DEFINES == _EXPORTING + +CONFIG += warn_off + +unix: CONFIG += static + +CONFIG(release, debug|release){ + markdown_parser_c.target = $$PWD/multimarkdown/src/markdown_parser.c + win32-msvc*: markdown_parser_c.commands = cmd /c $$PWD/multimarkdown/src/markdown_parser.bat + unix: markdown_parser_c.commands = $$PWD/multimarkdown/src/markdown_parser.sh + markdown_parser_c.depends = markdown_parser_c_nonexist + markdown_parser_c.CONFIG += recursive + + markdown_parser_c_nonexist.commands = @echo generating markdown_parser.c + QMAKE_EXTRA_TARGETS += markdown_parser_c markdown_parser_c_nonexist + PRE_TARGETDEPS += $$PWD/multimarkdown/src/markdown_parser.c +} + +#For pcre +INCLUDEPATH += $$PWD/pcre +CONFIG(debug, debug|release){ + LIBS += -L../debug -lmdcharm_pcre +} else { + LIBS += -L../release -lmdcharm_pcre +} + +#end pcre + +CONFIG(release, debug|release){ + DESTDIR = ../release +} else { + DESTDIR = ../debug +} + + +include(multimarkdown/src/multimarkdown_lib.pri) +include(../lib/markdown/markdown.pro) + +INCLUDEPATH += multimarkdown/src + +HEADERS += \ + core/markdowntohtml.h \ + core/languagedefinationxmlparser.h \ + core/highlighter.h \ + core/codesyntaxhighlighter.h + +SOURCES += \ + core/markdowntohtml.cpp \ + core/languagedefinationxmlparser.cpp \ + core/highlighter.cpp \ + core/codesyntaxhighlighter.cpp + + diff --git a/src/lib/core/codesyntaxhighlighter.cpp b/src/lib/core/codesyntaxhighlighter.cpp new file mode 100644 index 0000000..0852851 --- /dev/null +++ b/src/lib/core/codesyntaxhighlighter.cpp @@ -0,0 +1,310 @@ +#include "codesyntaxhighlighter.h" +#include "languagedefinationxmlparser.h" + +using namespace std; + +LanguageManager* LanguageManager::instance = NULL; + +LanguageManager::LanguageManager() +{ +} + +LanguageManager::~LanguageManager() +{ + for(map::iterator it = languages.begin(); it!=languages.end(); it++){ + delete (*it).second; + } +} + +void LanguageManager::addLanguage(const string &name, char *content) +{ + LanguageDefinationXmlParser ldxp; + Language *lan = ldxp.startParse(name.c_str(), content); + lan->compileLanguage(); + languages[name] = lan; +// lan->printDebugInfo(); +} + +Language* LanguageManager::getLanguage(const string &name) +{ + string realLan = name; + if(name=="c") + realLan = "cpp"; + else if(name=="html") + realLan = "xml"; + else if(name=="js") + realLan = "javascript"; + map::iterator it = languages.find(realLan); + if(it==languages.end()) + return NULL; + return (*it).second; +} + +LanguageManager* LanguageManager::getInstance() +{ + if(!instance) + instance = new LanguageManager(); + return instance; +} + +CodeSyntaxHighlighter::CodeSyntaxHighlighter() +{ + relevance = 0; + top = NULL; +} + +const string& CodeSyntaxHighlighter::highlight(const char *name, int len, const char *code, int codeLen) +{ + LanguageManager *lanManger = LanguageManager::getInstance(); + Language *targetLanguage = lanManger->getLanguage(string(name, len)); + if(!targetLanguage){ + result = escape(code, codeLen); + return result; + } + return highlight(targetLanguage, code, codeLen); +} + +const string& CodeSyntaxHighlighter::highlight(Language *lan, const char *code, int len) +{ + result.clear(); + modeBuffer.clear(); + while(!parentStack.empty()) + parentStack.pop(); + top = NULL; + this->lan = lan; + relevance = 0; + int index = 0; + while(true){ + FindResult fr; + if(top){ + top->getTerminatorsRe().setLastIndex(index); + fr = top->getTerminatorsRe().exec(code, len); + } else { + lan->getTerminatorsRe().setLastIndex(index); + fr = lan->getTerminatorsRe().exec(code, len); + } + if(!fr.isValid()) + break; + string match(code+fr.start, fr.end-fr.start); + int count = processLexem(string(code+index, fr.start-index), &match); + index = fr.start+count; + } + string left(code+index, len-index); + processLexem(left); + while(!parentStack.empty()){ + result.append(""); + parentStack.pop(); + } +// printf("%s\n", result.c_str()); + return result; +} + +int CodeSyntaxHighlighter::processLexem(const string &subCode, const string *matchCode) +{ +// printf("subCode: %s\n", subCode.c_str()); +// printf("matchCode: %s\n", matchCode->c_str()); +// return 0; + modeBuffer += subCode; + if(matchCode==NULL){ + result += processBuffer(); + return 0; + } + Contain* con = NULL; + if(top){ + con = top->findMatchedContain(*matchCode); + if(!con && top->isRefLanguageContains()) + con = lan->findMatchedContain(*matchCode); + } else { + con = lan->findMatchedContain(*matchCode); + } + if(con){ + result += processBuffer(); + processMatch(con, *matchCode); + return con->isReturnBegin() ? 0 : matchCode->length(); + } + + Contain *endContain = findEndContain(top, *matchCode); + if(endContain){ + if(!(endContain->isReturnEnd()||endContain->isExcludeEnd())){ + modeBuffer += *matchCode; + } + result += processBuffer(); + while(top!=endContain->getParent() && top){ + if(top->isShowClassName()) + result += ""; + if(!parentStack.empty()){ + top = parentStack.top(); + parentStack.pop(); + } else { + top = NULL; + } + } +// if(parentStack.empty()) +// top = NULL; + if(endContain->isExcludeEnd()){ + result += escape(matchCode->c_str(), matchCode->length()); + } + modeBuffer.clear(); + if(endContain->getStarts()) + processMatch(endContain->getStarts(), ""); + return endContain->isReturnEnd() ? 0 : matchCode->length(); + } + modeBuffer += *matchCode; + return matchCode->length()>0 ? matchCode->length() : 1; +} + +string CodeSyntaxHighlighter::processBuffer() +{ + //language no sub language + if(top) + return top->isHaveSubLanguage() ? processSubLanguage(top) : processKeywords(top); + else + return processKeywords(); +} + +string CodeSyntaxHighlighter::processKeywords() +{ + string buffer = escape(modeBuffer.c_str(), modeBuffer.length()); + if(lan->getKeywords().empty()) + return buffer; + string keywordResult; + int lastIndex = 0; + lan->getLexemsRe().setLastIndex(0); + FindResult fr = lan->getLexemsRe().exec(buffer.c_str(), buffer.length()); + while(fr.isValid()){ + keywordResult.append(buffer.substr(lastIndex, fr.start-lastIndex)); + string keyword = buffer.substr(fr.start, fr.end-fr.start); + int km = keywordMatch(keyword); + if(km!=Keywords::NotFound){ + keywordResult.append("") + .append(keyword) + .append(""); + } else { + keywordResult.append(keyword); + } + lastIndex = lan->getLexemsRe().getLastIndex(); + fr = lan->getLexemsRe().exec(buffer.c_str(), buffer.length()); + } + return keywordResult + buffer.substr(lastIndex); +} + +string CodeSyntaxHighlighter::processKeywords(Contain *contain) +{ + string buffer = escape(modeBuffer.c_str(), modeBuffer.length()); + if(contain->getKeywords().empty()&&!contain->isRefLanguageKeywords()) + return buffer; + string keywordResult; + int lastIndex = 0; + contain->getLexemsRe().setLastIndex(0); + FindResult fr = contain->getLexemsRe().exec(buffer.c_str(), buffer.length()); + while(fr.isValid()){ + keywordResult.append(buffer.substr(lastIndex, fr.start-lastIndex)); + string keyword = buffer.substr(fr.start, fr.end-fr.start); + int km = keywordMatch(keyword, contain); + if(km!=Keywords::NotFound){ + keywordResult.append("") + .append(keyword) + .append(""); + } else { + keywordResult.append(keyword); + } + lastIndex = contain->getLexemsRe().getLastIndex(); + fr = contain->getLexemsRe().exec(buffer.c_str(), buffer.length()); + } + return keywordResult + buffer.substr(lastIndex); +} + +string CodeSyntaxHighlighter::processSubLanguage(Contain *top) +{ + CodeSyntaxHighlighter *subHighlighter = new CodeSyntaxHighlighter; + LanguageManager *languageManager = LanguageManager::getInstance(); + Language* sub = languageManager->getLanguage(top->getSubLanguage()); + if(!sub) + return escape(modeBuffer.c_str(), modeBuffer.length()); + string r = "getSubLanguage()+"\">"; + r += subHighlighter->highlight(sub, modeBuffer.c_str(), modeBuffer.length()); + r += ""; + delete subHighlighter; + return r; +} + +void CodeSyntaxHighlighter::processMatch(Contain *contain, const string &match) +{ + string markup; + if(contain->isShowClassName()) + markup.append("getRealName()).append("\">"); + if(contain->isReturnBegin()){ + result += markup; + modeBuffer.clear(); + } else if (contain->isExcludeBegin()){ + result += escape(match.c_str(), match.length()) + markup; + modeBuffer.clear(); + } else { + result += markup; + modeBuffer = match; + } + //TODO: top = Object.create(mode, {parent: {value: top}}); +// parentStack.push(contain); + if(top){ + contain->setParent(top); + parentStack.push(top); + } + top = contain; + relevance += contain->getRelevance(); +} + +int CodeSyntaxHighlighter::keywordMatch(const string &match, Contain *contain) +{ + string matchStr(match); + if(!lan->isCaseSensitive()){//to lower case if not case sensitive + for(unsigned int i=0;iisRefLanguageKeywords()) ? contain->matchKeyword(matchStr) : lan->matchKeyword(matchStr); +} + +Contain* CodeSyntaxHighlighter::findEndContain(Contain *contain, const string &match) +{ + if(!contain) + return NULL; + if(!contain->getEnd().empty() && contain->getEndRe().test(match.c_str(), match.length())){ + return contain; + } + if(contain->isEndWithParent()){ + return findEndContain(contain->getParent(), match); + } + return NULL; +} + +string CodeSyntaxHighlighter::escape(const char* src, int len) +{ + string result; + if(len==0) + return result; + result.resize(len*2); + result.resize(0); + int index=0; + for(int i=0; i'){ + result.append(src+index, i-index); + result.append(">"); + index = i+1; + } + } + if(len>index) + result.append(src+index, len-index); + return result; +} diff --git a/src/lib/core/codesyntaxhighlighter.h b/src/lib/core/codesyntaxhighlighter.h new file mode 100644 index 0000000..e71da76 --- /dev/null +++ b/src/lib/core/codesyntaxhighlighter.h @@ -0,0 +1,58 @@ +#ifndef CODESYNTAXHIGHLIGHTER_H +#define CODESYNTAXHIGHLIGHTER_H + +#include +#include +#include + +#ifdef MARKDOWN_LIB + #include "../../dllglobal.h" +#else + #define AS_DYNAMIC_LIB +#endif + +class Language; +class Contain; +struct StackItem; + +class AS_DYNAMIC_LIB LanguageManager +{ +public: + ~LanguageManager(); + void addLanguage(const std::string &name, char *content); + Language* getLanguage(const std::string &name); + static LanguageManager* getInstance(); +private: + LanguageManager(); +private: + std::map languages; + static LanguageManager *instance; +}; + +class CodeSyntaxHighlighter +{ +public: + CodeSyntaxHighlighter(); + const std::string& highlight(const char *name, int len, const char *code, int codeLen); + const std::string& highlight(Language* lan, const char *code, int len); +private: + int processLexem(const std::string &subCode, const std::string *matchCode=NULL); + std::string processBuffer(); + std::string processKeywords(); + std::string processKeywords(Contain *contain); + std::string processSubLanguage(Contain *top); + + void processMatch(Contain* contain, const std::string& match); + int keywordMatch(const std::string &match, Contain *contain=NULL); + std::string escape(const char *src, int len); + Contain* findEndContain(Contain *contain, const std::string& match); +private: + std::string result; + std::string modeBuffer; + Language *lan; + Contain* top; + int relevance; + std::stack parentStack; +}; + +#endif // CODESYNTAXHIGHLIGHTER_H diff --git a/src/lib/core/highlighter.cpp b/src/lib/core/highlighter.cpp new file mode 100644 index 0000000..c1e1875 --- /dev/null +++ b/src/lib/core/highlighter.cpp @@ -0,0 +1,15 @@ +#include +#include + +#include "codesyntaxhighlighter.h" +#include "highlighter.h" +#include "buffer.h" + +using namespace std; + +void highlighter(struct buf *ob, const char *name, int len, const char *code, int codeLen) +{ + CodeSyntaxHighlighter highlighter; + const string& result = highlighter.highlight(name, len, code, codeLen); + bufput(ob, (const void*)result.c_str(), result.length()); +} diff --git a/src/lib/core/highlighter.h b/src/lib/core/highlighter.h new file mode 100644 index 0000000..f7dfc47 --- /dev/null +++ b/src/lib/core/highlighter.h @@ -0,0 +1,16 @@ +#ifndef HIGHLIGHTER_H +#define HIGHLIGHTER_H + +struct buf; + +#ifdef __cplusplus +extern "C" { +#endif + +void highlighter(struct buf *ob, const char *name, int len, const char *code, int codeLen); + +#ifdef __cplusplus +} +#endif + +#endif // HIGHLIGHTER_H diff --git a/src/lib/core/languagedefinationxmlparser.cpp b/src/lib/core/languagedefinationxmlparser.cpp new file mode 100644 index 0000000..131f786 --- /dev/null +++ b/src/lib/core/languagedefinationxmlparser.cpp @@ -0,0 +1,837 @@ +#include +#include +#include +#include + +#include "languagedefinationxmlparser.h" + +using namespace std; +using namespace rapidxml; +//------------------------------ RegExp ---------------------------------------- +RegExp::RegExp() +{ + re = NULL; + lastIndex = 0; +} + +bool RegExp::compile(const string &pattern, bool isCaseSensitive, bool isGlobal) +{ + const char *error; + int errorOffset; + int option = PCRE_MULTILINE; + if(!isCaseSensitive){ + option |= PCRE_CASELESS; + } + QString realPattern = QString::fromUtf8(pattern.c_str(), pattern.length()); + re = pcre16_compile(realPattern.utf16(), option, &error, &errorOffset, NULL); + assert(re); + global = isGlobal; + return re ? true : false; +} + +FindResult RegExp::exec(const char *code, int len) +{ + if(!re) + return FindResult(); + if(global && lastIndex>=len) + return FindResult(); + int spce[21]; + memset(spce, 0, 21); + QString realCode = QString::fromUtf8(code, len); + int rc = pcre16_exec(re, NULL, realCode.utf16(), len, global ? lastIndex : 0, 0, spce, 21); + if(rc<0) + return FindResult(); + else { + FindResult fr(spce[0], spce[1]); + lastIndex = spce[1]; + return fr; + } +} + +bool RegExp::test(const char *code, int len) +{ + int spec[21]; + memset(spec, 0, 21); + QString realCode = QString::fromUtf8(code, len); + int rc = pcre16_exec(re, NULL, realCode.utf16(), len, 0, 0, spec, 21); + return rc>=0; +} + + +int RegExp::getLastIndex() const +{ + return lastIndex; +} + +void RegExp::setLastIndex(int index) +{ + lastIndex = index; +} + +bool RegExp::isValid() const +{ + return re ? true : false; +} + +RegExp::~RegExp() +{ + if(re) + pcre16_free(re); +} + +//------------------------------- Keywords ------------------------------------- +Keywords::Keywords(KeywordsType t, const string &kw) +{ + type = t; + k = kw; +} + +const string& Keywords::getKeyword() +{ + return k; +} + +const char* Keywords::getKeyTypeString(int kt) +{ + static const char *errorTexts[] = { + "keyword", + "literal", + "constant", + "type", + "command", + "property", + "built_in", + "title", + "" + }; + if(kt>=8||kt<0) + return errorTexts[8]; + return errorTexts[kt]; +} + +//---------------------------- HighlightUtli ----------------------------------- +string HighlighterUtil::joinKeywords(list keywords, char sep) +{ + string result; + for(list::iterator it=keywords.begin(); it!=keywords.end(); it++){ + result.append((*it).getKeyword()); + result.push_back(sep); + } + result.erase(result.length()-1, 1); +// printf("%s\n", result.c_str()); + return result; +} + +string HighlighterUtil::joinStrings(list sl, char sep) +{ + string result; + for(list::iterator it=sl.begin(); it!=sl.end(); it++){ +// if(it->empty()) +// continue; + result.append((*it)); + result.push_back(sep); + } + result.erase(result.length()-1, 1); +// printf("%s\n", result.c_str()); + return result; +} + +//------------------------------- Contain -------------------------------------- +Contain::Contain() +{ + showClassName = true; + refLanguageKeywords = false; + endsWithParent = false; + beginWithKeyword = false; + ref = false; + returnBegin = false; + excludeBegin = false; + returnEnd = false; + excludeEnd = false; + refLanguageContains = false; + relevance = 1; + parent = NULL; + starts = NULL; +} + +Contain::~Contain() +{ +} + + + +void Contain::setShowClassName(bool s) +{ + showClassName = s; +} + +bool Contain::isShowClassName() +{ + return showClassName; +} + +void Contain::setEndWithParent(bool e) +{ + endsWithParent = e; +} + +bool Contain::isEndWithParent() +{ + return endsWithParent; +} + +void Contain::setBeginWithKeyword(bool b) +{ + beginWithKeyword = b; +} + +bool Contain::isBeginWithKeyword() +{ + return beginWithKeyword; +} + +void Contain::setRelevance(int relevance) +{ + this->relevance = relevance; +} + +int Contain::getRelevance() +{ + return relevance; +} + +void Contain::setRefLanguageKeywords(bool r) +{ + refLanguageKeywords = r; +} + +bool Contain::isRefLanguageKeywords() +{ + return refLanguageKeywords; +} + +bool Contain::isHaveSubLanguage() +{ + return !subLanguage.empty(); +} + +void Contain::setSubLanugage(const char * subLanguage) +{ + this->subLanguage = subLanguage; +} + +const string Contain::getSubLanguage() +{ + return subLanguage; +} + +void Contain::setName(const string &name) +{ + this->name = name; +} + +const char *Contain::getName() +{ + return name.c_str(); +} + +std::string Contain::getRealName() +{ +// return name; + return name.substr(0, name.find_first_of('|')); +} + +void Contain::setBegin(const string &begin) +{ + if(begin.empty()) //TODO: patch for xml whitespace, try find a more better method + this->begin.push_back(' '); + else + this->begin = begin; +} + +const string Contain::getBegin() +{ + if(!beginWithKeyword) + return begin; + return "\\b("+HighlighterUtil::joinKeywords(keywords, '|')+")\\s"; +} + +void Contain::setEnd(const string &end) +{ + if(end.empty())//TODO: same with setBegin() + this->end.push_back(' '); + else + this->end = end; +} + +const string& Contain::getEnd() +{ + return end; +} + +void Contain::setLexems(const string &lexems) +{ + this->lexems = lexems; +} + +void Contain::setIllegal(const string &illegal) +{ + this->illegal = illegal; +} + +void Contain::addKeyword(Keywords::KeywordsType kt, const string &keyword) +{ + keywords.push_back(Keywords(kt, keyword)); +} + +void Contain::addRefContain(Contain *contain) +{ + refContains.push_back(contain); +} + +void Contain::compile(Language *lan) +{ + if(keywords.size()>0 || refLanguageKeywords) + if(!lexems.empty()) + lexemsRe.compile(lexems, lan->isCaseSensitive(), true); + else + lexemsRe.compile("[a-zA-Z][a-zA-Z0-9_]*", lan->isCaseSensitive(), true); + + if(beginWithKeyword) + begin = "\\b("+HighlighterUtil::joinKeywords(keywords, '|')+")\\s"; + if(!begin.empty()) + beginRe.compile(begin, lan->isCaseSensitive()); + else + beginRe.compile("\\B|\\b", lan->isCaseSensitive()); + if(end.empty() && !endsWithParent) + end = "\\B|\\b"; + if(!end.empty()) + endRe.compile(end, lan->isCaseSensitive()); + terminatorEnd = end.empty() ? "" : end; + if(getParent() && endsWithParent && getParent()!=this && !getParent()->getTerminatorEnd().empty()) + if(!terminatorEnd.empty()) + terminatorEnd.append("|"+getParent()->getTerminatorEnd()); + else + terminatorEnd.append(getParent()->getTerminatorEnd()); + + if(!illegal.empty()) + illegalRe.compile(illegal, lan->isCaseSensitive()); + //TODO: starts + + list terminators; + for(list::iterator it=refContains.begin(); it!=refContains.end(); it++){ + Contain *contain = *it; + terminators.push_back(contain->getBegin()); + } + if(refLanguageContains){ + list& lanContains = lan->getContains(); + for(list::iterator it=lanContains.begin(); it!=lanContains.end(); it++){ + Contain *contain = *it; + if(!contain->isRef()){ + terminators.push_front(contain->getBegin()); + } + } + } + if(!terminatorEnd.empty()) + terminators.push_back(terminatorEnd); + if(!illegal.empty()) + terminators.push_back(illegal); +// printf("%s ", name.c_str()); + if(!terminators.empty()) + terminatorsRe.compile(HighlighterUtil::joinStrings(terminators, '|'), true, true); +} + +Contain* Contain::findMatchedContain(const string &match) +{ + if(parent && parent->getStarts() && parent->getStarts()==this) + return NULL; + for(list::iterator it=refContains.begin(); it!=refContains.end(); it++){ + Contain *contain = *it; + if(contain->getBeginRe().isValid()){ + FindResult fr = contain->getBeginRe().exec(match.c_str(), match.length()); + if(fr.isValid()) + return contain; + } + } + return NULL; +} + +RegExp& Contain::getBeginRe() +{ + return beginRe; +} + +RegExp& Contain::getEndRe() +{ + return endRe; +} + +RegExp& Contain::getTerminatorsRe() +{ + return terminatorsRe; +} + +RegExp& Contain::getLexemsRe() +{ + return lexemsRe; +} + +bool Contain::isRef() +{ + return ref; +} + +void Contain::setRef(bool r) +{ + ref = r; +} + +void Contain::setParent(Contain *contain) +{ + parent = contain; +} + +Contain* Contain::getParent() +{ + if(parent && parent->getStarts() && parent->getStarts()==this) + return parent->getParent(); + return parent; +} + +const string& Contain::getTerminatorEnd() +{ + return terminatorEnd; +} + +bool Contain::isExcludeBegin() +{ + return excludeBegin; +} + +bool Contain::isReturnBegin() +{ + return returnBegin; +} + +void Contain::setReturnBegin(bool b) +{ + returnBegin = b; +} + +void Contain::setReturnEnd(bool b) +{ + returnEnd = b; +} + +bool Contain::isReturnEnd() +{ + return returnEnd; +} +bool Contain::isExcludeEnd() +{ + return excludeEnd; +} + +void Contain::setExcludeEnd(bool b) +{ + excludeEnd = b; +} + +const std::list& Contain::getKeywords() +{ + return keywords; +} + +int Contain::matchKeyword(const string &k) +{ + for(list::iterator it=keywords.begin(); it!=keywords.end(); it++){ + if(it->getKeyword()==k) + return it->getType(); + } + return Keywords::NotFound; +} + +void Contain::setStarts(Contain *contain) +{ + starts = contain; +} + +Contain* Contain::getStarts() +{ + return starts; +} + +void Contain::setRefLanguageContains(bool b) +{ + refLanguageContains = b; +} + +bool Contain::isRefLanguageContains() +{ + return refLanguageContains; +} + +bool Contain::isStarts() +{ + return parent && parent->getStarts() && parent->getStarts()==this; +} + +//------------------------------- Language ------------------------------------- +Language::Language() +{ + compiled = false; + caseSensitive = true; +} + +void Language::addKeyword(Keywords::KeywordsType kt, const string &keyword) +{ + keywords.push_back(Keywords(kt, keyword)); +} + +void Language::addLiteral(const string &literal) +{ + keywords.push_back(Keywords(Keywords::Literal, literal)); +} + +void Language::addConstant(const string &constant) +{ + keywords.push_back(Keywords(Keywords::Constant, constant)); +} + +void Language::addType(const string &type) +{ + keywords.push_back(Keywords(Keywords::Type, type)); +} + +void Language::addCommand(const string &command) +{ + keywords.push_back(Keywords(Keywords::Command, command)); +} + +void Language::addProperty(const string &property) +{ + keywords.push_back(Keywords(Keywords::Property, property)); +} + +void Language::addBuiltIn(const string &builtIn) +{ + keywords.push_back(Keywords(Keywords::BuiltIn, builtIn)); +} + +void Language::addContain(Contain *contain) +{ + contains.push_front(contain); +} + +void Language::setCaseSensitive(bool s) +{ + caseSensitive = s; +} + +bool Language::isCaseSensitive() +{ + return caseSensitive; +} + +void Language::setName(const string &name) +{ + this->name = name; +} + +void Language::setIllegal(const string &illegal) +{ + this->illegal = illegal; +} + +void Language::setLexems(const string &lexems) +{ + this->lexems = lexems; +} + +const string& Language::getLexems() +{ + return lexems; +} + +const list& Language::getKeywords() +{ + return keywords; +} + +int Language::matchKeyword(const string &k) +{ + for(list::iterator it=keywords.begin(); it!=keywords.end(); it++){ + if(it->getKeyword()==k) + return it->getType(); + } + return Keywords::NotFound; +} + +Contain *Language::findRefContain(const char *name) +{ + for(list::iterator it=contains.begin(); it!=contains.end(); it++){ + if(0==strcmp((*it)->getName(), name)) + return *it; + } + return NULL; +} + +Contain *Language::findMatchedContain(const string &match) +{ + for(list::reverse_iterator it=contains.rbegin(); it!=contains.rend(); it++){ + Contain *contain = *it; + if(contain->getBeginRe().isValid() && !contain->isRef()){ + FindResult fr = contain->getBeginRe().exec(match.c_str(), match.length()); + if(fr.isValid()) + return contain; + } + } + return NULL; +} + +void Language::printDebugInfo() +{ + printf("-----------------------Debug Info------------------------------\n"); + printf("keywords number: %d\n", keywords.size()); + for(list::iterator it=contains.begin(); it!=contains.end(); it++){ + printf("name: %s\n", (*it)->getName()); + } +} + +bool Language::isCompiled() +{ + return compiled; +} + +void Language::compileLanguage() +{ + if(compiled) + return; + compiled = true; + if(keywords.size()>0){ + if(!lexems.empty()){ + lexemsRe.compile(lexems, caseSensitive, true); + } else { + lexemsRe.compile("[a-zA-Z][a-zA-Z0-9_]*", caseSensitive, true); + } + } + //start compile + list terminators; + for(list::iterator it=contains.begin(); it!=contains.end(); it++){ + Contain *contain = *it; + contain->compile(this); + if(!contain->isRef()) + terminators.push_front(contain->getBegin()); + } + if(!illegal.empty()){ + illegalRe.compile(illegal, caseSensitive); + terminators.push_back(illegal); + } + if(!terminators.empty()) + terminatorsRe.compile(HighlighterUtil::joinStrings(terminators, '|'), caseSensitive, true); +} + +RegExp& Language::getTerminatorsRe() +{ + return terminatorsRe; +} + +RegExp& Language::getLexemsRe() +{ + return lexemsRe; +} + +list& Language::getContains() +{ + return contains; +} + +Language::~Language() +{ + //free contains + for(list::iterator it= contains.begin(); it != contains.end(); it++){ + delete *it; + } +} +//---------------------------- LanguageDefinationXmlParser --------------------- +LanguageDefinationXmlParser::LanguageDefinationXmlParser() +{ +} + +Language* LanguageDefinationXmlParser::startParse(const char* name, char *src) +{ + Language *lan = new Language; + lan->setName(name); + xml_document<> doc; + doc.parse<0>(src); + xml_node<> *firstNode = doc.first_node("Language"); + parseLanguageNode(firstNode, lan); +// lan->printDebugInfo(); +// lan->compileLanguage(); + return lan; +} + +void LanguageDefinationXmlParser::parseLanguageNode(xml_node<> *languageNode, Language* lan) +{ + xml_attribute<> *caseSensitive = languageNode->first_attribute("casesensitive"); + if(caseSensitive && 0==strcmp(caseSensitive->value(), "false")) + lan->setCaseSensitive(false); + else + lan->setCaseSensitive(true);//it is true by default + xml_node<> *node = languageNode->first_node(); + while(node){ + if(0==strcmp(node->name(), "Contains")) + parseContainsNode(node, lan); + else if(0==strcmp(node->name(), "Illegal")) + lan->setIllegal(node->value()); + else if(0==strcmp(node->name(), "Keywords")){ + xml_node<> *kNode = node->first_node(); + while(kNode){ + if(0==strcmp(kNode->name(), "Keyword")) + lan->addKeyword(Keywords::Keyword, kNode->value()); + else if(0==strcmp(kNode->name(), "BuiltIn")) + lan->addKeyword(Keywords::BuiltIn, kNode->value()); + else if(0==strcmp(kNode->name(), "Literal")) + lan->addKeyword(Keywords::Literal, kNode->value()); + else if(0==strcmp(kNode->name(), "Constant")) + lan->addKeyword(Keywords::Constant, kNode->value()); + else if (0==strcmp(kNode->name(), "Type")) + lan->addKeyword(Keywords::Type, kNode->value()); + else if (0==strcmp(kNode->name(), "Command")) + lan->addKeyword(Keywords::Command, kNode->value()); + else if (0==strcmp(kNode->name(), "Property")) + lan->addKeyword(Keywords::Property, kNode->value()); + else if (0==strcmp(kNode->name(), "Title")) + lan->addKeyword(Keywords::Title, kNode->value()); + kNode = kNode->next_sibling(); + } + } else if(0==strcmp(node->name(), "Lexems")){ + lan->setLexems(node->value()); + } + node = node->next_sibling(); + } +} + +void LanguageDefinationXmlParser::parseContainsNode(xml_node<> *containsNode, Language *lan) +{ + xml_node<> *node = containsNode->first_node(); + while(node){ + if(0==strcmp(node->name(), "Contain")){ + Contain *contain = new Contain(); + if(parseContainNode(node, contain, lan)) + lan->addContain(contain); + else + delete contain; + } + node = node->next_sibling(); + } +} + +bool LanguageDefinationXmlParser::parseContainNode(xml_node<> *node, Contain *contain, Language *lan) +{ + //Deal Attribute: Name + xml_attribute<> *nameAttr = node->first_attribute("name"); + if(nameAttr) + contain->setName(nameAttr->value()); + xml_attribute<> *refLanguageContainAttr = node->first_attribute("refLanguageContains"); + if(refLanguageContainAttr&& 0==strcmp(refLanguageContainAttr->value(), "true")) + contain->setRefLanguageContains(true); + + //Deal Nodes + xml_node<> *currentNode = node->first_node(); + while(currentNode){ + if(0==strcmp(currentNode->name(), "Keywords")){ + xml_attribute<> *attr = currentNode->first_attribute("refLanguageKeywords"); + if(attr){ + if(0==strcmp(attr->value(), "true")) + contain->setRefLanguageKeywords(true); + //default is not ref language keyword, so we not deal 'else' + } + xml_node<> *kNode = currentNode->first_node(); + while(kNode){ + if(0==strcmp(kNode->name(), "Keyword")) + contain->addKeyword(Keywords::Keyword, kNode->value()); + else if(0==strcmp(kNode->name(), "BuiltIn")) + contain->addKeyword(Keywords::BuiltIn, kNode->value()); + else if(0==strcmp(kNode->name(), "Literal")) + contain->addKeyword(Keywords::Literal, kNode->value()); + else if(0==strcmp(kNode->name(), "Constant")) + contain->addKeyword(Keywords::Constant, kNode->value()); + else if (0==strcmp(kNode->name(), "Type")) + contain->addKeyword(Keywords::Type, kNode->value()); + else if (0==strcmp(kNode->name(), "Command")) + contain->addKeyword(Keywords::Command, kNode->value()); + else if (0==strcmp(kNode->name(), "Property")) + contain->addKeyword(Keywords::Property, kNode->value()); + else if (0==strcmp(kNode->name(), "Title")) + contain->addKeyword(Keywords::Title, kNode->value()); + kNode = kNode->next_sibling(); + } + } else if(0==strcmp(currentNode->name(), "RefContains")){ + xml_attribute<> *attr = currentNode->first_attribute("refSelf"); + if(attr && 0==strcmp(attr->value(), "true")) + contain->addRefContain(contain);//self + xml_node<> *refNode = currentNode->first_node(); + while(refNode){ + if(0==strcmp(refNode->name(), "RefContain")){ + xml_attribute<> *isRef = refNode->first_attribute("notSetRef"); + Contain *c = lan->findRefContain(refNode->value()); + if(!c){ + assert(0 && "this should not be happend"); + return false; + } + if(!isRef) + c->setRef(true); + c->setParent(contain); + contain->addRefContain(c); + } + refNode = refNode->next_sibling(); + } + } else if(0==strcmp(currentNode->name(), "ShowClassName")){ + if(0==strcmp(currentNode->value(), "false")) + contain->setShowClassName(false); + //it is true by default, so we donot deal "else" + } else if(0==strcmp(currentNode->name(), "Begin")){ + contain->setBegin(currentNode->value()); + } else if(0==strcmp(currentNode->name(), "End")){ + contain->setEnd(currentNode->value()); + } else if(0==strcmp(currentNode->name(), "EndsWithParent")){ + if(0==strcmp(currentNode->value(), "true")) + contain->setEndWithParent(true); + else + contain->setEndWithParent(false); + } else if(0==strcmp(currentNode->name(), "Relevance")){ + contain->setRelevance(atoi(currentNode->value())); + } else if(0==strcmp(currentNode->name(), "Illegal")){ + contain->setIllegal(currentNode->value()); + } else if (0==strcmp(currentNode->name(), "BeginWithKeyword")){ + if(0==strcmp(currentNode->value(), "true")) + contain->setBeginWithKeyword(true); + //it is false by default + } else if (0==strcmp(currentNode->name(), "Lexems")){ + contain->setLexems(currentNode->value()); + } else if (0==strcmp(currentNode->name(), "Starts")){ + Contain *c = lan->findRefContain(currentNode->value()); + if(!c){ + assert(0 && "this should not be happend"); + return false; + } + c->setRef(true); + c->setParent(contain); + contain->setStarts(c); + } else if (0==strcmp(currentNode->name(), "ExcludeEnd")){ + if(0==strcmp(currentNode->value(), "true")) + contain->setExcludeEnd(true); + } else if (0==strcmp(currentNode->name(), "ReturnBegin")){ + if(0==strcmp(currentNode->value(), "true")) + contain->setReturnBegin(true); + } else if (0==strcmp(currentNode->name(), "SubLanguage")){ + contain->setSubLanugage(currentNode->value()); + } else if (0==strcmp(currentNode->name(), "ReturnEnd")){ + if(0==strcmp(currentNode->value(), "true")) + contain->setReturnEnd(true); + } + currentNode = currentNode->next_sibling(); + } + return true; +} diff --git a/src/lib/core/languagedefinationxmlparser.h b/src/lib/core/languagedefinationxmlparser.h new file mode 100644 index 0000000..72033ac --- /dev/null +++ b/src/lib/core/languagedefinationxmlparser.h @@ -0,0 +1,234 @@ +#ifndef LANGUAGEDEFINATIONXMLPARSER_H +#define LANGUAGEDEFINATIONXMLPARSER_H + +#include +#include + +#include + +#include "rapidxml.hpp" +#include "pcre.h" + +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +class Language; +class Contain; + +struct StackItem +{ + StackItem(Language *l=NULL, Contain* c=NULL) + { + lan = l; + con = c; + } + + Language *lan; + Contain *con; +}; + +struct FindResult +{ + FindResult(){start=-1; end=-1;} + FindResult(int s, int e){start=s; end=e;} + bool isValid(){return start!=-1||end!=-1;} + int start; + int end; +}; + +class RegExp +{ +public: + RegExp(); + bool compile(const std::string &pattern, bool isCaseSensitive, bool isGlobal=false); + FindResult exec(const char* code, int len); + bool test(const char*code, int len); + int getLastIndex() const; + void setLastIndex(int index); + bool isValid() const; + ~RegExp(); +private: + DISALLOW_COPY_AND_ASSIGN(RegExp); +private: + pcre16* re; + bool global; + int lastIndex; +}; + +class Keywords +{ +public: + enum KeywordsType { + Keyword, + Literal, + Constant, + Type, + Command, + Property, + BuiltIn, + Title, + NotFound//for return + }; + + Keywords(KeywordsType t, const std::string &kw); + const std::string& getKeyword(); + const int getType(){return type;} + static const char* getKeyTypeString(int kt); + +private: + KeywordsType type; + std::string k; +}; + +class HighlighterUtil +{ +public: + static std::string joinKeywords(std::list keywords, char sep); + static std::string joinStrings(std::list sl, char sep); +}; + +class Contain +{ +public: + Contain(); + ~Contain(); + void setShowClassName(bool s); + bool isShowClassName(); + void setEndWithParent(bool e); + bool isEndWithParent(); + void setBeginWithKeyword(bool b); + bool isBeginWithKeyword(); + void setRelevance(int relevance); + int getRelevance(); + void setRefLanguageKeywords(bool r); + bool isRefLanguageKeywords(); + bool isHaveSubLanguage(); + void setSubLanugage(const char *subLanguage); + const std::string getSubLanguage(); + void setName(const std::string &name); + const char * getName(); + std::string getRealName(); + void setBegin(const std::string &begin); + const std::string getBegin(); + void setEnd(const std::string &end); + const std::string& getEnd(); + void setLexems(const std::string &lexems); + void setIllegal(const std::string &illegal); + void addKeyword(Keywords::KeywordsType kt, const std::string &keyword); + void addRefContain(Contain *contain); + void compile(Language *lan); + Contain *findMatchedContain(const std::string &match); + RegExp& getBeginRe(); + RegExp& getEndRe(); + RegExp& getTerminatorsRe(); + RegExp& getLexemsRe(); + bool isRef(); + void setRef(bool r); + void setParent(Contain *contain); + Contain* getParent(); + const std::string& getTerminatorEnd(); + bool isReturnBegin(); + void setReturnBegin(bool b); + void setReturnEnd(bool b); + bool isExcludeBegin(); + bool isReturnEnd(); + bool isExcludeEnd(); + void setExcludeEnd(bool b); + const std::list& getKeywords(); + int matchKeyword(const std::string &k); + void setStarts(Contain *contain); + Contain* getStarts(); + void setRefLanguageContains(bool b); + bool isRefLanguageContains(); + bool isStarts(); +private: + bool showClassName; + bool endsWithParent; + bool beginWithKeyword; + bool refLanguageKeywords; + bool ref; + bool returnBegin; + bool excludeBegin; + bool returnEnd; + bool excludeEnd; + bool refLanguageContains; + int relevance; + std::string name; + std::string begin; + RegExp beginRe; + std::string end; + RegExp endRe; + std::string illegal; + RegExp illegalRe; + std::string lexems; + RegExp lexemsRe; + std::string terminatorEnd; + RegExp terminatorsRe; + std::list keywords; + std::list refContains; + Contain *parent; + Contain *starts; + std::string subLanguage; +}; + +class Language +{ +public: + Language(); + ~Language(); + void addKeyword(Keywords::KeywordsType kt, const std::string &keyword); + void addLiteral(const std::string &literal); + void addConstant(const std::string &constant); + void addType(const std::string &type); + void addCommand(const std::string &command); + void addProperty(const std::string &property); + void addBuiltIn(const std::string &builtIn); + void addContain(Contain *contain); + void setCaseSensitive(bool s); + bool isCaseSensitive(); + void setName(const std::string &name); + void setIllegal(const std::string &illegal); + void setLexems(const std::string &lexems); + const std::string& getLexems(); + const std::list& getKeywords(); + int matchKeyword(const std::string &k); + Contain *findRefContain(const char *name); + Contain *findMatchedContain(const std::string &match); + void printDebugInfo(); + bool isCompiled(); + void compileLanguage(); + RegExp& getTerminatorsRe(); + RegExp& getLexemsRe(); + std::list& getContains(); +private: + bool compiled; + bool caseSensitive; + std::string name; + + std::string lexems; + RegExp lexemsRe; + + RegExp terminatorsRe; + //keywords type + std::list keywords; + + std::string illegal; + RegExp illegalRe; + std::list contains; +}; + +class LanguageDefinationXmlParser +{ +public: + LanguageDefinationXmlParser(); + Language *startParse(const char *name, char *src); +private: + void parseLanguageNode(rapidxml::xml_node<> *languageNode, Language *lan); + void parseKeywordsNode(rapidxml::xml_node<> *keywordsNode, Language *lan); + void parseContainsNode(rapidxml::xml_node<> *containsNode, Language *lan); + bool parseContainNode(rapidxml::xml_node<> *node, Contain *contain, Language *lan); +private: +}; + +#endif // LANGUAGEDEFINATIONXMLPARSER_H diff --git a/src/lib/core/markdowntohtml.cpp b/src/lib/core/markdowntohtml.cpp new file mode 100644 index 0000000..adfcc9b --- /dev/null +++ b/src/lib/core/markdowntohtml.cpp @@ -0,0 +1,111 @@ +#include "markdowntohtml.h" +#include "markdown_lib.h" +#include "markdown.h" +#include "html.h" +#include "buffer.h" + +using namespace std; + +MarkdownToHtml::MarkdownToHtml() +{ +} +/***************************************************************************//** +@brief generate html from markdown. +@param inMarkdown markdown source string +@param outHtml html generated from markdown source +@returns NOTHING -> empty +@returns SUCCESS -> ok +@returns ERROR -> something wrong +*******************************************************************************/ +MarkdownToHtml::MarkdownToHtmlResult +MarkdownToHtml::translateMarkdownToHtml(MarkdownToHtml::MarkdownType type, + const string &inMarkdown, + string &outHtml) +{ + return translateMarkdownToHtml(type, inMarkdown.c_str(), inMarkdown.length(), outHtml); +} + +MarkdownToHtml::MarkdownToHtmlResult +MarkdownToHtml::translateMarkdownToHtml(MarkdownToHtml::MarkdownType type, + const char *data, + const int length, string &outHtml) +{ + if(type == MarkdownToHtml::Markdown || type == MarkdownToHtml::PHPMarkdownExtra){ + return translateMarkdownExtraToHtml(type, data, length, outHtml); + } else { + return translateMultiMarkdownToHtml(type, data, length, outHtml); + } +} + +MarkdownToHtml::MarkdownToHtmlResult +MarkdownToHtml::translateMarkdownExtraToHtml(MarkdownToHtml::MarkdownType type, + const char *data, + const int length, string &outHtml) +{ + if (length == 0) //length is 0, just return + return NOTHING; + + buf *ib, *ob; + sd_callbacks callbacks; + html_renderopt options; + sd_markdown *markdown; + unsigned int extension=0; + + ib = bufnew(length); + if (ib == NULL) + { + return ERROR; + } + bufgrow(ib, length); + ib->size = length; + memcpy(ib->data, data, length); + + ob = bufnew(OUTPUT_UNIT); + if (ob == NULL) + { + bufrelease(ib); + return ERROR; + } + + sdhtml_renderer(&callbacks, &options, 0); + if(type==Markdown) + extension = 0; + else if(type==PHPMarkdownExtra) + extension = MKDEXT_NO_INTRA_EMPHASIS + |MKDEXT_TABLES + |MKDEXT_FENCED_CODE + |MKDEXT_AUTOLINK + |MKDEXT_STRIKETHROUGH +// |MKDEXT_SPACE_HEADERS + |MKDEXT_SUPERSCRIPT + |MKDEXT_LAX_SPACING + |MKDEXT_HEADER_ID_ATTRIBUTE + |MKDEXT_FOOTNOTE + ; + markdown = sd_markdown_new( extension, 16, &callbacks, &options); + if (markdown == NULL) + { + bufrelease(ib); + bufrelease(ob); + return ERROR; + } + + sd_markdown_render(ob, ib->data, ib->size, markdown); + sd_markdown_free(markdown); + + outHtml.assign((const char*)ob->data, ob->size); + + bufrelease(ib); + bufrelease(ob); + return SUCCESS; // success +} + +MarkdownToHtml::MarkdownToHtmlResult +MarkdownToHtml::translateMultiMarkdownToHtml(MarkdownType type, const char *data, + const int length, string &outHtml) +{ + char *result = markdown_to_string(data, 0, HTML_FORMAT); + outHtml.append(result); + free(result); + return SUCCESS; +} diff --git a/src/lib/core/markdowntohtml.h b/src/lib/core/markdowntohtml.h new file mode 100644 index 0000000..a9c67d8 --- /dev/null +++ b/src/lib/core/markdowntohtml.h @@ -0,0 +1,48 @@ +#ifndef MARKDOWNTOHTML_H +#define MARKDOWNTOHTML_H + +#include +#include +#include +#include +#include + +#ifdef MARKDOWN_LIB + #define DECLSPEC __declspec(dllexport) +#else + #define DECLSPEC +#endif + +class DECLSPEC MarkdownToHtml +{ +public: + const static int OUTPUT_UNIT = 512; + enum MarkdownToHtmlResult { + ERROR = -1, + NOTHING, + SUCCESS + }; + + enum MarkdownType { + Markdown, + PHPMarkdownExtra, + MultiMarkdown + }; + +public: + MarkdownToHtml(); + static MarkdownToHtmlResult translateMarkdownToHtml(MarkdownType type, + const std::string &inMarkdown, + std::string &outHtml); + static MarkdownToHtmlResult translateMarkdownToHtml(MarkdownType type, + const char* data, + const int length, std::string &outHtml); + static MarkdownToHtmlResult translateMarkdownExtraToHtml(MarkdownType type, + const char* data, + const int length, std::string &outHtml); + static MarkdownToHtmlResult translateMultiMarkdownToHtml(MarkdownType type, + const char* data, + const int length, std::string &outHtml); +}; + +#endif // MARKDOWNTOHTML_H diff --git a/src/lib/crashdump/BreakpadHandler.cpp b/src/lib/crashdump/BreakpadHandler.cpp new file mode 100644 index 0000000..34aa1f6 --- /dev/null +++ b/src/lib/crashdump/BreakpadHandler.cpp @@ -0,0 +1,219 @@ +/* + Copyright (c) 2009, Aleksey Palazhchenko + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER 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 "BreakpadHandler.h" + +#include +#include +#include + +#if defined(Q_OS_MAC) +#include "client/mac/handler/exception_handler.h" +#elif defined(Q_OS_LINUX) +#include "client/linux/handler/exception_handler.h" +#elif defined(Q_OS_WIN32) +#include "client/windows/handler/exception_handler.h" +#endif + +namespace BreakpadQt +{ + +class GlobalHandlerPrivate +{ +public: + GlobalHandlerPrivate(); + ~GlobalHandlerPrivate(); + +public: + static QString reporter_; + static QStringList reporterArguments_; + static google_breakpad::ExceptionHandler* handler_; + static ReportCrashesToSystem reportCrashesToSystem_; +}; + +QString GlobalHandlerPrivate::reporter_ = QString(); +QStringList GlobalHandlerPrivate::reporterArguments_ = QStringList(); +google_breakpad::ExceptionHandler* GlobalHandlerPrivate::handler_ = 0; +ReportCrashesToSystem GlobalHandlerPrivate::reportCrashesToSystem_ = ReportUnhandled; + + +bool launcher(const QString &program, const QStringList &arguments) +{ + // TODO launcher + if(!program.isEmpty()){ + return QProcess::startDetached(program, arguments); + } + return false; +} + + +#if defined(Q_OS_WIN32) +bool DumpCallback(const wchar_t* _dump_dir, + const wchar_t* _minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool success) +#else +bool DumpCallback(const char* _dump_dir, + const char* _minidump_id, + void *context, bool success) +#endif +{ + //Q_UNUSED(_dump_dir); + //Q_UNUSED(_minidump_id); + Q_UNUSED(context); +#if defined(Q_OS_WIN32) + Q_UNUSED(assertion); + Q_UNUSED(exinfo); +#endif + + /* + NO STACK USE, NO HEAP USE THERE !!! + Creating QString's, using qDebug, etc. - everything is crash-unfriendly. + */ + //QString dumpFilePath = QString::fromWCharArray(_dump_dir).append(QChar::fromLatin1('/'))+QString::fromWCharArray(_minidump_id); + QString dumpFilePathFormat = QLatin1String("%1/%2.dmp"); +#if defined(Q_OS_WIN32) + GlobalHandlerPrivate::reporterArguments_.prepend(dumpFilePathFormat + .arg(QString::fromWCharArray(_dump_dir)) + .arg(QString::fromWCharArray(_minidump_id))); +#else + GlobalHandlerPrivate::reporterArguments_.prepend(dumpFilePathFormat + .arg(QString::fromLocal8Bit(_dump_dir)) + .arg(QString::fromLocal8Bit(_minidump_id))); +#endif + launcher(GlobalHandlerPrivate::reporter_, GlobalHandlerPrivate::reporterArguments_); + return (GlobalHandlerPrivate::reportCrashesToSystem_ == ReportUnhandled) ? success : false; +} + + +GlobalHandlerPrivate::GlobalHandlerPrivate() +{ +#if defined(Q_OS_WIN32) + handler_ = new google_breakpad::ExceptionHandler(/*DumpPath*/ QDir::tempPath().toStdWString(), /*FilterCallback*/ 0, DumpCallback, /*context*/ 0, true); +#else + handler_ = new google_breakpad::ExceptionHandler(QDir::tempPath().toStdString(), 0, DumpCallback, 0, true); +#endif +} + +GlobalHandlerPrivate::~GlobalHandlerPrivate() +{ + delete handler_; + handler_ = 0; +} + + +GlobalHandler* GlobalHandler::instance() +{ + static GlobalHandler globalHandler; + return &globalHandler; +} + +GlobalHandler::GlobalHandler() +{ + d = new GlobalHandlerPrivate(); +} + +GlobalHandler::~GlobalHandler() +{ + delete d; + d = 0; +} + +void GlobalHandler::setDumpPath(const QString& path) +{ + QString absPath = path; + if(!QDir::isAbsolutePath(absPath)) { + absPath = QDir::cleanPath(qApp->applicationDirPath() + QLatin1String("/") + path); + qDebug("BreakpadQt: setDumpPath: %s -> %s", qPrintable(path), qPrintable(absPath)); + } + Q_ASSERT(QDir::isAbsolutePath(absPath)); + + QDir().mkpath(absPath); + Q_ASSERT(QDir().exists(absPath)); + +# if defined(Q_OS_WIN32) + d->handler_->set_dump_path(absPath.toStdWString()); +# else + d->handler_->set_dump_path(absPath.toStdString()); +# endif +} + +void GlobalHandler::setReporter(const QString& reporter) +{ + QString rep = reporter; + + if(!QDir::isAbsolutePath(rep)) { +# if defined(Q_OS_MAC) + // TODO(AlekSi) What to do if we are not inside bundle? + rep = QDir::cleanPath(qApp->applicationDirPath() + QLatin1String("/../Resources/") + rep); +# elif defined(Q_OS_LINUX) || defined(Q_OS_WIN32) + // MAYBE(AlekSi) Better place for Linux? libexec? or what? + rep = QDir::cleanPath(qApp->applicationDirPath() + QLatin1String("/") + rep); +# else + What is this?! +# endif + + qDebug("BreakpadQt: setReporter: %s -> %s", qPrintable(reporter), qPrintable(rep)); + } + Q_ASSERT(QDir::isAbsolutePath(rep)); + + // add .exe for Windows if needed +# if defined(Q_OS_WIN32) + if(!QDir().exists(rep)) { + rep += QLatin1String(".exe"); + } +# endif +// Q_ASSERT(QDir().exists(rep)); + + d->reporter_ = QString::fromLocal8Bit(QFile::encodeName(rep)); +} + +void GlobalHandler::appendArgument(const QString &systemInfoPath) +{ + GlobalHandlerPrivate::reporterArguments_.append(systemInfoPath); +} + +void GlobalHandler::setReportCrashesToSystem(ReportCrashesToSystem report) +{ + d->reportCrashesToSystem_ = report; +} + +bool GlobalHandler::writeMinidump() +{ + bool res = d->handler_->WriteMinidump(); + if (res) { + qDebug("BreakpadQt: writeMinidump() successed."); + } else { + qWarning("BreakpadQt: writeMinidump() failed."); + } + return res; +} + +} // namespace diff --git a/src/lib/crashdump/BreakpadHandler.h b/src/lib/crashdump/BreakpadHandler.h new file mode 100644 index 0000000..f6cedce --- /dev/null +++ b/src/lib/crashdump/BreakpadHandler.h @@ -0,0 +1,67 @@ +/* + Copyright (c) 2009, Aleksey Palazhchenko + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER 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 BREAKPAD_HANDLER_H +#define BREAKPAD_HANDLER_H + +#include +namespace google_breakpad { class ExceptionHandler; } + +namespace BreakpadQt +{ + +class GlobalHandlerPrivate; + +enum ReportCrashesToSystem +{ + ReportUnhandled = 1, + AlwaysReport = 2 +}; + +class GlobalHandler +{ +public: + static GlobalHandler* instance(); + + void setDumpPath(const QString& path); + void setReporter(const QString& reporter); + void appendArgument(const QString &systemInfoPath); + void setReportCrashesToSystem(ReportCrashesToSystem report); + bool writeMinidump(); + +private: + GlobalHandler(); + ~GlobalHandler(); + Q_DISABLE_COPY(GlobalHandler) + + GlobalHandlerPrivate* d; +}; + +} // namespace + +#endif // BREAKPAD_HANDLER_H diff --git a/src/lib/crashdump/crashdump.pro b/src/lib/crashdump/crashdump.pro new file mode 100644 index 0000000..5cfcb65 --- /dev/null +++ b/src/lib/crashdump/crashdump.pro @@ -0,0 +1,39 @@ +TEMPLATE = lib +CONFIG(debug, debug|release){ #debug + TARGET = gbreakpad_d + DESTDIR = ../../debug +} else { #release + TARGET = gbreakpad + DESTDIR = ../../release +} +## google breakpad svn 969 + +CONFIG += warn_off thread exceptions rtti stl +CONFIG += static +QT -= gui +##why +DEFINES += QT_NO_CAST_TO_ASCII +DEFINES += QT_NO_CAST_FROM_ASCII + +unix:!mac { + debug { + QMAKE_CFLAGS_DEBUG += -gstabs + QMAKE_CXXFLAGS_DEBUG += -gstabs + } +} + +mac { + LIBS += -lcrypto +} + +LIST = thread exceptions rtti stl +for(f, LIST) { + !CONFIG($$f){ + warning("Add '$$f' to CONFIG, or you will find yourself in 'funny' problems.") + } +} +INCLUDEPATH += $$PWD +HEADERS += $$PWD/BreakpadHandler.h +SOURCES += $$PWD/BreakpadHandler.cpp + +include($$PWD/gbreakpad/gbreakpad.pri) diff --git a/src/lib/crashdump/gbreakpad/client/linux/crash_generation/client_info.h b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/client_info.h new file mode 100644 index 0000000..173b34d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/client_info.h @@ -0,0 +1,44 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ + +namespace google_breakpad { + +class CrashGenerationServer; + +struct ClientInfo { + CrashGenerationServer* crash_server_; + pid_t pid_; +}; + +} + +#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.cc b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.cc new file mode 100644 index 0000000..3cc7eb4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/linux/crash_generation/crash_generation_client.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +bool +CrashGenerationClient::RequestDump(const void* blob, size_t blob_size) +{ + int fds[2]; + sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); + + struct kernel_msghdr msg; + my_memset(&msg, 0, sizeof(struct kernel_msghdr)); + struct kernel_iovec iov[1]; + iov[0].iov_base = const_cast(blob); + iov[0].iov_len = blob_size; + + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); + char cmsg[kControlMsgSize]; + my_memset(cmsg, 0, kControlMsgSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + int* p = reinterpret_cast(CMSG_DATA(hdr)); + *p = fds[1]; + + HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); + sys_close(fds[1]); + + // wait for an ACK from the server + char b; + HANDLE_EINTR(sys_read(fds[0], &b, 1)); + + return true; +} + +//static +CrashGenerationClient* +CrashGenerationClient::TryCreate(int server_fd) +{ + if (0 > server_fd) + return NULL; + return new CrashGenerationClient(server_fd); +} + +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.h b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.h new file mode 100644 index 0000000..7139dff --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_client.h @@ -0,0 +1,69 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include + +namespace google_breakpad { + +class CrashGenerationClient { +public: + ~CrashGenerationClient() + { + } + + // Request the crash server to generate a dump. |blob| is a hack, + // see exception_handler.h and minidump_writer.h + // + // Return true if the dump was successful; false otherwise. + bool RequestDump(const void* blob, size_t blob_size); + + // Return a new CrashGenerationClient if |server_fd| is valid and + // connects to a CrashGenerationServer. Otherwise, return NULL. + // The returned CrashGenerationClient* is owned by the caller of + // this function. + static CrashGenerationClient* TryCreate(int server_fd); + +private: + CrashGenerationClient(int server_fd) : server_fd_(server_fd) + { + } + + int server_fd_; + + // prevent copy construction and assignment + CrashGenerationClient(const CrashGenerationClient&); + CrashGenerationClient& operator=(const CrashGenerationClient&); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.cc b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.cc new file mode 100644 index 0000000..76c9d9c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.cc @@ -0,0 +1,467 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 +#include +#include +#include +#include +#include +#include + +#include + +#include "client/linux/crash_generation/crash_generation_server.h" +#include "client/linux/crash_generation/client_info.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/guid_creator.h" +#include "common/linux/safe_readlink.h" + +static const char kCommandQuit = 'x'; + +static bool +GetInodeForFileDescriptor(ino_t* inode_out, int fd) +{ + assert(inode_out); + + struct stat buf; + if (fstat(fd, &buf) < 0) + return false; + + if (!S_ISSOCK(buf.st_mode)) + return false; + + *inode_out = buf.st_ino; + return true; +} + +// expected prefix of the target of the /proc/self/fd/%d link for a socket +static const char kSocketLinkPrefix[] = "socket:["; + +// Parse a symlink in /proc/pid/fd/$x and return the inode number of the +// socket. +// inode_out: (output) set to the inode number on success +// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) +static bool +GetInodeForProcPath(ino_t* inode_out, const char* path) +{ + assert(inode_out); + assert(path); + + char buf[PATH_MAX]; + if (!SafeReadLink(path, buf)) { + return false; + } + + if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { + return false; + } + + char* endptr; + const u_int64_t inode_ul = + strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); + if (*endptr != ']') + return false; + + if (inode_ul == ULLONG_MAX) { + return false; + } + + *inode_out = inode_ul; + return true; +} + +static bool +FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) +{ + assert(pid_out); + bool already_found = false; + + DIR* proc = opendir("/proc"); + if (!proc) { + return false; + } + + std::vector pids; + + struct dirent* dent; + while ((dent = readdir(proc))) { + char* endptr; + const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); + if (pid_ul == ULONG_MAX || '\0' != *endptr) + continue; + pids.push_back(pid_ul); + } + closedir(proc); + + for (std::vector::const_iterator + i = pids.begin(); i != pids.end(); ++i) { + const pid_t current_pid = *i; + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); + DIR* fd = opendir(buf); + if (!fd) + continue; + + while ((dent = readdir(fd))) { + if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, + dent->d_name) >= static_cast(sizeof(buf))) { + continue; + } + + ino_t fd_inode; + if (GetInodeForProcPath(&fd_inode, buf) + && fd_inode == socket_inode) { + if (already_found) { + closedir(fd); + return false; + } + + already_found = true; + *pid_out = current_pid; + break; + } + } + + closedir(fd); + } + + return already_found; +} + +namespace google_breakpad { + +CrashGenerationServer::CrashGenerationServer( + const int listen_fd, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const std::string* dump_path) : + server_fd_(listen_fd), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + generate_dumps_(generate_dumps), + started_(false) +{ + if (dump_path) + dump_dir_ = *dump_path; + else + dump_dir_ = "/tmp"; +} + +CrashGenerationServer::~CrashGenerationServer() +{ + if (started_) + Stop(); +} + +bool +CrashGenerationServer::Start() +{ + if (started_ || 0 > server_fd_) + return false; + + int control_pipe[2]; + if (pipe(control_pipe)) + return false; + + if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) + return false; + if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) + return false; + + if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) + return false; + + control_pipe_in_ = control_pipe[0]; + control_pipe_out_ = control_pipe[1]; + + if (pthread_create(&thread_, NULL, + ThreadMain, reinterpret_cast(this))) + return false; + + started_ = true; + return true; +} + +void +CrashGenerationServer::Stop() +{ + assert(pthread_self() != thread_); + + if (!started_) + return; + + HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); + + void* dummy; + pthread_join(thread_, &dummy); + + started_ = false; +} + +//static +bool +CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) +{ + int fds[2]; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) + return false; + + static const int on = 1; + // Enable passcred on the server end of the socket + if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) + return false; + + if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) + return false; + if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) + return false; + + *client_fd = fds[0]; + *server_fd = fds[1]; + return true; +} + +// The following methods/functions execute on the server thread + +void +CrashGenerationServer::Run() +{ + struct pollfd pollfds[2]; + memset(&pollfds, 0, sizeof(pollfds)); + + pollfds[0].fd = server_fd_; + pollfds[0].events = POLLIN; + + pollfds[1].fd = control_pipe_in_; + pollfds[1].events = POLLIN; + + while (true) { + // infinite timeout + int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); + if (-1 == nevents) { + if (EINTR == errno) { + continue; + } else { + return; + } + } + + if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) + return; + + if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) + return; + } +} + +bool +CrashGenerationServer::ClientEvent(short revents) +{ + if (POLLHUP & revents) + return false; + assert(POLLIN & revents); + + // A process has crashed and has signaled us by writing a datagram + // to the death signal socket. The datagram contains the crash context needed + // for writing the minidump as well as a file descriptor and a credentials + // block so that they can't lie about their pid. + + // The length of the control message: + static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + // The length of the regular payload: + static const unsigned kCrashContextSize = + sizeof(google_breakpad::ExceptionHandler::CrashContext); + + struct msghdr msg = {0}; + struct iovec iov[1]; + char crash_context[kCrashContextSize]; + char control[kControlMsgSize]; + const ssize_t expected_msg_size = sizeof(crash_context); + + iov[0].iov_base = crash_context; + iov[0].iov_len = sizeof(crash_context); + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); + if (msg_size != expected_msg_size) + return true; + + if (msg.msg_controllen != kControlMsgSize || + msg.msg_flags & ~MSG_TRUNC) + return true; + + // Walk the control payload and extract the file descriptor and validated pid. + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + assert(len % sizeof(int) == 0u); + const unsigned num_fds = len / sizeof(int); + if (num_fds > 1 || num_fds == 0) { + // A nasty process could try and send us too many descriptors and + // force a leak. + for (unsigned i = 0; i < num_fds; ++i) + HANDLE_EINTR(close(reinterpret_cast(CMSG_DATA(hdr))[i])); + return true; + } else { + signal_fd = reinterpret_cast(CMSG_DATA(hdr))[0]; + } + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred *cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + if (crashing_pid == -1 || signal_fd == -1) { + if (signal_fd) + HANDLE_EINTR(close(signal_fd)); + return true; + } + + // Kernel bug workaround (broken in 2.6.30 at least): + // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID + // namespaces. Thus |crashing_pid| might be garbage from our point of view. + // In the future we can remove this workaround, but we have to wait a couple + // of years to be sure that it's worked its way out into the world. + + ino_t inode_number; + if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { + HANDLE_EINTR(close(signal_fd)); + return true; + } + + if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { + HANDLE_EINTR(close(signal_fd)); + return true; + } + + std::string minidump_filename; + if (!MakeMinidumpFilename(minidump_filename)) + return true; + + if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), + crashing_pid, crash_context, + kCrashContextSize)) { + HANDLE_EINTR(close(signal_fd)); + return true; + } + + if (dump_callback_) { + ClientInfo info; + + info.crash_server_ = this; + info.pid_ = crashing_pid; + + dump_callback_(dump_context_, &info, &minidump_filename); + } + + // Send the done signal to the process: it can exit now. + memset(&msg, 0, sizeof(msg)); + struct iovec done_iov; + done_iov.iov_base = const_cast("\x42"); + done_iov.iov_len = 1; + msg.msg_iov = &done_iov; + msg.msg_iovlen = 1; + + HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); + HANDLE_EINTR(close(signal_fd)); + + return true; +} + +bool +CrashGenerationServer::ControlEvent(short revents) +{ + if (POLLHUP & revents) + return false; + assert(POLLIN & revents); + + char command; + if (read(control_pipe_in_, &command, 1)) + return false; + + switch (command) { + case kCommandQuit: + return false; + default: + assert(0); + } + + return true; +} + +bool +CrashGenerationServer::MakeMinidumpFilename(std::string& outFilename) +{ + GUID guid; + char guidString[kGUIDStringLength+1]; + + if (!(CreateGUID(&guid) + && GUIDToString(&guid, guidString, sizeof(guidString)))) + return false; + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); + + outFilename = path; + return true; +} + +// static +void* +CrashGenerationServer::ThreadMain(void *arg) +{ + reinterpret_cast(arg)->Run(); + return NULL; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.h b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.h new file mode 100644 index 0000000..a9dc1f7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/crash_generation/crash_generation_server.h @@ -0,0 +1,133 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ + +#include + +#include + +namespace google_breakpad { + +class ClientInfo; + +class CrashGenerationServer { +public: + // WARNING: callbacks may be invoked on a different thread + // than that which creates the CrashGenerationServer. They must + // be thread safe. + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo* client_info, + const std::string* file_path); + + typedef void (*OnClientExitingCallback)(void* context, + const ClientInfo* client_info); + + // Create an instance with the given parameters. + // + // Parameter listen_fd: The server fd created by CreateReportChannel(). + // Parameter dump_callback: Callback for a client crash dump request. + // Parameter dump_context: Context for client crash dump request callback. + // Parameter exit_callback: Callback for client process exit. + // Parameter exit_context: Context for client exit callback. + // Parameter generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly + // in the crash dump request callback. In that case, false can be + // passed for this parameter. + // Parameter dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const int listen_fd, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const std::string* dump_path); + + ~CrashGenerationServer(); + + // Perform initialization steps needed to start listening to clients. + // + // Return true if initialization is successful; false otherwise. + bool Start(); + + // Stop the server. + void Stop(); + + // Create a "channel" that can be used by clients to report crashes + // to a CrashGenerationServer. |*server_fd| should be passed to + // this class's constructor, and |*client_fd| should be passed to + // the ExceptionHandler constructor in the client process. + static bool CreateReportChannel(int* server_fd, int* client_fd); + +private: + // Run the server's event loop + void Run(); + + // Invoked when an child process (client) event occurs + // Returning true => "keep running", false => "exit loop" + bool ClientEvent(short revents); + + // Invoked when the controlling thread (main) event occurs + // Returning true => "keep running", false => "exit loop" + bool ControlEvent(short revents); + + // Return a unique filename at which a minidump can be written + bool MakeMinidumpFilename(std::string& outFilename); + + // Trampoline to |Run()| + static void* ThreadMain(void* arg); + + int server_fd_; + + OnClientDumpRequestCallback dump_callback_; + void* dump_context_; + + OnClientExitingCallback exit_callback_; + void* exit_context_; + + bool generate_dumps_; + + std::string dump_dir_; + + bool started_; + + pthread_t thread_; + int control_pipe_in_; + int control_pipe_out_; + + // disable these + CrashGenerationServer(const CrashGenerationServer&); + CrashGenerationServer& operator=(const CrashGenerationServer&); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.cc b/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.cc new file mode 100644 index 0000000..5d9e93f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.cc @@ -0,0 +1,523 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// The ExceptionHandler object installs signal handlers for a number of +// signals. We rely on the signal handler running on the thread which crashed +// in order to identify it. This is true of the synchronous signals (SEGV etc), +// but not true of ABRT. Thus, if you send ABRT to yourself in a program which +// uses ExceptionHandler, you need to use tgkill to direct it to the current +// thread. +// +// The signal flow looks like this: +// +// SignalHandler (uses a global stack of ExceptionHandler objects to find +// | one to handle the signal. If the first rejects it, try +// | the second etc...) +// V +// HandleSignal ----------------------------| (clones a new process which +// | | shares an address space with +// (wait for cloned | the crashed process. This +// process) | allows us to ptrace the crashed +// | | process) +// V V +// (set signal handler to ThreadEntry (static function to bounce +// SIG_DFL and rethrow, | back into the object) +// killing the crashed | +// process) V +// DoDump (writes minidump) +// | +// V +// sys_exit +// + +// This code is a little fragmented. Different functions of the ExceptionHandler +// class run in a number of different contexts. Some of them run in a normal +// context and are easy to code, others run in a compromised context and the +// restrictions at the top of minidump_writer.cc apply: no libc and use the +// alternative malloc. Each function should have comment above it detailing the +// context which it runs in. + +#include "client/linux/handler/exception_handler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__ANDROID__) +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include "common/linux/linux_libc_support.h" +#include "common/memory.h" +#include "client/linux/log/log.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/guid_creator.h" +#include "common/linux/eintr_wrapper.h" +#include "third_party/lss/linux_syscall_support.h" + +#include "linux/sched.h" + +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#endif + +// A wrapper for the tgkill syscall: send a signal to a specific thread. +static int tgkill(pid_t tgid, pid_t tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); + return 0; +} + +namespace google_breakpad { + +// The list of signals which we consider to be crashes. The default action for +// all these signals must be Core (see man 7 signal) because we rethrow the +// signal after handling it and expect that it'll be fatal. +static const int kExceptionSignals[] = { + SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 +}; + +// We can stack multiple exception handlers. In that case, this is the global +// which holds the stack. +std::vector* ExceptionHandler::handler_stack_ = NULL; +unsigned ExceptionHandler::handler_stack_index_ = 0; +pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = + PTHREAD_MUTEX_INITIALIZER; + +// Runs before crashing: normal context. +ExceptionHandler::ExceptionHandler(const std::string &dump_path, + FilterCallback filter, + MinidumpCallback callback, + void *callback_context, + bool install_handler) + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + handler_installed_(install_handler) +{ + Init(dump_path, -1); +} + +ExceptionHandler::ExceptionHandler(const std::string &dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler, + const int server_fd) + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + handler_installed_(install_handler) +{ + Init(dump_path, server_fd); +} + +// Runs before crashing: normal context. +ExceptionHandler::~ExceptionHandler() { + UninstallHandlers(); +} + +void ExceptionHandler::Init(const std::string &dump_path, + const int server_fd) +{ + crash_handler_ = NULL; + if (0 <= server_fd) + crash_generation_client_ + .reset(CrashGenerationClient::TryCreate(server_fd)); + + if (handler_installed_) + InstallHandlers(); + + if (!IsOutOfProcess()) + set_dump_path(dump_path); + + pthread_mutex_lock(&handler_stack_mutex_); + if (handler_stack_ == NULL) + handler_stack_ = new std::vector; + handler_stack_->push_back(this); + pthread_mutex_unlock(&handler_stack_mutex_); +} + +// Runs before crashing: normal context. +bool ExceptionHandler::InstallHandlers() { + // We run the signal handlers on an alternative stack because we might have + // crashed because of a stack overflow. + + // We use this value rather than SIGSTKSZ because we would end up overrunning + // such a small stack. + static const unsigned kSigStackSize = 8192; + + stack_t stack; + // Only set an alternative stack if there isn't already one, or if the current + // one is too small. + if (sys_sigaltstack(NULL, &stack) == -1 || !stack.ss_sp || + stack.ss_size < kSigStackSize) { + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = malloc(kSigStackSize); + stack.ss_size = kSigStackSize; + + if (sys_sigaltstack(&stack, NULL) == -1) + return false; + } + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + + // mask all exception signals when we're handling one of them. + for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) + sigaddset(&sa.sa_mask, kExceptionSignals[i]); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO; + + for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) { + struct sigaction* old = new struct sigaction; + if (sigaction(kExceptionSignals[i], &sa, old) == -1) + return false; + old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old)); + } + return true; +} + +// Runs before crashing: normal context. +void ExceptionHandler::UninstallHandlers() { + for (unsigned i = 0; i < old_handlers_.size(); ++i) { + struct sigaction *action = + reinterpret_cast(old_handlers_[i].second); + sigaction(old_handlers_[i].first, action, NULL); + delete action; + } + pthread_mutex_lock(&handler_stack_mutex_); + std::vector::iterator handler = + std::find(handler_stack_->begin(), handler_stack_->end(), this); + handler_stack_->erase(handler); + pthread_mutex_unlock(&handler_stack_mutex_); + old_handlers_.clear(); +} + +// Runs before crashing: normal context. +void ExceptionHandler::UpdateNextID() { + GUID guid; + char guid_str[kGUIDStringLength + 1]; + if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { + next_minidump_id_ = guid_str; + next_minidump_id_c_ = next_minidump_id_.c_str(); + + char minidump_path[PATH_MAX]; + snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", + dump_path_c_, + guid_str); + + next_minidump_path_ = minidump_path; + next_minidump_path_c_ = next_minidump_path_.c_str(); + } +} + +// void ExceptionHandler::set_crash_handler(HandlerCallback callback) { +// crash_handler_ = callback; +// } + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +// static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { + // All the exception signals are blocked at this point. + pthread_mutex_lock(&handler_stack_mutex_); + + if (!handler_stack_->size()) { + pthread_mutex_unlock(&handler_stack_mutex_); + return; + } + + for (int i = handler_stack_->size() - 1; i >= 0; --i) { + if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) { + // successfully handled: We are in an invalid state since an exception + // signal has been delivered. We don't call the exit handlers because + // they could end up corrupting on-disk state. + break; + } + } + + pthread_mutex_unlock(&handler_stack_mutex_); + + if (info->si_pid) { + // This signal was triggered by somebody sending us the signal with kill(). + // In order to retrigger it, we have to queue a new signal by calling + // kill() ourselves. + if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { + // If we failed to kill ourselves (e.g. because a sandbox disallows us + // to do so), we instead resort to terminating our process. This will + // result in an incorrect exit code. + _exit(1); + } + } else { + // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). + // No need to reissue the signal. It will automatically trigger again, + // when we return from the signal handler. + } + + // As soon as we return from the signal handler, our signal will become + // unmasked. At that time, we will get terminated with the same signal that + // was triggered originally. This allows our parent to know that we crashed. + // The default action for all the signals which we catch is Core, so + // this is the end of us. + signal(sig, SIG_DFL); +} + +struct ThreadArgument { + pid_t pid; // the crashing process + ExceptionHandler* handler; + const void* context; // a CrashContext structure + size_t context_size; +}; + +// This is the entry function for the cloned process. We are in a compromised +// context here: see the top of the file. +// static +int ExceptionHandler::ThreadEntry(void *arg) { + const ThreadArgument *thread_arg = reinterpret_cast(arg); + + // Block here until the crashing process unblocks us when + // we're allowed to use ptrace + thread_arg->handler->WaitForContinueSignal(); + + return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, + thread_arg->context_size) == false; +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { + if (filter_ && !filter_(callback_context_)) + return false; + + // Allow ourselves to be dumped if the signal is trusted. + bool signal_trusted = info->si_code > 0; + bool signal_pid_trusted = info->si_code == SI_USER || + info->si_code == SI_TKILL; + if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { + sys_prctl(PR_SET_DUMPABLE, 1); + } + CrashContext context; + memcpy(&context.siginfo, info, sizeof(siginfo_t)); + memcpy(&context.context, uc, sizeof(struct ucontext)); +#if !defined(__ARM_EABI__) + // FP state is not part of user ABI on ARM Linux. + struct ucontext *uc_ptr = (struct ucontext*)uc; + if (uc_ptr->uc_mcontext.fpregs) { + memcpy(&context.float_state, + uc_ptr->uc_mcontext.fpregs, + sizeof(context.float_state)); + } +#endif + context.tid = syscall(__NR_gettid); + if (crash_handler_ != NULL) { + if (crash_handler_(&context, sizeof(context), + callback_context_)) { + return true; + } + } + return GenerateDump(&context); +} + +// This function may run in a compromised context: see the top of the file. +bool ExceptionHandler::GenerateDump(CrashContext *context) { + if (IsOutOfProcess()) + return crash_generation_client_->RequestDump(context, sizeof(*context)); + + static const unsigned kChildStackSize = 8000; + PageAllocator allocator; + uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); + if (!stack) + return false; + // clone() needs the top-most address. (scrub just to be safe) + stack += kChildStackSize; + my_memset(stack - 16, 0, 16); + + ThreadArgument thread_arg; + thread_arg.handler = this; + thread_arg.pid = getpid(); + thread_arg.context = context; + thread_arg.context_size = sizeof(*context); + + // We need to explicitly enable ptrace of parent processes on some + // kernels, but we need to know the PID of the cloned process before we + // can do this. Create a pipe here which we can use to block the + // cloned process after creating it, until we have explicitly enabled ptrace + if(sys_pipe(fdes) == -1) { + // Creating the pipe failed. We'll log an error but carry on anyway, + // as we'll probably still get a useful crash report. All that will happen + // is the write() and read() calls will fail with EBADF + static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \ + sys_pipe failed:"; + logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } + +#if defined(__ANDROID__) + const pid_t child = clone( + ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + &thread_arg); +#else + const pid_t child = sys_clone( + ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + &thread_arg, NULL, NULL, NULL); +#endif + int r, status; + // Allow the child to ptrace us + sys_prctl(PR_SET_PTRACER, child); + SendContinueSignalToChild(); + do { + r = sys_waitpid(child, &status, __WALL); + } while (r == -1 && errno == EINTR); + + sys_close(fdes[0]); + sys_close(fdes[1]); + + if (r == -1) { + static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } + + bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; + + if (callback_) + success = callback_(dump_path_c_, next_minidump_id_c_, + callback_context_, success); + + return success; +} + +// This function runs in a compromised context: see the top of the file. +void ExceptionHandler::SendContinueSignalToChild() { + static const char okToContinueMessage = 'a'; + int r; + r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); + if(r == -1) { + static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \ + sys_write failed:"; + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +void ExceptionHandler::WaitForContinueSignal() { + int r; + char receivedMessage; + r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); + if(r == -1) { + static const char msg[] = "ExceptionHandler::WaitForContinueSignal \ + sys_read failed:"; + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, + size_t context_size) { + return google_breakpad::WriteMinidump(next_minidump_path_c_, + crashing_process, + context, + context_size, + mapping_list_); +} + +// static +bool ExceptionHandler::WriteMinidump(const std::string &dump_path, + MinidumpCallback callback, + void* callback_context) { + ExceptionHandler eh(dump_path, NULL, callback, callback_context, false); + return eh.WriteMinidump(); +} + +bool ExceptionHandler::WriteMinidump() { +#if !defined(__ARM_EABI__) + // Allow ourselves to be dumped. + sys_prctl(PR_SET_DUMPABLE, 1); + + CrashContext context; + int getcontext_result = getcontext(&context.context); + if (getcontext_result) + return false; + memcpy(&context.float_state, context.context.uc_mcontext.fpregs, + sizeof(context.float_state)); + context.tid = sys_gettid(); + + bool success = GenerateDump(&context); + UpdateNextID(); + return success; +#else + return false; +#endif // !defined(__ARM_EABI__) +} + +void ExceptionHandler::AddMappingInfo(const std::string& name, + const u_int8_t identifier[sizeof(MDGUID)], + uintptr_t start_address, + size_t mapping_size, + size_t file_offset) { + MappingInfo info; + info.start_addr = start_address; + info.size = mapping_size; + info.offset = file_offset; + strncpy(info.name, name.c_str(), sizeof(info.name) - 1); + info.name[sizeof(info.name) - 1] = '\0'; + + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, identifier, sizeof(MDGUID)); + mapping_list_.push_back(mapping); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.h b/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.h new file mode 100644 index 0000000..94766eb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler.h @@ -0,0 +1,258 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ +#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ + +#include +#include + +#include +#include +#include +#include + +#if defined(__ANDROID__) +#include "client/linux/android_ucontext.h" +#endif +#include "client/linux/crash_generation/crash_generation_client.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "google_breakpad/common/minidump_format.h" +#include "processor/scoped_ptr.h" + +struct sigaction; + +namespace google_breakpad { + +class ExceptionHandler; + +// ExceptionHandler +// +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// (NOTE): There should be only be one this kind of exception handler +// object per process. +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the unqiue id of the minidump. The caller can use this +// id to collect and write additional application state, and to launch an +// external crash-reporting application. +// +// Caller should try to make the callbacks as crash-friendly as possible, +// it should avoid use heap memory allocation as much as possible. +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, + // Breakpad will immediately report the exception as unhandled without + // writing a minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void *context); + + // A callback function to run after the minidump has been written. + // minidump_id is a unique id for the dump, so the minidump + // file is \.dmp. context is the parameter supplied + // by the user as callback_context when the handler was created. succeeded + // indicates whether a minidump file was successfully written. + // + // If an exception occurred and the callback returns true, Breakpad will + // treat the exception as fully-handled, suppressing any other handlers from + // being notified of the exception. If the callback returns false, Breakpad + // will treat the exception as unhandled, and allow another handler to handle + // it. If there are no other handlers, Breakpad will report the exception to + // the system as unhandled, allowing a debugger or native crash dialog the + // opportunity to handle the exception. Most callback implementations + // should normally return the value of |succeeded|, or when they wish to + // not report an exception of handled, false. Callbacks will rarely want to + // return true directly (unless |succeeded| is true). + typedef bool (*MinidumpCallback)(const char *dump_path, + const char *minidump_id, + void *context, + bool succeeded); + + // In certain cases, a user may wish to handle the generation of the minidump + // themselves. In this case, they can install a handler callback which is + // called when a crash has occurred. If this function returns true, no other + // processing of occurs and the process will shortly be crashed. If this + // returns false, the normal processing continues. + typedef bool (*HandlerCallback)(const void* crash_context, + size_t crash_context_size, + void* context); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Before writing a minidump, the optional filter callback will be called. + // Its return value determines whether or not Breakpad should write a + // minidump. Minidump files will be written to dump_path, and the optional + // callback is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + ExceptionHandler(const std::string &dump_path, + FilterCallback filter, MinidumpCallback callback, + void *callback_context, + bool install_handler); + + // Creates a new ExceptionHandler instance that can attempt to + // perform out-of-process dump generation if server_fd is valid. If + // server_fd is invalid, in-process dump generation will be + // used. See the above ctor for a description of the other + // parameters. + ExceptionHandler(const std::string& dump_path, + FilterCallback filter, MinidumpCallback callback, + void* callback_context, + bool install_handler, + const int server_fd); + + ~ExceptionHandler(); + + // Get and set the minidump path. + std::string dump_path() const { return dump_path_; } + void set_dump_path(const std::string &dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + UpdateNextID(); + } + + void set_crash_handler(HandlerCallback callback) { + crash_handler_ = callback; + } + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump(); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const std::string &dump_path, + MinidumpCallback callback, + void *callback_context); + + // This structure is passed to minidump_writer.h:WriteMinidump via an opaque + // blob. It shouldn't be needed in any user code. + struct CrashContext { + siginfo_t siginfo; + pid_t tid; // the crashing thread. + struct ucontext context; +#if !defined(__ARM_EABI__) + // #ifdef this out because FP state is not part of user ABI for Linux ARM. + struct _libc_fpstate float_state; +#endif + }; + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { + return crash_generation_client_.get() != NULL; + } + + // Add information about a memory mapping. This can be used if + // a custom library loader is used that maps things in a way + // that the linux dumper can't handle by reading the maps file. + void AddMappingInfo(const std::string& name, + const u_int8_t identifier[sizeof(MDGUID)], + uintptr_t start_address, + size_t mapping_size, + size_t file_offset); + + private: + void Init(const std::string &dump_path, + const int server_fd); + bool InstallHandlers(); + void UninstallHandlers(); + void PreresolveSymbols(); + bool GenerateDump(CrashContext *context); + void SendContinueSignalToChild(); + void WaitForContinueSignal(); + + void UpdateNextID(); + static void SignalHandler(int sig, siginfo_t* info, void* uc); + bool HandleSignal(int sig, siginfo_t* info, void* uc); + static int ThreadEntry(void* arg); + bool DoDump(pid_t crashing_process, const void* context, + size_t context_size); + + const FilterCallback filter_; + const MinidumpCallback callback_; + void* const callback_context_; + + scoped_ptr crash_generation_client_; + + std::string dump_path_; + std::string next_minidump_path_; + std::string next_minidump_id_; + + // Pointers to C-string representations of the above. These are set + // when the above are set so we can avoid calling c_str during + // an exception. + const char* dump_path_c_; + const char* next_minidump_path_c_; + const char* next_minidump_id_c_; + + const bool handler_installed_; + HandlerCallback crash_handler_; + + // The global exception handler stack. This is need becuase there may exist + // multiple ExceptionHandler instances in a process. Each will have itself + // registered in this stack. + static std::vector *handler_stack_; + // The index of the handler that should handle the next exception. + static unsigned handler_stack_index_; + static pthread_mutex_t handler_stack_mutex_; + + // A vector of the old signal handlers. + std::vector > old_handlers_; + + // We need to explicitly enable ptrace of parent processes on some + // kernels, but we need to know the PID of the cloned process before we + // can do this. We create a pipe which we can use to block the + // cloned process after creating it, until we have explicitly enabled + // ptrace. This is used to store the file descriptors for the pipe + int fdes[2]; + + // Callers can add extra info about mappings for cases where the + // dumper code cannot extract enough information from /proc//maps. + MappingList mapping_list_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler_unittest.cc b/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler_unittest.cc new file mode 100644 index 0000000..6fcf251 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/handler/exception_handler_unittest.cc @@ -0,0 +1,786 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/tests/auto_tempdir.h" +#include "third_party/lss/linux_syscall_support.h" +#include "google_breakpad/processor/minidump.h" + +using namespace google_breakpad; + +// Length of a formatted GUID string = +// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator) +const int kGUIDStringSize = 37; + +static void sigchld_handler(int signo) { } + +class ExceptionHandlerTest : public ::testing::Test { + protected: + void SetUp() { + // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigchld_handler; + ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); + } + + void TearDown() { + sigaction(SIGCHLD, &old_action, NULL); + } + + struct sigaction old_action; +}; + +TEST(ExceptionHandlerTest, Simple) { + AutoTempDir temp_dir; + ExceptionHandler handler(temp_dir.path(), NULL, NULL, NULL, true); +} + +static bool DoneCallback(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded) { + if (!succeeded) + return succeeded; + + int fd = (intptr_t) context; + uint32_t len = my_strlen(minidump_id); + HANDLE_EINTR(sys_write(fd, &len, sizeof(len))); + HANDLE_EINTR(sys_write(fd, minidump_id, len)); + sys_close(fd); + + return true; +} + +TEST(ExceptionHandlerTest, ChildCrash) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, (void*) fds[1], + true); + *reinterpret_cast(NULL) = 0; + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); + ASSERT_LT(len, (uint32_t)2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = temp_dir.path() + "/" + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(minidump_filename.c_str()); +} + +// Test that memory around the instruction pointer is written +// to the dump as a MinidumpMemoryRegion. +TEST(ExceptionHandlerTest, InstructionPointerMemory) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const u_int32_t kMemorySize = 256; // bytes + const int kOffset = kMemorySize / 2; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGILL); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); + ASSERT_LT(len, (uint32_t)2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = temp_dir.path() + "/" + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT(0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + u_int64_t instruction_pointer; + switch (context->GetContextCPU()) { + case MD_CONTEXT_X86: + instruction_pointer = context->GetContextX86()->eip; + break; + case MD_CONTEXT_AMD64: + instruction_pointer = context->GetContextAMD64()->rip; + break; + case MD_CONTEXT_ARM: + instruction_pointer = context->GetContextARM()->iregs[15]; + break; + default: + FAIL() << "Unknown context CPU: " << context->GetContextCPU(); + break; + } + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemorySize, region->GetSize()); + const u_int8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + u_int8_t prefix_bytes[kOffset]; + u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + suffix_bytes, sizeof(suffix_bytes)) == 0); + + unlink(minidump_filename.c_str()); + free(filename); +} + +// Test that the memory region around the instruction pointer is +// bounded correctly on the low end. +TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const u_int32_t kMemorySize = 256; // bytes + const int kOffset = 0; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGILL); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); + ASSERT_LT(len, (uint32_t)2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = temp_dir.path() + "/" + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT(0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + u_int64_t instruction_pointer; + switch (context->GetContextCPU()) { + case MD_CONTEXT_X86: + instruction_pointer = context->GetContextX86()->eip; + break; + case MD_CONTEXT_AMD64: + instruction_pointer = context->GetContextAMD64()->rip; + break; + case MD_CONTEXT_ARM: + instruction_pointer = context->GetContextARM()->iregs[15]; + break; + default: + FAIL() << "Unknown context CPU: " << context->GetContextCPU(); + break; + } + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemorySize / 2, region->GetSize()); + const u_int8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + suffix_bytes, sizeof(suffix_bytes)) == 0); + + unlink(minidump_filename.c_str()); + free(filename); +} + +// Test that the memory region around the instruction pointer is +// bounded correctly on the high end. +TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + // Use 4k here because the OS will hand out a single page even + // if a smaller size is requested, and this test wants to + // test the upper bound of the memory range. + const u_int32_t kMemorySize = 4096; // bytes + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + const int kOffset = kMemorySize - sizeof(instructions); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGILL); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); + ASSERT_LT(len, (uint32_t)2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = temp_dir.path() + "/" + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT(0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + u_int64_t instruction_pointer; + switch (context->GetContextCPU()) { + case MD_CONTEXT_X86: + instruction_pointer = context->GetContextX86()->eip; + break; + case MD_CONTEXT_AMD64: + instruction_pointer = context->GetContextAMD64()->rip; + break; + case MD_CONTEXT_ARM: + instruction_pointer = context->GetContextARM()->iregs[15]; + break; + default: + FAIL() << "Unknown context CPU: " << context->GetContextCPU(); + break; + } + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + const size_t kPrefixSize = 128; // bytes + EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); + const u_int8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + u_int8_t prefix_bytes[kPrefixSize]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); + EXPECT_TRUE(memcmp(bytes + kPrefixSize, + instructions, sizeof(instructions)) == 0); + + unlink(minidump_filename.c_str()); + free(filename); +} + +// Ensure that an extra memory block doesn't get added when the +// instruction pointer is not in mapped memory. +TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(temp_dir.path(), NULL, DoneCallback, + (void*) fds[1], true); + // Try calling a NULL pointer. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(NULL); + memory_function(); + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); + ASSERT_LT(len, (uint32_t)2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = temp_dir.path() + "/" + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_EQ((unsigned int)1, memory_list->region_count()); + + unlink(minidump_filename.c_str()); + free(filename); +} + +static bool SimpleCallback(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded) { + if (!succeeded) + return succeeded; + + string* minidump_file = reinterpret_cast(context); + minidump_file->append(dump_path); + minidump_file->append("/"); + minidump_file->append(minidump_id); + minidump_file->append(".dmp"); + return true; +} + +// Test that anonymous memory maps can be annotated with names and IDs. +TEST(ExceptionHandlerTest, ModuleInfo) { + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "a fake module"; + const u_int8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + char module_identifier_buffer[kGUIDStringSize]; + FileID::ConvertIdentifierToString(kModuleGUID, + module_identifier_buffer, + sizeof(module_identifier_buffer)); + string module_identifier(module_identifier_buffer); + // Strip out dashes + size_t pos; + while ((pos = module_identifier.find('-')) != string::npos) { + module_identifier.erase(pos, 1); + } + // And append a zero, because module IDs include an "age" field + // which is always zero on Linux. + module_identifier += "0"; + + // Get some memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + string minidump_filename; + AutoTempDir temp_dir; + ExceptionHandler handler(temp_dir.path(), NULL, SimpleCallback, + (void*)&minidump_filename, true); + // Add info about the anonymous memory mapping. + handler.AddMappingInfo(kMemoryName, + kModuleGUID, + kMemoryAddress, + kMemorySize, + 0); + handler.WriteMinidump(); + + // Read the minidump. Load the module list, and ensure that + // the mmap'ed |memory| is listed with the given module name + // and debug ID. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = + module_list->GetModuleForAddress(kMemoryAddress); + ASSERT_TRUE(module); + + EXPECT_EQ(kMemoryAddress, module->base_address()); + EXPECT_EQ(kMemorySize, module->size()); + EXPECT_EQ(kMemoryName, module->code_file()); + EXPECT_EQ(module_identifier, module->debug_identifier()); + + unlink(minidump_filename.c_str()); +} + +static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + +static bool +CrashHandler(const void* crash_context, size_t crash_context_size, + void* context) { + const int fd = (intptr_t) context; + int fds[2]; + if (pipe(fds) == -1) { + // There doesn't seem to be any way to reliably handle + // this failure without the parent process hanging + // At least make sure that this process doesn't access + // unexpected file descriptors + fds[0] = -1; + fds[1] = -1; + } + struct kernel_msghdr msg = {0}; + struct kernel_iovec iov; + iov.iov_base = const_cast(crash_context); + iov.iov_len = crash_context_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + char cmsg[kControlMsgSize]; + memset(cmsg, 0, kControlMsgSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + *((int*) CMSG_DATA(hdr)) = fds[1]; + hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_CREDENTIALS; + hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + struct ucred *cred = reinterpret_cast(CMSG_DATA(hdr)); + cred->uid = getuid(); + cred->gid = getgid(); + cred->pid = getpid(); + + HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); + sys_close(fds[1]); + + char b; + HANDLE_EINTR(sys_read(fds[0], &b, 1)); + + return true; +} + +TEST(ExceptionHandlerTest, ExternalDumper) { + int fds[2]; + ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); + static const int on = 1; + setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true); + handler.set_crash_handler(CrashHandler); + *reinterpret_cast(NULL) = 0; + } + close(fds[1]); + struct msghdr msg = {0}; + struct iovec iov; + static const unsigned kCrashContextSize = + sizeof(ExceptionHandler::CrashContext); + char context[kCrashContextSize]; + char control[kControlMsgSize]; + iov.iov_base = context; + iov.iov_len = kCrashContextSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); + ASSERT_EQ(n, kCrashContextSize); + ASSERT_EQ(msg.msg_controllen, kControlMsgSize); + ASSERT_EQ(msg.msg_flags, 0); + ASSERT_EQ(close(fds[0]), 0); + + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + ASSERT_EQ(len, sizeof(int)); + signal_fd = *((int *) CMSG_DATA(hdr)); + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred *cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + ASSERT_NE(crashing_pid, -1); + ASSERT_NE(signal_fd, -1); + + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/exception-handler-unittest"; + ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context, + kCrashContextSize)); + static const char b = 0; + HANDLE_EINTR(write(signal_fd, &b, 1)); + ASSERT_EQ(close(signal_fd), 0); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct stat st; + ASSERT_EQ(stat(templ.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(templ.c_str()); +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/log/log.cc b/src/lib/crashdump/gbreakpad/client/linux/log/log.cc new file mode 100644 index 0000000..1863591 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/log/log.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2012 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/linux/log/log.h" + +#if defined(__ANDROID__) +#include +#else +#include "third_party/lss/linux_syscall_support.h" +#endif + +namespace logger { + +int write(const char* buf, size_t nbytes) { +#if defined(__ANDROID__) + return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf); +#else + return sys_write(2, buf, nbytes); +#endif +} + +} // namespace logger diff --git a/src/lib/crashdump/gbreakpad/client/linux/log/log.h b/src/lib/crashdump/gbreakpad/client/linux/log/log.h new file mode 100644 index 0000000..a50e30d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/log/log.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_LOG_LOG_H_ +#define CLIENT_LINUX_LOG_LOG_H_ + +#include + +namespace logger { + +int write(const char* buf, size_t nbytes); + +} // namespace logger + +#endif // CLIENT_LINUX_LOG_LOG_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader.h new file mode 100644 index 0000000..1336537 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader.h @@ -0,0 +1,105 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ + +#include +#include +#include +#include +#include +#include + +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for enumerating a directory without using diropen/readdir or other +// functions which may allocate memory. +class DirectoryReader { + public: + DirectoryReader(int fd) + : fd_(fd), + buf_used_(0) { + } + + // Return the next entry from the directory + // name: (output) the NUL terminated entry name + // + // Returns true iff successful (false on EOF). + // + // After calling this, one must call |PopEntry| otherwise you'll get the same + // entry over and over. + bool GetNextEntry(const char** name) { + struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + if (buf_used_ == 0) { + // need to read more entries. + const int n = sys_getdents(fd_, dent, sizeof(buf_)); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + } + + if (buf_used_ == 0 && hit_eof_) + return false; + + assert(buf_used_ > 0); + + *name = dent->d_name; + return true; + } + + void PopEntry() { + if (!buf_used_) + return; + + const struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + buf_used_ -= dent->d_reclen; + memmove(buf_, buf_ + dent->d_reclen, buf_used_); + } + + private: + const int fd_; + bool hit_eof_; + unsigned buf_used_; + uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader_unittest.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader_unittest.cc new file mode 100644 index 0000000..3034e61 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/directory_reader_unittest.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 + +#include "client/linux/minidump_writer/directory_reader.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test DirectoryReaderTest; +} + +TEST(DirectoryReaderTest, CompareResults) { + std::set dent_set; + + DIR *const dir = opendir("/proc/self"); + ASSERT_TRUE(dir != NULL); + + struct dirent* dent; + while ((dent = readdir(dir))) + dent_set.insert(dent->d_name); + + closedir(dir); + + const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); + ASSERT_GE(fd, 0); + + DirectoryReader dir_reader(fd); + unsigned seen = 0; + + const char* name; + while (dir_reader.GetNextEntry(&name)) { + ASSERT_TRUE(dent_set.find(name) != dent_set.end()); + seen++; + dir_reader.PopEntry(); + } + + ASSERT_TRUE(dent_set.find("status") != dent_set.end()); + ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); + ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); + + ASSERT_EQ(dent_set.size(), seen); + close(fd); +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader.h new file mode 100644 index 0000000..2d19c66 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader.h @@ -0,0 +1,130 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ + +#include +#include +#include + +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for reading a file, line by line, without using fopen/fgets or other +// functions which may allocate memory. +class LineReader { + public: + LineReader(int fd) + : fd_(fd), + hit_eof_(false), + buf_used_(0) { + } + + // The maximum length of a line. + static const size_t kMaxLineLen = 512; + + // Return the next line from the file. + // line: (output) a pointer to the start of the line. The line is NUL + // terminated. + // len: (output) the length of the line (not inc the NUL byte) + // + // Returns true iff successful (false on EOF). + // + // One must call |PopLine| after this function, otherwise you'll continue to + // get the same line over and over. + bool GetNextLine(const char **line, unsigned *len) { + for (;;) { + if (buf_used_ == 0 && hit_eof_) + return false; + + for (unsigned i = 0; i < buf_used_; ++i) { + if (buf_[i] == '\n' || buf_[i] == 0) { + buf_[i] = 0; + *len = i; + *line = buf_; + return true; + } + } + + if (buf_used_ == sizeof(buf_)) { + // we scanned the whole buffer and didn't find an end-of-line marker. + // This line is too long to process. + return false; + } + + // We didn't find any end-of-line terminators in the buffer. However, if + // this is the last line in the file it might not have one: + if (hit_eof_) { + assert(buf_used_); + // There's room for the NUL because of the buf_used_ == sizeof(buf_) + // check above. + buf_[buf_used_] = 0; + *len = buf_used_; + buf_used_ += 1; // since we appended the NUL. + *line = buf_; + return true; + } + + // Otherwise, we should pull in more data from the file + const ssize_t n = sys_read(fd_, buf_ + buf_used_, + sizeof(buf_) - buf_used_); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + + // At this point, we have either set the hit_eof_ flag, or we have more + // data to process... + } + } + + void PopLine(unsigned len) { + // len doesn't include the NUL byte at the end. + + assert(buf_used_ >= len + 1); + buf_used_ -= len + 1; + memmove(buf_, buf_ + len + 1, buf_used_); + } + + private: + const int fd_; + + bool hit_eof_; + unsigned buf_used_; + char buf_[kMaxLineLen]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader_unittest.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader_unittest.cc new file mode 100644 index 0000000..17a5467 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/line_reader_unittest.cc @@ -0,0 +1,197 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/linux/minidump_writer/line_reader.h" +#include "breakpad_googletest_includes.h" +#include "common/linux/eintr_wrapper.h" + +using namespace google_breakpad; + +#if !defined(__ANDROID__) +#define TEMPDIR "/tmp" +#else +#define TEMPDIR "/data/local/tmp" +#endif + +static int TemporaryFile() { + static const char templ[] = TEMPDIR "/line-reader-unittest-XXXXXX"; + char templ_copy[sizeof(templ)]; + memcpy(templ_copy, templ, sizeof(templ)); + const int fd = mkstemp(templ_copy); + if (fd >= 0) + unlink(templ_copy); + + return fd; +} + +namespace { +typedef testing::Test LineReaderTest; +} + +TEST(LineReaderTest, EmptyFile) { + const int fd = TemporaryFile(); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, OneLineTerminated) { + const int fd = TemporaryFile(); + const int r = HANDLE_EINTR(write(fd, "a\n", 2)); + ASSERT_EQ(2, r); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned int len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned int)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, OneLine) { + const int fd = TemporaryFile(); + const int r = HANDLE_EINTR(write(fd, "a", 1)); + ASSERT_EQ(1, r); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, TwoLinesTerminated) { + const int fd = TemporaryFile(); + const int r = HANDLE_EINTR(write(fd, "a\nb\n", 4)); + ASSERT_EQ(4, r); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('b', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, TwoLines) { + const int fd = TemporaryFile(); + const int r = HANDLE_EINTR(write(fd, "a\nb", 3)); + ASSERT_EQ(3, r); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('b', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, MaxLength) { + const int fd = TemporaryFile(); + char l[LineReader::kMaxLineLen - 1]; + memset(l, 'a', sizeof(l)); + const int r = HANDLE_EINTR(write(fd, l, sizeof(l))); + ASSERT_EQ(sizeof(l), r); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(sizeof(l), len); + ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); + ASSERT_EQ('\0', line[len]); + + close(fd); +} + +TEST(LineReaderTest, TooLong) { + const int fd = TemporaryFile(); + char l[LineReader::kMaxLineLen]; + memset(l, 'a', sizeof(l)); + const int r = HANDLE_EINTR(write(fd, l, sizeof(l))); + ASSERT_EQ(sizeof(l), r); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.cc new file mode 100644 index 0000000..3e8c92f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.cc @@ -0,0 +1,234 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper. +// See linux_core_dumper.h for details. + +#include "client/linux/minidump_writer/linux_core_dumper.h" + +#include +#include +#include +#include +#include +#include + +#include "common/linux/linux_libc_support.h" + +namespace google_breakpad { + +LinuxCoreDumper::LinuxCoreDumper(pid_t pid, + const char* core_path, + const char* procfs_path) + : LinuxDumper(pid), + core_path_(core_path), + procfs_path_(procfs_path), + thread_infos_(&allocator_, 8) { + assert(core_path_); +} + +bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, + const char* node) const { + if (!path || !node) + return false; + + size_t node_len = my_strlen(node); + if (node_len == 0) + return false; + + size_t procfs_path_len = my_strlen(procfs_path_); + size_t total_length = procfs_path_len + 1 + node_len; + if (total_length >= NAME_MAX) + return false; + + memcpy(path, procfs_path_, procfs_path_len); + path[procfs_path_len] = '/'; + memcpy(path + procfs_path_len + 1, node, node_len); + path[total_length] = '\0'; + return true; +} + +void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, + const void* src, size_t length) { + ElfCoreDump::Addr virtual_address = reinterpret_cast(src); + // TODO(benchan): Investigate whether the data to be copied could span + // across multiple segments in the core dump file. ElfCoreDump::CopyData + // and this method do not handle that case yet. + if (!core_.CopyData(dest, virtual_address, length)) { + // If the data segment is not found in the core dump, fill the result + // with marker characters. + memset(dest, 0xab, length); + } +} + +bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { + if (index >= thread_infos_.size()) + return false; + + *info = thread_infos_[index]; + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#else +#error "This code hasn't been ported to your platform yet." +#endif + + return GetStackInfo(&info->stack, &info->stack_len, + reinterpret_cast(stack_pointer)); +} + +bool LinuxCoreDumper::IsPostMortem() const { + return true; +} + +bool LinuxCoreDumper::ThreadsSuspend() { + return true; +} + +bool LinuxCoreDumper::ThreadsResume() { + return true; +} + +bool LinuxCoreDumper::EnumerateThreads() { + if (!mapped_core_file_.Map(core_path_)) { + fprintf(stderr, "Could not map core dump file into memory\n"); + return false; + } + + core_.SetContent(mapped_core_file_.content()); + if (!core_.IsValid()) { + fprintf(stderr, "Invalid core dump file\n"); + return false; + } + + ElfCoreDump::Note note = core_.GetFirstNote(); + if (!note.IsValid()) { + fprintf(stderr, "PT_NOTE section not found\n"); + return false; + } + + bool first_thread = true; + do { + ElfCoreDump::Word type = note.GetType(); + MemoryRange name = note.GetName(); + MemoryRange description = note.GetDescription(); + + if (type == 0 || name.IsEmpty() || description.IsEmpty()) { + fprintf(stderr, "Could not found a valid PT_NOTE.\n"); + return false; + } + + // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are + // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): + // Thread Name Type + // ------------------------------------------------------------------- + // 1st thread CORE NT_PRSTATUS + // process-wide CORE NT_PRPSINFO + // process-wide CORE NT_AUXV + // 1st thread CORE NT_FPREGSET + // 1st thread LINUX NT_PRXFPREG + // 1st thread LINUX NT_386_TLS + // + // 2nd thread CORE NT_PRSTATUS + // 2nd thread CORE NT_FPREGSET + // 2nd thread LINUX NT_PRXFPREG + // 2nd thread LINUX NT_386_TLS + // + // 3rd thread CORE NT_PRSTATUS + // 3rd thread CORE NT_FPREGSET + // 3rd thread LINUX NT_PRXFPREG + // 3rd thread LINUX NT_386_TLS + // + // The following code only works if notes are ordered as expected. + switch (type) { + case NT_PRSTATUS: { + if (description.length() != sizeof(elf_prstatus)) { + fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); + return false; + } + + const elf_prstatus* status = + reinterpret_cast(description.data()); + pid_t pid = status->pr_pid; + ThreadInfo info; + memset(&info, 0, sizeof(ThreadInfo)); + info.tgid = status->pr_pgrp; + info.ppid = status->pr_ppid; + memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); + if (first_thread) { + crash_thread_ = pid; + crash_signal_ = status->pr_info.si_signo; + } + first_thread = false; + threads_.push_back(pid); + thread_infos_.push_back(info); + break; + } +#if defined(__i386) || defined(__x86_64) + case NT_FPREGSET: { + if (thread_infos_.empty()) + return false; + + ThreadInfo* info = &thread_infos_.back(); + if (description.length() != sizeof(info->fpregs)) { + fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); + return false; + } + + memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); + break; + } +#endif +#if defined(__i386) + case NT_PRXFPREG: { + if (thread_infos_.empty()) + return false; + + ThreadInfo* info = &thread_infos_.back(); + if (description.length() != sizeof(info->fpxregs)) { + fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); + return false; + } + + memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); + break; + } +#endif + } + note = note.GetNextNote(); + } while (note.IsValid()); + + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.h new file mode 100644 index 0000000..edb9e73 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper +// class, which is derived from google_breakpad::LinuxDumper to extract +// information from a crashed process via its core dump and proc files. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ + +#include "client/linux/minidump_writer/linux_dumper.h" +#include "common/linux/elf_core_dump.h" +#include "common/linux/memory_mapped_file.h" + +namespace google_breakpad { + +class LinuxCoreDumper : public LinuxDumper { + public: + // Constructs a dumper for extracting information of a given process + // with a process ID of |pid| via its core dump file at |core_path| and + // its proc files at |procfs_path|. If |procfs_path| is a copy of + // /proc/, it should contain the following files: + // auxv, cmdline, environ, exe, maps, status + LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path); + + // Implements LinuxDumper::BuildProcPath(). + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result.|node| is the final node without any slashes. Return true on + // success. + // + // As this dumper performs a post-mortem dump and makes use of a copy + // of the proc files of the crashed process, this derived method does + // not actually make use of |pid| and always returns a subpath of + // |procfs_path_| regardless of whether |pid| corresponds to the main + // process or a thread of the process, i.e. assuming both the main process + // and its threads have the following proc files with the same content: + // auxv, cmdline, environ, exe, maps, status + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Implements LinuxDumper::CopyFromProcess(). + // Copies content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. This method extracts the content + // the core dump and fills |dest| with a sequence of marker bytes + // if the expected data is not found in the core dump. + virtual void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Implements LinuxDumper::GetThreadInfoByIndex(). + // Reads information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); + + // Implements LinuxDumper::IsPostMortem(). + // Always returns true to indicate that this dumper performs a + // post-mortem dump of a crashed process via a core dump file. + virtual bool IsPostMortem() const; + + // Implements LinuxDumper::ThreadsSuspend(). + // As the dumper performs a post-mortem dump via a core dump file, + // there is no threads to suspend. This method does nothing and + // always returns true. + virtual bool ThreadsSuspend(); + + // Implements LinuxDumper::ThreadsResume(). + // As the dumper performs a post-mortem dump via a core dump file, + // there is no threads to resume. This method does nothing and + // always returns true. + virtual bool ThreadsResume(); + + protected: + // Implements LinuxDumper::EnumerateThreads(). + // Enumerates all threads of the given process into |threads_|. + virtual bool EnumerateThreads(); + + private: + // Path of the core dump file. + const char* core_path_; + + // Path of the directory containing the proc files of the given process, + // which is usually a copy of /proc/. + const char* procfs_path_; + + // Memory-mapped core dump file at |core_path_|. + MemoryMappedFile mapped_core_file_; + + // Content of the core dump file. + ElfCoreDump core_; + + // Thread info found in the core dump file. + wasteful_vector thread_infos_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc new file mode 100644 index 0000000..1fb26ed --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_core_dumper_unittest.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_core_dumper_unittest.cc: +// Unit tests for google_breakpad::LinuxCoreDumoer. + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/linux_core_dumper.h" +#include "common/linux/tests/crash_generator.h" + +using std::string; +using namespace google_breakpad; + +TEST(LinuxCoreDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + const char procfs_path[] = "/procfs_copy"; + LinuxCoreDumper dumper(getpid(), "core_file", procfs_path); + + char maps_path[NAME_MAX] = ""; + char maps_path_expected[NAME_MAX]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "%s/maps", procfs_path); + EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); + EXPECT_STREQ(maps_path_expected, maps_path); + + EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); + + char long_node[NAME_MAX]; + size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1; + memset(long_node, 'a', long_node_len); + long_node[long_node_len] = '\0'; + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node)); +} + +TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to non-default core pattern\n"); + return; + } + + const unsigned kNumOfThreads = 3; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGABRT; + pid_t child_pid; + // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in + // CrashGenerator is identified and fixed. + if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, &child_pid)) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to no core dump generated\n"); + return; + } + + pid_t pid = getpid(); + const string core_file = crash_generator.GetCoreFilePath(); + const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); + LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); + dumper.Init(); + + EXPECT_TRUE(dumper.IsPostMortem()); + + // These are no-ops and should always return true. + EXPECT_TRUE(dumper.ThreadsSuspend()); + EXPECT_TRUE(dumper.ThreadsResume()); + + // LinuxCoreDumper cannot determine the crash address and thus it always + // sets the crash address to 0. + EXPECT_EQ(0, dumper.crash_address()); + EXPECT_EQ(kCrashSignal, dumper.crash_signal()); + EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), + dumper.crash_thread()); + + EXPECT_EQ(kNumOfThreads, dumper.threads().size()); + for (unsigned i = 0; i < kNumOfThreads; ++i) { + ThreadInfo info; + EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info)); + EXPECT_EQ(getpid(), info.ppid); + } +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.cc new file mode 100644 index 0000000..e9d211e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.cc @@ -0,0 +1,358 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_dumper.cc: Implement google_breakpad::LinuxDumper. +// See linux_dumper.h for details. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_dumper.h" + +#include +#include +#include +#include +#include + +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/memory_mapped_file.h" +#include "common/linux/safe_readlink.h" +#include "third_party/lss/linux_syscall_support.h" + +static const char kMappedFileUnsafePrefix[] = "/dev/"; +static const char kDeletedSuffix[] = " (deleted)"; + +inline static bool IsMappedFileOpenUnsafe( + const google_breakpad::MappingInfo& mapping) { + // It is unsafe to attempt to open a mapped file that lives under /dev, + // because the semantics of the open may be driver-specific so we'd risk + // hanging the crash dumper. And a file in /dev/ almost certainly has no + // ELF file identifier anyways. + return my_strncmp(mapping.name, + kMappedFileUnsafePrefix, + sizeof(kMappedFileUnsafePrefix) - 1) == 0; +} + +namespace google_breakpad { + +LinuxDumper::LinuxDumper(pid_t pid) + : pid_(pid), + crash_address_(0), + crash_signal_(0), + crash_thread_(0), + threads_(&allocator_, 8), + mappings_(&allocator_) { +} + +LinuxDumper::~LinuxDumper() { +} + +bool LinuxDumper::Init() { + return EnumerateThreads() && EnumerateMappings(); +} + +bool +LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + uint8_t identifier[sizeof(MDGUID)]) +{ + assert(!member || mapping_id < mappings_.size()); + my_memset(identifier, 0, sizeof(MDGUID)); + if (IsMappedFileOpenUnsafe(mapping)) + return false; + + // Special-case linux-gate because it's not a real file. + if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) { + const uintptr_t kPageSize = getpagesize(); + void* linux_gate = NULL; + if (pid_ == sys_getpid()) { + linux_gate = reinterpret_cast(mapping.start_addr); + } else { + linux_gate = allocator_.Alloc(kPageSize); + CopyFromProcess(linux_gate, pid_, + reinterpret_cast(mapping.start_addr), + kPageSize); + } + return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); + } + + char filename[NAME_MAX]; + size_t filename_len = my_strlen(mapping.name); + assert(filename_len < NAME_MAX); + if (filename_len >= NAME_MAX) + return false; + memcpy(filename, mapping.name, filename_len); + filename[filename_len] = '\0'; + bool filename_modified = HandleDeletedFileInMapping(filename); + + MemoryMappedFile mapped_file(filename); + if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? + return false; + + bool success = + FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); + if (success && member && filename_modified) { + mappings_[mapping_id]->name[filename_len - + sizeof(kDeletedSuffix) + 1] = '\0'; + } + + return success; +} + +void* +LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const { + char auxv_path[NAME_MAX]; + if (!BuildProcPath(auxv_path, pid, "auxv")) + return NULL; + + // Find the AT_SYSINFO_EHDR entry for linux-gate.so + // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more + // information. + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { + close(fd); + return reinterpret_cast(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + +void* +LinuxDumper::FindEntryPoint(pid_t pid) const { + char auxv_path[NAME_MAX]; + if (!BuildProcPath(auxv_path, pid, "auxv")) + return NULL; + + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + // Find the AT_ENTRY entry + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_ENTRY) { + close(fd); + return reinterpret_cast(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + +bool LinuxDumper::EnumerateMappings() { + char maps_path[NAME_MAX]; + if (!BuildProcPath(maps_path, pid_, "maps")) + return false; + + // linux_gate_loc is the beginning of the kernel's mapping of + // linux-gate.so in the process. It doesn't actually show up in the + // maps list as a filename, so we use the aux vector to find it's + // load location and special case it's entry when creating the list + // of mappings. + const void* linux_gate_loc; + linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); + // Although the initial executable is usually the first mapping, it's not + // guaranteed (see http://crosbug.com/25355); therefore, try to use the + // actual entry point to find the mapping. + const void* entry_point_loc = FindEntryPoint(pid_); + + const int fd = sys_open(maps_path, O_RDONLY, 0); + if (fd < 0) + return false; + LineReader* const line_reader = new(allocator_) LineReader(fd); + + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + uintptr_t start_addr, end_addr, offset; + + const char* i1 = my_read_hex_ptr(&start_addr, line); + if (*i1 == '-') { + const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); + if (*i2 == ' ') { + const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); + if (*i3 == ' ') { + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // it's the VDSO image. + if (((name = my_strchr(line, '/')) == NULL) && + linux_gate_loc && + reinterpret_cast(start_addr) == linux_gate_loc) { + name = kLinuxGateLibraryName; + offset = 0; + } + // Merge adjacent mappings with the same name into one module, + // assuming they're a single library mapped by the dynamic linker + if (name && !mappings_.empty()) { + MappingInfo* module = mappings_.back(); + if ((start_addr == module->start_addr + module->size) && + (my_strlen(name) == my_strlen(module->name)) && + (my_strncmp(name, module->name, my_strlen(name)) == 0)) { + module->size = end_addr - module->start_addr; + line_reader->PopLine(line_len); + continue; + } + } + MappingInfo* const module = new(allocator_) MappingInfo; + memset(module, 0, sizeof(MappingInfo)); + module->start_addr = start_addr; + module->size = end_addr - start_addr; + module->offset = offset; + if (name != NULL) { + const unsigned l = my_strlen(name); + if (l < sizeof(module->name)) + memcpy(module->name, name, l); + } + // If this is the entry-point mapping, and it's not already the + // first one, then we need to make it be first. This is because + // the minidump format assumes the first module is the one that + // corresponds to the main executable (as codified in + // processor/minidump.cc:MinidumpModuleList::GetMainModule()). + if (entry_point_loc && + (entry_point_loc >= + reinterpret_cast(module->start_addr)) && + (entry_point_loc < + reinterpret_cast(module->start_addr+module->size)) && + !mappings_.empty()) { + // push the module onto the front of the list. + mappings_.resize(mappings_.size() + 1); + for (size_t idx = mappings_.size() - 1; idx > 0; idx--) + mappings_[idx] = mappings_[idx - 1]; + mappings_[0] = module; + } else { + mappings_.push_back(module); + } + } + } + } + line_reader->PopLine(line_len); + } + + sys_close(fd); + + return !mappings_.empty(); +} + +// Get information about the stack, given the stack pointer. We don't try to +// walk the stack since we might not have all the information needed to do +// unwind. So we just grab, up to, 32k of stack. +bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, + uintptr_t int_stack_pointer) { + // Move the stack pointer to the bottom of the page that it's in. + const uintptr_t page_size = getpagesize(); + + uint8_t* const stack_pointer = + reinterpret_cast(int_stack_pointer & ~(page_size - 1)); + + // The number of bytes of stack which we try to capture. + static const ptrdiff_t kStackToCapture = 32 * 1024; + + const MappingInfo* mapping = FindMapping(stack_pointer); + if (!mapping) + return false; + const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; + const ptrdiff_t distance_to_end = + static_cast(mapping->size) - offset; + *stack_len = distance_to_end > kStackToCapture ? + kStackToCapture : distance_to_end; + *stack = stack_pointer; + return true; +} + +// Find the mapping which the given memory address falls in. +const MappingInfo* LinuxDumper::FindMapping(const void* address) const { + const uintptr_t addr = (uintptr_t) address; + + for (size_t i = 0; i < mappings_.size(); ++i) { + const uintptr_t start = static_cast(mappings_[i]->start_addr); + if (addr >= start && addr - start < mappings_[i]->size) + return mappings_[i]; + } + + return NULL; +} + +bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { + static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; + + // Check for ' (deleted)' in |path|. + // |path| has to be at least as long as "/x (deleted)". + const size_t path_len = my_strlen(path); + if (path_len < kDeletedSuffixLen + 2) + return false; + if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, + kDeletedSuffixLen) != 0) { + return false; + } + + // Check |path| against the /proc/pid/exe 'symlink'. + char exe_link[NAME_MAX]; + char new_path[NAME_MAX]; + if (!BuildProcPath(exe_link, pid_, "exe")) + return false; + if (!SafeReadLink(exe_link, new_path)) + return false; + if (my_strcmp(path, new_path) != 0) + return false; + + // Check to see if someone actually named their executable 'foo (deleted)'. + struct kernel_stat exe_stat; + struct kernel_stat new_path_stat; + if (sys_stat(exe_link, &exe_stat) == 0 && + sys_stat(new_path, &new_path_stat) == 0 && + exe_stat.st_dev == new_path_stat.st_dev && + exe_stat.st_ino == new_path_stat.st_ino) { + return false; + } + + memcpy(path, exe_link, NAME_MAX); + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.h new file mode 100644 index 0000000..42b2a99 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper.h @@ -0,0 +1,235 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which +// is a base class for extracting information of a crashed process. It +// was originally a complete implementation using the ptrace API, but +// has been refactored to allow derived implementations supporting both +// ptrace and core dump. A portion of the original implementation is now +// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for +// details). + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ + +#include +#include +#include +#include +#if !defined(__ANDROID__) +#include +#endif + +#include "common/memory.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +#if defined(__i386) || defined(__x86_64) +typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; +#endif + +// Typedef for our parsing of the auxv variables in /proc/pid/auxv. +#if defined(__i386) || defined(__ARM_EABI__) +#if !defined(__ANDROID__) +typedef Elf32_auxv_t elf_aux_entry; +#else +// Android is missing this structure definition +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + } a_un; +} elf_aux_entry; + +#if !defined(AT_SYSINFO_EHDR) +#define AT_SYSINFO_EHDR 33 +#endif +#endif // __ANDROID__ +#elif defined(__x86_64) +typedef Elf64_auxv_t elf_aux_entry; +#endif +// When we find the VDSO mapping in the process's address space, this +// is the name we use for it when writing it to the minidump. +// This should always be less than NAME_MAX! +const char kLinuxGateLibraryName[] = "linux-gate.so"; + +// We produce one of these structures for each thread in the crashed process. +struct ThreadInfo { + pid_t tgid; // thread group id + pid_t ppid; // parent process + + // Even on platforms where the stack grows down, the following will point to + // the smallest address in the stack. + const void* stack; // pointer to the stack area + size_t stack_len; // length of the stack to copy + + +#if defined(__i386) || defined(__x86_64) + user_regs_struct regs; + user_fpregs_struct fpregs; + static const unsigned kNumDebugRegisters = 8; + debugreg_t dregs[8]; +#if defined(__i386) + user_fpxregs_struct fpxregs; +#endif // defined(__i386) + +#elif defined(__ARM_EABI__) + // Mimicking how strace does this(see syscall.c, search for GETREGS) +#if defined(__ANDROID__) + struct pt_regs regs; +#else + struct user_regs regs; + struct user_fpregs fpregs; +#endif // __ANDROID__ +#endif +}; + +// One of these is produced for each mapping in the process (i.e. line in +// /proc/$x/maps). +struct MappingInfo { + uintptr_t start_addr; + size_t size; + size_t offset; // offset into the backed file. + char name[NAME_MAX]; +}; + +class LinuxDumper { + public: + explicit LinuxDumper(pid_t pid); + + virtual ~LinuxDumper(); + + // Parse the data for |threads| and |mappings|. + virtual bool Init(); + + // Return true if the dumper performs a post-mortem dump. + virtual bool IsPostMortem() const = 0; + + // Suspend/resume all threads in the given process. + virtual bool ThreadsSuspend() = 0; + virtual bool ThreadsResume() = 0; + + // Read information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0; + + // These are only valid after a call to |Init|. + const wasteful_vector &threads() { return threads_; } + const wasteful_vector &mappings() { return mappings_; } + const MappingInfo* FindMapping(const void* address) const; + + // Find a block of memory to take as the stack given the top of stack pointer. + // stack: (output) the lowest address in the memory area + // stack_len: (output) the length of the memory area + // stack_top: the current top of the stack + bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); + + PageAllocator* allocator() { return &allocator_; } + + // Copy content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. + virtual void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length) = 0; + + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result.|node| is the final node without any slashes. Returns true on + // success. + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; + + // Generate a File ID from the .text section of a mapped entry. + // If not a member, mapping_id is ignored. + bool ElfFileIdentifierForMapping(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + uint8_t identifier[sizeof(MDGUID)]); + + // Utility method to find the location of where the kernel has + // mapped linux-gate.so in memory(shows up in /proc/pid/maps as + // [vdso], but we can't guarantee that it's the only virtual dynamic + // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR + // is the safest way to go.) + void* FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const; + // Utility method to find the entry point location. + void* FindEntryPoint(pid_t pid) const; + + uintptr_t crash_address() const { return crash_address_; } + void set_crash_address(uintptr_t crash_address) { + crash_address_ = crash_address; + } + + int crash_signal() const { return crash_signal_; } + void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; } + + pid_t crash_thread() const { return crash_thread_; } + void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } + + protected: + virtual bool EnumerateMappings(); + + virtual bool EnumerateThreads() = 0; + + // For the case where a running program has been deleted, it'll show up in + // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then + // see if '/path/to/program (deleted)' matches /proc/pid/exe and return + // /proc/pid/exe in |path| so ELF identifier generation works correctly. This + // also checks to see if '/path/to/program (deleted)' exists, so it does not + // get fooled by a poorly named binary. + // For programs that don't end with ' (deleted)', this is a no-op. + // This assumes |path| is a buffer with length NAME_MAX. + // Returns true if |path| is modified. + bool HandleDeletedFileInMapping(char* path) const; + + // ID of the crashed process. + const pid_t pid_; + + // Virtual address at which the process crashed. + uintptr_t crash_address_; + + // Signal that terminated the crashed process. + int crash_signal_; + + // ID of the crashed thread. + pid_t crash_thread_; + + mutable PageAllocator allocator_; + + // IDs of all the threads. + wasteful_vector threads_; + + // Info from /proc//maps. + wasteful_vector mappings_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc new file mode 100644 index 0000000..418e7e6 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Helper program for the linux_dumper class, which creates a bunch of +// threads. The first word of each thread's stack is set to the thread +// id. + +#include +#include +#include +#include +#include +#include + +#include "third_party/lss/linux_syscall_support.h" + +#if defined(__ARM_EABI__) +#define TID_PTR_REGISTER "r3" +#elif defined(__i386) +#define TID_PTR_REGISTER "ecx" +#elif defined(__x86_64) +#define TID_PTR_REGISTER "rcx" +#else +#error This test has not been ported to this platform. +#endif + +void *thread_function(void *data) { + int pipefd = *static_cast(data); + volatile pid_t thread_id = syscall(__NR_gettid); + // Signal parent that a thread has started. + uint8_t byte = 1; + if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) { + perror("ERROR: parent notification failed"); + return NULL; + } + register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id; + while (true) + asm volatile ("" : : "r" (thread_id_ptr)); + return NULL; +} + +int main(int argc, char *argv[]) { + if (argc < 3) { + fprintf(stderr, + "usage: linux_dumper_unittest_helper <# of threads>\n"); + return 1; + } + int pipefd = atoi(argv[1]); + int num_threads = atoi(argv[2]); + if (num_threads < 1) { + fprintf(stderr, "ERROR: number of threads is 0"); + return 1; + } + pthread_t threads[num_threads]; + pthread_attr_t thread_attributes; + pthread_attr_init(&thread_attributes); + pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); + for (int i = 1; i < num_threads; i++) { + pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd); + } + thread_function(&pipefd); + return 0; +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc new file mode 100644 index 0000000..864bbad --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc @@ -0,0 +1,293 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper. +// See linux_ptrace_dumper.h for detals. +// This class was originally splitted from google_breakpad::LinuxDumper. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +// Suspends a thread by attaching to it. +static bool SuspendThread(pid_t pid) { + // This may fail if the thread has just died or debugged. + errno = 0; + if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && + errno != 0) { + return false; + } + while (sys_waitpid(pid, NULL, __WALL) < 0) { + if (errno != EINTR) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } + } +#if defined(__i386) || defined(__x86_64) + // On x86, the stack pointer is NULL or -1, when executing trusted code in + // the seccomp sandbox. Not only does this cause difficulties down the line + // when trying to dump the thread's stack, it also results in the minidumps + // containing information about the trusted threads. This information is + // generally completely meaningless and just pollutes the minidumps. + // We thus test the stack pointer and exclude any threads that are part of + // the seccomp sandbox's trusted code. + user_regs_struct regs; + if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || +#if defined(__i386) + !regs.esp +#elif defined(__x86_64) + !regs.rsp +#endif + ) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } +#endif + return true; +} + +// Resumes a thread by detaching from it. +static bool ResumeThread(pid_t pid) { + return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; +} + +namespace google_breakpad { + +LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) + : LinuxDumper(pid), + threads_suspended_(false) { +} + +bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, + const char* node) const { + if (!path || !node || pid <= 0) + return false; + + size_t node_len = my_strlen(node); + if (node_len == 0) + return false; + + const unsigned pid_len = my_int_len(pid); + const size_t total_length = 6 + pid_len + 1 + node_len; + if (total_length >= NAME_MAX) + return false; + + memcpy(path, "/proc/", 6); + my_itos(path + 6, pid, pid_len); + path[6 + pid_len] = '/'; + memcpy(path + 6 + pid_len + 1, node, node_len); + path[total_length] = '\0'; + return true; +} + +void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, + const void* src, size_t length) { + unsigned long tmp = 55; + size_t done = 0; + static const size_t word_size = sizeof(tmp); + uint8_t* const local = (uint8_t*) dest; + uint8_t* const remote = (uint8_t*) src; + + while (done < length) { + const size_t l = (length - done > word_size) ? word_size : (length - done); + if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { + tmp = 0; + } + memcpy(local + done, &tmp, l); + done += l; + } +} + +// Read thread info from /proc/$pid/status. +// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, +// these members are set to -1. Returns true iff all three members are +// available. +bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { + if (index >= threads_.size()) + return false; + + pid_t tid = threads_[index]; + + assert(info != NULL); + char status_path[NAME_MAX]; + if (!BuildProcPath(status_path, tid, "status")) + return false; + + const int fd = sys_open(status_path, O_RDONLY, 0); + if (fd < 0) + return false; + + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; + unsigned line_len; + + info->ppid = info->tgid = -1; + + while (line_reader->GetNextLine(&line, &line_len)) { + if (my_strncmp("Tgid:\t", line, 6) == 0) { + my_strtoui(&info->tgid, line + 6); + } else if (my_strncmp("PPid:\t", line, 6) == 0) { + my_strtoui(&info->ppid, line + 6); + } + + line_reader->PopLine(line_len); + } + sys_close(fd); + + if (info->ppid == -1 || info->tgid == -1) + return false; + + if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { + return false; + } + +#if !defined(__ANDROID__) + if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { + return false; + } +#endif + +#if defined(__i386) + if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) + return false; +#endif + +#if defined(__i386) || defined(__x86_64) + for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { + if (sys_ptrace( + PTRACE_PEEKUSER, tid, + reinterpret_cast (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), + &info->dregs[i]) == -1) { + return false; + } + } +#endif + + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#else +#error "This code hasn't been ported to your platform yet." +#endif + + return GetStackInfo(&info->stack, &info->stack_len, + (uintptr_t) stack_pointer); +} + +bool LinuxPtraceDumper::IsPostMortem() const { + return false; +} + +bool LinuxPtraceDumper::ThreadsSuspend() { + if (threads_suspended_) + return true; + for (size_t i = 0; i < threads_.size(); ++i) { + if (!SuspendThread(threads_[i])) { + // If the thread either disappeared before we could attach to it, or if + // it was part of the seccomp sandbox's trusted code, it is OK to + // silently drop it from the minidump. + memmove(&threads_[i], &threads_[i+1], + (threads_.size() - i - 1) * sizeof(threads_[i])); + threads_.resize(threads_.size() - 1); + --i; + } + } + threads_suspended_ = true; + return threads_.size() > 0; +} + +bool LinuxPtraceDumper::ThreadsResume() { + if (!threads_suspended_) + return false; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= ResumeThread(threads_[i]); + threads_suspended_ = false; + return good; +} + +// Parse /proc/$pid/task to list all the threads of the process identified by +// pid. +bool LinuxPtraceDumper::EnumerateThreads() { + char task_path[NAME_MAX]; + if (!BuildProcPath(task_path, pid_, "task")) + return false; + + const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) + return false; + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); + + // The directory may contain duplicate entries which we filter by assuming + // that they are consecutive. + int last_tid = -1; + const char* dent_name; + while (dir_reader->GetNextEntry(&dent_name)) { + if (my_strcmp(dent_name, ".") && + my_strcmp(dent_name, "..")) { + int tid = 0; + if (my_strtoui(&tid, dent_name) && + last_tid != tid) { + last_tid = tid; + threads_.push_back(tid); + } + } + dir_reader->PopEntry(); + } + + sys_close(fd); + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.h new file mode 100644 index 0000000..1e9bcfd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper.h @@ -0,0 +1,92 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper +// class, which is derived from google_breakpad::LinuxDumper to extract +// information from a crashed process via ptrace. +// This class was originally splitted from google_breakpad::LinuxDumper. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ + +#include "client/linux/minidump_writer/linux_dumper.h" + +namespace google_breakpad { + +class LinuxPtraceDumper : public LinuxDumper { + public: + // Constructs a dumper for extracting information of a given process + // with a process ID of |pid|. + explicit LinuxPtraceDumper(pid_t pid); + + // Implements LinuxDumper::BuildProcPath(). + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result. |node| is the final node without any slashes. Returns true on + // success. + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Implements LinuxDumper::CopyFromProcess(). + // Copies content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. This method uses ptrace to extract + // the content from the target process. + virtual void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Implements LinuxDumper::GetThreadInfoByIndex(). + // Reads information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); + + // Implements LinuxDumper::IsPostMortem(). + // Always returns false to indicate this dumper performs a dump of + // a crashed process via ptrace. + virtual bool IsPostMortem() const; + + // Implements LinuxDumper::ThreadsSuspend(). + // Suspends all threads in the given process. Returns true on success. + virtual bool ThreadsSuspend(); + + // Implements LinuxDumper::ThreadsResume(). + // Resumes all threads in the given process. Returns true on success. + virtual bool ThreadsResume(); + + protected: + // Implements LinuxDumper::EnumerateThreads(). + // Enumerates all threads of the given process into |threads_|. + virtual bool EnumerateThreads(); + + private: + // Set to true if all threads of the crashed process are suspended. + bool threads_suspended_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc new file mode 100644 index 0000000..ef47da9 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc @@ -0,0 +1,445 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// linux_ptrace_dumper_unittest.cc: +// Unit tests for google_breakpad::LinuxPtraceDumoer. +// +// This file was renamed from linux_dumper_unittest.cc and modified due +// to LinuxDumper being splitted into two classes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" +#include "common/memory.h" + +using std::string; +using namespace google_breakpad; + +namespace { + +typedef testing::Test LinuxPtraceDumperTest; + +string GetHelperBinary() { + // Locate helper binary next to the current binary. + char self_path[PATH_MAX]; + if (!SafeReadLink("/proc/self/exe", self_path)) { + return ""; + } + string helper_path(self_path); + size_t pos = helper_path.rfind('/'); + if (pos == string::npos) { + return ""; + } + helper_path.erase(pos + 1); + helper_path += "linux_dumper_unittest_helper"; + + return helper_path; +} + +} // namespace + +TEST(LinuxPtraceDumperTest, Setup) { + LinuxPtraceDumper dumper(getpid()); +} + +TEST(LinuxPtraceDumperTest, FindMappings) { + LinuxPtraceDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(getpid))); + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(printf))); + ASSERT_FALSE(dumper.FindMapping(NULL)); +} + +TEST(LinuxPtraceDumperTest, ThreadList) { + LinuxPtraceDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_GE(dumper.threads().size(), (size_t)1); + bool found = false; + for (size_t i = 0; i < dumper.threads().size(); ++i) { + if (dumper.threads()[i] == getpid()) { + found = true; + break; + } + } +} + +// Helper stack class to close a file descriptor and unmap +// a mmap'ed mapping. +class StackHelper { + public: + StackHelper(int fd, char* mapping, size_t size) + : fd_(fd), mapping_(mapping), size_(size) {} + ~StackHelper() { + munmap(mapping_, size_); + close(fd_); + } + + private: + int fd_; + char* mapping_; + size_t size_; +}; + +TEST(LinuxPtraceDumperTest, MergedMappings) { + string helper_path(GetHelperBinary()); + if (helper_path.empty()) { + FAIL() << "Couldn't find helper binary"; + exit(1); + } + + // mmap two segments out of the helper binary, one + // enclosed in the other, but with different protections. + const size_t kPageSize = sysconf(_SC_PAGESIZE); + const size_t kMappingSize = 3 * kPageSize; + int fd = open(helper_path.c_str(), O_RDONLY); + ASSERT_NE(-1, fd); + char* mapping = + reinterpret_cast(mmap(NULL, + kMappingSize, + PROT_READ, + MAP_SHARED, + fd, + 0)); + ASSERT_TRUE(mapping); + + const uintptr_t kMappingAddress = reinterpret_cast(mapping); + + // Ensure that things get cleaned up. + StackHelper helper(fd, mapping, kMappingSize); + + // Carve a page out of the first mapping with different permissions. + char* inside_mapping = reinterpret_cast( + mmap(mapping + 2 *kPageSize, + kPageSize, + PROT_NONE, + MAP_SHARED | MAP_FIXED, + fd, + // Map a different offset just to + // better test real-world conditions. + kPageSize)); + ASSERT_TRUE(inside_mapping); + + // Now check that LinuxPtraceDumper interpreted the mappings properly. + LinuxPtraceDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + int mapping_count = 0; + for (unsigned i = 0; i < dumper.mappings().size(); ++i) { + const MappingInfo& mapping = *dumper.mappings()[i]; + if (strcmp(mapping.name, helper_path.c_str()) == 0) { + // This mapping should encompass the entire original mapped + // range. + EXPECT_EQ(kMappingAddress, mapping.start_addr); + EXPECT_EQ(kMappingSize, mapping.size); + EXPECT_EQ(0, mapping.offset); + mapping_count++; + } + } + EXPECT_EQ(1, mapping_count); +} + +TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { + static const int kNumberOfThreadsInHelperProgram = 5; + char kNumberOfThreadsArgument[2]; + sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); + + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + pid_t child_pid = fork(); + if (child_pid == 0) { + // In child process. + close(fds[0]); + + string helper_path(GetHelperBinary()); + if (helper_path.empty()) { + FAIL() << "Couldn't find helper binary"; + exit(1); + } + + // Pass the pipe fd and the number of threads as arguments. + char pipe_fd_string[8]; + sprintf(pipe_fd_string, "%d", fds[1]); + execl(helper_path.c_str(), + "linux_dumper_unittest_helper", + pipe_fd_string, + kNumberOfThreadsArgument, + NULL); + // Kill if we get here. + printf("Errno from exec: %d", errno); + FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno); + exit(0); + } + close(fds[1]); + + // Wait for all child threads to indicate that they have started + for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + uint8_t junk; + ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), sizeof(junk)); + } + close(fds[0]); + + // There is a race here because we may stop a child thread before + // it is actually running the busy loop. Empirically this sleep + // is sufficient to avoid the race. + usleep(100000); + + // Children are ready now. + LinuxPtraceDumper dumper(child_pid); + ASSERT_TRUE(dumper.Init()); + EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); + EXPECT_TRUE(dumper.ThreadsSuspend()); + + ThreadInfo one_thread; + for (size_t i = 0; i < dumper.threads().size(); ++i) { + EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread)); + // In the helper program, we stored a pointer to the thread id in a + // specific register. Check that we can recover its value. +#if defined(__ARM_EABI__) + pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]); +#elif defined(__i386) + pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx); +#elif defined(__x86_64) + pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx); +#else +#error This test has not been ported to this platform. +#endif + pid_t one_thread_id; + dumper.CopyFromProcess(&one_thread_id, + dumper.threads()[i], + process_tid_location, + 4); + EXPECT_EQ(dumper.threads()[i], one_thread_id); + } + EXPECT_TRUE(dumper.ThreadsResume()); + kill(child_pid, SIGKILL); + + // Reap child + int status; + ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(SIGKILL, WTERMSIG(status)); +} + +TEST(LinuxPtraceDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + LinuxPtraceDumper dumper(pid); + + char maps_path[NAME_MAX] = ""; + char maps_path_expected[NAME_MAX]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "/proc/%d/maps", pid); + EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); + EXPECT_STREQ(maps_path_expected, maps_path); + + EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); + + char long_node[NAME_MAX]; + size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; + memset(long_node, 'a', long_node_len); + long_node[long_node_len] = '\0'; + EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); +} + +#if !defined(__ARM_EABI__) +// Ensure that the linux-gate VDSO is included in the mapping list. +TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) { + LinuxPtraceDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); + ASSERT_TRUE(linux_gate_loc); + bool found_linux_gate = false; + + const wasteful_vector mappings = dumper.mappings(); + const MappingInfo* mapping; + for (unsigned i = 0; i < mappings.size(); ++i) { + mapping = mappings[i]; + if (!strcmp(mapping->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + break; + } + } + EXPECT_TRUE(found_linux_gate); + EXPECT_EQ(linux_gate_loc, reinterpret_cast(mapping->start_addr)); + EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); +} + +// Ensure that the linux-gate VDSO can generate a non-zeroed File ID. +TEST(LinuxPtraceDumperTest, LinuxGateMappingID) { + LinuxPtraceDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + bool found_linux_gate = false; + const wasteful_vector mappings = dumper.mappings(); + unsigned index = 0; + for (unsigned i = 0; i < mappings.size(); ++i) { + if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + index = i; + break; + } + } + ASSERT_TRUE(found_linux_gate); + + uint8_t identifier[sizeof(MDGUID)]; + ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], + true, + index, + identifier)); + uint8_t empty_identifier[sizeof(MDGUID)]; + memset(empty_identifier, 0, sizeof(empty_identifier)); + EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); +} + +// Ensure that the linux-gate VDSO can generate a non-zeroed File ID +// from a child process. +TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // Fork a child so ptrace works. + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + // Now wait forever for the parent. + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + LinuxPtraceDumper dumper(child); + ASSERT_TRUE(dumper.Init()); + + bool found_linux_gate = false; + const wasteful_vector mappings = dumper.mappings(); + unsigned index = 0; + for (unsigned i = 0; i < mappings.size(); ++i) { + if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + index = i; + break; + } + } + ASSERT_TRUE(found_linux_gate); + + // Need to suspend the child so ptrace actually works. + ASSERT_TRUE(dumper.ThreadsSuspend()); + uint8_t identifier[sizeof(MDGUID)]; + ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], + true, + index, + identifier)); + uint8_t empty_identifier[sizeof(MDGUID)]; + memset(empty_identifier, 0, sizeof(empty_identifier)); + EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); + EXPECT_TRUE(dumper.ThreadsResume()); + close(fds[1]); +} +#endif + +TEST(LinuxPtraceDumperTest, FileIDsMatch) { + // Calculate the File ID of our binary using both + // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping + // and ensure that we get the same result from both. + char exe_name[PATH_MAX]; + ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); + + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // Fork a child so ptrace works. + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + // Now wait forever for the parent. + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + LinuxPtraceDumper dumper(child); + ASSERT_TRUE(dumper.Init()); + const wasteful_vector mappings = dumper.mappings(); + bool found_exe = false; + unsigned i; + for (i = 0; i < mappings.size(); ++i) { + const MappingInfo* mapping = mappings[i]; + if (!strcmp(mapping->name, exe_name)) { + found_exe = true; + break; + } + } + ASSERT_TRUE(found_exe); + + uint8_t identifier1[sizeof(MDGUID)]; + uint8_t identifier2[sizeof(MDGUID)]; + EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, + identifier1)); + FileID fileid(exe_name); + EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); + char identifier_string1[37]; + char identifier_string2[37]; + FileID::ConvertIdentifierToString(identifier1, identifier_string1, + 37); + FileID::ConvertIdentifierToString(identifier2, identifier_string2, + 37); + EXPECT_STREQ(identifier_string1, identifier_string2); + close(fds[1]); +} diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_extension_linux.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_extension_linux.h new file mode 100644 index 0000000..90acc6a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_extension_linux.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2010, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_extension_linux.h: A definition of exception codes for + * Linux + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Adam Langley + * Split into its own file: Markus Gutschke */ + + +#ifndef SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_ +#define SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_ + +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" + +// These are additional minidump stream values which are specific to the linux +// breakpad implementation. +enum { + MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */ + MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */ + MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */ + MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */ + MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ + MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */ + MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */ + MD_LINUX_DSO_DEBUG = 0x4767000A /* DSO data */ +}; + +typedef struct { + void* addr; + MDRVA name; + void* ld; +} MDRawLinkMap; + +typedef struct { + u_int32_t version; + MDRVA map; + u_int32_t dso_count; + void* brk; + void* ldbase; + void* dynamic; +} MDRawDebug; + +#endif // SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.cc new file mode 100644 index 0000000..35a291d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.cc @@ -0,0 +1,1349 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This code writes out minidump files: +// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx +// +// Minidumps are a Microsoft format which Breakpad uses for recording crash +// dumps. This code has to run in a compromised environment (the address space +// may have received SIGSEGV), thus the following rules apply: +// * You may not enter the dynamic linker. This means that we cannot call +// any symbols in a shared library (inc libc). Because of this we replace +// libc functions in linux_libc_support.h. +// * You may not call syscalls via the libc wrappers. This rule is a subset +// of the first rule but it bears repeating. We have direct wrappers +// around the system calls in linux_syscall_support.h. +// * You may not malloc. There's an alternative allocator in memory.h and +// a canonical instance in the LinuxDumper object. We use the placement +// new form to allocate objects and we don't delete them. + +#include "client/linux/minidump_writer/minidump_writer.h" +#include "client/minidump_file_writer-inl.h" + +#include +#include +#include +#if !defined(__ANDROID__) +#include +#endif +#include +#if !defined(__ANDROID__) +#include +#include +#endif +#include +#include + +#include + +#include "client/minidump_file_writer.h" +#include "google_breakpad/common/minidump_format.h" + +#if defined(__ANDROID__) +#include "client/linux/android_link.h" +#include "client/linux/android_ucontext.h" +#endif +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" +#include "client/linux/minidump_writer/minidump_extension_linux.h" +#include "client/minidump_file_writer.h" +#include "common/linux/linux_libc_support.h" +#include "google_breakpad/common/minidump_format.h" +#include "third_party/lss/linux_syscall_support.h" + +// Minidump defines register structures which are different from the raw +// structures which we get from the kernel. These are platform specific +// functions to juggle the ucontext and user structures into minidump format. +#if defined(__i386) +typedef MDRawContextX86 RawContextCPU; + +// Write a uint16_t to memory +// out: memory location to write to +// v: value to write. +static void U16(void* out, uint16_t v) { + memcpy(out, &v, sizeof(v)); +} + +// Write a uint32_t to memory +// out: memory location to write to +// v: value to write. +static void U32(void* out, uint32_t v) { + memcpy(out, &v, sizeof(v)); +} + +// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format +// out: the minidump structure +// info: the collection of register structures. +static void CPUFillFromThreadInfo(MDRawContextX86 *out, + const google_breakpad::ThreadInfo &info) { + out->context_flags = MD_CONTEXT_X86_ALL; + + out->dr0 = info.dregs[0]; + out->dr1 = info.dregs[1]; + out->dr2 = info.dregs[2]; + out->dr3 = info.dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = info.dregs[6]; + out->dr7 = info.dregs[7]; + + out->gs = info.regs.xgs; + out->fs = info.regs.xfs; + out->es = info.regs.xes; + out->ds = info.regs.xds; + + out->edi = info.regs.edi; + out->esi = info.regs.esi; + out->ebx = info.regs.ebx; + out->edx = info.regs.edx; + out->ecx = info.regs.ecx; + out->eax = info.regs.eax; + + out->ebp = info.regs.ebp; + out->eip = info.regs.eip; + out->cs = info.regs.xcs; + out->eflags = info.regs.eflags; + out->esp = info.regs.esp; + out->ss = info.regs.xss; + + out->float_save.control_word = info.fpregs.cwd; + out->float_save.status_word = info.fpregs.swd; + out->float_save.tag_word = info.fpregs.twd; + out->float_save.error_offset = info.fpregs.fip; + out->float_save.error_selector = info.fpregs.fcs; + out->float_save.data_offset = info.fpregs.foo; + out->float_save.data_selector = info.fpregs.fos; + + // 8 registers * 10 bytes per register. + memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8); + + // This matches the Intel fpsave format. + U16(out->extended_registers + 0, info.fpregs.cwd); + U16(out->extended_registers + 2, info.fpregs.swd); + U16(out->extended_registers + 4, info.fpregs.twd); + U16(out->extended_registers + 6, info.fpxregs.fop); + U32(out->extended_registers + 8, info.fpxregs.fip); + U16(out->extended_registers + 12, info.fpxregs.fcs); + U32(out->extended_registers + 16, info.fpregs.foo); + U16(out->extended_registers + 20, info.fpregs.fos); + U32(out->extended_registers + 24, info.fpxregs.mxcsr); + + memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128); + memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128); +} + +// Juggle an x86 ucontext into minidump format +// out: the minidump structure +// info: the collection of register structures. +static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc, + const struct _libc_fpstate* fp) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_X86_FULL | + MD_CONTEXT_X86_FLOATING_POINT; + + out->gs = regs[REG_GS]; + out->fs = regs[REG_FS]; + out->es = regs[REG_ES]; + out->ds = regs[REG_DS]; + + out->edi = regs[REG_EDI]; + out->esi = regs[REG_ESI]; + out->ebx = regs[REG_EBX]; + out->edx = regs[REG_EDX]; + out->ecx = regs[REG_ECX]; + out->eax = regs[REG_EAX]; + + out->ebp = regs[REG_EBP]; + out->eip = regs[REG_EIP]; + out->cs = regs[REG_CS]; + out->eflags = regs[REG_EFL]; + out->esp = regs[REG_UESP]; + out->ss = regs[REG_SS]; + + out->float_save.control_word = fp->cw; + out->float_save.status_word = fp->sw; + out->float_save.tag_word = fp->tag; + out->float_save.error_offset = fp->ipoff; + out->float_save.error_selector = fp->cssel; + out->float_save.data_offset = fp->dataoff; + out->float_save.data_selector = fp->datasel; + + // 8 registers * 10 bytes per register. + memcpy(out->float_save.register_area, fp->_st, 10 * 8); +} + +#elif defined(__x86_64) +typedef MDRawContextAMD64 RawContextCPU; + +static void CPUFillFromThreadInfo(MDRawContextAMD64 *out, + const google_breakpad::ThreadInfo &info) { + out->context_flags = MD_CONTEXT_AMD64_FULL | + MD_CONTEXT_AMD64_SEGMENTS; + + out->cs = info.regs.cs; + + out->ds = info.regs.ds; + out->es = info.regs.es; + out->fs = info.regs.fs; + out->gs = info.regs.gs; + + out->ss = info.regs.ss; + out->eflags = info.regs.eflags; + + out->dr0 = info.dregs[0]; + out->dr1 = info.dregs[1]; + out->dr2 = info.dregs[2]; + out->dr3 = info.dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = info.dregs[6]; + out->dr7 = info.dregs[7]; + + out->rax = info.regs.rax; + out->rcx = info.regs.rcx; + out->rdx = info.regs.rdx; + out->rbx = info.regs.rbx; + + out->rsp = info.regs.rsp; + + out->rbp = info.regs.rbp; + out->rsi = info.regs.rsi; + out->rdi = info.regs.rdi; + out->r8 = info.regs.r8; + out->r9 = info.regs.r9; + out->r10 = info.regs.r10; + out->r11 = info.regs.r11; + out->r12 = info.regs.r12; + out->r13 = info.regs.r13; + out->r14 = info.regs.r14; + out->r15 = info.regs.r15; + + out->rip = info.regs.rip; + + out->flt_save.control_word = info.fpregs.cwd; + out->flt_save.status_word = info.fpregs.swd; + out->flt_save.tag_word = info.fpregs.ftw; + out->flt_save.error_opcode = info.fpregs.fop; + out->flt_save.error_offset = info.fpregs.rip; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_offset = info.fpregs.rdp; + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = info.fpregs.mxcsr; + out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask; + memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16); + memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16); +} + +static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc, + const struct _libc_fpstate* fpregs) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_AMD64_FULL; + + out->cs = regs[REG_CSGSFS] & 0xffff; + + out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; + out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; + + out->eflags = regs[REG_EFL]; + + out->rax = regs[REG_RAX]; + out->rcx = regs[REG_RCX]; + out->rdx = regs[REG_RDX]; + out->rbx = regs[REG_RBX]; + + out->rsp = regs[REG_RSP]; + out->rbp = regs[REG_RBP]; + out->rsi = regs[REG_RSI]; + out->rdi = regs[REG_RDI]; + out->r8 = regs[REG_R8]; + out->r9 = regs[REG_R9]; + out->r10 = regs[REG_R10]; + out->r11 = regs[REG_R11]; + out->r12 = regs[REG_R12]; + out->r13 = regs[REG_R13]; + out->r14 = regs[REG_R14]; + out->r15 = regs[REG_R15]; + + out->rip = regs[REG_RIP]; + + out->flt_save.control_word = fpregs->cwd; + out->flt_save.status_word = fpregs->swd; + out->flt_save.tag_word = fpregs->ftw; + out->flt_save.error_opcode = fpregs->fop; + out->flt_save.error_offset = fpregs->rip; + out->flt_save.data_offset = fpregs->rdp; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = fpregs->mxcsr; + out->flt_save.mx_csr_mask = fpregs->mxcr_mask; + memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); + memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); +} + +#elif defined(__ARMEL__) +typedef MDRawContextARM RawContextCPU; + +static void CPUFillFromThreadInfo(MDRawContextARM *out, + const google_breakpad::ThreadInfo &info) { + out->context_flags = MD_CONTEXT_ARM_FULL; + + for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) + out->iregs[i] = info.regs.uregs[i]; + // No CPSR register in ThreadInfo(it's not accessible via ptrace) + out->cpsr = 0; +#if !defined(__ANDROID__) + out->float_save.fpscr = info.fpregs.fpsr | + (static_cast(info.fpregs.fpcr) << 32); + // TODO: sort this out, actually collect floating point registers + memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); + memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); +#endif +} + +static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc, + const struct _libc_fpstate* fpregs) { + out->context_flags = MD_CONTEXT_ARM_FULL; + + out->iregs[0] = uc->uc_mcontext.arm_r0; + out->iregs[1] = uc->uc_mcontext.arm_r1; + out->iregs[2] = uc->uc_mcontext.arm_r2; + out->iregs[3] = uc->uc_mcontext.arm_r3; + out->iregs[4] = uc->uc_mcontext.arm_r4; + out->iregs[5] = uc->uc_mcontext.arm_r5; + out->iregs[6] = uc->uc_mcontext.arm_r6; + out->iregs[7] = uc->uc_mcontext.arm_r7; + out->iregs[8] = uc->uc_mcontext.arm_r8; + out->iregs[9] = uc->uc_mcontext.arm_r9; + out->iregs[10] = uc->uc_mcontext.arm_r10; + + out->iregs[11] = uc->uc_mcontext.arm_fp; + out->iregs[12] = uc->uc_mcontext.arm_ip; + out->iregs[13] = uc->uc_mcontext.arm_sp; + out->iregs[14] = uc->uc_mcontext.arm_lr; + out->iregs[15] = uc->uc_mcontext.arm_pc; + + out->cpsr = uc->uc_mcontext.arm_cpsr; + + // TODO: fix this after fixing ExceptionHandler + out->float_save.fpscr = 0; + memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); + memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); +} + +#else +#error "This code has not been ported to your platform yet." +#endif + +namespace google_breakpad { + +class MinidumpWriter { + public: + MinidumpWriter(const char* filename, + const ExceptionHandler::CrashContext* context, + const MappingList& mappings, + LinuxDumper* dumper) + : filename_(filename), + ucontext_(context ? &context->context : NULL), +#if !defined(__ARM_EABI__) + float_state_(context ? &context->float_state : NULL), +#else + // TODO: fix this after fixing ExceptionHandler + float_state_(NULL), +#endif + dumper_(dumper), + memory_blocks_(dumper_->allocator()), + mapping_list_(mappings) { + } + + bool Init() { + return dumper_->Init() && minidump_writer_.Open(filename_) && + dumper_->ThreadsSuspend(); + } + + ~MinidumpWriter() { + minidump_writer_.Close(); + dumper_->ThreadsResume(); + } + + bool Dump() { + // The dynamic linker makes information available that helps gdb find all + // DSOs loaded into the program. If we can access this information, we dump + // it to a MD_LINUX_DSO_DEBUG stream. + struct r_debug* r_debug = NULL; + uint32_t dynamic_length = 0; +#if !defined(__ANDROID__) + // This code assumes the crashing process is the same as this process and + // may hang or take a long time to complete if not so. + // Thus, we skip this code for a post-mortem based dump. + if (!dumper_->IsPostMortem()) { + // The Android NDK is missing structure definitions for most of this. + // For now, it's simpler just to skip it. + for (int i = 0;;) { + ElfW(Dyn) dyn; + dynamic_length += sizeof(dyn); + // NOTE: Use of _DYNAMIC assumes this is the same process as the + // crashing process. This loop will go forever if it's out of bounds. + dumper_->CopyFromProcess(&dyn, GetCrashThread(), _DYNAMIC+i++, + sizeof(dyn)); + if (dyn.d_tag == DT_DEBUG) { + r_debug = (struct r_debug*)dyn.d_un.d_ptr; + continue; + } else if (dyn.d_tag == DT_NULL) { + break; + } + } + } +#endif + + // A minidump file contains a number of tagged streams. This is the number + // of stream which we write. + unsigned kNumWriters = 12; + if (r_debug) + ++kNumWriters; + + TypedMDRVA header(&minidump_writer_); + TypedMDRVA dir(&minidump_writer_); + if (!header.Allocate()) + return false; + if (!dir.AllocateArray(kNumWriters)) + return false; + memset(header.get(), 0, sizeof(MDRawHeader)); + + header.get()->signature = MD_HEADER_SIGNATURE; + header.get()->version = MD_HEADER_VERSION; + header.get()->time_date_stamp = time(NULL); + header.get()->stream_count = kNumWriters; + header.get()->stream_directory_rva = dir.position(); + + unsigned dir_index = 0; + MDRawDirectory dirent; + + if (!WriteThreadListStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteMappings(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteMemoryListStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteExceptionStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteSystemInfoStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CPU_INFO; + if (!WriteFile(&dirent.location, "/proc/cpuinfo")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_PROC_STATUS; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "status")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_LSB_RELEASE; + if (!WriteFile(&dirent.location, "/etc/lsb-release")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CMD_LINE; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "cmdline")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_ENVIRON; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "environ")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_AUXV; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "auxv")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_MAPS; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "maps")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + if (r_debug) { + dirent.stream_type = MD_LINUX_DSO_DEBUG; + if (!WriteDSODebugStream(&dirent, r_debug, dynamic_length)) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + } + + // If you add more directory entries, don't forget to update kNumWriters, + // above. + + dumper_->ThreadsResume(); + return true; + } + + // Check if the top of the stack is part of a system call that has been + // redirected by the seccomp sandbox. If so, try to pop the stack frames + // all the way back to the point where the interception happened. + void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread, + uint8_t* stack_copy) { +#if defined(__x86_64) + u_int64_t bp = cpu->rbp; + u_int64_t top = thread.stack.start_of_memory_range; + for (int i = 4; i--; ) { + if (bp < top || + bp + sizeof(bp) > thread.stack.start_of_memory_range + + thread.stack.memory.data_size || + bp & 1) { + break; + } + uint64_t old_top = top; + top = bp; + u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range; + memcpy(&bp, bp_addr, sizeof(bp)); + if (bp == 0xDEADBEEFDEADBEEFull) { + struct { + uint64_t r15; + uint64_t r14; + uint64_t r13; + uint64_t r12; + uint64_t r11; + uint64_t r10; + uint64_t r9; + uint64_t r8; + uint64_t rdi; + uint64_t rsi; + uint64_t rdx; + uint64_t rcx; + uint64_t rbx; + uint64_t deadbeef; + uint64_t rbp; + uint64_t fakeret; + uint64_t ret; + /* char redzone[128]; */ + } seccomp_stackframe; + if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top || + top - offsetof(typeof(seccomp_stackframe), deadbeef) + + sizeof(seccomp_stackframe) > + thread.stack.start_of_memory_range+thread.stack.memory.data_size) { + break; + } + memcpy(&seccomp_stackframe, + bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef), + sizeof(seccomp_stackframe)); + cpu->rbx = seccomp_stackframe.rbx; + cpu->rcx = seccomp_stackframe.rcx; + cpu->rdx = seccomp_stackframe.rdx; + cpu->rsi = seccomp_stackframe.rsi; + cpu->rdi = seccomp_stackframe.rdi; + cpu->rbp = seccomp_stackframe.rbp; + cpu->rsp = top + 4*sizeof(uint64_t) + 128; + cpu->r8 = seccomp_stackframe.r8; + cpu->r9 = seccomp_stackframe.r9; + cpu->r10 = seccomp_stackframe.r10; + cpu->r11 = seccomp_stackframe.r11; + cpu->r12 = seccomp_stackframe.r12; + cpu->r13 = seccomp_stackframe.r13; + cpu->r14 = seccomp_stackframe.r14; + cpu->r15 = seccomp_stackframe.r15; + cpu->rip = seccomp_stackframe.fakeret; + return; + } + } +#elif defined(__i386) + u_int32_t bp = cpu->ebp; + u_int32_t top = thread.stack.start_of_memory_range; + for (int i = 4; i--; ) { + if (bp < top || + bp + sizeof(bp) > thread.stack.start_of_memory_range + + thread.stack.memory.data_size || + bp & 1) { + break; + } + uint32_t old_top = top; + top = bp; + u_int8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range; + memcpy(&bp, bp_addr, sizeof(bp)); + if (bp == 0xDEADBEEFu) { + struct { + uint32_t edi; + uint32_t esi; + uint32_t edx; + uint32_t ecx; + uint32_t ebx; + uint32_t deadbeef; + uint32_t ebp; + uint32_t fakeret; + uint32_t ret; + } seccomp_stackframe; + if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top || + top - offsetof(typeof(seccomp_stackframe), deadbeef) + + sizeof(seccomp_stackframe) > + thread.stack.start_of_memory_range+thread.stack.memory.data_size) { + break; + } + memcpy(&seccomp_stackframe, + bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef), + sizeof(seccomp_stackframe)); + cpu->ebx = seccomp_stackframe.ebx; + cpu->ecx = seccomp_stackframe.ecx; + cpu->edx = seccomp_stackframe.edx; + cpu->esi = seccomp_stackframe.esi; + cpu->edi = seccomp_stackframe.edi; + cpu->ebp = seccomp_stackframe.ebp; + cpu->esp = top + 4*sizeof(void*); + cpu->eip = seccomp_stackframe.fakeret; + return; + } + } +#endif + } + + // Write information about the threads. + bool WriteThreadListStream(MDRawDirectory* dirent) { + const unsigned num_threads = dumper_->threads().size(); + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) + return false; + + dirent->stream_type = MD_THREAD_LIST_STREAM; + dirent->location = list.location(); + + *list.get() = num_threads; + + for (unsigned i = 0; i < num_threads; ++i) { + MDRawThread thread; + my_memset(&thread, 0, sizeof(thread)); + thread.thread_id = dumper_->threads()[i]; + // We have a different source of information for the crashing thread. If + // we used the actual state of the thread we would find it running in the + // signal handler with the alternative stack, which would be deeply + // unhelpful. + if (static_cast(thread.thread_id) == GetCrashThread() && + !dumper_->IsPostMortem()) { + const void* stack; + size_t stack_len; + if (!dumper_->GetStackInfo(&stack, &stack_len, GetStackPointer())) + return false; + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(stack_len)) + return false; + uint8_t* stack_copy = reinterpret_cast(Alloc(stack_len)); + dumper_->CopyFromProcess(stack_copy, thread.thread_id, stack, + stack_len); + memory.Copy(stack_copy, stack_len); + thread.stack.start_of_memory_range = (uintptr_t) (stack); + thread.stack.memory = memory.location(); + memory_blocks_.push_back(thread.stack); + + // Copy 256 bytes around crashing instruction pointer to minidump. + const size_t kIPMemorySize = 256; + u_int64_t ip = GetInstructionPointer(); + // Bound it to the upper and lower bounds of the memory map + // it's contained within. If it's not in mapped memory, + // don't bother trying to write it. + bool ip_is_mapped = false; + MDMemoryDescriptor ip_memory_d; + for (unsigned j = 0; j < dumper_->mappings().size(); ++j) { + const MappingInfo& mapping = *dumper_->mappings()[j]; + if (ip >= mapping.start_addr && + ip < mapping.start_addr + mapping.size) { + ip_is_mapped = true; + // Try to get 128 bytes before and after the IP, but + // settle for whatever's available. + ip_memory_d.start_of_memory_range = + std::max(mapping.start_addr, + uintptr_t(ip - (kIPMemorySize / 2))); + uintptr_t end_of_range = + std::min(uintptr_t(ip + (kIPMemorySize / 2)), + uintptr_t(mapping.start_addr + mapping.size)); + ip_memory_d.memory.data_size = + end_of_range - ip_memory_d.start_of_memory_range; + break; + } + } + + if (ip_is_mapped) { + UntypedMDRVA ip_memory(&minidump_writer_); + if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) + return false; + uint8_t* memory_copy = + reinterpret_cast(Alloc(ip_memory_d.memory.data_size)); + dumper_->CopyFromProcess( + memory_copy, + thread.thread_id, + reinterpret_cast(ip_memory_d.start_of_memory_range), + ip_memory_d.memory.data_size); + ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size); + ip_memory_d.memory = ip_memory.location(); + memory_blocks_.push_back(ip_memory_d); + } + + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + CPUFillFromUContext(cpu.get(), ucontext_, float_state_); + PopSeccompStackFrame(cpu.get(), thread, stack_copy); + thread.thread_context = cpu.location(); + crashing_thread_context_ = cpu.location(); + } else { + ThreadInfo info; + if (!dumper_->GetThreadInfoByIndex(i, &info)) + return false; + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(info.stack_len)) + return false; + uint8_t* stack_copy = reinterpret_cast(Alloc(info.stack_len)); + dumper_->CopyFromProcess(stack_copy, thread.thread_id, info.stack, + info.stack_len); + memory.Copy(stack_copy, info.stack_len); + thread.stack.start_of_memory_range = (uintptr_t)(info.stack); + thread.stack.memory = memory.location(); + memory_blocks_.push_back(thread.stack); + + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + CPUFillFromThreadInfo(cpu.get(), info); + PopSeccompStackFrame(cpu.get(), thread, stack_copy); + thread.thread_context = cpu.location(); + if (dumper_->threads()[i] == GetCrashThread()) { + assert(dumper_->IsPostMortem()); + crashing_thread_context_ = cpu.location(); + } + } + + list.CopyIndexAfterObject(i, &thread, sizeof(thread)); + } + + return true; + } + + static bool ShouldIncludeMapping(const MappingInfo& mapping) { + if (mapping.name[0] == 0 || // only want modules with filenames. + mapping.offset || // only want to include one mapping per shared lib. + mapping.size < 4096) { // too small to get a signature for. + return false; + } + + return true; + } + + // If there is caller-provided information about this mapping + // in the mapping_list_ list, return true. Otherwise, return false. + bool HaveMappingInfo(const MappingInfo& mapping) { + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + // Ignore any mappings that are wholly contained within + // mappings in the mapping_info_ list. + if (mapping.start_addr >= iter->first.start_addr && + (mapping.start_addr + mapping.size) <= + (iter->first.start_addr + iter->first.size)) { + return true; + } + } + return false; + } + + // Write information about the mappings in effect. Because we are using the + // minidump format, the information about the mappings is pretty limited. + // Because of this, we also include the full, unparsed, /proc/$x/maps file in + // another stream in the file. + bool WriteMappings(MDRawDirectory* dirent) { + const unsigned num_mappings = dumper_->mappings().size(); + unsigned num_output_mappings = mapping_list_.size(); + + for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; + if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping)) + num_output_mappings++; + } + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) + return false; + + dirent->stream_type = MD_MODULE_LIST_STREAM; + dirent->location = list.location(); + *list.get() = num_output_mappings; + + // First write all the mappings from the dumper + unsigned int j = 0; + for (unsigned i = 0; i < num_mappings; ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; + if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping)) + continue; + + MDRawModule mod; + if (!FillRawModule(mapping, true, i, mod, NULL)) + return false; + list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); + } + // Next write all the mappings provided by the caller + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + MDRawModule mod; + if (!FillRawModule(iter->first, false, 0, mod, iter->second)) + return false; + list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); + } + + return true; + } + + // Fill the MDRawModule |mod| with information about the provided + // |mapping|. If |identifier| is non-NULL, use it instead of calculating + // a file ID from the mapping. + bool FillRawModule(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + MDRawModule& mod, + const u_int8_t* identifier) { + my_memset(&mod, 0, MD_MODULE_SIZE); + + mod.base_of_image = mapping.start_addr; + mod.size_of_image = mapping.size; + const size_t filepath_len = my_strlen(mapping.name); + + // Figure out file name from path + const char* filename_ptr = mapping.name + filepath_len - 1; + while (filename_ptr >= mapping.name) { + if (*filename_ptr == '/') + break; + filename_ptr--; + } + filename_ptr++; + + const size_t filename_len = mapping.name + filepath_len - filename_ptr; + + uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; + uint8_t* cv_ptr = cv_buf; + UntypedMDRVA cv(&minidump_writer_); + if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1)) + return false; + + const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE; + memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); + cv_ptr += sizeof(cv_signature); + uint8_t* signature = cv_ptr; + cv_ptr += sizeof(MDGUID); + if (identifier) { + // GUID was provided by caller. + memcpy(signature, identifier, sizeof(MDGUID)); + } else { + dumper_->ElfFileIdentifierForMapping(mapping, member, + mapping_id, signature); + } + my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. + cv_ptr += sizeof(uint32_t); + + // Write pdb_file_name + memcpy(cv_ptr, filename_ptr, filename_len + 1); + cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); + + mod.cv_record = cv.location(); + + MDLocationDescriptor ld; + if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) + return false; + mod.module_name_rva = ld.rva; + return true; + } + + bool WriteMemoryListStream(MDRawDirectory* dirent) { + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(memory_blocks_.size(), + sizeof(MDMemoryDescriptor))) + return false; + + dirent->stream_type = MD_MEMORY_LIST_STREAM; + dirent->location = list.location(); + + *list.get() = memory_blocks_.size(); + + for (size_t i = 0; i < memory_blocks_.size(); ++i) { + list.CopyIndexAfterObject(i, &memory_blocks_[i], + sizeof(MDMemoryDescriptor)); + } + return true; + } + + bool WriteExceptionStream(MDRawDirectory* dirent) { + TypedMDRVA exc(&minidump_writer_); + if (!exc.Allocate()) + return false; + my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); + + dirent->stream_type = MD_EXCEPTION_STREAM; + dirent->location = exc.location(); + + exc.get()->thread_id = GetCrashThread(); + exc.get()->exception_record.exception_code = dumper_->crash_signal(); + exc.get()->exception_record.exception_address = dumper_->crash_address(); + exc.get()->thread_context = crashing_thread_context_; + + return true; + } + + bool WriteSystemInfoStream(MDRawDirectory* dirent) { + TypedMDRVA si(&minidump_writer_); + if (!si.Allocate()) + return false; + my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); + + dirent->stream_type = MD_SYSTEM_INFO_STREAM; + dirent->location = si.location(); + + WriteCPUInformation(si.get()); + WriteOSInformation(si.get()); + + return true; + } + + bool WriteDSODebugStream(MDRawDirectory* dirent, struct r_debug* r_debug, + uint32_t dynamic_length) { +#if defined(__ANDROID__) + return false; +#else + // The caller provided us with a pointer to "struct r_debug". We can + // look up the "r_map" field to get a linked list of all loaded DSOs. + // Our list of DSOs potentially is different from the ones in the crashing + // process. So, we have to be careful to never dereference pointers + // directly. Instead, we use CopyFromProcess() everywhere. + // See for a more detailed discussion of the how the dynamic + // loader communicates with debuggers. + + // Count the number of loaded DSOs + int dso_count = 0; + struct r_debug debug_entry; + dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug, + sizeof(debug_entry)); + for (struct link_map* ptr = debug_entry.r_map; ptr; ) { + struct link_map map; + dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)); + ptr = map.l_next; + dso_count++; + } + + MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA; + if (dso_count > 0) { + // If we have at least one DSO, create an array of MDRawLinkMap + // entries in the minidump file. + TypedMDRVA linkmap(&minidump_writer_); + if (!linkmap.AllocateArray(dso_count)) + return false; + linkmap_rva = linkmap.location().rva; + int idx = 0; + + // Iterate over DSOs and write their information to mini dump + for (struct link_map* ptr = debug_entry.r_map; ptr; ) { + struct link_map map; + dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)); + ptr = map.l_next; + char filename[257] = { 0 }; + if (map.l_name) { + dumper_->CopyFromProcess(filename, GetCrashThread(), map.l_name, + sizeof(filename) - 1); + } + MDLocationDescriptor location; + if (!minidump_writer_.WriteString(filename, 0, &location)) + return false; + MDRawLinkMap entry; + entry.name = location.rva; + entry.addr = (void*)map.l_addr; + entry.ld = (void*)map.l_ld; + linkmap.CopyIndex(idx++, &entry); + } + } + + // Write MD_LINUX_DSO_DEBUG record + TypedMDRVA debug(&minidump_writer_); + if (!debug.AllocateObjectAndArray(1, dynamic_length)) + return false; + my_memset(debug.get(), 0, sizeof(MDRawDebug)); + dirent->stream_type = MD_LINUX_DSO_DEBUG; + dirent->location = debug.location(); + + debug.get()->version = debug_entry.r_version; + debug.get()->map = linkmap_rva; + debug.get()->dso_count = dso_count; + debug.get()->brk = (void*)debug_entry.r_brk; + debug.get()->ldbase = (void*)debug_entry.r_ldbase; + debug.get()->dynamic = (void*)&_DYNAMIC; + + char *dso_debug_data = new char[dynamic_length]; + dumper_->CopyFromProcess(dso_debug_data, GetCrashThread(), &_DYNAMIC, + dynamic_length); + debug.CopyIndexAfterObject(0, dso_debug_data, dynamic_length); + delete[] dso_debug_data; + + return true; +#endif + } + + private: + void* Alloc(unsigned bytes) { + return dumper_->allocator()->Alloc(bytes); + } + + pid_t GetCrashThread() const { + return dumper_->crash_thread(); + } + +#if defined(__i386) + uintptr_t GetStackPointer() { + return ucontext_->uc_mcontext.gregs[REG_ESP]; + } + + uintptr_t GetInstructionPointer() { + return ucontext_->uc_mcontext.gregs[REG_EIP]; + } +#elif defined(__x86_64) + uintptr_t GetStackPointer() { + return ucontext_->uc_mcontext.gregs[REG_RSP]; + } + + uintptr_t GetInstructionPointer() { + return ucontext_->uc_mcontext.gregs[REG_RIP]; + } +#elif defined(__ARM_EABI__) + uintptr_t GetStackPointer() { + return ucontext_->uc_mcontext.arm_sp; + } + + uintptr_t GetInstructionPointer() { + return ucontext_->uc_mcontext.arm_ip; + } +#else +#error "This code has not been ported to your platform yet." +#endif + + void NullifyDirectoryEntry(MDRawDirectory* dirent) { + dirent->stream_type = 0; + dirent->location.data_size = 0; + dirent->location.rva = 0; + } + + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; + static const char vendor_id_name[] = "vendor_id"; + static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; + + struct CpuInfoEntry { + const char* info_name; + int value; + bool found; + } cpu_info_table[] = { + { "processor", -1, false }, + { "model", 0, false }, + { "stepping", 0, false }, + { "cpu family", 0, false }, + }; + + // processor_architecture should always be set, do this first + sys_info->processor_architecture = +#if defined(__i386) + MD_CPU_ARCHITECTURE_X86; +#elif defined(__x86_64) + MD_CPU_ARCHITECTURE_AMD64; +#elif defined(__arm__) + MD_CPU_ARCHITECTURE_ARM; +#else +#error "Unknown CPU arch" +#endif + + const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); + if (fd < 0) + return false; + + { + PageAllocator allocator; + LineReader* const line_reader = new(allocator) LineReader(fd); + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + for (size_t i = 0; + i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); + i++) { + CpuInfoEntry* entry = &cpu_info_table[i]; + if (entry->found && i) + continue; + if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { + const char* value = strchr(line, ':'); + if (!value) + continue; + + // the above strncmp only matches the prefix, it might be the wrong + // line. i.e. we matched "model name" instead of "model". + // check and make sure there is only spaces between the prefix and + // the colon. + const char* space_ptr = line + strlen(entry->info_name); + for (; space_ptr < value; space_ptr++) { + if (!isspace(*space_ptr)) { + break; + } + } + if (space_ptr != value) + continue; + + sscanf(++value, " %d", &(entry->value)); + entry->found = true; + } + } + + // special case for vendor_id + if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { + const char* value = strchr(line, ':'); + if (!value) + goto popline; + + // skip ':" and all the spaces that follows + do { + value++; + } while (isspace(*value)); + + if (*value) { + size_t length = strlen(value); + if (length == 0) + goto popline; + // we don't want the trailing newline + if (value[length - 1] == '\n') + length--; + // ensure we have space for the value + if (length < sizeof(vendor_id)) + strncpy(vendor_id, value, length); + } + } + + popline: + line_reader->PopLine(line_len); + } + sys_close(fd); + } + + // make sure we got everything we wanted + for (size_t i = 0; + i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); + i++) { + if (!cpu_info_table[i].found) { + return false; + } + } + // /proc/cpuinfo contains cpu id, change it into number by adding one. + cpu_info_table[0].value++; + + sys_info->number_of_processors = cpu_info_table[0].value; + sys_info->processor_level = cpu_info_table[3].value; + sys_info->processor_revision = cpu_info_table[1].value << 8 | + cpu_info_table[2].value; + + if (vendor_id[0] != '\0') { + memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, + sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); + } + return true; + } + + bool WriteFile(MDLocationDescriptor* result, const char* filename) { + const int fd = sys_open(filename, O_RDONLY, 0); + if (fd < 0) + return false; + + // We can't stat the files because several of the files that we want to + // read are kernel seqfiles, which always have a length of zero. So we have + // to read as much as we can into a buffer. + static const unsigned kBufSize = 1024 - 2*sizeof(void*); + struct Buffers { + Buffers* next; + size_t len; + uint8_t data[kBufSize]; + } *buffers = reinterpret_cast(Alloc(sizeof(Buffers))); + buffers->next = NULL; + buffers->len = 0; + + size_t total = 0; + for (Buffers* bufptr = buffers;;) { + ssize_t r; + do { + r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len); + } while (r == -1 && errno == EINTR); + + if (r < 1) + break; + + total += r; + bufptr->len += r; + if (bufptr->len == kBufSize) { + bufptr->next = reinterpret_cast(Alloc(sizeof(Buffers))); + bufptr = bufptr->next; + bufptr->next = NULL; + bufptr->len = 0; + } + } + sys_close(fd); + + if (!total) + return false; + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(total)) + return false; + for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) { + // Check for special case of a zero-length buffer. This should only + // occur if a file's size happens to be a multiple of the buffer's + // size, in which case the final sys_read() will have resulted in + // zero bytes being read after the final buffer was just allocated. + if (buffers->len == 0) { + // This can only occur with final buffer. + assert(buffers->next == NULL); + continue; + } + memory.Copy(pos, &buffers->data, buffers->len); + pos += buffers->len; + } + *result = memory.location(); + return true; + } + + bool WriteOSInformation(MDRawSystemInfo* sys_info) { + sys_info->platform_id = MD_OS_LINUX; + + struct utsname uts; + if (uname(&uts)) + return false; + + static const size_t buf_len = 512; + char buf[buf_len] = {0}; + size_t space_left = buf_len - 1; + const char* info_table[] = { + uts.sysname, + uts.release, + uts.version, + uts.machine, + NULL + }; + bool first_item = true; + for (const char** cur_info = info_table; *cur_info; cur_info++) { + static const char* separator = " "; + size_t separator_len = strlen(separator); + size_t info_len = strlen(*cur_info); + if (info_len == 0) + continue; + + if (space_left < info_len + (first_item ? 0 : separator_len)) + break; + + if (!first_item) { + strcat(buf, separator); + space_left -= separator_len; + } + + first_item = false; + strcat(buf, *cur_info); + space_left -= info_len; + } + + MDLocationDescriptor location; + if (!minidump_writer_.WriteString(buf, 0, &location)) + return false; + sys_info->csd_version_rva = location.rva; + + return true; + } + + bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, + const char* filename) { + char buf[NAME_MAX]; + if (!dumper_->BuildProcPath(buf, pid, filename)) + return false; + return WriteFile(result, buf); + } + + const char* const filename_; // output filename + const struct ucontext* const ucontext_; // also from the signal handler + const struct _libc_fpstate* const float_state_; // ditto + LinuxDumper* dumper_; + MinidumpFileWriter minidump_writer_; + MDLocationDescriptor crashing_thread_context_; + // Blocks of memory written to the dump. These are all currently + // written while writing the thread list stream, but saved here + // so a memory list stream can be written afterwards. + wasteful_vector memory_blocks_; + // Additional information about some mappings provided by the caller. + const MappingList& mapping_list_; +}; + +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size) { + MappingList m; + return WriteMinidump(filename, crashing_process, blob, blob_size, m); +} + +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings) { + if (blob_size != sizeof(ExceptionHandler::CrashContext)) + return false; + const ExceptionHandler::CrashContext* context = + reinterpret_cast(blob); + LinuxPtraceDumper dumper(crashing_process); + dumper.set_crash_address( + reinterpret_cast(context->siginfo.si_addr)); + dumper.set_crash_signal(context->siginfo.si_signo); + dumper.set_crash_thread(context->tid); + MinidumpWriter writer(filename, context, mappings, &dumper); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +bool WriteMinidump(const char* filename, + const MappingList& mappings, + LinuxDumper* dumper) { + MinidumpWriter writer(filename, NULL, mappings, dumper); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.h b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.h new file mode 100644 index 0000000..e79eb79 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer.h @@ -0,0 +1,78 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ + +#include +#include + +#include +#include + +#include "client/linux/minidump_writer/linux_dumper.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class ExceptionHandler; + +struct MappingEntry { + MappingInfo first; + u_int8_t second[sizeof(MDGUID)]; +}; + +// A list of +typedef std::list MappingList; + +// Write a minidump to the filesystem. This function does not malloc nor use +// libc functions which may. Thus, it can be used in contexts where the state +// of the heap may be corrupt. +// filename: the filename to write to. This is opened O_EXCL and fails if +// open fails. +// crashing_process: the pid of the crashing process. This must be trusted. +// blob: a blob of data from the crashing process. See exception_handler.h +// blob_size: the length of |blob|, in bytes +// +// Returns true iff successful. +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size); + +// This overload also allows passing a list of known mappings. +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings); + +bool WriteMinidump(const char* filename, + const MappingList& mappings, + LinuxDumper* dumper); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer_unittest.cc new file mode 100644 index 0000000..91aafed --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -0,0 +1,390 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" +#include "common/tests/auto_tempdir.h" +#include "google_breakpad/processor/minidump.h" + +using namespace google_breakpad; + +// Length of a formatted GUID string = +// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator) +const int kGUIDStringSize = 37; + +namespace { +typedef testing::Test MinidumpWriterTest; +} + +TEST(MinidumpWriterTest, Setup) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/minidump-writer-unittest"; + // Set a non-zero tid to avoid tripping asserts. + context.tid = 1; + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); + struct stat st; + ASSERT_EQ(stat(templ.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + + close(fds[1]); +} + +// Test that mapping info can be specified when writing a minidump, +// and that it ends up in the module list of the minidump. +TEST(MinidumpWriterTest, MappingInfo) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "a fake module"; + const u_int8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + char module_identifier_buffer[kGUIDStringSize]; + FileID::ConvertIdentifierToString(kModuleGUID, + module_identifier_buffer, + sizeof(module_identifier_buffer)); + string module_identifier(module_identifier_buffer); + // Strip out dashes + size_t pos; + while ((pos = module_identifier.find('-')) != string::npos) { + module_identifier.erase(pos, 1); + } + // And append a zero, because module IDs include an "age" field + // which is always zero on Linux. + module_identifier += "0"; + + // Get some memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + context.tid = 1; + + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/minidump-writer-unittest"; + + // Add information about the mapped memory. + MappingInfo info; + info.start_addr = kMemoryAddress; + info.size = kMemorySize; + info.offset = 0; + strcpy(info.name, kMemoryName); + + MappingList mappings; + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); + mappings.push_back(mapping); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + mappings)); + + // Read the minidump. Load the module list, and ensure that + // the mmap'ed |memory| is listed with the given module name + // and debug ID. + Minidump minidump(templ.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = + module_list->GetModuleForAddress(kMemoryAddress); + ASSERT_TRUE(module); + + EXPECT_EQ(kMemoryAddress, module->base_address()); + EXPECT_EQ(kMemorySize, module->size()); + EXPECT_EQ(kMemoryName, module->code_file()); + EXPECT_EQ(module_identifier, module->debug_identifier()); + + close(fds[1]); +} + +// Test that mapping info can be specified, and that it overrides +// existing mappings that are wholly contained within the specified +// range. +TEST(MinidumpWriterTest, MappingInfoContained) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "a fake module"; + const u_int8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + char module_identifier_buffer[kGUIDStringSize]; + FileID::ConvertIdentifierToString(kModuleGUID, + module_identifier_buffer, + sizeof(module_identifier_buffer)); + string module_identifier(module_identifier_buffer); + // Strip out dashes + size_t pos; + while ((pos = module_identifier.find('-')) != string::npos) { + module_identifier.erase(pos, 1); + } + // And append a zero, because module IDs include an "age" field + // which is always zero on Linux. + module_identifier += "0"; + + // mmap a file + AutoTempDir temp_dir; + std::string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp"; + int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0); + ASSERT_NE(-1, fd); + unlink(tempfile.c_str()); + // fill with zeros + char buffer[kMemorySize]; + memset(buffer, 0, kMemorySize); + ASSERT_EQ(kMemorySize, write(fd, buffer, kMemorySize)); + lseek(fd, 0, SEEK_SET); + + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + fd, + 0)); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + close(fd); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + context.tid = 1; + + std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest"; + + // Add information about the mapped memory. Report it as being larger than + // it actually is. + MappingInfo info; + info.start_addr = kMemoryAddress - kMemorySize; + info.size = kMemorySize * 3; + info.offset = 0; + strcpy(info.name, kMemoryName); + + MappingList mappings; + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); + mappings.push_back(mapping); + ASSERT_TRUE( + WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), + mappings)); + + // Read the minidump. Load the module list, and ensure that + // the mmap'ed |memory| is listed with the given module name + // and debug ID. + Minidump minidump(dumpfile.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = + module_list->GetModuleForAddress(kMemoryAddress); + ASSERT_TRUE(module); + + EXPECT_EQ(info.start_addr, module->base_address()); + EXPECT_EQ(info.size, module->size()); + EXPECT_EQ(kMemoryName, module->code_file()); + EXPECT_EQ(module_identifier, module->debug_identifier()); + + close(fds[1]); +} + +TEST(MinidumpWriterTest, DeletedBinary) { + static const int kNumberOfThreadsInHelperProgram = 1; + char kNumberOfThreadsArgument[2]; + sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); + + // Locate helper binary next to the current binary. + char self_path[PATH_MAX]; + if (!SafeReadLink("/proc/self/exe", self_path)) { + FAIL() << "readlink failed"; + exit(1); + } + string helper_path(self_path); + size_t pos = helper_path.rfind('/'); + if (pos == string::npos) { + FAIL() << "no trailing slash in path: " << helper_path; + exit(1); + } + helper_path.erase(pos + 1); + helper_path += "linux_dumper_unittest_helper"; + + // Copy binary to a temp file. + AutoTempDir temp_dir; + std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper"; + char cmdline[2 * PATH_MAX]; + sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(), + binpath.c_str()); + ASSERT_EQ(0, system(cmdline)); + ASSERT_EQ(0, chmod(binpath.c_str(), 0755)); + + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + pid_t child_pid = fork(); + if (child_pid == 0) { + // In child process. + close(fds[0]); + + // Pass the pipe fd and the number of threads as arguments. + char pipe_fd_string[8]; + sprintf(pipe_fd_string, "%d", fds[1]); + execl(binpath.c_str(), + binpath.c_str(), + pipe_fd_string, + kNumberOfThreadsArgument, + NULL); + } + close(fds[1]); + // Wait for the child process to signal that it's ready. + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + uint8_t junk; + const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk))); + ASSERT_EQ(sizeof(junk), nr); + close(fds[0]); + + // Child is ready now. + // Unlink the test binary. + unlink(binpath.c_str()); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + std::string templ = temp_dir.path() + "/minidump-writer-unittest"; + // Set a non-zero tid to avoid tripping asserts. + context.tid = 1; + ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context, + sizeof(context))); + kill(child_pid, SIGKILL); + + struct stat st; + ASSERT_EQ(stat(templ.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + + + + Minidump minidump(templ.c_str()); + ASSERT_TRUE(minidump.Read()); + + // Check that the main module filename is correct. + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = module_list->GetMainModule(); + EXPECT_STREQ(binpath.c_str(), module->code_file().c_str()); + // Check that the file ID is correct. + FileID fileid(helper_path.c_str()); + uint8_t identifier[sizeof(MDGUID)]; + EXPECT_TRUE(fileid.ElfFileIdentifier(identifier)); + char identifier_string[kGUIDStringSize]; + FileID::ConvertIdentifierToString(identifier, + identifier_string, + kGUIDStringSize); + string module_identifier(identifier_string); + // Strip out dashes + while ((pos = module_identifier.find('-')) != string::npos) { + module_identifier.erase(pos, 1); + } + // And append a zero, because module IDs include an "age" field + // which is always zero on Linux. + module_identifier += "0"; + EXPECT_EQ(module_identifier, module->debug_identifier()); +} diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.h b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.h new file mode 100644 index 0000000..f057533 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.h @@ -0,0 +1,83 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Utility class that can persist a SimpleStringDictionary to disk. + +#import + +#import "common/mac/SimpleStringDictionary.h" + +namespace google_breakpad { + +BOOL EnsureDirectoryPathExists(NSString *dirPath); + +//============================================================================= +class ConfigFile { + public: + ConfigFile() { + config_file_ = -1; + config_file_path_[0] = 0; + has_created_file_ = false; + }; + + ~ConfigFile() { + }; + + void WriteFile(const char* directory, + const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id); + + const char *GetFilePath() { return config_file_path_; } + + void Unlink() { + if (config_file_ != -1) + unlink(config_file_path_); + + config_file_ = -1; + } + + private: + BOOL WriteData(const void *data, size_t length); + + BOOL AppendConfigData(const char *key, + const void *data, + size_t length); + + BOOL AppendConfigString(const char *key, + const char *value); + + BOOL AppendCrashTimeParameters(const char *processStartTimeString); + + int config_file_; // descriptor for config file + char config_file_path_[PATH_MAX]; // Path to configuration file + bool has_created_file_; +}; + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.mm b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.mm new file mode 100644 index 0000000..ee2d944 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/ConfigFile.mm @@ -0,0 +1,190 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Utility class that can persist a SimpleStringDictionary to disk. + +#import "client/mac/crash_generation/ConfigFile.h" + +#import +#include +#include + +#import "client/apple/Framework/BreakpadDefines.h" +#import "common/mac/SimpleStringDictionary.h" +#import "GTMDefines.h" + +#define VERBOSE 0 + +#if VERBOSE + bool gDebugLog = true; +#else + bool gDebugLog = false; +#endif + +#define DEBUGLOG if (gDebugLog) fprintf + +namespace google_breakpad { + +//============================================================================= +BOOL EnsureDirectoryPathExists(NSString *dirPath) { + NSFileManager *mgr = [NSFileManager defaultManager]; + + NSDictionary *attrs = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] + forKey:NSFilePosixPermissions]; + + return [mgr createDirectoryAtPath:dirPath + withIntermediateDirectories:YES + attributes:attrs + error:nil]; +} + +//============================================================================= +BOOL ConfigFile::WriteData(const void *data, size_t length) { + size_t result = write(config_file_, data, length); + + return result == length; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigData(const char *key, + const void *data, size_t length) { + assert(config_file_ != -1); + + if (!key) { + DEBUGLOG(stderr, "Breakpad: Missing Key\n"); + return NO; + } + + if (!data) { + DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key : + ""); + return NO; + } + + // Write the key, \n, length of data (ascii integer), \n, data + char buffer[16]; + char nl = '\n'; + BOOL result = WriteData(key, strlen(key)); + + snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); + result &= WriteData(buffer, strlen(buffer)); + result &= WriteData(data, length); + result &= WriteData(&nl, 1); + return result; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigString(const char *key, + const char *value) { + return AppendConfigData(key, value, strlen(value)); +} + +//============================================================================= +BOOL ConfigFile::AppendCrashTimeParameters(const char *processStartTimeString) { + // Set process uptime parameter + struct timeval tv; + gettimeofday(&tv, NULL); + + char processUptimeString[32], processCrashtimeString[32]; + // Set up time if we've received the start time. + if (processStartTimeString) { + time_t processStartTime = strtol(processStartTimeString, NULL, 10); + time_t processUptime = tv.tv_sec - processStartTime; + // Store the uptime in milliseconds. + sprintf(processUptimeString, "%llu", + static_cast(processUptime) * 1000); + if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString)) + return false; + } + + sprintf(processCrashtimeString, "%zd", tv.tv_sec); + return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME, + processCrashtimeString); +} + +//============================================================================= +void ConfigFile::WriteFile(const char* directory, + const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id) { + + assert(config_file_ == -1); + + // Open and write out configuration file preamble + if (directory) { + snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX", + directory); + } else { + strlcpy(config_file_path_, "/tmp/Config-XXXXXX", + sizeof(config_file_path_)); + } + config_file_ = mkstemp(config_file_path_); + + if (config_file_ == -1) { + DEBUGLOG(stderr, + "mkstemp(config_file_path_) == -1 (%s)\n", + strerror(errno)); + return; + } + else { + DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_); + } + + has_created_file_ = true; + + // Add the minidump dir + AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); + AppendConfigString(kReporterMinidumpIDKey, minidump_id); + + // Write out the configuration parameters + BOOL result = YES; + const SimpleStringDictionary &dictionary = *configurationParameters; + + const KeyValueEntry *entry = NULL; + SimpleStringDictionaryIterator iter(dictionary); + + while ((entry = iter.Next())) { + DEBUGLOG(stderr, + "config: (%s) -> (%s)\n", + entry->GetKey(), + entry->GetValue()); + result = AppendConfigString(entry->GetKey(), entry->GetValue()); + + if (!result) + break; + } + AppendCrashTimeParameters( + configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME)); + + close(config_file_); + config_file_ = -1; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.h b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.h new file mode 100644 index 0000000..11a0dec --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.h @@ -0,0 +1,166 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Interface file between the Breakpad.framework and +// the Inspector process. + +#import "common/mac/SimpleStringDictionary.h" + +#import +#include + +#import "client/mac/crash_generation/ConfigFile.h" +#import "client/mac/handler/minidump_generator.h" + +extern bool gDebugLog; + +#define DEBUGLOG if (gDebugLog) fprintf + +// Types of mach messsages (message IDs) +enum { + kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo + kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData + kMsgType_InspectorAcknowledgement = 2 // no data sent +}; + +// Initial information sent from the crashed process by +// Breakpad.framework to the Inspector process +// The mach message with this struct as data will also include +// several descriptors for sending mach port rights to the crashed +// task, etc. +struct InspectorInfo { + int exception_type; + int exception_code; + int exception_subcode; + unsigned int parameter_count; // key-value pairs +}; + +// Key/value message data to be sent to the Inspector +struct KeyValueMessageData { + public: + KeyValueMessageData() {} + KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) { + strlcpy(key, inEntry.GetKey(), sizeof(key) ); + strlcpy(value, inEntry.GetValue(), sizeof(value) ); + } + + char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; +}; + +using std::string; +using google_breakpad::MinidumpGenerator; + +namespace google_breakpad { + +//============================================================================= +class MinidumpLocation { + public: + MinidumpLocation(NSString *minidumpDir) { + // Ensure that the path exists. Fallback to /tmp if unable to locate path. + assert(minidumpDir); + if (!EnsureDirectoryPathExists(minidumpDir)) { + DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]); + minidumpDir = @"/tmp"; + } + + strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation], + sizeof(minidump_dir_path_)); + + // now generate a unique ID + string dump_path(minidump_dir_path_); + string next_minidump_id; + + string next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id)); + + strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_)); + }; + + const char *GetPath() { return minidump_dir_path_; } + const char *GetID() { return minidump_id_; } + + private: + char minidump_dir_path_[PATH_MAX]; // Path to minidump directory + char minidump_id_[128]; +}; + +//============================================================================= +class Inspector { + public: + Inspector() {}; + + // given a bootstrap service name, receives mach messages + // from a crashed process, then inspects it, creates a minidump file + // and asks the user if he wants to upload it to a server. + void Inspect(const char *receive_port_name); + + private: + // The Inspector is invoked with its bootstrap port set to the bootstrap + // subset established in OnDemandServer.mm OnDemandServer::Initialize. + // For proper communication with the system, the sender (which will inherit + // the Inspector's bootstrap port) needs the per-session bootstrap namespace + // available directly in its bootstrap port. OnDemandServer stashed this + // port into the subset namespace under a special name. ResetBootstrapPort + // recovers this port and switches this task to use it as its own bootstrap + // (ensuring that children like the sender will inherit it), and saves the + // subset in bootstrap_subset_port_ for use by ServiceCheckIn and + // ServiceCheckOut. + kern_return_t ResetBootstrapPort(); + + kern_return_t ServiceCheckIn(const char *receive_port_name); + kern_return_t ServiceCheckOut(const char *receive_port_name); + + kern_return_t ReadMessages(); + + bool InspectTask(); + kern_return_t SendAcknowledgement(); + void LaunchReporter(const char *inConfigFilePath); + + // The bootstrap port in which the inspector is registered and into which it + // must check in. + mach_port_t bootstrap_subset_port_; + + mach_port_t service_rcv_port_; + + int exception_type_; + int exception_code_; + int exception_subcode_; + mach_port_t remote_task_; + mach_port_t crashing_thread_; + mach_port_t handler_thread_; + mach_port_t ack_port_; + + SimpleStringDictionary config_params_; + + ConfigFile config_file_; +}; + + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.mm b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.mm new file mode 100644 index 0000000..ea5d19a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/Inspector.mm @@ -0,0 +1,431 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Utility that can inspect another process and write a crash dump + +#include +#include +#include +#include +#include +#include + +#import "client/mac/crash_generation/Inspector.h" + +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/handler/minidump_generator.h" + +#import "common/mac/SimpleStringDictionary.h" +#import "common/mac/MachIPC.h" +#include "common/mac/bootstrap_compat.h" + +#import "GTMDefines.h" + +#import + +namespace google_breakpad { + +//============================================================================= +void Inspector::Inspect(const char *receive_port_name) { + kern_return_t result = ResetBootstrapPort(); + if (result != KERN_SUCCESS) { + return; + } + + result = ServiceCheckIn(receive_port_name); + + if (result == KERN_SUCCESS) { + result = ReadMessages(); + + if (result == KERN_SUCCESS) { + // Inspect the task and write a minidump file. + bool wrote_minidump = InspectTask(); + + // Send acknowledgement to the crashed process that the inspection + // has finished. It will then be able to cleanly exit. + // The return value is ignored because failure isn't fatal. If the process + // didn't get the message there's nothing we can do, and we still want to + // send the report. + SendAcknowledgement(); + + if (wrote_minidump) { + // Ask the user if he wants to upload the crash report to a server, + // and do so if he agrees. + LaunchReporter(config_file_.GetFilePath()); + } else { + fprintf(stderr, "Inspection of crashed process failed\n"); + } + + // Now that we're done reading messages, cleanup the service, but only + // if there was an actual exception + // Otherwise, it means the dump was generated on demand and the process + // lives on, and we might be needed again in the future. + if (exception_code_) { + ServiceCheckOut(receive_port_name); + } + } else { + PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()"); + } + } +} + +//============================================================================= +kern_return_t Inspector::ResetBootstrapPort() { + // A reasonable default, in case anything fails. + bootstrap_subset_port_ = bootstrap_port; + + mach_port_t self_task = mach_task_self(); + + kern_return_t kr = task_get_bootstrap_port(self_task, + &bootstrap_subset_port_); + if (kr != KERN_SUCCESS) { + NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)", + mach_error_string(kr), kr); + return kr; + } + + mach_port_t bootstrap_parent_port; + kr = bootstrap_look_up(bootstrap_subset_port_, + const_cast(BREAKPAD_BOOTSTRAP_PARENT_PORT), + &bootstrap_parent_port); + if (kr != BOOTSTRAP_SUCCESS) { + NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)", +#if defined(MAC_OS_X_VERSION_10_5) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + bootstrap_strerror(kr), +#else + mach_error_string(kr), +#endif + kr); + return kr; + } + + kr = task_set_bootstrap_port(self_task, bootstrap_parent_port); + if (kr != KERN_SUCCESS) { + NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)", + mach_error_string(kr), kr); + return kr; + } + + // Some things access the bootstrap port through this global variable + // instead of calling task_get_bootstrap_port. + bootstrap_port = bootstrap_parent_port; + + return KERN_SUCCESS; +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) { + // We need to get the mach port representing this service, so we can + // get information from the crashed process. + kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_, + (char*)receive_port_name, + &service_rcv_port_); + + if (kr != KERN_SUCCESS) { +#if VERBOSE + PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()"); +#endif + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) { + // We're done receiving mach messages from the crashed process, + // so clean up a bit. + kern_return_t kr; + + // DO NOT use mach_port_deallocate() here -- it will fail and the + // following bootstrap_register() will also fail leaving our service + // name hanging around forever (until reboot) + kr = mach_port_destroy(mach_task_self(), service_rcv_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, + "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()"); + return kr; + } + + // Unregister the service associated with the receive port. + kr = breakpad::BootstrapRegister(bootstrap_subset_port_, + (char*)receive_port_name, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()"); + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ReadMessages() { + // Wait for an initial message from the crashed process containing basic + // information about the crash. + ReceivePort receive_port(service_rcv_port_); + + MachReceiveMessage message; + kern_return_t result = receive_port.WaitForMessage(&message, 1000); + + if (result == KERN_SUCCESS) { + InspectorInfo &info = (InspectorInfo &)*message.GetData(); + exception_type_ = info.exception_type; + exception_code_ = info.exception_code; + exception_subcode_ = info.exception_subcode; + +#if VERBOSE + printf("message ID = %d\n", message.GetMessageID()); +#endif + + remote_task_ = message.GetTranslatedPort(0); + crashing_thread_ = message.GetTranslatedPort(1); + handler_thread_ = message.GetTranslatedPort(2); + ack_port_ = message.GetTranslatedPort(3); + +#if VERBOSE + printf("exception_type = %d\n", exception_type_); + printf("exception_code = %d\n", exception_code_); + printf("exception_subcode = %d\n", exception_subcode_); + printf("remote_task = %d\n", remote_task_); + printf("crashing_thread = %d\n", crashing_thread_); + printf("handler_thread = %d\n", handler_thread_); + printf("ack_port_ = %d\n", ack_port_); + printf("parameter count = %d\n", info.parameter_count); +#endif + + // In certain situations where multiple crash requests come + // through quickly, we can end up with the mach IPC messages not + // coming through correctly. Since we don't know what parameters + // we've missed, we can't do much besides abort the crash dump + // situation in this case. + unsigned int parameters_read = 0; + // The initial message contains the number of key value pairs that + // we are expected to read. + // Read each key/value pair, one mach message per key/value pair. + for (unsigned int i = 0; i < info.parameter_count; ++i) { + MachReceiveMessage parameter_message; + result = receive_port.WaitForMessage(¶meter_message, 1000); + + if(result == KERN_SUCCESS) { + KeyValueMessageData &key_value_data = + (KeyValueMessageData&)*parameter_message.GetData(); + // If we get a blank key, make sure we don't increment the + // parameter count; in some cases (notably on-demand generation + // many times in a short period of time) caused the Mach IPC + // messages to not come through correctly. + if (strlen(key_value_data.key) == 0) { + continue; + } + parameters_read++; + + config_params_.SetKeyValue(key_value_data.key, key_value_data.value); + } else { + PRINT_MACH_RESULT(result, "Inspector: key/value message"); + break; + } + } + if (parameters_read != info.parameter_count) { + DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash " + "dump generation.", parameters_read, info.parameter_count); + return KERN_FAILURE; + } + } + + return result; +} + +//============================================================================= +bool Inspector::InspectTask() { + // keep the task quiet while we're looking at it + task_suspend(remote_task_); + DEBUGLOG(stderr, "Suspended Remote task\n"); + + NSString *minidumpDir; + + const char *minidumpDirectory = + config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY); + + // If the client app has not specified a minidump directory, + // use a default of Library// + if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { + NSArray *libraryDirectories = + NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, + NSUserDomainMask, + YES); + + NSString *applicationSupportDirectory = + [libraryDirectories objectAtIndex:0]; + NSString *library_subdirectory = [NSString + stringWithUTF8String:kDefaultLibrarySubdirectory]; + NSString *breakpad_product = [NSString + stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)]; + + NSArray *path_components = [NSArray + arrayWithObjects:applicationSupportDirectory, + library_subdirectory, + breakpad_product, + nil]; + + minidumpDir = [NSString pathWithComponents:path_components]; + } else { + minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory] + stringByExpandingTildeInPath]; + } + DEBUGLOG(stderr, + "Writing minidump to directory (%s)\n", + [minidumpDir UTF8String]); + + MinidumpLocation minidumpLocation(minidumpDir); + + // Obscure bug alert: + // Don't use [NSString stringWithFormat] to build up the path here since it + // assumes system encoding and in RTL locales will prepend an LTR override + // character for paths beginning with '/' which fileSystemRepresentation does + // not remove. Filed as rdar://6889706 . + NSString *path_ns = [NSString + stringWithUTF8String:minidumpLocation.GetPath()]; + NSString *pathid_ns = [NSString + stringWithUTF8String:minidumpLocation.GetID()]; + NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns]; + minidumpPath = [minidumpPath + stringByAppendingPathExtension:@"dmp"]; + + DEBUGLOG(stderr, + "minidump path (%s)\n", + [minidumpPath UTF8String]); + + + config_file_.WriteFile( 0, + &config_params_, + minidumpLocation.GetPath(), + minidumpLocation.GetID()); + + + MinidumpGenerator generator(remote_task_, handler_thread_); + + if (exception_type_ && exception_code_) { + generator.SetExceptionInformation(exception_type_, + exception_code_, + exception_subcode_, + crashing_thread_); + } + + + bool result = generator.Write([minidumpPath fileSystemRepresentation]); + + if (result) { + DEBUGLOG(stderr, "Wrote minidump - OK\n"); + } else { + DEBUGLOG(stderr, "Error writing minidump - errno=%s\n", strerror(errno)); + } + + // let the task continue + task_resume(remote_task_); + DEBUGLOG(stderr, "Resumed remote task\n"); + + return result; +} + +//============================================================================= +// The crashed task needs to be told that the inspection has finished. +// It will wait on a mach port (with timeout) until we send acknowledgement. +kern_return_t Inspector::SendAcknowledgement() { + if (ack_port_ != MACH_PORT_DEAD) { + MachPortSender sender(ack_port_); + MachSendMessage ack_message(kMsgType_InspectorAcknowledgement); + + DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n", + ack_port_); + + kern_return_t result = sender.SendMessage(ack_message, 2000); + +#if VERBOSE + PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement"); +#endif + + return result; + } + + DEBUGLOG(stderr, "Inspector: port translation failure!\n"); + return KERN_INVALID_NAME; +} + +//============================================================================= +void Inspector::LaunchReporter(const char *inConfigFilePath) { + // Extract the path to the reporter executable. + const char *reporterExecutablePath = + config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION); + DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath); + + // Setup and launch the crash dump sender. + const char *argv[3]; + argv[0] = reporterExecutablePath; + argv[1] = inConfigFilePath; + argv[2] = NULL; + + // Launch the reporter + pid_t pid = fork(); + + // If we're in the child, load in our new executable and run. + // The parent will not wait for the child to complete. + if (pid == 0) { + execv(argv[0], (char * const *)argv); + config_file_.Unlink(); // launch failed - get rid of config file + DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n"); + _exit(1); + } + + // Wait until the Reporter child process exits. + // + + // We'll use a timeout of one minute. + int timeoutCount = 60; // 60 seconds + + while (timeoutCount-- > 0) { + int status; + pid_t result = waitpid(pid, &status, WNOHANG); + + if (result == 0) { + // The child has not yet finished. + sleep(1); + } else if (result == -1) { + DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n", + errno); + break; + } else { + // child has finished + break; + } + } +} + +} // namespace google_breakpad + diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/InspectorMain.mm b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/InspectorMain.mm new file mode 100644 index 0000000..137c6a1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/InspectorMain.mm @@ -0,0 +1,65 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Main driver for Inspector + +#import "client/mac/crash_generation/Inspector.h" +#import + +namespace google_breakpad { + +//============================================================================= +extern "C" { + +int main(int argc, char *const argv[]) { +#if DEBUG + // Since we're launched on-demand, this is necessary to see debugging + // output in the console window. + freopen("/dev/console", "w", stdout); + freopen("/dev/console", "w", stderr); +#endif + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (argc != 2) { + exit(0); + } + // Our first command-line argument contains the name of the service + // that we're providing. + google_breakpad::Inspector inspector; + inspector.Inspect(argv[1]); + + [pool release]; + + return 0; +} + +} // extern "C" + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/client_info.h b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/client_info.h new file mode 100644 index 0000000..a3a95dc --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/client_info.h @@ -0,0 +1,47 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ +#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ + +namespace google_breakpad { + +class ClientInfo { + public: + explicit ClientInfo(pid_t pid) : pid_(pid) {} + + pid_t pid() const { return pid_; } + + private: + pid_t pid_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.cc b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.cc new file mode 100644 index 0000000..ceeb3b3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.cc @@ -0,0 +1,72 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/mac/crash_generation/crash_generation_client.h" + +#include "client/mac/crash_generation/crash_generation_server.h" +#include "common/mac/MachIPC.h" + +namespace google_breakpad { + +bool CrashGenerationClient::RequestDumpForException( + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread) { + // The server will send a message to this port indicating that it + // has finished its work. + ReceivePort acknowledge_port; + + MachSendMessage message(kDumpRequestMessage); + message.AddDescriptor(mach_task_self()); // this task + message.AddDescriptor(crashing_thread); // crashing thread + message.AddDescriptor(mach_thread_self()); // handler thread + message.AddDescriptor(acknowledge_port.GetPort()); // message receive port + + ExceptionInfo info; + info.exception_type = exception_type; + info.exception_code = exception_code; + info.exception_subcode = exception_subcode; + message.SetData(&info, sizeof(info)); + + const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; + kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs); + if (result != KERN_SUCCESS) + return false; + + // Give the server slightly longer to reply since it has to + // inspect this task and write the minidump. + const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000; + MachReceiveMessage acknowledge_message; + result = acknowledge_port.WaitForMessage(&acknowledge_message, + kReceiveTimeoutMs); + return result == KERN_SUCCESS; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.h b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.h new file mode 100644 index 0000000..527f577 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_client.h @@ -0,0 +1,65 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include "common/mac/MachIPC.h" + +namespace google_breakpad { + +class CrashGenerationClient { + public: + explicit CrashGenerationClient(const char* mach_port_name) + : sender_(mach_port_name) { + } + + // Request the crash server to generate a dump. + // + // Return true if the dump was successful; false otherwise. + bool RequestDumpForException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread); + + bool RequestDump() { + return RequestDumpForException(0, 0, 0, MACH_PORT_NULL); + } + + private: + MachPortSender sender_; + + // Prevent copy construction and assignment. + CrashGenerationClient(const CrashGenerationClient&); + CrashGenerationClient& operator=(const CrashGenerationClient&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.cc b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.cc new file mode 100644 index 0000000..44548ef --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.cc @@ -0,0 +1,160 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/mac/crash_generation/crash_generation_server.h" + +#include "client/mac/crash_generation/client_info.h" +#include "client/mac/handler/minidump_generator.h" +#include "common/mac/scoped_task_suspend-inl.h" + +namespace google_breakpad { + +CrashGenerationServer::CrashGenerationServer( + const char *mach_port_name, + OnClientDumpRequestCallback dump_callback, + void *dump_context, + OnClientExitingCallback exit_callback, + void *exit_context, + bool generate_dumps, + const std::string &dump_path) + : dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + generate_dumps_(generate_dumps), + dump_dir_(dump_path.empty() ? "/tmp" : dump_path), + started_(false), + receive_port_(mach_port_name), + mach_port_name_(mach_port_name) { +} + +CrashGenerationServer::~CrashGenerationServer() { + if (started_) + Stop(); +} + +bool CrashGenerationServer::Start() { + int thread_create_result = pthread_create(&server_thread_, NULL, + &WaitForMessages, this); + started_ = thread_create_result == 0; + return started_; +} + +bool CrashGenerationServer::Stop() { + if (!started_) + return false; + + // Send a quit message to the background thread, and then join it. + MachPortSender sender(mach_port_name_.c_str()); + MachSendMessage quit_message(kQuitMessage); + const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; + kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs); + if (result == KERN_SUCCESS) { + int thread_join_result = pthread_join(server_thread_, NULL); + started_ = thread_join_result != 0; + } + + return !started_; +} + +// static +void *CrashGenerationServer::WaitForMessages(void *server) { + CrashGenerationServer *self = + reinterpret_cast(server); + while (self->WaitForOneMessage()) {} + return NULL; +} + +bool CrashGenerationServer::WaitForOneMessage() { + MachReceiveMessage message; + kern_return_t result = receive_port_.WaitForMessage(&message, + MACH_MSG_TIMEOUT_NONE); + if (result == KERN_SUCCESS) { + switch (message.GetMessageID()) { + case kDumpRequestMessage: { + ExceptionInfo &info = (ExceptionInfo &)*message.GetData(); + + mach_port_t remote_task = message.GetTranslatedPort(0); + mach_port_t crashing_thread = message.GetTranslatedPort(1); + mach_port_t handler_thread = message.GetTranslatedPort(2); + mach_port_t ack_port = message.GetTranslatedPort(3); + pid_t remote_pid = -1; + pid_for_task(remote_task, &remote_pid); + ClientInfo client(remote_pid); + + bool result; + std::string dump_path; + if (generate_dumps_) { + ScopedTaskSuspend suspend(remote_task); + + MinidumpGenerator generator(remote_task, handler_thread); + dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL); + + if (info.exception_type && info.exception_code) { + generator.SetExceptionInformation(info.exception_type, + info.exception_code, + info.exception_subcode, + crashing_thread); + } + result = generator.Write(dump_path.c_str()); + } else { + result = true; + } + + if (result && dump_callback_) { + dump_callback_(dump_context_, client, dump_path); + } + + // TODO(ted): support a way for the client to send additional data, + // perhaps with a callback so users of the server can read the data + // themselves? + + if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) { + MachPortSender sender(ack_port); + MachSendMessage ack_message(kAcknowledgementMessage); + const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; + + sender.SendMessage(ack_message, kSendTimeoutMs); + } + + if (exit_callback_) { + exit_callback_(exit_context_, client); + } + break; + } + case kQuitMessage: + return false; + } + } else { // result != KERN_SUCCESS + return false; + } + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.h b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.h new file mode 100644 index 0000000..6e6cb44 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/crash_generation/crash_generation_server.h @@ -0,0 +1,141 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ +#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ + +#include + +#include + +#include "common/mac/MachIPC.h" + +namespace google_breakpad { + +class ClientInfo; + +// Messages the server can read via its mach port +enum { + kDumpRequestMessage = 1, + kAcknowledgementMessage = 2, + kQuitMessage = 3 +}; + +// Exception details sent by the client when requesting a dump. +struct ExceptionInfo { + int32_t exception_type; + int32_t exception_code; + int32_t exception_subcode; +}; + +class CrashGenerationServer { + public: + // WARNING: callbacks may be invoked on a different thread + // than that which creates the CrashGenerationServer. They must + // be thread safe. + typedef void (*OnClientDumpRequestCallback)(void *context, + const ClientInfo &client_info, + const std::string &file_path); + + typedef void (*OnClientExitingCallback)(void *context, + const ClientInfo &client_info); + + // Create an instance with the given parameters. + // + // mach_port_name: Named server port to listen on. + // dump_callback: Callback for a client crash dump request. + // dump_context: Context for client crash dump request callback. + // exit_callback: Callback for client process exit. + // exit_context: Context for client exit callback. + // generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly + // in the crash dump request callback. In that case, false can be + // passed for this parameter. + // dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const char *mach_port_name, + OnClientDumpRequestCallback dump_callback, + void *dump_context, + OnClientExitingCallback exit_callback, + void *exit_context, + bool generate_dumps, + const std::string &dump_path); + + ~CrashGenerationServer(); + + // Perform initialization steps needed to start listening to clients. + // + // Return true if initialization is successful; false otherwise. + bool Start(); + + // Stop the server. + bool Stop(); + + private: + // Return a unique filename at which a minidump can be written. + bool MakeMinidumpFilename(std::string &outFilename); + + // Loop reading client messages and responding to them until + // a quit message is received. + static void *WaitForMessages(void *server); + + // Wait for a single client message and respond to it. Returns false + // if a quit message was received or if an error occurred. + bool WaitForOneMessage(); + + OnClientDumpRequestCallback dump_callback_; + void *dump_context_; + + OnClientExitingCallback exit_callback_; + void *exit_context_; + + bool generate_dumps_; + + std::string dump_dir_; + + bool started_; + + // The mach port that receives requests to dump from child processes. + ReceivePort receive_port_; + + // The name of the mach port. Stored so the Stop method can message + // the background thread to shut it down. + std::string mach_port_name_; + + // The thread that waits on the receive port. + pthread_t server_thread_; + + // Disable copy constructor and operator=. + CrashGenerationServer(const CrashGenerationServer&); + CrashGenerationServer& operator=(const CrashGenerationServer&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.cc new file mode 100644 index 0000000..b50aa03 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.cc @@ -0,0 +1,413 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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. + */ + + +/* + * This file was copied from libc/gen/nlist.c from Darwin's source code + * The version of nlist used as a base is from 10.5.2, libc-498 + * http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c + * + * The full tarball is at: + * http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz + * + * I've modified it to be compatible with 64-bit images. +*/ + +#include "breakpad_nlist_64.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Stuff lifted from and since they are gone */ +/* + * Header prepended to each a.out file. + */ +struct exec { + unsigned short a_machtype; /* machine type */ + unsigned short a_magic; /* magic number */ + unsigned long a_text; /* size of text segment */ + unsigned long a_data; /* size of initialized data */ + unsigned long a_bss; /* size of uninitialized data */ + unsigned long a_syms; /* size of symbol table */ + unsigned long a_entry; /* entry point */ + unsigned long a_trsize; /* size of text relocation */ + unsigned long a_drsize; /* size of data relocation */ +}; + +#define OMAGIC 0407 /* old impure format */ +#define NMAGIC 0410 /* read-only text */ +#define ZMAGIC 0413 /* demand load format */ + +#define N_BADMAG(x) \ + (((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC) +#define N_TXTOFF(x) \ + ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec)) +#define N_SYMOFF(x) \ + (N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize) + +// Traits structs for specializing function templates to handle +// 32-bit/64-bit Mach-O files. +template +struct MachBits {}; + +typedef struct nlist nlist32; +typedef struct nlist_64 nlist64; + +template<> +struct MachBits { + typedef mach_header mach_header_type; + typedef uint32_t word_type; + static const uint32_t magic = MH_MAGIC; +}; + +template<> +struct MachBits { + typedef mach_header_64 mach_header_type; + typedef uint64_t word_type; + static const uint32_t magic = MH_MAGIC_64; +}; + +template +int +__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, + cpu_type_t cpu_type); + +/* + * nlist - retreive attributes from name list (string table version) + */ + +template +int breakpad_nlist_common(const char *name, + nlist_type *list, + const char **symbolNames, + cpu_type_t cpu_type) { + int fd = open(name, O_RDONLY, 0); + if (fd < 0) + return -1; + int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type); + close(fd); + return n; +} + +int breakpad_nlist(const char *name, + struct nlist *list, + const char **symbolNames, + cpu_type_t cpu_type) { + return breakpad_nlist_common(name, list, symbolNames, cpu_type); +} + +int breakpad_nlist(const char *name, + struct nlist_64 *list, + const char **symbolNames, + cpu_type_t cpu_type) { + return breakpad_nlist_common(name, list, symbolNames, cpu_type); +} + +/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */ + +template +int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, + cpu_type_t cpu_type) { + typedef typename MachBits::mach_header_type mach_header_type; + typedef typename MachBits::word_type word_type; + + const uint32_t magic = MachBits::magic; + + int maxlen = 500; + int nreq = 0; + for (nlist_type* q = list; + symbolNames[q-list] && symbolNames[q-list][0]; + q++, nreq++) { + + q->n_type = 0; + q->n_value = 0; + q->n_desc = 0; + q->n_sect = 0; + q->n_un.n_strx = 0; + } + + struct exec buf; + if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) || + (N_BADMAG(buf) && *((uint32_t *)&buf) != magic && + CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC && + /* The following is the big-endian ppc64 check */ + (*((uint32_t*)&buf)) != FAT_MAGIC)) { + return -1; + } + + /* Deal with fat file if necessary */ + unsigned arch_offset = 0; + if (CFSwapInt32BigToHost(*((uint32_t *)&buf)) == FAT_MAGIC || + /* The following is the big-endian ppc64 check */ + *((unsigned int *)&buf) == FAT_MAGIC) { + /* Get host info */ + host_t host = mach_host_self(); + unsigned i = HOST_BASIC_INFO_COUNT; + struct host_basic_info hbi; + kern_return_t kr; + if ((kr = host_info(host, HOST_BASIC_INFO, + (host_info_t)(&hbi), &i)) != KERN_SUCCESS) { + return -1; + } + mach_port_deallocate(mach_task_self(), host); + + /* Read in the fat header */ + struct fat_header fh; + if (lseek(fd, 0, SEEK_SET) == -1) { + return -1; + } + if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) { + return -1; + } + + /* Convert fat_narchs to host byte order */ + fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch); + + /* Read in the fat archs */ + struct fat_arch *fat_archs = + (struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch)); + if (fat_archs == NULL) { + return -1; + } + if (read(fd, (char *)fat_archs, + sizeof(struct fat_arch) * fh.nfat_arch) != + (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) { + free(fat_archs); + return -1; + } + + /* + * Convert archs to host byte ordering (a constraint of + * cpusubtype_getbestarch() + */ + for (unsigned i = 0; i < fh.nfat_arch; i++) { + fat_archs[i].cputype = + CFSwapInt32BigToHost(fat_archs[i].cputype); + fat_archs[i].cpusubtype = + CFSwapInt32BigToHost(fat_archs[i].cpusubtype); + fat_archs[i].offset = + CFSwapInt32BigToHost(fat_archs[i].offset); + fat_archs[i].size = + CFSwapInt32BigToHost(fat_archs[i].size); + fat_archs[i].align = + CFSwapInt32BigToHost(fat_archs[i].align); + } + + struct fat_arch *fap = NULL; + for (unsigned i = 0; i < fh.nfat_arch; i++) { + if (fat_archs[i].cputype == cpu_type) { + fap = &fat_archs[i]; + break; + } + } + + if (!fap) { + free(fat_archs); + return -1; + } + arch_offset = fap->offset; + free(fat_archs); + + /* Read in the beginning of the architecture-specific file */ + if (lseek(fd, arch_offset, SEEK_SET) == -1) { + return -1; + } + if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) { + return -1; + } + } + + off_t sa; /* symbol address */ + off_t ss; /* start of strings */ + register register_t n; + if (*((unsigned int *)&buf) == magic) { + if (lseek(fd, arch_offset, SEEK_SET) == -1) { + return -1; + } + mach_header_type mh; + if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) { + return -1; + } + + struct load_command *load_commands = + (struct load_command *)malloc(mh.sizeofcmds); + if (load_commands == NULL) { + return -1; + } + if (read(fd, (char *)load_commands, mh.sizeofcmds) != + (ssize_t)mh.sizeofcmds) { + free(load_commands); + return -1; + } + struct symtab_command *stp = NULL; + struct load_command *lcp = load_commands; + // iterate through all load commands, looking for + // LC_SYMTAB load command + for (uint32_t i = 0; i < mh.ncmds; i++) { + if (lcp->cmdsize % sizeof(word_type) != 0 || + lcp->cmdsize <= 0 || + (char *)lcp + lcp->cmdsize > + (char *)load_commands + mh.sizeofcmds) { + free(load_commands); + return -1; + } + if (lcp->cmd == LC_SYMTAB) { + if (lcp->cmdsize != + sizeof(struct symtab_command)) { + free(load_commands); + return -1; + } + stp = (struct symtab_command *)lcp; + break; + } + lcp = (struct load_command *) + ((char *)lcp + lcp->cmdsize); + } + if (stp == NULL) { + free(load_commands); + return -1; + } + // sa points to the beginning of the symbol table + sa = stp->symoff + arch_offset; + // ss points to the beginning of the string table + ss = stp->stroff + arch_offset; + // n is the number of bytes in the symbol table + // each symbol table entry is an nlist structure + n = stp->nsyms * sizeof(nlist_type); + free(load_commands); + } else { + sa = N_SYMOFF(buf) + arch_offset; + ss = sa + buf.a_syms + arch_offset; + n = buf.a_syms; + } + + if (lseek(fd, sa, SEEK_SET) == -1) { + return -1; + } + + // the algorithm here is to read the nlist entries in m-sized + // chunks into q. q is then iterated over. for each entry in q, + // use the string table index(q->n_un.n_strx) to read the symbol + // name, then scan the nlist entries passed in by the user(via p), + // and look for a match + while (n) { + nlist_type space[BUFSIZ/sizeof (nlist_type)]; + register register_t m = sizeof (space); + + if (n < m) + m = n; + if (read(fd, (char *)space, m) != m) + break; + n -= m; + long savpos = lseek(fd, 0, SEEK_CUR); + if (savpos == -1) { + return -1; + } + for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) { + char nambuf[BUFSIZ]; + + if (q->n_un.n_strx == 0 || q->n_type & N_STAB) + continue; + + // seek to the location in the binary where the symbol + // name is stored & read it into memory + if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) { + return -1; + } + if (read(fd, nambuf, maxlen+1) == -1) { + return -1; + } + const char *s2 = nambuf; + for (nlist_type *p = list; + symbolNames[p-list] && symbolNames[p-list][0]; + p++) { + // get the symbol name the user has passed in that + // corresponds to the nlist entry that we're looking at + const char *s1 = symbolNames[p - list]; + while (*s1) { + if (*s1++ != *s2++) + goto cont; + } + if (*s2) + goto cont; + + p->n_value = q->n_value; + p->n_type = q->n_type; + p->n_desc = q->n_desc; + p->n_sect = q->n_sect; + p->n_un.n_strx = q->n_un.n_strx; + if (--nreq == 0) + return nreq; + + break; + cont: ; + } + } + if (lseek(fd, savpos, SEEK_SET) == -1) { + return -1; + } + } + return nreq; +} diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.h b/src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.h new file mode 100644 index 0000000..1d2c639 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/breakpad_nlist_64.h @@ -0,0 +1,47 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// breakpad_nlist.h +// +// This file is meant to provide a header for clients of the modified +// nlist function implemented to work on 64-bit. + +#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ + +#include + +int breakpad_nlist(const char *name, + struct nlist *list, + const char **symbolNames, + cpu_type_t cpu_type); +int breakpad_nlist(const char *name, + struct nlist_64 *list, + const char **symbolNames, + cpu_type_t cpu_type); + +#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.cc new file mode 100644 index 0000000..ef5743c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.cc @@ -0,0 +1,578 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/mac/handler/dynamic_images.h" + +extern "C" { // needed to compile on Leopard + #include + #include + #include +} + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "breakpad_nlist_64.h" + +#if !TARGET_OS_IPHONE +#include + +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 + +// Fallback declarations for TASK_DYLD_INFO and friends, introduced in +// in the Mac OS X 10.6 SDK. +#define TASK_DYLD_INFO 17 +struct task_dyld_info { + mach_vm_address_t all_image_info_addr; + mach_vm_size_t all_image_info_size; +}; +typedef struct task_dyld_info task_dyld_info_data_t; +typedef struct task_dyld_info *task_dyld_info_t; +#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) + +#endif + +#endif // !TARGET_OS_IPHONE + +namespace google_breakpad { + +using std::string; +using std::vector; + +//============================================================================== +// Returns the size of the memory region containing |address| and the +// number of bytes from |address| to the end of the region. +// We potentially, will extend the size of the original +// region by the size of the following region if it's contiguous with the +// first in order to handle cases when we're reading strings and they +// straddle two vm regions. +// +static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task, + const uint64_t address, + mach_vm_size_t *size_to_end) { + mach_vm_address_t region_base = (mach_vm_address_t)address; + mach_vm_size_t region_size; + natural_t nesting_level = 0; + vm_region_submap_info_64 submap_info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + + // Get information about the vm region containing |address| + vm_region_recurse_info_t region_info; + region_info = reinterpret_cast(&submap_info); + + kern_return_t result = + mach_vm_region_recurse(target_task, + ®ion_base, + ®ion_size, + &nesting_level, + region_info, + &info_count); + + if (result == KERN_SUCCESS) { + // Get distance from |address| to the end of this region + *size_to_end = region_base + region_size -(mach_vm_address_t)address; + + // If we want to handle strings as long as 4096 characters we may need + // to check if there's a vm region immediately following the first one. + // If so, we need to extend |*size_to_end| to go all the way to the end + // of the second region. + if (*size_to_end < 4096) { + // Second region starts where the first one ends + mach_vm_address_t region_base2 = + (mach_vm_address_t)(region_base + region_size); + mach_vm_size_t region_size2; + + // Get information about the following vm region + result = + mach_vm_region_recurse(target_task, + ®ion_base2, + ®ion_size2, + &nesting_level, + region_info, + &info_count); + + // Extend region_size to go all the way to the end of the 2nd region + if (result == KERN_SUCCESS + && region_base2 == region_base + region_size) { + region_size += region_size2; + } + } + + *size_to_end = region_base + region_size -(mach_vm_address_t)address; + } else { + region_size = 0; + *size_to_end = 0; + } + + return region_size; +} + +#define kMaxStringLength 8192 +//============================================================================== +// Reads a NULL-terminated string from another task. +// +// Warning! This will not read any strings longer than kMaxStringLength-1 +// +static string ReadTaskString(task_port_t target_task, + const uint64_t address) { + // The problem is we don't know how much to read until we know how long + // the string is. And we don't know how long the string is, until we've read + // the memory! So, we'll try to read kMaxStringLength bytes + // (or as many bytes as we can until we reach the end of the vm region). + mach_vm_size_t size_to_end; + GetMemoryRegionSize(target_task, address, &size_to_end); + + if (size_to_end > 0) { + mach_vm_size_t size_to_read = + size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end; + + vector bytes; + if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) != + KERN_SUCCESS) + return string(); + + return string(reinterpret_cast(&bytes[0])); + } + + return string(); +} + +//============================================================================== +// Reads an address range from another task. The bytes read will be returned +// in bytes, which will be resized as necessary. +kern_return_t ReadTaskMemory(task_port_t target_task, + const uint64_t address, + size_t length, + vector &bytes) { + int systemPageSize = getpagesize(); + + // use the negative of the page size for the mask to find the page address + mach_vm_address_t page_address = address & (-systemPageSize); + + mach_vm_address_t last_page_address = + (address + length + (systemPageSize - 1)) & (-systemPageSize); + + mach_vm_size_t page_size = last_page_address - page_address; + uint8_t* local_start; + uint32_t local_length; + + kern_return_t r = mach_vm_read(target_task, + page_address, + page_size, + reinterpret_cast(&local_start), + &local_length); + + if (r != KERN_SUCCESS) + return r; + + bytes.resize(length); + memcpy(&bytes[0], + &local_start[(mach_vm_address_t)address - page_address], + length); + mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length); + return KERN_SUCCESS; +} + +#pragma mark - + +//============================================================================== +// Traits structs for specializing function templates to handle +// 32-bit/64-bit Mach-O files. +struct MachO32 { + typedef mach_header mach_header_type; + typedef segment_command mach_segment_command_type; + typedef dyld_image_info32 dyld_image_info; + typedef dyld_all_image_infos32 dyld_all_image_infos; + typedef struct nlist nlist_type; + static const uint32_t magic = MH_MAGIC; + static const uint32_t segment_load_command = LC_SEGMENT; +}; + +struct MachO64 { + typedef mach_header_64 mach_header_type; + typedef segment_command_64 mach_segment_command_type; + typedef dyld_image_info64 dyld_image_info; + typedef dyld_all_image_infos64 dyld_all_image_infos; + typedef struct nlist_64 nlist_type; + static const uint32_t magic = MH_MAGIC_64; + static const uint32_t segment_load_command = LC_SEGMENT_64; +}; + +template +bool FindTextSection(DynamicImage& image) { + typedef typename MachBits::mach_header_type mach_header_type; + typedef typename MachBits::mach_segment_command_type + mach_segment_command_type; + + const mach_header_type* header = + reinterpret_cast(&image.header_[0]); + + if(header->magic != MachBits::magic) { + return false; + } + + const struct load_command *cmd = + reinterpret_cast(header + 1); + + bool found_text_section = false; + bool found_dylib_id_command = false; + for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { + if (!found_text_section) { + if (cmd->cmd == MachBits::segment_load_command) { + const mach_segment_command_type *seg = + reinterpret_cast(cmd); + + if (!strcmp(seg->segname, "__TEXT")) { + image.vmaddr_ = seg->vmaddr; + image.vmsize_ = seg->vmsize; + image.slide_ = 0; + + if (seg->fileoff == 0 && seg->filesize != 0) { + image.slide_ = + (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; + } + found_text_section = true; + } + } + } + + if (!found_dylib_id_command) { + if (cmd->cmd == LC_ID_DYLIB) { + const struct dylib_command *dc = + reinterpret_cast(cmd); + + image.version_ = dc->dylib.current_version; + found_dylib_id_command = true; + } + } + + if (found_dylib_id_command && found_text_section) { + return true; + } + + cmd = reinterpret_cast + (reinterpret_cast(cmd) + cmd->cmdsize); + } + + return false; +} + +//============================================================================== +// Initializes vmaddr_, vmsize_, and slide_ +void DynamicImage::CalculateMemoryAndVersionInfo() { + // unless we can process the header, ensure that calls to + // IsValid() will return false + vmaddr_ = 0; + vmsize_ = 0; + slide_ = 0; + version_ = 0; + + // The function template above does all the real work. + if (Is64Bit()) + FindTextSection(*this); + else + FindTextSection(*this); +} + +//============================================================================== +// The helper function template abstracts the 32/64-bit differences. +template +uint32_t GetFileTypeFromHeader(DynamicImage& image) { + typedef typename MachBits::mach_header_type mach_header_type; + + const mach_header_type* header = + reinterpret_cast(&image.header_[0]); + return header->filetype; +} + +uint32_t DynamicImage::GetFileType() { + if (Is64Bit()) + return GetFileTypeFromHeader(*this); + + return GetFileTypeFromHeader(*this); +} + +#pragma mark - + +//============================================================================== +// Loads information about dynamically loaded code in the given task. +DynamicImages::DynamicImages(mach_port_t task) + : task_(task), + cpu_type_(DetermineTaskCPUType(task)), + image_list_() { + ReadImageInfoForTask(); +} + +template +static uint64_t LookupSymbol(const char* symbol_name, + const char* filename, + cpu_type_t cpu_type) { + typedef typename MachBits::nlist_type nlist_type; + + nlist_type symbol_info[8] = {}; + const char *symbolNames[2] = { symbol_name, "\0" }; + nlist_type &list = symbol_info[0]; + int invalidEntriesCount = breakpad_nlist(filename, + &list, + symbolNames, + cpu_type); + + if(invalidEntriesCount != 0) { + return 0; + } + + assert(list.n_value); + return list.n_value; +} + +#if TARGET_OS_IPHONE +static bool HasTaskDyldInfo() { + return true; +} +#else +static SInt32 GetOSVersionInternal() { + SInt32 os_version = 0; + Gestalt(gestaltSystemVersion, &os_version); + return os_version; +} + +static SInt32 GetOSVersion() { + static SInt32 os_version = GetOSVersionInternal(); + return os_version; +} + +static bool HasTaskDyldInfo() { +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 + return true; +#else + return GetOSVersion() >= 0x1060; +#endif +} +#endif // TARGET_OS_IPHONE + +uint64_t DynamicImages::GetDyldAllImageInfosPointer() { + if (HasTaskDyldInfo()) { + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, + &count) != KERN_SUCCESS) { + return 0; + } + + return (uint64_t)task_dyld_info.all_image_info_addr; + } else { + const char *imageSymbolName = "_dyld_all_image_infos"; + const char *dyldPath = "/usr/lib/dyld"; + + if (Is64Bit()) + return LookupSymbol(imageSymbolName, dyldPath, cpu_type_); + return LookupSymbol(imageSymbolName, dyldPath, cpu_type_); + } +} + +//============================================================================== +// This code was written using dyld_debug.c (from Darwin) as a guide. + +template +void ReadImageInfo(DynamicImages& images, + uint64_t image_list_address) { + typedef typename MachBits::dyld_image_info dyld_image_info; + typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos; + typedef typename MachBits::mach_header_type mach_header_type; + + // Read the structure inside of dyld that contains information about + // loaded images. We're reading from the desired task's address space. + + // Here we make the assumption that dyld loaded at the same address in + // the crashed process vs. this one. This is an assumption made in + // "dyld_debug.c" and is said to be nearly always valid. + vector dyld_all_info_bytes; + if (ReadTaskMemory(images.task_, + image_list_address, + sizeof(dyld_all_image_infos), + dyld_all_info_bytes) != KERN_SUCCESS) + return; + + dyld_all_image_infos *dyldInfo = + reinterpret_cast(&dyld_all_info_bytes[0]); + + // number of loaded images + int count = dyldInfo->infoArrayCount; + + // Read an array of dyld_image_info structures each containing + // information about a loaded image. + vector dyld_info_array_bytes; + if (ReadTaskMemory(images.task_, + dyldInfo->infoArray, + count * sizeof(dyld_image_info), + dyld_info_array_bytes) != KERN_SUCCESS) + return; + + dyld_image_info *infoArray = + reinterpret_cast(&dyld_info_array_bytes[0]); + images.image_list_.reserve(count); + + for (int i = 0; i < count; ++i) { + dyld_image_info &info = infoArray[i]; + + // First read just the mach_header from the image in the task. + vector mach_header_bytes; + if (ReadTaskMemory(images.task_, + info.load_address_, + sizeof(mach_header_type), + mach_header_bytes) != KERN_SUCCESS) + continue; // bail on this dynamic image + + mach_header_type *header = + reinterpret_cast(&mach_header_bytes[0]); + + // Now determine the total amount necessary to read the header + // plus all of the load commands. + size_t header_size = + sizeof(mach_header_type) + header->sizeofcmds; + + if (ReadTaskMemory(images.task_, + info.load_address_, + header_size, + mach_header_bytes) != KERN_SUCCESS) + continue; + + header = reinterpret_cast(&mach_header_bytes[0]); + + // Read the file name from the task's memory space. + string file_path; + if (info.file_path_) { + // Although we're reading kMaxStringLength bytes, it's copied in the + // the DynamicImage constructor below with the correct string length, + // so it's not really wasting memory. + file_path = ReadTaskString(images.task_, info.file_path_); + } + + // Create an object representing this image and add it to our list. + DynamicImage *new_image; + new_image = new DynamicImage(&mach_header_bytes[0], + header_size, + info.load_address_, + file_path, + info.file_mod_date_, + images.task_, + images.cpu_type_); + + if (new_image->IsValid()) { + images.image_list_.push_back(DynamicImageRef(new_image)); + } else { + delete new_image; + } + } + + // sorts based on loading address + sort(images.image_list_.begin(), images.image_list_.end()); + // remove duplicates - this happens in certain strange cases + // You can see it in DashboardClient when Google Gadgets plugin + // is installed. Apple's crash reporter log and gdb "info shared" + // both show the same library multiple times at the same address + + vector::iterator it = unique(images.image_list_.begin(), + images.image_list_.end()); + images.image_list_.erase(it, images.image_list_.end()); +} + +void DynamicImages::ReadImageInfoForTask() { + uint64_t imageList = GetDyldAllImageInfosPointer(); + + if (imageList) { + if (Is64Bit()) + ReadImageInfo(*this, imageList); + else + ReadImageInfo(*this, imageList); + } +} + +//============================================================================== +DynamicImage *DynamicImages::GetExecutableImage() { + int executable_index = GetExecutableImageIndex(); + + if (executable_index >= 0) { + return GetImage(executable_index); + } + + return NULL; +} + +//============================================================================== +// returns -1 if failure to find executable +int DynamicImages::GetExecutableImageIndex() { + int image_count = GetImageCount(); + + for (int i = 0; i < image_count; ++i) { + DynamicImage *image = GetImage(i); + if (image->GetFileType() == MH_EXECUTE) { + return i; + } + } + + return -1; +} + +//============================================================================== +// static +cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) { + if (task == mach_task_self()) + return GetNativeCPUType(); + + int mib[CTL_MAXNAME]; + size_t mibLen = CTL_MAXNAME; + int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen); + if (err == 0) { + assert(mibLen < CTL_MAXNAME); + pid_for_task(task, &mib[mibLen]); + mibLen += 1; + + cpu_type_t cpu_type; + size_t cpuTypeSize = sizeof(cpu_type); + sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0); + return cpu_type; + } + + return GetNativeCPUType(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.h b/src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.h new file mode 100644 index 0000000..d039eda --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/dynamic_images.h @@ -0,0 +1,317 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// dynamic_images.h +// +// Implements most of the function of the dyld API, but allowing an +// arbitrary task to be introspected, unlike the dyld API which +// only allows operation on the current task. The current implementation +// is limited to use by 32-bit tasks. + +#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ +#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ + +#include +#include +#include +#include + +#include +#include + +#include "mach_vm_compat.h" + +namespace google_breakpad { + +using std::string; +using std::vector; + +//============================================================================== +// The memory layout of this struct matches the dyld_image_info struct +// defined in "dyld_gdb.h" in the darwin source. +typedef struct dyld_image_info32 { + uint32_t load_address_; // struct mach_header* + uint32_t file_path_; // char* + uint32_t file_mod_date_; +} dyld_image_info32; + +typedef struct dyld_image_info64 { + uint64_t load_address_; // struct mach_header* + uint64_t file_path_; // char* + uint64_t file_mod_date_; +} dyld_image_info64; + +//============================================================================== +// This is as defined in "dyld_gdb.h" in the darwin source. +// _dyld_all_image_infos (in dyld) is a structure of this type +// which will be used to determine which dynamic code has been loaded. +typedef struct dyld_all_image_infos32 { + uint32_t version; // == 1 in Mac OS X 10.4 + uint32_t infoArrayCount; + uint32_t infoArray; // const struct dyld_image_info* + uint32_t notification; + bool processDetachedFromSharedRegion; +} dyld_all_image_infos32; + +typedef struct dyld_all_image_infos64 { + uint32_t version; // == 1 in Mac OS X 10.4 + uint32_t infoArrayCount; + uint64_t infoArray; // const struct dyld_image_info* + uint64_t notification; + bool processDetachedFromSharedRegion; +} dyld_all_image_infos64; + +// some typedefs to isolate 64/32 bit differences +#ifdef __LP64__ +typedef mach_header_64 breakpad_mach_header; +typedef segment_command_64 breakpad_mach_segment_command; +#else +typedef mach_header breakpad_mach_header; +typedef segment_command breakpad_mach_segment_command; +#endif + +// Helper functions to deal with 32-bit/64-bit Mach-O differences. +class DynamicImage; +template +bool FindTextSection(DynamicImage& image); + +template +uint32_t GetFileTypeFromHeader(DynamicImage& image); + +//============================================================================== +// Represents a single dynamically loaded mach-o image +class DynamicImage { + public: + DynamicImage(uint8_t *header, // data is copied + size_t header_size, // includes load commands + uint64_t load_address, + string file_path, + uintptr_t image_mod_date, + mach_port_t task, + cpu_type_t cpu_type) + : header_(header, header + header_size), + header_size_(header_size), + load_address_(load_address), + vmaddr_(0), + vmsize_(0), + slide_(0), + version_(0), + file_path_(file_path), + file_mod_date_(image_mod_date), + task_(task), + cpu_type_(cpu_type) { + CalculateMemoryAndVersionInfo(); + } + + // Size of mach_header plus load commands + size_t GetHeaderSize() const {return header_.size();} + + // Full path to mach-o binary + string GetFilePath() {return file_path_;} + + uint64_t GetModDate() const {return file_mod_date_;} + + // Actual address where the image was loaded + uint64_t GetLoadAddress() const {return load_address_;} + + // Address where the image should be loaded + mach_vm_address_t GetVMAddr() const {return vmaddr_;} + + // Difference between GetLoadAddress() and GetVMAddr() + ptrdiff_t GetVMAddrSlide() const {return slide_;} + + // Size of the image + mach_vm_size_t GetVMSize() const {return vmsize_;} + + // Task owning this loaded image + mach_port_t GetTask() {return task_;} + + // CPU type of the task + cpu_type_t GetCPUType() {return cpu_type_;} + + // filetype from the Mach-O header. + uint32_t GetFileType(); + + // Return true if the task is a 64-bit architecture. + bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } + + uint32_t GetVersion() {return version_;} + // For sorting + bool operator<(const DynamicImage &inInfo) { + return GetLoadAddress() < inInfo.GetLoadAddress(); + } + + // Sanity checking + bool IsValid() {return GetVMSize() != 0;} + + private: + DynamicImage(const DynamicImage &); + DynamicImage &operator=(const DynamicImage &); + + friend class DynamicImages; + template + friend bool FindTextSection(DynamicImage& image); + template + friend uint32_t GetFileTypeFromHeader(DynamicImage& image); + + // Initializes vmaddr_, vmsize_, and slide_ + void CalculateMemoryAndVersionInfo(); + + const vector header_; // our local copy of the header + size_t header_size_; // mach_header plus load commands + uint64_t load_address_; // base address image is mapped into + mach_vm_address_t vmaddr_; + mach_vm_size_t vmsize_; + ptrdiff_t slide_; + uint32_t version_; // Dylib version + string file_path_; // path dyld used to load the image + uintptr_t file_mod_date_; // time_t of image file + + mach_port_t task_; + cpu_type_t cpu_type_; // CPU type of task_ +}; + +//============================================================================== +// DynamicImageRef is just a simple wrapper for a pointer to +// DynamicImage. The reason we use it instead of a simple typedef is so +// that we can use stl::sort() on a vector of DynamicImageRefs +// and simple class pointers can't implement operator<(). +// +class DynamicImageRef { + public: + explicit DynamicImageRef(DynamicImage *inP) : p(inP) {} + // The copy constructor is required by STL + DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {} + + bool operator<(const DynamicImageRef &inRef) const { + return (*const_cast(this)->p) + < (*const_cast(inRef).p); + } + + bool operator==(const DynamicImageRef &inInfo) const { + return (*const_cast(this)->p).GetLoadAddress() == + (*const_cast(inInfo)).GetLoadAddress(); + } + + // Be just like DynamicImage* + DynamicImage *operator->() {return p;} + operator DynamicImage*() {return p;} + + private: + DynamicImage *p; +}; + +// Helper function to deal with 32-bit/64-bit Mach-O differences. +class DynamicImages; +template +void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); + +//============================================================================== +// An object of type DynamicImages may be created to allow introspection of +// an arbitrary task's dynamically loaded mach-o binaries. This makes the +// assumption that the current task has send rights to the target task. +class DynamicImages { + public: + explicit DynamicImages(mach_port_t task); + + ~DynamicImages() { + for (int i = 0; i < GetImageCount(); ++i) { + delete image_list_[i]; + } + } + + // Returns the number of dynamically loaded mach-o images. + int GetImageCount() const {return static_cast(image_list_.size());} + + // Returns an individual image. + DynamicImage *GetImage(int i) { + if (i < (int)image_list_.size()) { + return image_list_[i]; + } + return NULL; + } + + // Returns the image corresponding to the main executable. + DynamicImage *GetExecutableImage(); + int GetExecutableImageIndex(); + + // Returns the task which we're looking at. + mach_port_t GetTask() const {return task_;} + + // CPU type of the task + cpu_type_t GetCPUType() {return cpu_type_;} + + // Return true if the task is a 64-bit architecture. + bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } + + // Determine the CPU type of the task being dumped. + static cpu_type_t DetermineTaskCPUType(task_t task); + + // Get the native CPU type of this task. + static cpu_type_t GetNativeCPUType() { +#if defined(__i386__) + return CPU_TYPE_I386; +#elif defined(__x86_64__) + return CPU_TYPE_X86_64; +#elif defined(__ppc__) + return CPU_TYPE_POWERPC; +#elif defined(__ppc64__) + return CPU_TYPE_POWERPC64; +#elif defined(__arm__) + return CPU_TYPE_ARM; +#else +#error "GetNativeCPUType not implemented for this architecture" +#endif + } + + private: + template + friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); + + bool IsOurTask() {return task_ == mach_task_self();} + + // Initialization + void ReadImageInfoForTask(); + uint64_t GetDyldAllImageInfosPointer(); + + mach_port_t task_; + cpu_type_t cpu_type_; // CPU type of task_ + vector image_list_; +}; + +// Fill bytes with the contents of memory at a particular +// location in another task. +kern_return_t ReadTaskMemory(task_port_t target_task, + const uint64_t address, + size_t length, + vector &bytes); + +} // namespace google_breakpad + +#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.cc new file mode 100644 index 0000000..4043019 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.cc @@ -0,0 +1,830 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 +#include + +#include "client/mac/handler/exception_handler.h" +#include "client/mac/handler/minidump_generator.h" +#include "common/mac/macho_utilities.h" +#include "common/mac/scoped_task_suspend-inl.h" +#include "google_breakpad/common/minidump_exception_mac.h" + +#ifndef USE_PROTECTED_ALLOCATIONS +#if TARGET_OS_IPHONE +#define USE_PROTECTED_ALLOCATIONS 1 +#else +#define USE_PROTECTED_ALLOCATIONS 0 +#endif +#endif + +// If USE_PROTECTED_ALLOCATIONS is activated then the +// gBreakpadAllocator needs to be setup in other code +// ahead of time. Please see ProtectedMemoryAllocator.h +// for more details. +#if USE_PROTECTED_ALLOCATIONS + #include "protected_memory_allocator.h" + extern ProtectedMemoryAllocator *gBreakpadAllocator; +#endif + +namespace google_breakpad { + +static union { +#if USE_PROTECTED_ALLOCATIONS + char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +#endif + google_breakpad::ExceptionHandler *handler; +} gProtectedData; + +using std::map; + +// These structures and techniques are illustrated in +// Mac OS X Internals, Amit Singh, ch 9.7 +struct ExceptionMessage { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + NDR_record_t ndr; + exception_type_t exception; + mach_msg_type_number_t code_count; + integer_t code[EXCEPTION_CODE_MAX]; + char padding[512]; +}; + +struct ExceptionParameters { + ExceptionParameters() : count(0) {} + mach_msg_type_number_t count; + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; +}; + +struct ExceptionReplyMessage { + mach_msg_header_t header; + NDR_record_t ndr; + kern_return_t return_code; +}; + +// Only catch these three exceptions. The other ones are nebulously defined +// and may result in treating a non-fatal exception as fatal. +exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | +EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; + +#if !TARGET_OS_IPHONE +extern "C" +{ + // Forward declarations for functions that need "C" style compilation + boolean_t exc_server(mach_msg_header_t *request, + mach_msg_header_t *reply); + + // This symbol must be visible to dlsym() - see + // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. + kern_return_t catch_exception_raise(mach_port_t target_port, + mach_port_t failed_thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) + __attribute__((visibility("default"))); +} +#endif + +kern_return_t ForwardException(mach_port_t task, + mach_port_t failed_thread, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count); + +#if TARGET_OS_IPHONE +// Implementation is based on the implementation generated by mig. +boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP) { + OutHeadP->msgh_bits = + MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0); + OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port; + /* Minimal size: routine() will update it if different */ + OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); + OutHeadP->msgh_local_port = MACH_PORT_NULL; + OutHeadP->msgh_id = InHeadP->msgh_id + 100; + + if (InHeadP->msgh_id != 2401) { + ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record; + ((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID; + return FALSE; + } + +#ifdef __MigPackStructs +#pragma pack(4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + integer_t code[2]; + mach_msg_trailer_t trailer; + } Request; + + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#ifdef __MigPackStructs +#pragma pack() +#endif + + Request *In0P = (Request *)InHeadP; + Reply *OutP = (Reply *)OutHeadP; + + if (In0P->task.name != mach_task_self()) { + return FALSE; + } + OutP->RetCode = ForwardException(In0P->task.name, + In0P->thread.name, + In0P->exception, + In0P->code, + In0P->codeCnt); + OutP->NDR = NDR_record; + return TRUE; +} +#else +boolean_t breakpad_exc_server(mach_msg_header_t *request, + mach_msg_header_t *reply) { + return exc_server(request, reply); +} + +// Callback from exc_server() +kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) { + if (task != mach_task_self()) { + return KERN_FAILURE; + } + return ForwardException(task, failed_thread, exception, code, code_count); +} +#endif + +ExceptionHandler::ExceptionHandler(const string &dump_path, + FilterCallback filter, + MinidumpCallback callback, + void *callback_context, + bool install_handler, + const char *port_name) + : dump_path_(), + filter_(filter), + callback_(callback), + callback_context_(callback_context), + directCallback_(NULL), + handler_thread_(NULL), + handler_port_(MACH_PORT_NULL), + previous_(NULL), + installed_exception_handler_(false), + is_in_teardown_(false), + last_minidump_write_result_(false), + use_minidump_write_mutex_(false) { + // This will update to the ID and C-string pointers + set_dump_path(dump_path); + MinidumpGenerator::GatherSystemInformation(); +#if !TARGET_OS_IPHONE + if (port_name) + crash_generation_client_.reset(new CrashGenerationClient(port_name)); +#endif + Setup(install_handler); +} + +// special constructor if we want to bypass minidump writing and +// simply get a callback with the exception information +ExceptionHandler::ExceptionHandler(DirectCallback callback, + void *callback_context, + bool install_handler) + : dump_path_(), + filter_(NULL), + callback_(NULL), + callback_context_(callback_context), + directCallback_(callback), + handler_thread_(NULL), + handler_port_(MACH_PORT_NULL), + previous_(NULL), + installed_exception_handler_(false), + is_in_teardown_(false), + last_minidump_write_result_(false), + use_minidump_write_mutex_(false) { + MinidumpGenerator::GatherSystemInformation(); + Setup(install_handler); +} + +ExceptionHandler::~ExceptionHandler() { + Teardown(); +} + +bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { + // If we're currently writing, just return + if (use_minidump_write_mutex_) + return false; + + use_minidump_write_mutex_ = true; + last_minidump_write_result_ = false; + + // Lock the mutex. Since we just created it, this will return immediately. + if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { + // Send an empty message to the handle port so that a minidump will + // be written + SendMessageToHandlerThread(write_exception_stream ? + kWriteDumpWithExceptionMessage : + kWriteDumpMessage); + + // Wait for the minidump writer to complete its writing. It will unlock + // the mutex when completed + pthread_mutex_lock(&minidump_write_mutex_); + } + + use_minidump_write_mutex_ = false; + UpdateNextID(); + return last_minidump_write_result_; +} + +// static +bool ExceptionHandler::WriteMinidump(const string &dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void *callback_context) { + ExceptionHandler handler(dump_path, NULL, callback, callback_context, false, + NULL); + return handler.WriteMinidump(write_exception_stream); +} + +// static +bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, + mach_port_t child_blamed_thread, + const string &dump_path, + MinidumpCallback callback, + void *callback_context) { + ScopedTaskSuspend suspend(child); + + MinidumpGenerator generator(child, MACH_PORT_NULL); + string dump_id; + string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id); + + generator.SetExceptionInformation(EXC_BREAKPOINT, +#if defined (__i386__) || defined(__x86_64__) + EXC_I386_BPT, +#elif defined (__ppc__) || defined (__ppc64__) + EXC_PPC_BREAKPOINT, +#elif defined (__arm__) + EXC_ARM_BREAKPOINT, +#else +#error architecture not supported +#endif + 0, + child_blamed_thread); + bool result = generator.Write(dump_filename.c_str()); + + if (callback) { + return callback(dump_path.c_str(), dump_id.c_str(), + callback_context, result); + } + return result; +} + +bool ExceptionHandler::WriteMinidumpWithException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t thread_name, + bool exit_after_write, + bool report_current_thread) { + bool result = false; + + if (directCallback_) { + if (directCallback_(callback_context_, + exception_type, + exception_code, + exception_subcode, + thread_name) ) { + if (exit_after_write) + _exit(exception_type); + } +#if !TARGET_OS_IPHONE + } else if (IsOutOfProcess()) { + if (exception_type && exception_code) { + // If this is a real exception, give the filter (if any) a chance to + // decide if this should be sent. + if (filter_ && !filter_(callback_context_)) + return false; + return crash_generation_client_->RequestDumpForException( + exception_type, + exception_code, + exception_subcode, + thread_name); + } +#endif + } else { + string minidump_id; + + // Putting the MinidumpGenerator in its own context will ensure that the + // destructor is executed, closing the newly created minidump file. + if (!dump_path_.empty()) { + MinidumpGenerator md(mach_task_self(), + report_current_thread ? MACH_PORT_NULL : + mach_thread_self()); + if (exception_type && exception_code) { + // If this is a real exception, give the filter (if any) a chance to + // decide if this should be sent. + if (filter_ && !filter_(callback_context_)) + return false; + + md.SetExceptionInformation(exception_type, exception_code, + exception_subcode, thread_name); + } + + result = md.Write(next_minidump_path_c_); + } + + // Call user specified callback (if any) + if (callback_) { + // If the user callback returned true and we're handling an exception + // (rather than just writing out the file), then we should exit without + // forwarding the exception to the next handler. + if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, + result)) { + if (exit_after_write) + _exit(exception_type); + } + } + } + + return result; +} + +kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) { + // At this time, we should have called Uninstall() on the exception handler + // so that the current exception ports are the ones that we should be + // forwarding to. + ExceptionParameters current; + + current.count = EXC_TYPES_COUNT; + mach_port_t current_task = mach_task_self(); + task_get_exception_ports(current_task, + s_exception_mask, + current.masks, + ¤t.count, + current.ports, + current.behaviors, + current.flavors); + + // Find the first exception handler that matches the exception + unsigned int found; + for (found = 0; found < current.count; ++found) { + if (current.masks[found] & (1 << exception)) { + break; + } + } + + // Nothing to forward + if (found == current.count) { + fprintf(stderr, "** No previous ports for forwarding!! \n"); + exit(KERN_FAILURE); + } + + mach_port_t target_port = current.ports[found]; + exception_behavior_t target_behavior = current.behaviors[found]; + + kern_return_t result; + switch (target_behavior) { + case EXCEPTION_DEFAULT: + result = exception_raise(target_port, failed_thread, task, exception, + code, code_count); + break; + + default: + fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior); + result = KERN_FAILURE; + break; + } + + return result; +} + +// static +void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { + ExceptionHandler *self = + reinterpret_cast(exception_handler_class); + ExceptionMessage receive; + + // Wait for the exception info + while (1) { + receive.header.msgh_local_port = self->handler_port_; + receive.header.msgh_size = static_cast(sizeof(receive)); + kern_return_t result = mach_msg(&(receive.header), + MACH_RCV_MSG | MACH_RCV_LARGE, 0, + receive.header.msgh_size, + self->handler_port_, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + + if (result == KERN_SUCCESS) { + // Uninstall our handler so that we don't get in a loop if the process of + // writing out a minidump causes an exception. However, if the exception + // was caused by a fork'd process, don't uninstall things + + // If the actual exception code is zero, then we're calling this handler + // in a way that indicates that we want to either exit this thread or + // generate a minidump + // + // While reporting, all threads (except this one) must be suspended + // to avoid misleading stacks. If appropriate they will be resumed + // afterwards. + if (!receive.exception) { + // Don't touch self, since this message could have been sent + // from its destructor. + if (receive.header.msgh_id == kShutdownMessage) + return NULL; + + self->SuspendThreads(); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + + mach_port_t thread = MACH_PORT_NULL; + int exception_type = 0; + int exception_code = 0; + if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) { + thread = receive.thread.name; + exception_type = EXC_BREAKPOINT; +#if defined (__i386__) || defined(__x86_64__) + exception_code = EXC_I386_BPT; +#elif defined (__ppc__) || defined (__ppc64__) + exception_code = EXC_PPC_BREAKPOINT; +#elif defined (__arm__) + exception_code = EXC_ARM_BREAKPOINT; +#else +#error architecture not supported +#endif + } + + // Write out the dump and save the result for later retrieval + self->last_minidump_write_result_ = + self->WriteMinidumpWithException(exception_type, exception_code, + 0, thread, + false, false); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif + + self->ResumeThreads(); + + if (self->use_minidump_write_mutex_) + pthread_mutex_unlock(&self->minidump_write_mutex_); + } else { + // When forking a child process with the exception handler installed, + // if the child crashes, it will send the exception back to the parent + // process. The check for task == self_task() ensures that only + // exceptions that occur in the parent process are caught and + // processed. If the exception was not caused by this task, we + // still need to call into the exception server and have it return + // KERN_FAILURE (see catch_exception_raise) in order for the kernel + // to move onto the host exception handler for the child task + if (receive.task.name == mach_task_self()) { + self->SuspendThreads(); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + + int subcode = 0; + if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) + subcode = receive.code[1]; + + // Generate the minidump with the exception data. + self->WriteMinidumpWithException(receive.exception, receive.code[0], + subcode, receive.thread.name, true, + false); + +#if USE_PROTECTED_ALLOCATIONS + // This may have become protected again within + // WriteMinidumpWithException, but it needs to be unprotected for + // UninstallHandler. + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + + self->UninstallHandler(true); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif + } + // Pass along the exception to the server, which will setup the + // message and call catch_exception_raise() and put the return + // code into the reply. + ExceptionReplyMessage reply; + if (!breakpad_exc_server(&receive.header, &reply.header)) + exit(1); + + // Send a reply and exit + mach_msg(&(reply.header), MACH_SEND_MSG, + reply.header.msgh_size, 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + } + } + } + + return NULL; +} + +//static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + gProtectedData.handler->WriteMinidumpWithException( + EXC_SOFTWARE, + MD_EXCEPTION_CODE_MAC_ABORT, + 0, + mach_thread_self(), + true, + true); +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif +} + +bool ExceptionHandler::InstallHandler() { + // If a handler is already installed, something is really wrong. + if (gProtectedData.handler != NULL) { + return false; + } +#if TARGET_OS_IPHONE + if (!IsOutOfProcess()) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGABRT); + sa.sa_sigaction = ExceptionHandler::SignalHandler; + sa.sa_flags = SA_SIGINFO; + + scoped_ptr old(new struct sigaction); + if (sigaction(SIGABRT, &sa, old.get()) == -1) { + return false; + } + old_handler_.swap(old); + gProtectedData.handler = this; +#if USE_PROTECTED_ALLOCATIONS + assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); +#endif + } +#endif + + try { +#if USE_PROTECTED_ALLOCATIONS + previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) + ExceptionParameters(); +#else + previous_ = new ExceptionParameters(); +#endif + + } + catch (std::bad_alloc) { + return false; + } + + // Save the current exception ports so that we can forward to them + previous_->count = EXC_TYPES_COUNT; + mach_port_t current_task = mach_task_self(); + kern_return_t result = task_get_exception_ports(current_task, + s_exception_mask, + previous_->masks, + &previous_->count, + previous_->ports, + previous_->behaviors, + previous_->flavors); + + // Setup the exception ports on this task + if (result == KERN_SUCCESS) + result = task_set_exception_ports(current_task, s_exception_mask, + handler_port_, EXCEPTION_DEFAULT, + THREAD_STATE_NONE); + + installed_exception_handler_ = (result == KERN_SUCCESS); + + return installed_exception_handler_; +} + +bool ExceptionHandler::UninstallHandler(bool in_exception) { + kern_return_t result = KERN_SUCCESS; + + if (old_handler_.get()) { + sigaction(SIGABRT, old_handler_.get(), NULL); +#if USE_PROTECTED_ALLOCATIONS + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, + PROT_READ | PROT_WRITE); +#endif + old_handler_.reset(); + gProtectedData.handler = NULL; + } + + if (installed_exception_handler_) { + mach_port_t current_task = mach_task_self(); + + // Restore the previous ports + for (unsigned int i = 0; i < previous_->count; ++i) { + result = task_set_exception_ports(current_task, previous_->masks[i], + previous_->ports[i], + previous_->behaviors[i], + previous_->flavors[i]); + if (result != KERN_SUCCESS) + return false; + } + + // this delete should NOT happen if an exception just occurred! + if (!in_exception) { +#if USE_PROTECTED_ALLOCATIONS + previous_->~ExceptionParameters(); +#else + delete previous_; +#endif + } + + previous_ = NULL; + installed_exception_handler_ = false; + } + + return result == KERN_SUCCESS; +} + +bool ExceptionHandler::Setup(bool install_handler) { + if (pthread_mutex_init(&minidump_write_mutex_, NULL)) + return false; + + // Create a receive right + mach_port_t current_task = mach_task_self(); + kern_return_t result = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &handler_port_); + // Add send right + if (result == KERN_SUCCESS) + result = mach_port_insert_right(current_task, handler_port_, handler_port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (install_handler && result == KERN_SUCCESS) + if (!InstallHandler()) + return false; + + if (result == KERN_SUCCESS) { + // Install the handler in its own thread, detached as we won't be joining. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + int thread_create_result = pthread_create(&handler_thread_, &attr, + &WaitForMessage, this); + pthread_attr_destroy(&attr); + result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS; + } + + return result == KERN_SUCCESS ? true : false; +} + +bool ExceptionHandler::Teardown() { + kern_return_t result = KERN_SUCCESS; + is_in_teardown_ = true; + + if (!UninstallHandler(false)) + return false; + + // Send an empty message so that the handler_thread exits + if (SendMessageToHandlerThread(kShutdownMessage)) { + mach_port_t current_task = mach_task_self(); + result = mach_port_deallocate(current_task, handler_port_); + if (result != KERN_SUCCESS) + return false; + } else { + return false; + } + + handler_thread_ = NULL; + handler_port_ = MACH_PORT_NULL; + pthread_mutex_destroy(&minidump_write_mutex_); + + return result == KERN_SUCCESS; +} + +bool ExceptionHandler::SendMessageToHandlerThread( + HandlerThreadMessage message_id) { + ExceptionMessage msg; + memset(&msg, 0, sizeof(msg)); + msg.header.msgh_id = message_id; + if (message_id == kWriteDumpMessage || + message_id == kWriteDumpWithExceptionMessage) { + // Include this thread's port. + msg.thread.name = mach_thread_self(); + msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND; + msg.thread.type = MACH_MSG_PORT_DESCRIPTOR; + } + msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding); + msg.header.msgh_remote_port = handler_port_; + msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, + MACH_MSG_TYPE_MAKE_SEND_ONCE); + kern_return_t result = mach_msg(&(msg.header), + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + msg.header.msgh_size, 0, 0, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + return result == KERN_SUCCESS; +} + +void ExceptionHandler::UpdateNextID() { + next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); + + next_minidump_path_c_ = next_minidump_path_.c_str(); + next_minidump_id_c_ = next_minidump_id_.c_str(); +} + +bool ExceptionHandler::SuspendThreads() { + thread_act_port_array_t threads_for_task; + mach_msg_type_number_t thread_count; + + if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) + return false; + + // suspend all of the threads except for this one + for (unsigned int i = 0; i < thread_count; ++i) { + if (threads_for_task[i] != mach_thread_self()) { + if (thread_suspend(threads_for_task[i])) + return false; + } + } + + return true; +} + +bool ExceptionHandler::ResumeThreads() { + thread_act_port_array_t threads_for_task; + mach_msg_type_number_t thread_count; + + if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) + return false; + + // resume all of the threads except for this one + for (unsigned int i = 0; i < thread_count; ++i) { + if (threads_for_task[i] != mach_thread_self()) { + if (thread_resume(threads_for_task[i])) + return false; + } + } + + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.h b/src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.h new file mode 100644 index 0000000..ec09134 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/exception_handler.h @@ -0,0 +1,277 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// exception_handler.h: MacOS exception handler +// This class can install a Mach exception port handler to trap most common +// programming errors. If an exception occurs, a minidump file will be +// generated which contains detailed information about the process and the +// exception. + +#ifndef CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include + +#include + +#include "processor/scoped_ptr.h" + +#if !TARGET_OS_IPHONE +#include "client/mac/crash_generation/crash_generation_client.h" +#endif + +namespace google_breakpad { + +using std::string; + +struct ExceptionParameters; + +enum HandlerThreadMessage { + // Message ID telling the handler thread to write a dump. + kWriteDumpMessage = 0, + // Message ID telling the handler thread to write a dump and include + // an exception stream. + kWriteDumpWithExceptionMessage = 1, + // Message ID telling the handler thread to quit. + kShutdownMessage = 2 +}; + +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, Breakpad + // will immediately report the exception as unhandled without writing a + // minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void *context); + + // A callback function to run after the minidump has been written. + // |minidump_id| is a unique id for the dump, so the minidump + // file is /.dmp. + // |context| is the value passed into the constructor. + // |succeeded| indicates whether a minidump file was successfully written. + // Return true if the exception was fully handled and breakpad should exit. + // Return false to allow any other exception handlers to process the + // exception. + typedef bool (*MinidumpCallback)(const char *dump_dir, + const char *minidump_id, + void *context, bool succeeded); + + // A callback function which will be called directly if an exception occurs. + // This bypasses the minidump file writing and simply gives the client + // the exception information. + typedef bool (*DirectCallback)( void *context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t thread_name); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Minidump files will be written to dump_path, and the optional callback + // is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + // If port_name is non-NULL, attempt to perform out-of-process dump generation + // If port_name is NULL, in-process dump generation will be used. + ExceptionHandler(const string &dump_path, + FilterCallback filter, MinidumpCallback callback, + void *callback_context, bool install_handler, + const char *port_name); + + // A special constructor if we want to bypass minidump writing and + // simply get a callback with the exception information. + ExceptionHandler(DirectCallback callback, + void *callback_context, + bool install_handler); + + ~ExceptionHandler(); + + // Get and set the minidump path. + string dump_path() const { return dump_path_; } + void set_dump_path(const string &dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. + } + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump() { + return WriteMinidump(false); + } + + bool WriteMinidump(bool write_exception_stream); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const string &dump_path, MinidumpCallback callback, + void *callback_context) { + return WriteMinidump(dump_path, false, callback, callback_context); + } + + static bool WriteMinidump(const string &dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void *callback_context); + + // Write a minidump of child immediately. This can be used to capture + // the execution state of a child process independently of a crash. + static bool WriteMinidumpForChild(mach_port_t child, + mach_port_t child_blamed_thread, + const std::string &dump_path, + MinidumpCallback callback, + void *callback_context); + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { +#if TARGET_OS_IPHONE + return false; +#else + return crash_generation_client_.get() != NULL; +#endif + } + + private: + // Install the mach exception handler + bool InstallHandler(); + + // Uninstall the mach exception handler (if any) + bool UninstallHandler(bool in_exception); + + // Setup the handler thread, and if |install_handler| is true, install the + // mach exception port handler + bool Setup(bool install_handler); + + // Uninstall the mach exception handler (if any) and terminate the helper + // thread + bool Teardown(); + + // Send a mach message to the exception handler. Return true on + // success, false otherwise. + bool SendMessageToHandlerThread(HandlerThreadMessage message_id); + + // All minidump writing goes through this one routine + bool WriteMinidumpWithException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t thread_name, + bool exit_after_write, + bool report_current_thread); + + // When installed, this static function will be call from a newly created + // pthread with |this| as the argument + static void *WaitForMessage(void *exception_handler_class); + + // Signal handler for SIGABRT. + static void SignalHandler(int sig, siginfo_t* info, void* uc); + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler &); + void operator=(const ExceptionHandler &); + + // Generates a new ID and stores it in next_minidump_id_, and stores the + // path of the next minidump to be written in next_minidump_path_. + void UpdateNextID(); + + // These functions will suspend/resume all threads except for the + // reporting thread + bool SuspendThreads(); + bool ResumeThreads(); + + // The destination directory for the minidump + string dump_path_; + + // The basename of the next minidump w/o extension + string next_minidump_id_; + + // The full path to the next minidump to be written, including extension + string next_minidump_path_; + + // Pointers to the UTF-8 versions of above + const char *dump_path_c_; + const char *next_minidump_id_c_; + const char *next_minidump_path_c_; + + // The callback function and pointer to be passed back after the minidump + // has been written + FilterCallback filter_; + MinidumpCallback callback_; + void *callback_context_; + + // The callback function to be passed back when we don't want a minidump + // file to be written + DirectCallback directCallback_; + + // The thread that is created for the handler + pthread_t handler_thread_; + + // The port that is waiting on an exception message to be sent, if the + // handler is installed + mach_port_t handler_port_; + + // These variables save the previous exception handler's data so that it + // can be re-installed when this handler is uninstalled + ExceptionParameters *previous_; + + // True, if we've installed the exception handler + bool installed_exception_handler_; + + // True, if we're in the process of uninstalling the exception handler and + // the thread. + bool is_in_teardown_; + + // Save the last result of the last minidump + bool last_minidump_write_result_; + + // A mutex for use when writing out a minidump that was requested on a + // thread other than the exception handler. + pthread_mutex_t minidump_write_mutex_; + + // True, if we're using the mutext to indicate when mindump writing occurs + bool use_minidump_write_mutex_; + + // Old signal handler for SIGABRT. Used to be able to restore it when + // uninstalling. + scoped_ptr old_handler_; + +#if !TARGET_OS_IPHONE + // Client for out-of-process dump generation. + scoped_ptr crash_generation_client_; +#endif +}; + +} // namespace google_breakpad + +#endif // CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/mach_vm_compat.h b/src/lib/crashdump/gbreakpad/client/mac/handler/mach_vm_compat.h new file mode 100644 index 0000000..e0459be --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/mach_vm_compat.h @@ -0,0 +1,49 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ +#define CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ + +#include + +// On iOS 5, mach/mach_vm.h is not supported anymore. As the architecture is 32 +// bits, we can use the simple vm_ functions instead of the mach_vm_ ones. +#if TARGET_OS_IPHONE +#include +#define mach_vm_address_t vm_address_t +#define mach_vm_deallocate vm_deallocate +#define mach_vm_read vm_read +#define mach_vm_region vm_region +#define mach_vm_region_recurse vm_region_recurse +#define mach_vm_size_t vm_size_t +#else +#include +#endif // TARGET_OS_IPHONE + +#endif // CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.cc new file mode 100644 index 0000000..b1d429c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.cc @@ -0,0 +1,1432 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 +#include +#include +#include + +#include + +#include "client/mac/handler/minidump_generator.h" + +#ifdef HAS_ARM_SUPPORT +#include +#endif +#ifdef HAS_PPC_SUPPORT +#include +#endif +#ifdef HAS_X86_SUPPORT +#include +#endif + +#include "client/minidump_file_writer-inl.h" +#include "common/mac/file_id.h" +#include "common/mac/macho_id.h" +#include "common/mac/string_utilities.h" + +using MacStringUtils::ConvertToString; +using MacStringUtils::IntegerValueAtIndex; + +namespace google_breakpad { + +#if __LP64__ +#define LC_SEGMENT_ARCH LC_SEGMENT_64 +#else +#define LC_SEGMENT_ARCH LC_SEGMENT +#endif + +// constructor when generating from within the crashed process +MinidumpGenerator::MinidumpGenerator() + : writer_(), + exception_type_(0), + exception_code_(0), + exception_subcode_(0), + exception_thread_(0), + crashing_task_(mach_task_self()), + handler_thread_(mach_thread_self()), + cpu_type_(DynamicImages::GetNativeCPUType()), + dynamic_images_(NULL), + memory_blocks_(&allocator_) { + GatherSystemInformation(); +} + +// constructor when generating from a different process than the +// crashed process +MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, + mach_port_t handler_thread) + : writer_(), + exception_type_(0), + exception_code_(0), + exception_subcode_(0), + exception_thread_(0), + crashing_task_(crashing_task), + handler_thread_(handler_thread), + cpu_type_(DynamicImages::GetNativeCPUType()), + dynamic_images_(NULL), + memory_blocks_(&allocator_) { + if (crashing_task != mach_task_self()) { + dynamic_images_ = new DynamicImages(crashing_task_); + cpu_type_ = dynamic_images_->GetCPUType(); + } else { + dynamic_images_ = NULL; + cpu_type_ = DynamicImages::GetNativeCPUType(); + } + + GatherSystemInformation(); +} + +MinidumpGenerator::~MinidumpGenerator() { + delete dynamic_images_; +} + +char MinidumpGenerator::build_string_[16]; +int MinidumpGenerator::os_major_version_ = 0; +int MinidumpGenerator::os_minor_version_ = 0; +int MinidumpGenerator::os_build_number_ = 0; + +// static +void MinidumpGenerator::GatherSystemInformation() { + // If this is non-zero, then we've already gathered the information + if (os_major_version_) + return; + + // This code extracts the version and build information from the OS + CFStringRef vers_path = + CFSTR("/System/Library/CoreServices/SystemVersion.plist"); + CFURLRef sys_vers = + CFURLCreateWithFileSystemPath(NULL, + vers_path, + kCFURLPOSIXPathStyle, + false); + CFDataRef data; + SInt32 error; + CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL, + &error); + + if (!data) { + CFRelease(sys_vers); + return; + } + + CFDictionaryRef list = static_cast + (CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable, + NULL)); + if (!list) { + CFRelease(sys_vers); + CFRelease(data); + return; + } + + CFStringRef build_version = static_cast + (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion"))); + CFStringRef product_version = static_cast + (CFDictionaryGetValue(list, CFSTR("ProductVersion"))); + string build_str = ConvertToString(build_version); + string product_str = ConvertToString(product_version); + + CFRelease(list); + CFRelease(sys_vers); + CFRelease(data); + + strlcpy(build_string_, build_str.c_str(), sizeof(build_string_)); + + // Parse the string that looks like "10.4.8" + os_major_version_ = IntegerValueAtIndex(product_str, 0); + os_minor_version_ = IntegerValueAtIndex(product_str, 1); + os_build_number_ = IntegerValueAtIndex(product_str, 2); +} + +string MinidumpGenerator::UniqueNameInDirectory(const string &dir, + string *unique_name) { + CFUUIDRef uuid = CFUUIDCreate(NULL); + CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + string file_name(ConvertToString(uuid_cfstr)); + CFRelease(uuid_cfstr); + string path(dir); + + // Ensure that the directory (if non-empty) has a trailing slash so that + // we can append the file name and have a valid pathname. + if (!dir.empty()) { + if (dir.at(dir.size() - 1) != '/') + path.append(1, '/'); + } + + path.append(file_name); + path.append(".dmp"); + + if (unique_name) + *unique_name = file_name; + + return path; +} + +bool MinidumpGenerator::Write(const char *path) { + WriteStreamFN writers[] = { + &MinidumpGenerator::WriteThreadListStream, + &MinidumpGenerator::WriteMemoryListStream, + &MinidumpGenerator::WriteSystemInfoStream, + &MinidumpGenerator::WriteModuleListStream, + &MinidumpGenerator::WriteMiscInfoStream, + &MinidumpGenerator::WriteBreakpadInfoStream, + // Exception stream needs to be the last entry in this array as it may + // be omitted in the case where the minidump is written without an + // exception. + &MinidumpGenerator::WriteExceptionStream, + }; + bool result = false; + + // If opening was successful, create the header, directory, and call each + // writer. The destructor for the TypedMDRVAs will cause the data to be + // flushed. The destructor for the MinidumpFileWriter will close the file. + if (writer_.Open(path)) { + TypedMDRVA header(&writer_); + TypedMDRVA dir(&writer_); + + if (!header.Allocate()) + return false; + + int writer_count = static_cast(sizeof(writers) / sizeof(writers[0])); + + // If we don't have exception information, don't write out the + // exception stream + if (!exception_thread_ && !exception_type_) + --writer_count; + + // Add space for all writers + if (!dir.AllocateArray(writer_count)) + return false; + + MDRawHeader *header_ptr = header.get(); + header_ptr->signature = MD_HEADER_SIGNATURE; + header_ptr->version = MD_HEADER_VERSION; + time(reinterpret_cast(&(header_ptr->time_date_stamp))); + header_ptr->stream_count = writer_count; + header_ptr->stream_directory_rva = dir.position(); + + MDRawDirectory local_dir; + result = true; + for (int i = 0; (result) && (i < writer_count); ++i) { + result = (this->*writers[i])(&local_dir); + + if (result) + dir.CopyIndex(i, &local_dir); + } + } + return result; +} + +size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) { + mach_vm_address_t stack_region_base = start_addr; + mach_vm_size_t stack_region_size; + natural_t nesting_level = 0; + vm_region_submap_info_64 submap_info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + + vm_region_recurse_info_t region_info; + region_info = reinterpret_cast(&submap_info); + + if (start_addr == 0) { + return 0; + } + + kern_return_t result = + mach_vm_region_recurse(crashing_task_, &stack_region_base, + &stack_region_size, &nesting_level, + region_info, &info_count); + + if (result != KERN_SUCCESS || start_addr < stack_region_base) { + // Failure or stack corruption, since mach_vm_region had to go + // higher in the process address space to find a valid region. + return 0; + } + + unsigned int tag = submap_info.user_tag; + + // If the user tag is VM_MEMORY_STACK, look for more readable regions with + // the same tag placed immediately above the computed stack region. Under + // some circumstances, the stack for thread 0 winds up broken up into + // multiple distinct abutting regions. This can happen for several reasons, + // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes + // the access on stack pages by calling mprotect. + if (tag == VM_MEMORY_STACK) { + while (true) { + mach_vm_address_t next_region_base = stack_region_base + + stack_region_size; + mach_vm_address_t proposed_next_region_base = next_region_base; + mach_vm_size_t next_region_size; + nesting_level = 0; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + result = mach_vm_region_recurse(crashing_task_, &next_region_base, + &next_region_size, &nesting_level, + region_info, &info_count); + if (result != KERN_SUCCESS || + next_region_base != proposed_next_region_base || + submap_info.user_tag != tag || + (submap_info.protection & VM_PROT_READ) == 0) { + break; + } + + stack_region_size += next_region_size; + } + } + + return stack_region_base + stack_region_size - start_addr; +} + +bool MinidumpGenerator::WriteStackFromStartAddress( + mach_vm_address_t start_addr, + MDMemoryDescriptor *stack_location) { + UntypedMDRVA memory(&writer_); + + bool result = false; + size_t size = CalculateStackSize(start_addr); + + if (size == 0) { + // In some situations the stack address for the thread can come back 0. + // In these cases we skip over the threads in question and stuff the + // stack with a clearly borked value. + start_addr = 0xDEADBEEF; + size = 16; + if (!memory.Allocate(size)) + return false; + + unsigned long long dummy_stack[2]; // Fill dummy stack with 16 bytes of + // junk. + dummy_stack[0] = 0xDEADBEEF; + dummy_stack[1] = 0xDEADBEEF; + + result = memory.Copy(dummy_stack, size); + } else { + + if (!memory.Allocate(size)) + return false; + + if (dynamic_images_) { + vector stack_memory; + if (ReadTaskMemory(crashing_task_, + start_addr, + size, + stack_memory) != KERN_SUCCESS) { + return false; + } + + result = memory.Copy(&stack_memory[0], size); + } else { + result = memory.Copy(reinterpret_cast(start_addr), size); + } + } + + stack_location->start_of_memory_range = start_addr; + stack_location->memory = memory.location(); + + return result; +} + +bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return WriteStackARM(state, stack_location); +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + return WriteStackPPC(state, stack_location); + case CPU_TYPE_POWERPC64: + return WriteStackPPC64(state, stack_location); +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + return WriteStackX86(state, stack_location); + case CPU_TYPE_X86_64: + return WriteStackX86_64(state, stack_location); +#endif + default: + return false; + } +} + +bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return WriteContextARM(state, register_location); +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + return WriteContextPPC(state, register_location); + case CPU_TYPE_POWERPC64: + return WriteContextPPC64(state, register_location); +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + return WriteContextX86(state, register_location); + case CPU_TYPE_X86_64: + return WriteContextX86_64(state, register_location); +#endif + default: + return false; + } +} + +u_int64_t MinidumpGenerator::CurrentPCForStack( + breakpad_thread_state_data_t state) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return CurrentPCForStackARM(state); +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + return CurrentPCForStackPPC(state); + case CPU_TYPE_POWERPC64: + return CurrentPCForStackPPC64(state); +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + return CurrentPCForStackX86(state); + case CPU_TYPE_X86_64: + return CurrentPCForStackX86_64(state); +#endif + default: + assert("Unknown CPU type!"); + return 0; + } +} + +#ifdef HAS_ARM_SUPPORT +bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + arm_thread_state_t *machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +u_int64_t +MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) { + arm_thread_state_t *machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, pc); +} + +bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) +{ + TypedMDRVA context(&writer_); + arm_thread_state_t *machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextARM *context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_ARM_FULL; + +#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a]) + + context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp); + context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr); + context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc); + context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); +#undef AddReg +#undef AddGPR + + return true; +} +#endif + +#ifdef HAS_PCC_SUPPORT +bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + ppc_thread_state_t *machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + ppc_thread_state64_t *machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +u_int64_t +MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) { + ppc_thread_state_t *machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, srr0); +} + +u_int64_t +MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) { + ppc_thread_state64_t *machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, srr0); +} + +bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) +{ + TypedMDRVA context(&writer_); + ppc_thread_state_t *machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextPPC *context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_PPC_BASE; + +#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) +#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a) + + AddReg(srr0); + AddReg(cr); + AddReg(xer); + AddReg(ctr); + AddReg(lr); + AddReg(vrsave); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); + AddGPR(13); + AddGPR(14); + AddGPR(15); + AddGPR(16); + AddGPR(17); + AddGPR(18); + AddGPR(19); + AddGPR(20); + AddGPR(21); + AddGPR(22); + AddGPR(23); + AddGPR(24); + AddGPR(25); + AddGPR(26); + AddGPR(27); + AddGPR(28); + AddGPR(29); + AddGPR(30); + AddGPR(31); + AddReg(mq); +#undef AddReg +#undef AddGPR + + return true; +} + +bool MinidumpGenerator::WriteContextPPC64( + breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) { + TypedMDRVA context(&writer_); + ppc_thread_state64_t *machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextPPC64 *context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_PPC_BASE; + +#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) +#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a) + + AddReg(srr0); + AddReg(cr); + AddReg(xer); + AddReg(ctr); + AddReg(lr); + AddReg(vrsave); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); + AddGPR(13); + AddGPR(14); + AddGPR(15); + AddGPR(16); + AddGPR(17); + AddGPR(18); + AddGPR(19); + AddGPR(20); + AddGPR(21); + AddGPR(22); + AddGPR(23); + AddGPR(24); + AddGPR(25); + AddGPR(26); + AddGPR(27); + AddGPR(28); + AddGPR(29); + AddGPR(30); + AddGPR(31); +#undef AddReg +#undef AddGPR + + return true; +} + +#endif + +#ifdef HAS_X86_SUPPORT +bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + i386_thread_state_t *machine_state = + reinterpret_cast(state); + + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location) { + x86_thread_state64_t *machine_state = + reinterpret_cast(state); + + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +u_int64_t +MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) { + i386_thread_state_t *machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, eip); +} + +u_int64_t +MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) { + x86_thread_state64_t *machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, rip); +} + +bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) +{ + TypedMDRVA context(&writer_); + i386_thread_state_t *machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextX86 *context_ptr = context.get(); + +#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) + + context_ptr->context_flags = MD_CONTEXT_X86; + AddReg(eax); + AddReg(ebx); + AddReg(ecx); + AddReg(edx); + AddReg(esi); + AddReg(edi); + AddReg(ebp); + AddReg(esp); + + AddReg(cs); + AddReg(ds); + AddReg(ss); + AddReg(es); + AddReg(fs); + AddReg(gs); + AddReg(eflags); + + AddReg(eip); +#undef AddReg + + return true; +} + +bool MinidumpGenerator::WriteContextX86_64( + breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location) { + TypedMDRVA context(&writer_); + x86_thread_state64_t *machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextAMD64 *context_ptr = context.get(); + +#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) + + context_ptr->context_flags = MD_CONTEXT_AMD64; + AddReg(rax); + AddReg(rbx); + AddReg(rcx); + AddReg(rdx); + AddReg(rdi); + AddReg(rsi); + AddReg(rbp); + AddReg(rsp); + AddReg(r8); + AddReg(r9); + AddReg(r10); + AddReg(r11); + AddReg(r12); + AddReg(r13); + AddReg(r14); + AddReg(r15); + AddReg(rip); + // according to AMD's software developer guide, bits above 18 are + // not used in the flags register. Since the minidump format + // specifies 32 bits for the flags register, we can truncate safely + // with no loss. + context_ptr->eflags = static_cast(REGISTER_FROM_THREADSTATE(machine_state, rflags)); + AddReg(cs); + AddReg(fs); + AddReg(gs); +#undef AddReg + + return true; +} +#endif + +bool MinidumpGenerator::GetThreadState(thread_act_t target_thread, + thread_state_t state, + mach_msg_type_number_t *count) { + thread_state_flavor_t flavor; + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + flavor = ARM_THREAD_STATE; + break; +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + flavor = PPC_THREAD_STATE; + break; + case CPU_TYPE_POWERPC64: + flavor = PPC_THREAD_STATE64; + break; +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + flavor = i386_THREAD_STATE; + break; + case CPU_TYPE_X86_64: + flavor = x86_THREAD_STATE64; + break; +#endif + default: + return false; + } + return thread_get_state(target_thread, flavor, + state, count) == KERN_SUCCESS; +} + +bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id, + MDRawThread *thread) { + breakpad_thread_state_data_t state; + mach_msg_type_number_t state_count + = static_cast(sizeof(state)); + + if (GetThreadState(thread_id, state, &state_count)) { + if (!WriteStack(state, &thread->stack)) + return false; + + memory_blocks_.push_back(thread->stack); + + if (!WriteContext(state, &thread->thread_context)) + return false; + + thread->thread_id = thread_id; + } else { + return false; + } + + return true; +} + +bool MinidumpGenerator::WriteThreadListStream( + MDRawDirectory *thread_list_stream) { + TypedMDRVA list(&writer_); + thread_act_port_array_t threads_for_task; + mach_msg_type_number_t thread_count; + int non_generator_thread_count; + + if (task_threads(crashing_task_, &threads_for_task, &thread_count)) + return false; + + // Don't include the generator thread + if (handler_thread_ != MACH_PORT_NULL) + non_generator_thread_count = thread_count - 1; + else + non_generator_thread_count = thread_count; + if (!list.AllocateObjectAndArray(non_generator_thread_count, + sizeof(MDRawThread))) + return false; + + thread_list_stream->stream_type = MD_THREAD_LIST_STREAM; + thread_list_stream->location = list.location(); + + list.get()->number_of_threads = non_generator_thread_count; + + MDRawThread thread; + int thread_idx = 0; + + for (unsigned int i = 0; i < thread_count; ++i) { + memset(&thread, 0, sizeof(MDRawThread)); + + if (threads_for_task[i] != handler_thread_) { + if (!WriteThreadStream(threads_for_task[i], &thread)) + return false; + + list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread)); + } + } + + return true; +} + +bool MinidumpGenerator::WriteMemoryListStream( + MDRawDirectory *memory_list_stream) { + TypedMDRVA list(&writer_); + + // If the dump has an exception, include some memory around the + // instruction pointer. + const size_t kIPMemorySize = 256; // bytes + bool have_ip_memory = false; + MDMemoryDescriptor ip_memory_d; + if (exception_thread_ && exception_type_) { + breakpad_thread_state_data_t state; + mach_msg_type_number_t stateCount + = static_cast(sizeof(state)); + + if (thread_get_state(exception_thread_, + BREAKPAD_MACHINE_THREAD_STATE, + state, + &stateCount) == KERN_SUCCESS) { + u_int64_t ip = CurrentPCForStack(state); + // Bound it to the upper and lower bounds of the region + // it's contained within. If it's not in a known memory region, + // don't bother trying to write it. + mach_vm_address_t addr = ip; + mach_vm_size_t size; + natural_t nesting_level = 0; + vm_region_submap_info_64 info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + + kern_return_t ret = + mach_vm_region_recurse(crashing_task_, + &addr, + &size, + &nesting_level, + (vm_region_recurse_info_t)&info, + &info_count); + if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) { + // Try to get 128 bytes before and after the IP, but + // settle for whatever's available. + ip_memory_d.start_of_memory_range = + std::max(uintptr_t(addr), + uintptr_t(ip - (kIPMemorySize / 2))); + uintptr_t end_of_range = + std::min(uintptr_t(ip + (kIPMemorySize / 2)), + uintptr_t(addr + size)); + ip_memory_d.memory.data_size = + end_of_range - ip_memory_d.start_of_memory_range; + have_ip_memory = true; + // This needs to get appended to the list even though + // the memory bytes aren't filled in yet so the entire + // list can be written first. The memory bytes will get filled + // in after the memory list is written. + memory_blocks_.push_back(ip_memory_d); + } + } + } + + // Now fill in the memory list and write it. + unsigned memory_count = memory_blocks_.size(); + if (!list.AllocateObjectAndArray(memory_count, + sizeof(MDMemoryDescriptor))) + return false; + + memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM; + memory_list_stream->location = list.location(); + + list.get()->number_of_memory_ranges = memory_count; + + unsigned int i; + for (i = 0; i < memory_count; ++i) { + list.CopyIndexAfterObject(i, &memory_blocks_[i], + sizeof(MDMemoryDescriptor)); + } + + if (have_ip_memory) { + // Now read the memory around the instruction pointer. + UntypedMDRVA ip_memory(&writer_); + if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) + return false; + + if (dynamic_images_) { + // Out-of-process. + vector memory; + if (ReadTaskMemory(crashing_task_, + ip_memory_d.start_of_memory_range, + ip_memory_d.memory.data_size, + memory) != KERN_SUCCESS) { + return false; + } + + ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size); + } else { + // In-process, just copy from local memory. + ip_memory.Copy( + reinterpret_cast(ip_memory_d.start_of_memory_range), + ip_memory_d.memory.data_size); + } + + ip_memory_d.memory = ip_memory.location(); + // Write this again now that the data location is filled in. + list.CopyIndexAfterObject(i - 1, &ip_memory_d, + sizeof(MDMemoryDescriptor)); + } + + return true; +} + +bool +MinidumpGenerator::WriteExceptionStream(MDRawDirectory *exception_stream) { + TypedMDRVA exception(&writer_); + + if (!exception.Allocate()) + return false; + + exception_stream->stream_type = MD_EXCEPTION_STREAM; + exception_stream->location = exception.location(); + MDRawExceptionStream *exception_ptr = exception.get(); + exception_ptr->thread_id = exception_thread_; + + // This naming is confusing, but it is the proper translation from + // mach naming to minidump naming. + exception_ptr->exception_record.exception_code = exception_type_; + exception_ptr->exception_record.exception_flags = exception_code_; + + breakpad_thread_state_data_t state; + mach_msg_type_number_t state_count + = static_cast(sizeof(state)); + + if (!GetThreadState(exception_thread_, state, &state_count)) + return false; + + if (!WriteContext(state, &exception_ptr->thread_context)) + return false; + + if (exception_type_ == EXC_BAD_ACCESS) + exception_ptr->exception_record.exception_address = exception_subcode_; + else + exception_ptr->exception_record.exception_address = CurrentPCForStack(state); + + return true; +} + +bool MinidumpGenerator::WriteSystemInfoStream( + MDRawDirectory *system_info_stream) { + TypedMDRVA info(&writer_); + + if (!info.Allocate()) + return false; + + system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM; + system_info_stream->location = info.location(); + + // CPU Information + uint32_t number_of_processors; + size_t len = sizeof(number_of_processors); + sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0); + MDRawSystemInfo *info_ptr = info.get(); + + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM; + break; +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC; + break; +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + if (cpu_type_ == CPU_TYPE_I386) + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86; + else + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64; +#ifdef __i386__ + // ebx is used for PIC code, so we need + // to preserve it. +#define cpuid(op,eax,ebx,ecx,edx) \ + asm ("pushl %%ebx \n\t" \ + "cpuid \n\t" \ + "movl %%ebx,%1 \n\t" \ + "popl %%ebx" \ + : "=a" (eax), \ + "=g" (ebx), \ + "=c" (ecx), \ + "=d" (edx) \ + : "0" (op)) +#elif defined(__x86_64__) + +#define cpuid(op,eax,ebx,ecx,edx) \ + asm ("cpuid \n\t" \ + : "=a" (eax), \ + "=b" (ebx), \ + "=c" (ecx), \ + "=d" (edx) \ + : "0" (op)) +#endif + +#if defined(__i386__) || defined(__x86_64__) + int unused, unused2; + // get vendor id + cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0], + info_ptr->cpu.x86_cpu_info.vendor_id[2], + info_ptr->cpu.x86_cpu_info.vendor_id[1]); + // get version and feature info + cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, + info_ptr->cpu.x86_cpu_info.feature_information); + + // family + info_ptr->processor_level = + (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; + // 0xMMSS (Model, Stepping) + info_ptr->processor_revision = + (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | + ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); + + // decode extended model info + if (info_ptr->processor_level == 0xF || + info_ptr->processor_level == 0x6) { + info_ptr->processor_revision |= + ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); + } + + // decode extended family info + if (info_ptr->processor_level == 0xF) { + info_ptr->processor_level += + ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); + } + +#endif // __i386__ || __x86_64_ + break; +#endif // HAS_X86_SUPPORT + default: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; + break; + } + + info_ptr->number_of_processors = number_of_processors; +#if TARGET_OS_IPHONE + info_ptr->platform_id = MD_OS_IOS; +#else + info_ptr->platform_id = MD_OS_MAC_OS_X; +#endif // TARGET_OS_IPHONE + + MDLocationDescriptor build_string_loc; + + if (!writer_.WriteString(build_string_, 0, + &build_string_loc)) + return false; + + info_ptr->csd_version_rva = build_string_loc.rva; + info_ptr->major_version = os_major_version_; + info_ptr->minor_version = os_minor_version_; + info_ptr->build_number = os_build_number_; + + return true; +} + +bool MinidumpGenerator::WriteModuleStream(unsigned int index, + MDRawModule *module) { + if (dynamic_images_) { + // we're in a different process than the crashed process + DynamicImage *image = dynamic_images_->GetImage(index); + + if (!image) + return false; + + memset(module, 0, sizeof(MDRawModule)); + + MDLocationDescriptor string_location; + + string name = image->GetFilePath(); + if (!writer_.WriteString(name.c_str(), 0, &string_location)) + return false; + + module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide(); + module->size_of_image = static_cast(image->GetVMSize()); + module->module_name_rva = string_location.rva; + + // We'll skip the executable module, because they don't have + // LC_ID_DYLIB load commands, and the crash processing server gets + // version information from the Plist file, anyway. + if (index != (uint32_t)FindExecutableModule()) { + module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE; + module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION; + // Convert MAC dylib version format, which is a 32 bit number, to the + // format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits> + // so it fits nicely into the windows version with some massaging + // The mapping is: + // 1) upper 16 bits of MAC version go to lower 16 bits of product HI + // 2) Next most significant 8 bits go to upper 16 bits of product LO + // 3) Least significant 8 bits go to lower 16 bits of product LO + uint32_t modVersion = image->GetVersion(); + module->version_info.file_version_hi = 0; + module->version_info.file_version_hi = modVersion >> 16; + module->version_info.file_version_lo |= (modVersion & 0xff00) << 8; + module->version_info.file_version_lo |= (modVersion & 0xff); + } + + if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) { + return false; + } + } else { + // Getting module info in the crashed process + const breakpad_mach_header *header; + header = (breakpad_mach_header*)_dyld_get_image_header(index); + if (!header) + return false; + +#ifdef __LP64__ + assert(header->magic == MH_MAGIC_64); + + if(header->magic != MH_MAGIC_64) + return false; +#else + assert(header->magic == MH_MAGIC); + + if(header->magic != MH_MAGIC) + return false; +#endif + + int cpu_type = header->cputype; + unsigned long slide = _dyld_get_image_vmaddr_slide(index); + const char* name = _dyld_get_image_name(index); + const struct load_command *cmd = + reinterpret_cast(header + 1); + + memset(module, 0, sizeof(MDRawModule)); + + for (unsigned int i = 0; cmd && (i < header->ncmds); i++) { + if (cmd->cmd == LC_SEGMENT_ARCH) { + + const breakpad_mach_segment_command *seg = + reinterpret_cast(cmd); + + if (!strcmp(seg->segname, "__TEXT")) { + MDLocationDescriptor string_location; + + if (!writer_.WriteString(name, 0, &string_location)) + return false; + + module->base_of_image = seg->vmaddr + slide; + module->size_of_image = static_cast(seg->vmsize); + module->module_name_rva = string_location.rva; + + bool in_memory = false; +#if TARGET_OS_IPHONE + in_memory = true; +#endif + if (!WriteCVRecord(module, cpu_type, name, in_memory)) + return false; + + return true; + } + } + + cmd = reinterpret_cast((char *)cmd + cmd->cmdsize); + } + } + + return true; +} + +int MinidumpGenerator::FindExecutableModule() { + if (dynamic_images_) { + int index = dynamic_images_->GetExecutableImageIndex(); + + if (index >= 0) { + return index; + } + } else { + int image_count = _dyld_image_count(); + const struct mach_header *header; + + for (int index = 0; index < image_count; ++index) { + header = _dyld_get_image_header(index); + + if (header->filetype == MH_EXECUTE) + return index; + } + } + + // failed - just use the first image + return 0; +} + +bool MinidumpGenerator::WriteCVRecord(MDRawModule *module, int cpu_type, + const char *module_path, bool in_memory) { + TypedMDRVA cv(&writer_); + + // Only return the last path component of the full module path + const char *module_name = strrchr(module_path, '/'); + + // Increment past the slash + if (module_name) + ++module_name; + else + module_name = ""; + + size_t module_name_length = strlen(module_name); + + if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) + return false; + + if (!cv.CopyIndexAfterObject(0, module_name, module_name_length)) + return false; + + module->cv_record = cv.location(); + MDCVInfoPDB70 *cv_ptr = cv.get(); + cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; + cv_ptr->age = 0; + + // Get the module identifier + unsigned char identifier[16]; + bool result = false; + if (in_memory) { + MacFileUtilities::MachoID macho(module_path, + reinterpret_cast(module->base_of_image), + static_cast(module->size_of_image)); + result = macho.UUIDCommand(cpu_type, identifier); + if (!result) + result = macho.MD5(cpu_type, identifier); + } + + if (!result) { + FileID file_id(module_path); + result = file_id.MachoIdentifier(cpu_type, identifier); + } + + if (result) { + cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | + (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | + (uint32_t)identifier[3]; + cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; + cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; + cv_ptr->signature.data4[0] = identifier[8]; + cv_ptr->signature.data4[1] = identifier[9]; + cv_ptr->signature.data4[2] = identifier[10]; + cv_ptr->signature.data4[3] = identifier[11]; + cv_ptr->signature.data4[4] = identifier[12]; + cv_ptr->signature.data4[5] = identifier[13]; + cv_ptr->signature.data4[6] = identifier[14]; + cv_ptr->signature.data4[7] = identifier[15]; + } + + return true; +} + +bool MinidumpGenerator::WriteModuleListStream( + MDRawDirectory *module_list_stream) { + TypedMDRVA list(&writer_); + + size_t image_count = dynamic_images_ ? + static_cast(dynamic_images_->GetImageCount()) : + _dyld_image_count(); + + if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE)) + return false; + + module_list_stream->stream_type = MD_MODULE_LIST_STREAM; + module_list_stream->location = list.location(); + list.get()->number_of_modules = image_count; + + // Write out the executable module as the first one + MDRawModule module; + size_t executableIndex = FindExecutableModule(); + + if (!WriteModuleStream(executableIndex, &module)) { + return false; + } + + list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE); + int destinationIndex = 1; // Write all other modules after this one + + for (size_t i = 0; i < image_count; ++i) { + if (i != executableIndex) { + if (!WriteModuleStream(i, &module)) { + return false; + } + + list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE); + } + } + + return true; +} + +bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory *misc_info_stream) { + TypedMDRVA info(&writer_); + + if (!info.Allocate()) + return false; + + misc_info_stream->stream_type = MD_MISC_INFO_STREAM; + misc_info_stream->location = info.location(); + + MDRawMiscInfo *info_ptr = info.get(); + info_ptr->size_of_info = static_cast(sizeof(MDRawMiscInfo)); + info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID | + MD_MISCINFO_FLAGS1_PROCESS_TIMES | + MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO; + + // Process ID + info_ptr->process_id = getpid(); + + // Times + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) != -1) { + // Omit the fractional time since the MDRawMiscInfo only wants seconds + info_ptr->process_user_time = + static_cast(usage.ru_utime.tv_sec); + info_ptr->process_kernel_time = + static_cast(usage.ru_stime.tv_sec); + } + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, + static_cast(info_ptr->process_id) }; + u_int mibsize = static_cast(sizeof(mib) / sizeof(mib[0])); + struct kinfo_proc proc; + size_t size = sizeof(proc); + if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) { + info_ptr->process_create_time = + static_cast(proc.kp_proc.p_starttime.tv_sec); + } + + // Speed + uint64_t speed; + const uint64_t kOneMillion = 1000 * 1000; + size = sizeof(speed); + sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0); + info_ptr->processor_max_mhz = static_cast(speed / kOneMillion); + info_ptr->processor_mhz_limit = static_cast(speed / kOneMillion); + size = sizeof(speed); + sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0); + info_ptr->processor_current_mhz = static_cast(speed / kOneMillion); + + return true; +} + +bool MinidumpGenerator::WriteBreakpadInfoStream( + MDRawDirectory *breakpad_info_stream) { + TypedMDRVA info(&writer_); + + if (!info.Allocate()) + return false; + + breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM; + breakpad_info_stream->location = info.location(); + MDRawBreakpadInfo *info_ptr = info.get(); + + if (exception_thread_ && exception_type_) { + info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + info_ptr->dump_thread_id = handler_thread_; + info_ptr->requesting_thread_id = exception_thread_; + } else { + info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID; + info_ptr->dump_thread_id = handler_thread_; + info_ptr->requesting_thread_id = 0; + } + + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.h b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.h new file mode 100644 index 0000000..80bb116 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_generator.h @@ -0,0 +1,218 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump_generator.h: Create a minidump of the current MacOS process. + +#ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ +#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ + +#include +#include + +#include + +#include "client/minidump_file_writer.h" +#include "common/memory.h" +#include "common/mac/macho_utilities.h" +#include "google_breakpad/common/minidump_format.h" + +#include "dynamic_images.h" +#include "mach_vm_compat.h" + +#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) + #define HAS_PPC_SUPPORT +#endif +#if defined(__arm__) + #define HAS_ARM_SUPPORT +#elif defined(__i386__) || defined(__x86_64__) + #define HAS_X86_SUPPORT +#endif + +namespace google_breakpad { + +using std::string; + +// Use the REGISTER_FROM_THREADSTATE to access a register name from the +// breakpad_thread_state_t structure. +#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM +// In The 10.5 SDK Headers Apple prepended __ to the variable names in the +// i386_thread_state_t structure. There's no good way to tell what version of +// the SDK we're compiling against so we just toggle on the same preprocessor +// symbol Apple's headers use. +#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b) +#else +#define REGISTER_FROM_THREADSTATE(a, b) (a->b) +#endif + +// Creates a minidump file of the current process. If there is exception data, +// use SetExceptionInformation() to add this to the minidump. The minidump +// file is generated by the Write() function. +// Usage: +// MinidumpGenerator minidump(); +// minidump.Write("/tmp/minidump"); +// +class MinidumpGenerator { + public: + MinidumpGenerator(); + MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread); + + virtual ~MinidumpGenerator(); + + // Return /.dmp + // Sets |unique_name| (if requested) to the unique name for the minidump + static string UniqueNameInDirectory(const string &dir, string *unique_name); + + // Write out the minidump into |path| + // All of the components of |path| must exist and be writable + // Return true if successful, false otherwise + bool Write(const char *path); + + // Specify some exception information, if applicable + void SetExceptionInformation(int type, int code, int subcode, + mach_port_t thread_name) { + exception_type_ = type; + exception_code_ = code; + exception_subcode_ = subcode; + exception_thread_ = thread_name; + } + + // Gather system information. This should be call at least once before using + // the MinidumpGenerator class. + static void GatherSystemInformation(); + + protected: + // Overridable Stream writers + virtual bool WriteExceptionStream(MDRawDirectory *exception_stream); + + // Overridable Helper + virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread); + + private: + typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *); + + // Stream writers + bool WriteThreadListStream(MDRawDirectory *thread_list_stream); + bool WriteMemoryListStream(MDRawDirectory *memory_list_stream); + bool WriteSystemInfoStream(MDRawDirectory *system_info_stream); + bool WriteModuleListStream(MDRawDirectory *module_list_stream); + bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream); + bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream); + + // Helpers + u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state); + bool GetThreadState(thread_act_t target_thread, thread_state_t state, + mach_msg_type_number_t *count); + bool WriteStackFromStartAddress(mach_vm_address_t start_addr, + MDMemoryDescriptor *stack_location); + bool WriteStack(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContext(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + bool WriteCVRecord(MDRawModule *module, int cpu_type, + const char *module_path, bool in_memory); + bool WriteModuleStream(unsigned int index, MDRawModule *module); + size_t CalculateStackSize(mach_vm_address_t start_addr); + int FindExecutableModule(); + + // Per-CPU implementations of these methods +#ifdef HAS_ARM_SUPPORT + bool WriteStackARM(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContextARM(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + u_int64_t CurrentPCForStackARM(breakpad_thread_state_data_t state); +#endif +#ifdef HAS_PPC_SUPPORT + bool WriteStackPPC(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContextPPC(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + u_int64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state); + bool WriteStackPPC64(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContextPPC64(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state); +#endif +#ifdef HAS_X86_SUPPORT + bool WriteStackX86(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContextX86(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + u_int64_t CurrentPCForStackX86(breakpad_thread_state_data_t state); + bool WriteStackX86_64(breakpad_thread_state_data_t state, + MDMemoryDescriptor *stack_location); + bool WriteContextX86_64(breakpad_thread_state_data_t state, + MDLocationDescriptor *register_location); + u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state); +#endif + + // disallow copy ctor and operator= + explicit MinidumpGenerator(const MinidumpGenerator &); + void operator=(const MinidumpGenerator &); + + protected: + // Use this writer to put the data to disk + MinidumpFileWriter writer_; + + private: + // Exception information + int exception_type_; + int exception_code_; + int exception_subcode_; + mach_port_t exception_thread_; + mach_port_t crashing_task_; + mach_port_t handler_thread_; + + // CPU type of the task being dumped. + cpu_type_t cpu_type_; + + // System information + static char build_string_[16]; + static int os_major_version_; + static int os_minor_version_; + static int os_build_number_; + + // Information about dynamically loaded code + DynamicImages *dynamic_images_; + + // PageAllocator makes it possible to allocate memory + // directly from the system, even while handling an exception. + mutable PageAllocator allocator_; + + protected: + // Blocks of memory written to the dump. These are all currently + // written while writing the thread list stream, but saved here + // so a memory list stream can be written afterwards. + wasteful_vector memory_blocks_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2a597d5 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_test.xcodeproj/project.pbxproj @@ -0,0 +1,841 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; }; + 8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; }; + 8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; }; + 8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; }; + 8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; }; + 9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; }; + 9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; }; + 9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */; }; + 9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */; }; + 9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; }; + 9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; }; + 9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; }; + 9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; }; + 9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */; }; + 9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */; }; + 9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; }; + 9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; }; + 9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; }; + 9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0A0B0133520055103E /* exception_handler.h */; }; + 9BD82C110B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; }; + 9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0C0B0133520055103E /* minidump_generator.h */; }; + 9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; }; + 9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; }; + 9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C240B01344C0055103E /* minidump_file_writer.h */; }; + 9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; }; + 9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; }; + 9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C2C0B01345E0055103E /* string_utilities.h */; }; + D2F651000BEF947200920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; }; + D2F651010BEF947200920385 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FB0BEF947200920385 /* file_id.h */; }; + D2F651020BEF947200920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; }; + D2F651030BEF947200920385 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FD0BEF947200920385 /* macho_id.h */; }; + D2F651040BEF947200920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; }; + D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FF0BEF947200920385 /* macho_utilities.h */; }; + D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F651080BEF949A00920385 /* dynamic_images.h */; }; + D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; }; + D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F6510D0BEF94EB00920385 /* macho_walker.h */; }; + D2F651110BEF951700920385 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; }; + D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF590B267D5F008DE8C7 /* string_conversion.h */; }; + D2F651150BEF953000920385 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; }; + D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */; }; + D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + D2F6511D0BEF973500920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; }; + D2F6511E0BEF973600920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; }; + D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; }; + D2F651210BEF975400920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; }; + F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; }; + F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; }; + F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; }; + F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; }; + F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; }; + F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.mm */; }; + F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; }; + F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; }; + F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; }; + F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; }; + F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; }; + F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; }; + F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; }; + F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; }; + F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F690486A84900D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */, + 9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */, + 9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */, + 9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */, + 9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */, + D2F651010BEF947200920385 /* file_id.h in CopyFiles */, + D2F651030BEF947200920385 /* macho_id.h in CopyFiles */, + D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */, + D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */, + D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */, + D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */, + D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; }; + 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; }; + 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; }; + 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; }; + 8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; }; + 8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; }; + 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; }; + 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; }; + 9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; }; + 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = ""; }; + 9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler_test.cc; sourceTree = SOURCE_ROOT; }; + 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator_test.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C090B0133520055103E /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C0A0B0133520055103E /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = SOURCE_ROOT; }; + 9BD82C0B0B0133520055103E /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C0C0B0133520055103E /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = SOURCE_ROOT; }; + 9BD82C230B01344C0055103E /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C240B01344C0055103E /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../../minidump_file_writer.h; sourceTree = SOURCE_ROOT; }; + 9BD82C2B0B01345E0055103E /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C2C0B01345E0055103E /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; }; + 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; }; + D2F650FA0BEF947200920385 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + D2F650FB0BEF947200920385 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + D2F650FC0BEF947200920385 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + D2F650FD0BEF947200920385 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + D2F650FE0BEF947200920385 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; }; + D2F650FF0BEF947200920385 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; }; + D2F651070BEF949A00920385 /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = ""; }; + D2F651080BEF949A00920385 /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = ""; }; + D2F6510C0BEF94EB00920385 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + D2F6510D0BEF94EB00920385 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; + F917C4F70E03265A00F86017 /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = breakpad_exc_server.c; sourceTree = ""; }; + F917C4F80E03265A00F86017 /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_exc_server.h; sourceTree = ""; }; + F93A88750E8B4C700026AF89 /* octestcases.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = octestcases.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "obj-cTestCases-Info.plist"; sourceTree = ""; }; + F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = ""; }; + F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = ""; }; + F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; }; + F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; }; + F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; + F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; + F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; + F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; + F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; }; + F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = ""; }; + F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = ""; }; + F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = ""; }; + F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = ""; }; + F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests32-Info.plist"; sourceTree = ""; }; + F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests64.cptest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests32.cptest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests64-Info.plist"; sourceTree = ""; }; + F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicImagesTests.h; sourceTree = ""; }; + F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicImagesTests.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F660486A84900D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */, + 8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B7CA84C0B1297F200CD3A1D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9BD82A990B00267E0055103E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */, + 8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93A88720E8B4C700026AF89 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */, + 8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */, + 8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE19C00DB04A9500C98454 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */, + 8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE5B300DBFDBA300505983 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */, + F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */, + 8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */, + 8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = { + isa = PBXGroup; + children = ( + 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */, + 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */, + 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */, + F9721FA80E8B0E4800D7E813 /* md5.cc */, + F9721F760E8B0DC700D7E813 /* bytereader.cc */, + F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */, + F9721F780E8B0DC700D7E813 /* functioninfo.cc */, + F9721F390E8B0D0D00D7E813 /* dump_syms.mm */, + F9721F380E8B0CFC00D7E813 /* dump_syms.h */, + F917C4F70E03265A00F86017 /* breakpad_exc_server.c */, + F917C4F80E03265A00F86017 /* breakpad_exc_server.h */, + F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */, + F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */, + D2F6510C0BEF94EB00920385 /* macho_walker.cc */, + D2F6510D0BEF94EB00920385 /* macho_walker.h */, + D2F651070BEF949A00920385 /* dynamic_images.cc */, + D2F651080BEF949A00920385 /* dynamic_images.h */, + D2F650FA0BEF947200920385 /* file_id.cc */, + D2F650FB0BEF947200920385 /* file_id.h */, + D2F650FC0BEF947200920385 /* macho_id.cc */, + D2F650FD0BEF947200920385 /* macho_id.h */, + D2F650FE0BEF947200920385 /* macho_utilities.cc */, + D2F650FF0BEF947200920385 /* macho_utilities.h */, + F9C5A41F0DB82DB000209C76 /* testcases */, + 9BD82C040B0133420055103E /* Breakpad */, + 08FB7795FE84155DC02AAC07 /* Source */, + 9B37CEEA0AF98EB600FA4BD4 /* Frameworks */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */, + F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */, + F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */, + ); + name = MinidumpWriter; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */, + 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */, + 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76F6C0486A84900D96B5E /* generator_test */, + 9BD82A9B0B00267E0055103E /* handler_test */, + 9B7CA84E0B1297F200CD3A1D /* unit_test */, + F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */, + F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */, + F93A88750E8B4C700026AF89 /* octestcases.octest */, + ); + name = Products; + sourceTree = ""; + }; + 9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */, + 8BFC815411FF9B7F002CB4DC /* Carbon.framework */, + F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */, + F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */, + 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */, + 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9BD82C040B0133420055103E /* Breakpad */ = { + isa = PBXGroup; + children = ( + 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */, + 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */, + 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */, + 9B35FF590B267D5F008DE8C7 /* string_conversion.h */, + 9BD82C090B0133520055103E /* exception_handler.cc */, + 9BD82C0A0B0133520055103E /* exception_handler.h */, + 9BD82C0B0B0133520055103E /* minidump_generator.cc */, + 9BD82C0C0B0133520055103E /* minidump_generator.h */, + 9BD82C230B01344C0055103E /* minidump_file_writer.cc */, + 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */, + 9BD82C240B01344C0055103E /* minidump_file_writer.h */, + 9BD82C2B0B01345E0055103E /* string_utilities.cc */, + 9BD82C2C0B01345E0055103E /* string_utilities.h */, + ); + name = Breakpad; + sourceTree = ""; + }; + F9C5A41F0DB82DB000209C76 /* testcases */ = { + isa = PBXGroup; + children = ( + F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */, + F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */, + F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */, + F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */, + F9721F300E8B07E800D7E813 /* dwarftests.h */, + F9721F310E8B07E800D7E813 /* dwarftests.mm */, + ); + path = testcases; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F620486A84900D96B5E /* generator_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */; + buildPhases = ( + 8DD76F640486A84900D96B5E /* Sources */, + 8DD76F660486A84900D96B5E /* Frameworks */, + 8DD76F690486A84900D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = generator_test; + productInstallPath = "$(HOME)/bin"; + productName = MinidumpWriter; + productReference = 8DD76F6C0486A84900D96B5E /* generator_test */; + productType = "com.apple.product-type.tool"; + }; + 9B7CA84D0B1297F200CD3A1D /* unit_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */; + buildPhases = ( + 9B7CA84B0B1297F200CD3A1D /* Sources */, + 9B7CA84C0B1297F200CD3A1D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = unit_test; + productName = "filewriter unit test"; + productReference = 9B7CA84E0B1297F200CD3A1D /* unit_test */; + productType = "com.apple.product-type.tool"; + }; + 9BD82A9A0B00267E0055103E /* handler_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */; + buildPhases = ( + 9BD82A980B00267E0055103E /* Sources */, + 9BD82A990B00267E0055103E /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = handler_test; + productName = ExceptionTester; + productReference = 9BD82A9B0B00267E0055103E /* handler_test */; + productType = "com.apple.product-type.tool"; + }; + F93A88740E8B4C700026AF89 /* obj-c_TestCases */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */; + buildPhases = ( + F93A88700E8B4C700026AF89 /* Resources */, + F93A88710E8B4C700026AF89 /* Sources */, + F93A88720E8B4C700026AF89 /* Frameworks */, + F93A88730E8B4C700026AF89 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "obj-c_TestCases"; + productName = octestcases; + productReference = F93A88750E8B4C700026AF89 /* octestcases.octest */; + productType = "com.apple.product-type.bundle"; + }; + F9AE19C20DB04A9500C98454 /* minidump_tests64 */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */; + buildPhases = ( + F9AE19BE0DB04A9500C98454 /* Resources */, + F9AE19BF0DB04A9500C98454 /* Sources */, + F9AE19C00DB04A9500C98454 /* Frameworks */, + F9AE19C10DB04A9500C98454 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_tests64; + productName = minidump_tests; + productReference = F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */; + productType = "com.apple.product-type.bundle"; + }; + F9AE5B320DBFDBA300505983 /* minidump_tests32 */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */; + buildPhases = ( + F9AE5B2E0DBFDBA300505983 /* Resources */, + F9AE5B2F0DBFDBA300505983 /* Sources */, + F9AE5B300DBFDBA300505983 /* Frameworks */, + F9AE5B310DBFDBA300505983 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_tests32; + productName = Untitled; + productReference = F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DD76F620486A84900D96B5E /* generator_test */, + 9BD82A9A0B00267E0055103E /* handler_test */, + 9B7CA84D0B1297F200CD3A1D /* unit_test */, + F9AE19C20DB04A9500C98454 /* minidump_tests64 */, + F9AE5B320DBFDBA300505983 /* minidump_tests32 */, + F93A88740E8B4C700026AF89 /* obj-c_TestCases */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F93A88700E8B4C700026AF89 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE19BE0DB04A9500C98454 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE5B2E0DBFDBA300505983 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F93A88730E8B4C700026AF89 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; + }; + F9AE19C10DB04A9500C98454 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n# Run gcov on the framework getting tested\nif [ \"${CONFIGURATION}\" = 'Coverage' ];\nthen\n FRAMEWORK_NAME=minidump_tests64\n FRAMEWORK_OBJ_DIR=${OBJROOT}/${PROJECT_NAME}.build/${CONFIGURATION}/${FRAMEWORK_NAME}.build/Objects-normal/${NATIVE_ARCH_ACTUAL}\n mkdir -p coverage\n pushd coverage\n echo find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n popd\nfi "; + }; + F9AE5B310DBFDBA300505983 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F640486A84900D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */, + 9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */, + 9BD82C110B0133520055103E /* minidump_generator.cc in Sources */, + 9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */, + 9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */, + D2F651000BEF947200920385 /* file_id.cc in Sources */, + D2F651020BEF947200920385 /* macho_id.cc in Sources */, + D2F651040BEF947200920385 /* macho_utilities.cc in Sources */, + D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */, + D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */, + D2F651110BEF951700920385 /* string_conversion.cc in Sources */, + D2F651150BEF953000920385 /* convert_UTF.c in Sources */, + 8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B7CA84B0B1297F200CD3A1D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */, + 9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */, + 9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */, + 9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */, + 8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9BD82A980B00267E0055103E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */, + 9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */, + 9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */, + 9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */, + 9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */, + 9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */, + 9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */, + D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */, + D2F6511D0BEF973500920385 /* file_id.cc in Sources */, + D2F6511E0BEF973600920385 /* macho_id.cc in Sources */, + D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */, + D2F651210BEF975400920385 /* macho_walker.cc in Sources */, + 8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93A88710E8B4C700026AF89 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */, + F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */, + F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */, + F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */, + F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */, + F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */, + F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */, + F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */, + F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */, + F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE19BF0DB04A9500C98454 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */, + F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */, + F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */, + F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE5B2F0DBFDBA300505983 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */, + F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */, + 8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB923208733DC60010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Debug; + }; + 1DEB923308733DC60010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Release; + }; + 1DEB923608733DC60010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 1DEB923708733DC60010E9CD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 9B7CA8510B12984300CD3A1D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = unit_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Debug; + }; + 9B7CA8520B12984300CD3A1D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = unit_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Release; + }; + 9BD82AA70B0026BF0055103E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)"; + }; + name = Debug; + }; + 9BD82AA80B0026BF0055103E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)"; + }; + name = Release; + }; + F93A88770E8B4C700026AF89 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "obj-cTestCases-Info.plist"; + PRODUCT_NAME = octestcases; + USER_HEADER_SEARCH_PATHS = "../../../..//**"; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + F93A88780E8B4C700026AF89 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "obj-cTestCases-Info.plist"; + PRODUCT_NAME = octestcases; + USER_HEADER_SEARCH_PATHS = "../../../..//**"; + WRAPPER_EXTENSION = octest; + }; + name = Release; + }; + F9AE19C40DB04A9500C98454 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests64-Info.plist"; + PRODUCT_NAME = minidump_tests64; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Debug; + }; + F9AE19C50DB04A9500C98454 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests64-Info.plist"; + PRODUCT_NAME = minidump_tests64; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Release; + }; + F9AE5B350DBFDBA300505983 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests32-Info.plist"; + PRODUCT_NAME = minidump_tests32; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Debug; + }; + F9AE5B370DBFDBA300505983 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests32-Info.plist"; + PRODUCT_NAME = minidump_tests32; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB923208733DC60010E9CD /* Debug */, + 1DEB923308733DC60010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB923608733DC60010E9CD /* Debug */, + 1DEB923708733DC60010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9B7CA8510B12984300CD3A1D /* Debug */, + 9B7CA8520B12984300CD3A1D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9BD82AA70B0026BF0055103E /* Debug */, + 9BD82AA80B0026BF0055103E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93A88770E8B4C700026AF89 /* Debug */, + F93A88780E8B4C700026AF89 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9AE19C40DB04A9500C98454 /* Debug */, + F9AE19C50DB04A9500C98454 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9AE5B350DBFDBA300505983 /* Debug */, + F9AE5B370DBFDBA300505983 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests32-Info.plist b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests32-Info.plist new file mode 100644 index 0000000..921ebf3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests32-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.google.breakpad.minidump_tests32 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests64-Info.plist b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests64-Info.plist new file mode 100644 index 0000000..acfbd30 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/minidump_tests64-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.google.breakpad.minidump_tests64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + yes + + diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/obj-cTestCases-Info.plist b/src/lib/crashdump/gbreakpad/client/mac/handler/obj-cTestCases-Info.plist new file mode 100644 index 0000000..6501355 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/obj-cTestCases-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.cc new file mode 100644 index 0000000..6142ad1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// ProtectedMemoryAllocator +// +// See the header file for documentation + +#include "protected_memory_allocator.h" +#include + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ProtectedMemoryAllocator::ProtectedMemoryAllocator(vm_size_t pool_size) + : pool_size_(pool_size), + next_alloc_offset_(0), + valid_(false) { + + kern_return_t result = vm_allocate(mach_task_self(), + &base_address_, + pool_size, + TRUE + ); + + valid_ = (result == KERN_SUCCESS); + assert(valid_); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ProtectedMemoryAllocator::~ProtectedMemoryAllocator() { + vm_deallocate(mach_task_self(), + base_address_, + pool_size_ + ); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) { + if (valid_ && next_alloc_offset_ + bytes <= pool_size_) { + char *p = (char*)base_address_ + next_alloc_offset_; + next_alloc_offset_ += bytes; + return p; + } + + return NULL; // ran out of memory in our allocation block +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +kern_return_t ProtectedMemoryAllocator::Protect() { + kern_return_t result = vm_protect(mach_task_self(), + base_address_, + pool_size_, + FALSE, + VM_PROT_READ); + + return result; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +kern_return_t ProtectedMemoryAllocator::Unprotect() { + kern_return_t result = vm_protect(mach_task_self(), + base_address_, + pool_size_, + FALSE, + VM_PROT_READ | VM_PROT_WRITE); + + return result; +} diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.h b/src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.h new file mode 100644 index 0000000..7e188db --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/protected_memory_allocator.h @@ -0,0 +1,85 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// ProtectedMemoryAllocator +// +// A very simple allocator class which allows allocation, but not deallocation. +// The allocations can be made read-only with the Protect() method. +// This class is NOT useful as a general-purpose memory allocation system, +// since it does not allow deallocation. It is useful to use for a group +// of allocations which are created in the same time-frame and destroyed +// in the same time-frame. It is useful for making allocations of memory +// which will not need to change often once initialized. This memory can then +// be protected from memory smashers by calling the Protect() method. + +#ifndef PROTECTED_MEMORY_ALLOCATOR_H__ +#define PROTECTED_MEMORY_ALLOCATOR_H__ + +#include + +// +class ProtectedMemoryAllocator { + public: + ProtectedMemoryAllocator(vm_size_t pool_size); + ~ProtectedMemoryAllocator(); + + // Returns a pointer to an allocation of size n within the pool. + // Fails by returning NULL is no more space is available. + // Please note that the pointers returned from this method should not + // be freed in any way (for example by calling free() on them ). + char * Allocate(vm_size_t n); + + // Returns the base address of the allocation pool. + char * GetBaseAddress() { return (char*)base_address_; } + + // Returns the size of the allocation pool, including allocated + // plus free space. + vm_size_t GetTotalSize() { return pool_size_; } + + // Returns the number of bytes already allocated in the pool. + vm_size_t GetAllocatedSize() { return next_alloc_offset_; } + + // Returns the number of bytes available for allocation. + vm_size_t GetFreeSize() { return pool_size_ - next_alloc_offset_; } + + // Makes the entire allocation pool read-only including, of course, + // all allocations made from the pool. + kern_return_t Protect(); + + // Makes the entire allocation pool read/write. + kern_return_t Unprotect(); + + private: + vm_size_t pool_size_; + vm_address_t base_address_; + vm_size_t next_alloc_offset_; + bool valid_; +}; + +#endif // PROTECTED_MEMORY_ALLOCATOR_H__ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.cc new file mode 100644 index 0000000..0fc7825 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// +// DynamicImagesTests.cpp +// minidump_test +// +// Created by Neal Sidhwaney on 4/17/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#include "client/mac/handler/testcases/DynamicImagesTests.h" +#include "client/mac/handler/dynamic_images.h" + +DynamicImagesTests test2(TEST_INVOCATION(DynamicImagesTests, + ReadTaskMemoryTest)); +DynamicImagesTests test3(TEST_INVOCATION(DynamicImagesTests, + ReadLibrariesFromLocalTaskTest)); + +DynamicImagesTests::DynamicImagesTests(TestInvocation *invocation) + : TestCase(invocation) { +} + +DynamicImagesTests::~DynamicImagesTests() { +} + +void DynamicImagesTests::ReadTaskMemoryTest() { + kern_return_t kr; + + // pick test2 as a symbol we know to be valid to read + // anything will work, really + void *addr = reinterpret_cast(&test2); + std::vector buf(getpagesize()); + + fprintf(stderr, "reading 0x%p\n", addr); + kr = google_breakpad::ReadTaskMemory(mach_task_self(), + (uint64_t)addr, + getpagesize(), + buf); + + CPTAssert(kr == KERN_SUCCESS); + + CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize())); +} + +void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() { + + mach_port_t me = mach_task_self(); + google_breakpad::DynamicImages *d = new google_breakpad::DynamicImages(me); + + fprintf(stderr,"Local task image count: %d\n", d->GetImageCount()); + + CPTAssert(d->GetImageCount() > 0); +} diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.h b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.h new file mode 100644 index 0000000..e1e7999 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/DynamicImagesTests.h @@ -0,0 +1,52 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// +// DynamicImagesTests.h +// minidump_test +// +// Created by Neal Sidhwaney on 4/17/08. +// Copyright 2008 Google Inc. All rights reserved. +// +// + +#ifndef _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ +#define _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ + +#include + +class DynamicImagesTests : public TestCase { + public: + explicit DynamicImagesTests(TestInvocation* invocation); + virtual ~DynamicImagesTests(); + + void ReadTaskMemoryTest(); + void ReadLibrariesFromLocalTaskTest(); +}; + +#endif /* _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ */ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.cc b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.cc new file mode 100644 index 0000000..e7332bf --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// +// breakpad_nlist_test.cc +// minidump_test +// +// Created by Neal Sidhwaney on 4/13/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#include "client/mac/handler/testcases/breakpad_nlist_test.h" +#include +#include "client/mac/handler/breakpad_nlist_64.h" + +BreakpadNlistTest test1(TEST_INVOCATION(BreakpadNlistTest, CompareToNM)); + +BreakpadNlistTest::BreakpadNlistTest(TestInvocation *invocation) + : TestCase(invocation) { +} + + +BreakpadNlistTest::~BreakpadNlistTest() { +} + +void BreakpadNlistTest::CompareToNM() { +#if TARGET_CPU_X86_64 + system("/usr/bin/nm -arch x86_64 /usr/lib/dyld > /tmp/dyld-namelist.txt"); +#elif TARGET_CPU_PPC64 + system("/usr/bin/nm -arch ppc64 /usr/lib/dyld > /tmp/dyld-namelist.txt"); +#endif + + FILE *fd = fopen("/tmp/dyld-namelist.txt", "rt"); + + char oneNMAddr[30]; + char symbolType; + char symbolName[500]; + while (!feof(fd)) { + fscanf(fd, "%s %c %s", oneNMAddr, &symbolType, symbolName); + breakpad_nlist symbolList[2]; + breakpad_nlist &list = symbolList[0]; + + memset(symbolList, 0, sizeof(breakpad_nlist)*2); + const char *symbolNames[2]; + symbolNames[0] = (const char*)symbolName; + symbolNames[1] = "\0"; + breakpad_nlist_64("/usr/lib/dyld", &list, symbolNames); + uint64_t nmAddr = strtol(oneNMAddr, NULL, 16); + if (!IsSymbolMoreThanOnceInDyld(symbolName)) { + CPTAssert(nmAddr == symbolList[0].n_value); + } + } + + fclose(fd); +} + +bool BreakpadNlistTest::IsSymbolMoreThanOnceInDyld(const char *symbolName) { + // These are the symbols that occur more than once when nm dumps + // the symbol table of /usr/lib/dyld. Our nlist program returns + // the first address because it's doing a search so we need to exclude + // these from causing the test to fail + const char *multipleSymbols[] = { + "__Z41__static_initialization_and_destruction_0ii", + "___tcf_0", + "___tcf_1", + "_read_encoded_value_with_base", + "_read_sleb128", + "_read_uleb128", + "\0"}; + + bool found = false; + + for (int i = 0; multipleSymbols[i][0]; i++) { + if (!strcmp(multipleSymbols[i], symbolName)) { + found = true; + break; + } + } + + return found; +} diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.h b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.h new file mode 100644 index 0000000..e93657c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/breakpad_nlist_test.h @@ -0,0 +1,62 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// +// breakpad_nlist_test.h +// minidump_test +// +// Created by Neal Sidhwaney on 4/13/08. +// Copyright 2008 Google Inc. All rights reserved. +// +// + +#ifndef CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__ +#define CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__ + +#include + +class BreakpadNlistTest : public TestCase { + private: + + // nm dumps multiple addresses for the same symbol in + // /usr/lib/dyld. So we track those so we don't report failures + // in mismatches between what our nlist returns and what nm has + // for the duplicate symbols. + bool IsSymbolMoreThanOnceInDyld(const char *symbolName); + + public: + explicit BreakpadNlistTest(TestInvocation* invocation); + virtual ~BreakpadNlistTest(); + + + /* This test case runs nm on /usr/lib/dyld and then compares the + output of every symbol to what our nlist implementation returns */ + void CompareToNM(); +}; + +#endif /* CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__*/ diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.h b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.h new file mode 100644 index 0000000..21ff7a4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.h @@ -0,0 +1,46 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// +// dwarftests.h +// minidump_test +// +// Created by Neal Sidhwaney on 9/24/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#import + + +@interface dwarftests : SenTestCase { + +} + +- (void) testDWARFSymbolFileGeneration; + +@end diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.mm b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.mm new file mode 100644 index 0000000..40c69af --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/dwarftests.mm @@ -0,0 +1,60 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// +// dwarftests.m +// minidump_test +// +// Created by Neal Sidhwaney on 9/24/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#import "dwarftests.h" +#import "dump_syms.h" + +@implementation dwarftests +- (void) testDWARFSymbolFileGeneration { + NSString *inputBreakpadSymbolFile = @"testcases/testdata/dump_syms_i386_breakpad.sym"; + NSString *outputBreakpadSymbolFile = @"/tmp/dump_syms_i386.breakpad"; + + DumpSymbols *dump = [[DumpSymbols alloc] initWithContentsOfFile:@"testcases/testdata/dump_syms_dwarf_data"]; + + STAssertNotNil(dump, @"DumpSymbols is nil"); + [dump setArchitecture:@"i386"]; + [dump writeSymbolFile:outputBreakpadSymbolFile]; + + NSData *d = [[NSData alloc] initWithContentsOfFile:inputBreakpadSymbolFile]; + STAssertNotNil(d, @"Input breakpad symbol file not found"); + + NSData *d1 = [[NSData alloc] initWithContentsOfFile:outputBreakpadSymbolFile]; + STAssertNotNil(d1, @"Output breakpad symbol file not found"); + + STAssertTrue([d isEqualToData:d1], + @"Symbol files were not equal!",nil); +} +@end diff --git a/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/testdata/dump_syms_dwarf_data b/src/lib/crashdump/gbreakpad/client/mac/handler/testcases/testdata/dump_syms_dwarf_data new file mode 100644 index 0000000000000000000000000000000000000000..5be17aeedcf1bb46e7a5290fbd7d9eee5969caf0 GIT binary patch literal 702795 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
    cIv3Wg*uhd*&oFmA@X+bQu90#b^;@|uEIzX#m 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 k{?`6sx!}hi#-lsDJ^r5DtA=x+y}HLq, std::allocator > >) +4078 15 82 3 +408d 13 83 3 +40a0 1e 85 3 +40be 42 89 3 +4100 20 90 3 +4120 2b 91 3 +414b 1a 92 3 +4165 23 93 3 +4188 46 96 3 +41ce 46 99 3 +4214 33 83 3 +4247 8 102 3 +424f 1 102 3 +FUNC 4250 3ef 0 -[DumpSymbols processDWARFFunctionInfo:] +4250 15 473 3 +4265 25 474 3 +428a 1e 476 3 +42a8 a 480 3 +42b2 3c 481 3 +42ee 3d 483 3 +432b 23 485 3 +434e 26 487 3 +4374 6 489 3 +437a 37 490 3 +43b1 2a 491 3 +43db 17 492 3 +43f2 30 496 3 +4422 3d 497 3 +445f 2e 498 3 +448d 30 502 3 +44bd 64 504 3 +4521 34 507 3 +4555 9d 509 3 +45f2 45 474 3 +4637 8 513 3 +463f 1 513 3 +FUNC 4640 1f5 0 -[DumpSymbols processDWARFLineNumberInfo:] +4640 15 515 3 +4655 25 516 3 +467a 39 520 3 +46b3 26 521 3 +46d9 6 523 3 +46df 37 524 3 +4716 2a 525 3 +4740 17 526 3 +4757 30 529 3 +4787 61 531 3 +47e8 45 516 3 +482d 8 534 3 +4835 1 534 3 +FUNC 4836 10f 0 -[DumpSymbols dealloc] +4836 13 1145 3 +4849 1c 1146 3 +4865 1c 1147 3 +4881 1c 1148 3 +489d 1c 1149 3 +48b9 1c 1150 3 +48d5 1c 1151 3 +48f1 25 1152 3 +4916 29 1154 3 +493f 6 1155 3 +4945 1 1155 3 +FUNC 4946 512 0 -[DumpSymbols loadDWARFSymbolInfo:offset:] +4946 17 402 3 +495d 9 405 3 +4966 10 406 3 +4976 2b 408 3 +49a1 38 409 3 +49d9 3a 410 3 +4a13 2e 411 3 +4a41 31 416 3 +4a72 e 418 3 +4a80 24 420 3 +4aa4 5 422 3 +4aa9 b 424 3 +4ab4 b 425 3 +4abf e 426 3 +4acd 2b 427 3 +4af8 2b 428 3 +4b23 2c 431 3 +4b4f 52 439 3 +4ba1 34 444 3 +4bd5 1a 446 3 +4bef 21 451 3 +4c10 1e 452 3 +4c2e 21 453 3 +4c4f 40 422 3 +4c8f 6 453 3 +4c95 170 422 3 +4e05 43 456 3 +4e48 10 457 3 +FUNC 4e58 4fd 0 -[DumpSymbols generateSectionDictionary:] +4e58 18 663 3 +4e70 10 665 3 +4e80 2e 666 3 +4eae 9 668 3 +4eb7 2b 669 3 +4ee2 7 670 3 +4ee9 2e 672 3 +4f17 d 676 3 +4f24 32 678 3 +4f56 29 680 3 +4f7f a 684 3 +4f89 3c 685 3 +4fc5 31 688 3 +4ff6 5d 689 3 +5053 26 692 3 +5079 21 694 3 +509a c 698 3 +50a6 e 699 3 +50b4 6 700 3 +50ba 9 701 3 +50c3 2e 702 3 +50f1 c 704 3 +50fd 3c 706 3 +5139 66 709 3 +519f 1c 712 3 +51bb fb 714 3 +52b6 6 717 3 +52bc 5 718 3 +52c1 19 704 3 +52da 25 714 3 +52ff 2e 722 3 +532d 9 723 3 +5336 17 698 3 +534d 8 725 3 +5355 1 725 3 +FUNC 5356 24a 0 -[DumpSymbols getSectionMapForArchitecture:] +5356 14 643 3 +536a 43 645 3 +53ad 1a 648 3 +53c7 1c 645 3 +53e3 18 648 3 +53fb 40 650 3 +543b 20 651 3 +545b 17 652 3 +5472 16 651 3 +5488 cb 652 3 +5553 11 654 3 +5564 32 657 3 +5596 a 658 3 +FUNC 55a0 3fe 0 -[DumpSymbols initWithContentsOfFile:] +55a0 14 1056 3 +55b4 3b 1057 3 +55ef 44 1059 3 +5633 17 1060 3 +564a c 1061 3 +5656 1f 1064 3 +5675 2b 1067 3 +56a0 a 1069 3 +56aa 35 1083 3 +56df 2 1087 3 +56e1 1a 1088 3 +56fb 3d 1087 3 +5738 33 1092 3 +576b 6 1094 3 +5771 e 1095 3 +577f 17 1096 3 +5796 c 1097 3 +57a2 1c 1101 3 +57be 1f 1103 3 +57dd 18 1104 3 +57f5 23 1107 3 +5818 25 1109 3 +583d 1c 1107 3 +5859 17 1110 3 +5870 c 1111 3 +587c 2a 1115 3 +58a6 8 1116 3 +58ae a 1118 3 +58b8 9 1119 3 +58c1 d 1122 3 +58ce 29 1124 3 +58f7 20 1126 3 +5917 20 1128 3 +5937 57 1132 3 +598e 9 1136 3 +5997 7 1137 3 +FUNC 599e d74 0 -[DumpSymbols outputSymbolFile:] +599e 18 877 3 +59b6 2e 879 3 +59e4 30 880 3 +5a14 5d 882 3 +5a71 30 883 3 +5aa1 5d 885 3 +5afe 2e 888 3 +5b2c 38 891 3 +5b64 46 892 3 +5baa 26 893 3 +5bd0 20 895 3 +5bf0 20 904 3 +5c10 30 898 3 +5c40 f 899 3 +5c4f 1e 904 3 +5c6d 17 907 3 +5c84 17 908 3 +5c9b 44 911 3 +5cdf 44 914 3 +5d23 a 917 3 +5d2d 36 921 3 +5d63 30 923 3 +5d93 9 18 4 +5d9c 9 19 4 +5da5 c 20 4 +5db1 56 923 3 +5e07 74 925 3 +5e7b f 927 3 +5e8a 44 932 3 +5ece 20 933 3 +5eee c 934 3 +5efa 4e 935 3 +5f48 41 936 3 +5f89 f 937 3 +5f98 14 934 3 +5fac 7 941 3 +5fb3 14 942 3 +5fc7 14 943 3 +5fdb 1d 946 3 +5ff8 c 948 3 +6004 24 949 3 +6028 29 950 3 +6051 9 953 3 +605a 28 954 3 +6082 2e 955 3 +60b0 1e 957 3 +60ce 7 959 3 +60d5 26 962 3 +60fb 2a 963 3 +6125 2a 964 3 +614f 6 966 3 +6155 2a 967 3 +617f e 971 3 +618d 43 972 3 +61d0 4c 974 3 +621c 8 975 3 +6224 2e 979 3 +6252 2e 982 3 +6280 2e 985 3 +62ae 2e 988 3 +62dc 2e 991 3 +630a 2e 994 3 +6338 2e 997 3 +6366 2e 1000 3 +6394 54 1004 3 +63e8 c 1005 3 +63f4 e 1007 3 +6402 27 1008 3 +6429 8 1009 3 +6431 34 1010 3 +6465 24 1012 3 +6489 2 1013 3 +648b 2a 1017 3 +64b5 a 1019 3 +64bf 14 1020 3 +64d3 1d 1021 3 +64f0 a 1025 3 +64fa 32 1026 3 +652c 33 1028 3 +655f c 1029 3 +656b 55 1034 3 +65c0 f 1036 3 +65cf 16 1040 3 +65e5 61 1041 3 +6646 f 1043 3 +6655 47 1046 3 +669c c 1048 3 +66a8 11 948 3 +66b9 4e 1052 3 +6707 b 1053 3 +FUNC 6712 11 0 operator new(unsigned long, void*) +6712 c 94 5 +671e 5 94 5 +6723 1 94 5 +FUNC 6724 e 0 operator delete(void*, void*) +6724 c 98 5 +6730 2 98 5 +673e 7 76 6 +6745 2 77 6 +6747 1a 78 6 +6761 d 77 6 +676e 3 79 6 +6771 2 80 6 +6773 1 80 6 +6780 d 95 6 +678d 1 95 6 +678e 13 127 74 +67a1 2a 127 74 +67cb 1 127 74 +67cc 13 127 74 +67df 2a 127 74 +6809 1 127 74 +680a 13 127 74 +681d 2a 127 74 +6847 1 127 74 +FUNC 6848 e 0 dwarf2reader::LineInfoHandler::DefineDir(std::string const&, unsigned int) +6848 c 131 7 +6854 2 131 74 +FUNC 6856 26 0 dwarf2reader::LineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long) +6856 24 142 7 +687a 2 142 74 +FUNC 687c 1a 0 dwarf2reader::LineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int) +687c 18 150 7 +6894 2 150 74 +6896 12 299 74 +68a8 12 299 74 +68ba 13 301 74 +68cd 2a 301 74 +68f7 1 301 74 +68f8 13 301 74 +690b 2a 301 74 +6935 1 301 74 +6936 13 301 74 +6949 2a 301 74 +6973 1 301 74 +FUNC 6974 44 0 dwarf2reader::Dwarf2Handler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char) +6974 39 308 7 +69ad b 308 74 +FUNC 69b8 1f 0 dwarf2reader::Dwarf2Handler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list, std::allocator > > const&) +69b8 18 314 7 +69d0 7 314 74 +69d7 1 314 74 +FUNC 69d8 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long) +69d8 24 323 7 +69fc 2 323 74 +FUNC 69fe 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeSigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, long long) +69fe 24 332 7 +6a22 2 332 74 +FUNC 6a24 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeBuffer(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, char const*, unsigned long long) +6a24 24 345 7 +6a48 2 345 74 +FUNC 6a4a 1a 0 dwarf2reader::Dwarf2Handler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&) +6a4a 18 354 7 +6a62 2 354 74 +FUNC 6a64 1a 0 dwarf2reader::Dwarf2Handler::EndDIE(unsigned long long) +6a64 18 360 7 +6a7c 2 360 74 +6a7e c 44 8 +6a8a 2 44 8 +6a8c 13 55 32 +6a9f 35 55 32 +6ad4 13 91 32 +6ae7 73 96 32 +6b5a 13 98 32 +6b6d 35 98 32 +6bae 1a 75 3 +6bc8 2 76 3 +FUNC 6bca 20 0 std::_Rb_tree_const_iterator >::operator!=(std::_Rb_tree_const_iterator > const&) const +6bca c 287 10 +6bd6 14 288 40 +FUNC 6bea 16 0 std::_Rb_tree_const_iterator >::operator->() const +6bea c 249 10 +6bf6 a 250 40 +6c0c 7 614 72 +6c13 1 614 72 +6c14 c 241 40 +6c20 c 242 40 +FUNC 6c2c 16 0 std::_Rb_tree_const_iterator >::operator*() const +6c2c c 245 11 +6c38 a 246 40 +6c42 c 241 40 +6c4e c 242 40 +FUNC 6c5a 20 0 std::_Rb_tree_const_iterator > >::operator!=(std::_Rb_tree_const_iterator > > const&) const +6c5a c 287 11 +6c66 14 288 40 +FUNC 6c7a 16 0 std::_Rb_tree_const_iterator > >::operator->() const +6c7a c 249 11 +6c86 a 250 40 +6c90 c 185 34 +6c9c 18 186 34 +6cc0 14 204 34 +6cd4 c 69 70 +6ce0 d 69 70 +6ced 1 69 70 +6cee c 89 70 +6cfa 20 90 70 +6d1a c 69 70 +6d26 d 69 70 +6d33 1 69 70 +6d34 c 69 70 +6d40 d 69 70 +6d4d 1 69 70 +FUNC 6d4e 25 0 std::_Rb_tree_const_iterator >::operator++() +6d4e c 253 13 +6d5a 14 255 40 +6d6e 5 256 40 +6d73 1 256 40 +FUNC 6d74 25 0 std::_Rb_tree_const_iterator > >::operator++() +6d74 c 253 13 +6d80 14 255 40 +6d94 5 256 40 +6d99 1 256 40 +FUNC 6d9a 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_begin() +6d9a c 461 13 +6da6 8 462 40 +FUNC 6dae 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_begin() +6dae c 461 13 +6dba 8 462 40 +6dc2 c 65 68 +6dce 2 65 68 +6dd0 c 72 68 +6ddc 2 72 68 +6dde c 97 69 +6dea d 97 69 +6df7 1 97 69 +6df8 c 105 69 +6e04 d 105 69 +6e11 1 105 69 +6e12 c 105 69 +6e1e d 105 69 +6e2b 1 105 69 +6e2c c 67 68 +6e38 2 67 68 +6e3a c 99 69 +6e46 14 100 69 +6e5a c 99 69 +6e66 14 100 69 +FUNC 6e7a 2b 0 std::_Vector_base >::get_allocator() const +6e7a 10 93 16 +6e8a 1b 94 71 +6ea5 1 94 71 +6ea6 c 65 68 +6eb2 2 65 68 +6eb4 c 72 68 +6ec0 2 72 68 +6ec2 c 97 69 +6ece d 97 69 +6edb 1 97 69 +6edc c 105 69 +6ee8 d 105 69 +6ef5 1 105 69 +6ef6 c 105 69 +6f02 d 105 69 +6f0f 1 105 69 +6f10 c 67 68 +6f1c 2 67 68 +6f1e c 99 69 +6f2a 14 100 69 +6f3e c 99 69 +6f4a 14 100 69 +FUNC 6f5e 2b 0 std::_Vector_base >::get_allocator() const +6f5e 10 93 16 +6f6e 1b 94 71 +6f89 1 94 71 +6f8a c 603 72 +6f96 c 603 72 +FUNC 6fa2 23 0 std::vector >::begin() +6fa2 c 333 16 +6fae 17 334 71 +6fc5 1 334 71 +FUNC 6fc6 26 0 std::vector >::end() +6fc6 c 351 16 +6fd2 1a 352 71 +6ff8 5 666 72 +6ffd 1 666 72 +6ffe c 608 72 +700a 14 609 72 +702a 5 666 72 +702f 1 666 72 +FUNC 7030 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +7030 d 693 16 +703d 28 694 72 +7065 1 694 72 +7066 c 603 72 +7072 c 603 72 +708a 27 629 72 +70b1 1 629 72 +70b2 c 84 70 +70be 1f 85 70 +70dd 1 85 70 +FUNC 70de 32 0 std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*> std::make_pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*>(std::string, __gnu_cxx::hash_map, __gnu_cxx::hash, std::equal_to, std::allocator > >*) +70de 10 144 16 +70ee 22 145 70 +711c a 190 34 +7132 d 194 34 +713f 1 194 34 +7140 c 84 70 +714c 17 85 70 +7163 1 85 70 +FUNC 7164 2d 0 std::pair std::make_pair(char const*, unsigned long) +7164 c 144 16 +7170 21 145 70 +7191 1 145 70 +7192 c 84 70 +719e 1d 85 70 +71bb 1 85 70 +FUNC 71bc 30 0 std::pair > std::make_pair >(char*, std::pair) +71bc 10 144 16 +71cc 20 145 70 +71ec c 89 70 +71f8 20 90 70 +7218 d 89 70 +7225 70 90 70 +7295 1 90 70 +FUNC 7296 12 0 std::iterator_traits::iterator_category std::__iterator_category(unsigned long const* const&) +7296 c 164 17 +72a2 6 165 17 +FUNC 72a8 1d 0 std::iterator_traits::difference_type std::__distance(unsigned long const*, unsigned long const*, std::random_access_iterator_tag) +72a8 c 92 18 +72b4 11 97 18 +72c5 1 97 18 +FUNC 72c6 33 0 std::iterator_traits::difference_type std::distance(unsigned long const*, unsigned long const*) +72c6 c 114 18 +72d2 27 118 18 +72f9 1 118 18 +FUNC 72fa 20 0 void std::__advance(unsigned long const*&, int, std::random_access_iterator_tag) +72fa c 150 18 +7306 14 155 18 +FUNC 731a 33 0 void std::advance(unsigned long const*&, int) +731a c 172 18 +7326 27 175 18 +734d 1 175 18 +FUNC 734e 7a 0 unsigned long const* std::lower_bound(unsigned long const*, unsigned long const*, unsigned long const&) +734e c 2625 19 +735a 15 2642 19 +736f 2 2646 19 +7371 8 2648 19 +7379 6 2649 19 +737f 12 2650 19 +7391 e 2651 19 +739f 6 2653 19 +73a5 4 2654 19 +73a9 e 2655 19 +73b7 6 2658 19 +73bd 6 2646 19 +73c3 5 2660 19 +73db b 227 34 +73e6 e 228 34 +73f4 1c 229 34 +7410 20 230 34 +7430 6 231 34 +7436 c 72 68 +7442 2 72 68 +7444 c 105 69 +7450 d 105 69 +745d 1 105 69 +745e c 105 69 +746a d 105 69 +7477 1 105 69 +7478 c 80 71 +7484 d 80 71 +7491 1 80 71 +7492 c 67 68 +749e 2 67 68 +74a0 c 99 69 +74ac 14 100 69 +FUNC 74c0 2b 0 std::_Vector_base >::get_allocator() const +74c0 10 93 19 +74d0 1b 94 71 +74eb 1 94 71 +74ec c 238 40 +74f8 a 239 40 +FUNC 7502 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::begin() const +7502 c 585 19 +750e 1a 588 40 +FUNC 7528 19 0 std::map, std::allocator > >::begin() const +7528 c 243 20 +7534 d 244 45 +7541 1 244 45 +FUNC 7542 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::end() const +7542 c 596 20 +754e 1a 597 40 +FUNC 7568 19 0 std::map, std::allocator > >::end() const +7568 c 260 20 +7574 d 261 45 +7581 1 261 45 +7582 c 65 68 +758e 2 65 68 +7590 c 72 68 +759c 2 72 68 +759e c 97 69 +75aa d 97 69 +75b7 1 97 69 +75b8 c 105 69 +75c4 d 105 69 +75d1 1 105 69 +75d2 c 72 68 +75de 2 72 68 +75e0 c 105 69 +75ec d 105 69 +75f9 1 105 69 +75fa c 397 40 +7606 d 397 40 +7613 1 397 40 +7614 c 105 69 +7620 d 105 69 +762d 1 105 69 +FUNC 762e 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_right(std::_Rb_tree_node_base*) +762e c 496 20 +763a 8 497 40 +FUNC 7642 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_left(std::_Rb_tree_node_base*) +7642 c 488 20 +764e 8 489 40 +7656 c 65 68 +7662 2 65 68 +7664 c 72 68 +7670 2 72 68 +7672 c 97 69 +767e d 97 69 +768b 1 97 69 +768c c 105 69 +7698 d 105 69 +76a5 1 105 69 +76a6 c 72 68 +76b2 2 72 68 +76b4 c 105 69 +76c0 d 105 69 +76cd 1 105 69 +76ce c 397 40 +76da d 397 40 +76e7 1 397 40 +76e8 c 105 69 +76f4 d 105 69 +7701 1 105 69 +FUNC 7702 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_right(std::_Rb_tree_node_base*) +7702 c 496 20 +770e 8 497 40 +FUNC 7716 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_left(std::_Rb_tree_node_base*) +7716 c 488 20 +7722 8 489 40 +772a c 84 71 +7736 2f 85 71 +7765 2 86 71 +7767 1 86 71 +7768 c 80 71 +7774 d 80 71 +7781 1 80 71 +7782 c 96 71 +778e 12 97 71 +77a0 2 98 71 +77a2 c 84 71 +77ae 2f 85 71 +77dd 2 86 71 +77df 1 86 71 +77e0 c 80 71 +77ec d 80 71 +77f9 1 80 71 +77fa c 96 71 +7806 12 97 71 +7818 2 98 71 +7826 d 107 68 +7833 1 107 68 +FUNC 7834 2e 0 void std::_Destroy >(std::string*, std::string*, std::allocator) +7834 c 171 21 +7840 2 173 73 +7842 12 174 73 +7854 c 173 73 +7860 2 174 73 +7862 c 167 40 +786e a 168 40 +FUNC 7878 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::begin() +7878 c 581 21 +7884 1a 582 40 +FUNC 789e 19 0 std::map, std::allocator > >::begin() +789e c 234 21 +78aa d 235 45 +78b7 1 235 45 +FUNC 78b8 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::end() +78b8 c 592 21 +78c4 1a 593 40 +FUNC 78de 19 0 std::map, std::allocator > >::end() +78de c 251 21 +78ea d 252 45 +78f7 1 252 45 +78f8 c 167 40 +7904 a 168 40 +FUNC 790e 26 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::begin() +790e c 581 21 +791a 1a 582 40 +FUNC 7934 19 0 std::map, std::less, std::allocator > > >::begin() +7934 c 234 21 +7940 d 235 45 +794d 1 235 45 +FUNC 794e 26 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::end() +794e c 592 21 +795a 1a 593 40 +FUNC 7974 19 0 std::map, std::less, std::allocator > > >::end() +7974 c 251 21 +7980 d 252 45 +798d 1 252 45 +FUNC 798e 11 0 std::_Select1st, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >::operator()(std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*>&) const +798e c 546 22 +799a 5 547 41 +799f 1 547 41 +79a0 c 128 34 +79ac 13 129 34 +79bf 1 129 34 +79cc 22 396 34 +79fa d 199 42 +7a07 1 199 42 +7a08 c 65 68 +7a14 2 65 68 +7a16 c 72 68 +7a22 2 72 68 +7a24 c 97 69 +7a30 d 97 69 +7a3d 1 97 69 +7a3e c 105 69 +7a4a d 105 69 +7a57 1 105 69 +7a58 c 65 68 +7a64 2 65 68 +7a66 c 72 68 +7a72 2 72 68 +7a74 c 105 69 +7a80 d 105 69 +7a8d 1 105 69 +7a8e c 97 69 +7a9a d 97 69 +7aa7 1 97 69 +7aa8 c 72 68 +7ab4 2 72 68 +7ab6 c 105 69 +7ac2 d 105 69 +7acf 1 105 69 +7adc d 94 68 +7ae9 1 94 68 +FUNC 7aea 2f 0 std::_Vector_base >::_M_deallocate(dwarf2reader::CompilationUnit::Abbrev*, unsigned long) +7aea c 120 23 +7af6 6 122 71 +7afc 1d 123 71 +7b19 1 123 71 +7b1a c 108 71 +7b26 43 109 71 +7b69 1 109 71 +7b6a c 65 68 +7b76 2 65 68 +7b78 c 103 69 +7b84 d 103 69 +7b91 1 103 69 +7b92 c 65 68 +7b9e 2 65 68 +7ba0 c 103 69 +7bac d 103 69 +7bb9 1 103 69 +7bc6 d 94 68 +7bd3 1 94 68 +FUNC 7bd4 2f 0 std::_Vector_base >::_M_deallocate(dwarf2reader::SourceFileInfo*, unsigned long) +7bd4 c 120 23 +7be0 6 122 71 +7be6 1d 123 71 +7c03 1 123 71 +7c04 c 108 71 +7c10 43 109 71 +7c53 1 109 71 +7c54 c 188 71 +7c60 12 189 71 +7c72 2 190 71 +7c74 c 35 32 +7c80 d 35 32 +7c8d 1 35 32 +7c9a d 107 68 +7ca7 1 107 68 +FUNC 7ca8 2e 0 void std::_Destroy >(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, std::allocator) +7ca8 c 171 23 +7cb4 2 173 73 +7cb6 12 174 73 +7cc8 c 173 73 +7cd4 2 174 73 +7cd6 d 272 71 +7ce3 8c 273 71 +7d6f 1 273 71 +7d7c d 94 68 +7d89 1 94 68 +FUNC 7d8a 2f 0 std::_Vector_base >::_M_deallocate(std::string*, unsigned long) +7d8a c 120 23 +7d96 6 122 71 +7d9c 1d 123 71 +7db9 1 123 71 +7dba c 108 71 +7dc6 3d 109 71 +7e03 1 109 71 +7e04 c 188 71 +7e10 12 189 71 +7e22 2 190 71 +7e24 d 272 71 +7e31 8c 273 71 +7ebd 1 273 71 +7eca 2b 596 34 +7ef5 1 596 34 +7f02 7 614 72 +7f09 1 614 72 +7f0a c 65 68 +7f16 2 65 68 +7f18 c 72 68 +7f24 2 72 68 +7f26 c 103 69 +7f32 d 103 69 +7f3f 1 103 69 +7f40 c 105 69 +7f4c d 105 69 +7f59 1 105 69 +7f5a c 65 68 +7f66 2 65 68 +7f68 c 72 68 +7f74 2 72 68 +7f76 c 103 69 +7f82 d 103 69 +7f8f 1 103 69 +7f90 c 105 69 +7f9c d 105 69 +7fa9 1 105 69 +7faa c 105 69 +7fb6 d 105 69 +7fc3 1 105 69 +7fd0 d 575 34 +7fdd 1 575 34 +7fea d 575 34 +7ff7 1 575 34 +FUNC 7ff8 11 0 std::_Select1st, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >::operator()(std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*> const&) const +7ff8 c 550 23 +8004 5 551 41 +8009 1 551 41 +8016 2f 600 34 +8045 1 600 34 +8046 c 84 70 +8052 1e 85 70 +FUNC 8070 11 0 std::_Select1st > >::operator()(std::pair >&) const +8070 c 546 23 +807c 5 547 41 +8081 1 547 41 +FUNC 8082 11 0 std::_Select1st > >::operator()(std::pair > const&) const +8082 c 550 23 +808e 5 551 41 +8093 1 551 41 +8094 c 128 34 +80a0 13 129 34 +80b3 1 129 34 +80b4 c 84 70 +80c0 1e 85 70 +80de c 65 68 +80ea 2 65 68 +80ec c 103 69 +80f8 d 103 69 +8105 1 103 69 +8106 c 65 68 +8112 2 65 68 +8114 c 72 68 +8120 2 72 68 +8122 c 105 69 +812e d 105 69 +813b 1 105 69 +813c c 103 69 +8148 d 103 69 +8155 1 103 69 +8156 c 105 69 +8162 d 105 69 +816f 1 105 69 +8170 c 80 71 +817c d 80 71 +8189 1 80 71 +818a c 67 68 +8196 2 67 68 +8198 c 99 69 +81a4 14 100 69 +FUNC 81b8 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::get_allocator() const +81b8 10 93 23 +81c8 1b 94 71 +81e3 1 94 71 +81e4 c 99 69 +81f0 14 100 69 +8210 2 107 68 +FUNC 8212 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +8212 c 171 23 +821e 2 173 73 +8220 12 174 73 +8232 c 173 73 +823e 2 174 73 +824c d 107 68 +8259 1 107 68 +825a c 67 68 +8266 2 67 68 +8268 c 99 69 +8274 14 100 69 +8288 c 403 40 +8294 1c 404 40 +82b0 a 406 40 +82ba a 407 40 +82c4 c 408 40 +82d0 e 409 40 +82de c 553 40 +82ea 36 554 40 +8320 2 555 40 +8322 c 103 69 +832e d 103 69 +833b 1 103 69 +FUNC 833c 2b 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::get_allocator() const +833c 10 350 23 +834c 1b 351 40 +8367 1 351 40 +8368 c 69 70 +8374 2 69 70 +8382 d 107 68 +838f 1 107 68 +839c d 94 68 +83a9 1 94 68 +FUNC 83aa 2a 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_put_node(std::_Rb_tree_node >*) +83aa c 359 23 +83b6 1e 360 40 +FUNC 83d4 59 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::destroy_node(std::_Rb_tree_node >*) +83d4 d 387 23 +83e1 35 389 40 +8416 17 390 40 +842d 1 390 40 +FUNC 842e 56 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_erase(std::_Rb_tree_node >*) +842e c 1051 23 +843a 2 1054 40 +843c 1a 1056 40 +8456 e 1057 40 +8464 12 1058 40 +8476 6 1059 40 +847c 6 1054 40 +8482 2 1059 40 +8484 d 569 40 +8491 58 570 40 +84e9 1 570 40 +84ea c 147 45 +84f6 31 148 45 +8527 1 148 45 +8528 c 92 45 +8534 d 92 45 +8541 1 92 45 +8542 c 67 68 +854e 2 67 68 +8550 c 99 69 +855c 14 100 69 +8570 c 403 40 +857c 1c 404 40 +8598 a 406 40 +85a2 a 407 40 +85ac c 408 40 +85b8 e 409 40 +85c6 c 553 40 +85d2 36 554 40 +8608 2 555 40 +860a c 103 69 +8616 d 103 69 +8623 1 103 69 +FUNC 8624 2b 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::get_allocator() const +8624 10 350 23 +8634 1b 351 40 +864f 1 351 40 +8650 c 69 70 +865c d 69 70 +8669 1 69 70 +866a c 69 70 +8676 30 69 70 +86b2 d 107 68 +86bf 1 107 68 +86cc d 94 68 +86d9 1 94 68 +FUNC 86da 2a 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_put_node(std::_Rb_tree_node > >*) +86da c 359 23 +86e6 1e 360 40 +FUNC 8704 59 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::destroy_node(std::_Rb_tree_node > >*) +8704 d 387 23 +8711 35 389 40 +8746 17 390 40 +875d 1 390 40 +FUNC 875e 56 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_erase(std::_Rb_tree_node > >*) +875e c 1051 23 +876a 2 1054 40 +876c 1a 1056 40 +8786 e 1057 40 +8794 12 1058 40 +87a6 6 1059 40 +87ac 6 1054 40 +87b2 2 1059 40 +87b4 d 569 40 +87c1 58 570 40 +8819 1 570 40 +881a c 147 45 +8826 31 148 45 +8857 1 148 45 +8858 c 92 45 +8864 d 92 45 +8871 1 92 45 +8872 c 603 72 +887e c 603 72 +FUNC 888a 23 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::begin() +888a c 333 23 +8896 17 334 71 +88ad 1 334 71 +88ba 2a 654 72 +FUNC 88e4 42 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::operator[](unsigned long) +88e4 c 494 23 +88f0 36 495 71 +FUNC 8926 26 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::end() +8926 c 351 23 +8932 1a 352 71 +FUNC 894c 28 0 bool std::operator==, std::allocator >(std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&) +894c c 2115 24 +8958 1c 2116 37 +FUNC 8974 23 0 std::equal_to::operator()(std::string const&, std::string const&) const +8974 c 199 24 +8980 17 200 41 +8997 1 200 41 +8998 c 80 71 +89a4 d 80 71 +89b1 1 80 71 +89b2 c 67 68 +89be 2 67 68 +89c0 c 99 69 +89cc 14 100 69 +FUNC 89e0 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::get_allocator() const +89e0 10 93 24 +89f0 1b 94 71 +8a0b 1 94 71 +8a0c c 99 69 +8a18 14 100 69 +8a2c c 84 71 +8a38 2f 85 71 +8a67 2 86 71 +8a69 1 86 71 +8a6a c 96 71 +8a76 12 97 71 +8a88 2 98 71 +8a96 2 107 68 +FUNC 8a98 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node > >**, std::allocator<__gnu_cxx::_Hashtable_node > >*> >(__gnu_cxx::_Hashtable_node > >**, __gnu_cxx::_Hashtable_node > >**, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +8a98 c 171 24 +8aa4 2 173 73 +8aa6 12 174 73 +8ab8 c 173 73 +8ac4 2 174 73 +FUNC 8ac6 13 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::max_size() const +8ac6 c 407 24 +8ad2 7 408 71 +8ad9 1 408 71 +8ada c 603 72 +8ae6 c 603 72 +FUNC 8af2 26 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::end() +8af2 c 351 24 +8afe 1a 352 71 +FUNC 8b18 23 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::begin() +8b18 c 333 24 +8b24 17 334 71 +8b3b 1 334 71 +8b48 2a 654 72 +8b7e 7 614 72 +8b85 1 614 72 +FUNC 8b86 42 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::operator[](unsigned long) +8b86 c 494 24 +8b92 36 495 71 +8bd4 d 107 68 +8be1 1 107 68 +FUNC 8be2 28 0 void std::swap<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**&, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**&) +8be2 c 92 25 +8bee 8 97 61 +8bf6 a 98 61 +8c00 a 99 61 +FUNC 8c0a 50 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >&) +8c0a c 733 25 +8c16 12 735 71 +8c28 18 736 71 +8c40 1a 737 71 +8c66 2b 596 34 +8c91 1 596 34 +8c9e 2f 600 34 +8ccd 1 600 34 +FUNC 8cce 28 0 void std::swap<__gnu_cxx::_Hashtable_node > >**>(__gnu_cxx::_Hashtable_node > >**&, __gnu_cxx::_Hashtable_node > >**&) +8cce c 92 25 +8cda 8 97 61 +8ce2 a 98 61 +8cec a 99 61 +FUNC 8cf6 50 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >&) +8cf6 c 733 25 +8d02 12 735 71 +8d14 18 736 71 +8d2c 1a 737 71 +8d46 c 84 71 +8d52 2f 85 71 +8d81 2 86 71 +8d83 1 86 71 +8d84 c 96 71 +8d90 12 97 71 +8da2 2 98 71 +FUNC 8da4 13 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::max_size() const +8da4 c 407 25 +8db0 7 408 71 +8db7 1 408 71 +8dc4 d 94 68 +8dd1 1 94 68 +FUNC 8dd2 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long) +8dd2 c 120 25 +8dde 6 122 71 +8de4 1d 123 71 +8e01 1 123 71 +8e02 c 108 71 +8e0e 3d 109 71 +8e4b 1 109 71 +8e4c c 272 71 +8e58 4b 273 71 +8ea3 1 273 71 +8ea4 c 188 71 +8eb0 12 189 71 +8ec2 2 190 71 +8ec4 c 603 72 +8ed0 c 603 72 +FUNC 8edc 2b 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::begin() const +8edc c 342 25 +8ee8 1f 343 71 +8f07 1 343 71 +FUNC 8f08 2c 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::end() const +8f08 c 360 25 +8f14 20 361 71 +8f40 5 666 72 +8f45 1 666 72 +8f53 2b 759 72 +FUNC 8f7e 3c 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::size() const +8f7e c 402 25 +8f8a 30 403 71 +8fc6 26 588 34 +8ff8 15 511 34 +900d 79 513 34 +9086 21 517 34 +90a7 1 517 34 +90b4 14 225 42 +90d4 26 592 34 +FUNC 90fa 49 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::capacity() const +90fa c 449 25 +9106 3d 451 71 +9143 1 451 71 +9144 c 103 69 +9150 d 103 69 +915d 1 103 69 +916e 1b 286 34 +9189 1 286 34 +9196 d 94 68 +91a3 1 94 68 +91b0 1e 301 34 +91db 56 622 34 +9231 17 623 34 +9254 9 1080 34 +925d 1a 1082 34 +9277 2 1083 34 +9279 8 1085 34 +9281 12 1086 34 +9293 6 1087 34 +9299 6 1083 34 +929f 1b 1089 34 +92ba 1d 1080 34 +92d7 c 1091 34 +92e3 1 1091 34 +92e4 d 360 34 +92f1 77 361 34 +9368 c 93 42 +9374 d 93 42 +9381 1 93 42 +9382 c 72 68 +938e 2 72 68 +9390 c 105 69 +939c d 105 69 +93a9 1 105 69 +93aa c 301 66 +93b6 d 301 66 +93c3 1 301 66 +93d0 d 94 68 +93dd 1 94 68 +FUNC 93de 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node > >**, unsigned long) +93de c 120 26 +93ea 6 122 71 +93f0 1d 123 71 +940d 1 123 71 +940e c 108 71 +941a 3d 109 71 +9457 1 109 71 +9458 c 188 71 +9464 12 189 71 +9476 2 190 71 +9478 c 272 71 +9484 4b 273 71 +94cf 1 273 71 +94d0 c 603 72 +94dc c 603 72 +FUNC 94e8 2b 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::begin() const +94e8 c 342 26 +94f4 1f 343 71 +9513 1 343 71 +FUNC 9514 2c 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::end() const +9514 c 360 26 +9520 20 361 71 +954c 2d 662 72 +9579 1 662 72 +FUNC 957a 2d 0 unsigned long const& std::max(unsigned long const&, unsigned long const&) +957a c 206 26 +9586 e 211 61 +9594 8 212 61 +959c b 213 61 +95a7 1 213 61 +95b4 19 650 72 +95cd 1 650 72 +95da 5 666 72 +95df 1 666 72 +95ed 2b 759 72 +9624 5 666 72 +9629 1 666 72 +9637 2b 759 72 +FUNC 9662 49 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::capacity() const +9662 c 449 26 +966e 3d 451 71 +96ab 1 451 71 +FUNC 96ac 3c 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::size() const +96ac c 402 26 +96b8 30 403 71 +96f4 26 588 34 +9726 26 592 34 +974c c 103 69 +9758 d 103 69 +9765 1 103 69 +9776 1b 286 34 +9791 1 286 34 +979e d 94 68 +97ab 1 94 68 +97b8 1e 301 34 +97e3 56 622 34 +9839 17 623 34 +985c 9 1080 34 +9865 1a 1082 34 +987f 2 1083 34 +9881 8 1085 34 +9889 12 1086 34 +989b 6 1087 34 +98a1 6 1083 34 +98a7 1b 1089 34 +98c2 1d 1080 34 +98df c 1091 34 +98eb 1 1091 34 +98ec d 360 34 +98f9 77 361 34 +9970 c 69 70 +997c 20 69 70 +99a9 5c 104 68 +9a05 1 104 68 +9a06 c 69 70 +9a12 2c 69 70 +9a4b 5c 104 68 +9aa7 1 104 68 +9ab4 2d 662 72 +9ae1 1 662 72 +9aee 19 650 72 +9b07 1 650 72 +9b14 5 666 72 +9b19 1 666 72 +9b27 2b 759 72 +9b52 c 72 68 +9b5e 2 72 68 +9b60 c 105 69 +9b6c d 105 69 +9b79 1 105 69 +9b7a c 69 70 +9b86 2 69 70 +9b94 d 107 68 +9ba1 1 107 68 +9bae d 94 68 +9bbb 1 94 68 +FUNC 9bbc 2a 0 std::_List_base, std::allocator > >::_M_put_node(std::_List_node >*) +9bbc c 315 26 +9bc8 1e 316 66 +FUNC 9be6 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > const&) +9be6 d 699 26 +9bf3 28 700 72 +9c1b 1 700 72 +9c28 d 623 72 +9c35 5 624 72 +FUNC 9c3a 4b 0 void std::__fill::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >* const&) +9c3a c 539 61 +9c46 8 541 61 +9c4e 2 542 61 +9c50 12 543 61 +9c62 21 542 61 +9c83 2 543 61 +9c85 1 543 61 +FUNC 9c86 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >* const&) +9c86 c 560 26 +9c92 4 567 61 +9c96 1b 568 61 +9cb1 1 568 61 +FUNC 9cb2 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node > >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +9cb2 c 171 26 +9cbe 2 173 73 +9cc0 1a 174 73 +9cda 21 173 73 +9cfb 2 174 73 +9cfd 1 174 73 +9d0a 7 98 68 +9d11 1 98 68 +9d1e 1d 85 68 +9d3b 5 86 68 +9d40 16 88 68 +9d62 1d 297 34 +9d7f 1 297 34 +9d8d e 605 34 +9d9b 9 606 34 +9da4 3c 609 34 +9de0 b 610 34 +9deb 11 609 34 +9dfc b 612 34 +9e07 12 614 34 +9e19 b 615 34 +9e24 13 612 34 +9e37 8 615 34 +9e3f 1 615 34 +9e4d 15 751 34 +9e62 1a 752 34 +9e7c b 754 34 +9e87 49 755 34 +9ed0 3b 756 34 +9f0b 12 754 34 +9f1d 15 758 34 +9f32 8 759 34 +9f3a 1c 760 34 +9f56 f 761 34 +9f65 41 762 34 +9fb2 7 98 68 +9fb9 1 98 68 +9fc6 1d 85 68 +9fe3 5 86 68 +9fe8 17 88 68 +9fff 1 88 68 +a00c 1d 297 34 +a029 1 297 34 +a037 e 605 34 +a045 9 606 34 +a04e 3c 609 34 +a08a b 610 34 +a095 11 609 34 +a0a6 b 612 34 +a0b1 12 614 34 +a0c3 b 615 34 +a0ce 13 612 34 +a0e1 8 615 34 +a0e9 1 615 34 +a0f7 15 751 34 +a10c 1a 752 34 +a126 b 754 34 +a131 49 755 34 +a17a 3b 756 34 +a1b5 12 754 34 +a1c7 15 758 34 +a1dc 8 759 34 +a1e4 1c 760 34 +a200 f 761 34 +a20f 41 762 34 +FUNC a250 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > > const&) +a250 d 699 26 +a25d 28 700 72 +a285 1 700 72 +a292 d 623 72 +a29f 5 624 72 +FUNC a2a4 4b 0 void std::__fill::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a2a4 c 539 61 +a2b0 8 541 61 +a2b8 2 542 61 +a2ba 12 543 61 +a2cc 21 542 61 +a2ed 2 543 61 +a2ef 1 543 61 +FUNC a2f0 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a2f0 c 560 26 +a2fc 4 567 61 +a300 1b 568 61 +a31b 1 568 61 +FUNC a31c 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +a31c c 171 26 +a328 2 173 73 +a32a 1a 174 73 +a344 21 173 73 +a365 2 174 73 +a367 1 174 73 +a368 c 65 68 +a374 2 65 68 +a376 c 103 69 +a382 d 103 69 +a38f 1 103 69 +FUNC a390 2b 0 std::_List_base, std::allocator > >::get_allocator() const +a390 10 322 26 +a3a0 1b 324 66 +a3bb 1 324 66 +FUNC a3bc 7b 0 std::_List_base, std::allocator > >::_M_clear() +a3bc d 69 27 +a3c9 8 72 77 +a3d1 2 73 77 +a3d3 6 75 77 +a3d9 8 76 77 +a3e1 35 77 77 +a416 12 78 77 +a428 a 73 77 +a432 5 78 77 +a437 1 78 77 +a438 c 331 66 +a444 18 332 66 +a45c c 392 66 +a468 d 392 66 +a475 1 392 66 +a476 c 211 74 +a482 10 211 74 +a49e d 107 68 +a4ab 1 107 68 +FUNC a4ac 2e 0 void std::_Destroy >(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) +a4ac c 171 27 +a4b8 2 173 73 +a4ba 12 174 73 +a4cc c 173 73 +a4d8 2 174 73 +a4da c 272 71 +a4e6 4b 273 71 +a531 1 273 71 +a532 13 196 74 +a545 10 196 74 +a555 2f 197 74 +a584 1a 198 74 +a59e 13 196 74 +a5b1 10 196 74 +a5c1 2f 197 74 +a5f0 1a 198 74 +a616 7 98 68 +a61d 1 98 68 +a62a 1d 85 68 +a647 5 86 68 +a64c 10 88 68 +FUNC a65c 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_allocate(unsigned long) +a65c c 116 27 +a668 1e 117 71 +a686 d 100 71 +a693 12 101 71 +a6a5 19 103 71 +a6be b 104 71 +a6c9 3a 105 71 +a703 1 105 71 +a710 7 98 68 +a717 1 98 68 +a724 1d 85 68 +a741 5 86 68 +a746 10 88 68 +FUNC a756 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_allocate(unsigned long) +a756 c 116 27 +a762 1e 117 71 +a780 d 100 71 +a78d 12 101 71 +a79f 19 103 71 +a7b8 b 104 71 +a7c3 3a 105 71 +a7fd 1 105 71 +a80b 12 424 61 +a81d 2e 425 61 +a84b 13 426 61 +a86a 4 440 61 +a86e 1b 443 61 +a889 1 443 61 +a898 56 482 61 +a8fa 4 514 61 +a8fe 4 515 61 +a902 1b 517 61 +a91d 1 517 61 +a92a 8 616 61 +a932 2 617 61 +a934 8 618 61 +a93c f 617 61 +a94b 5 619 61 +a95c 4 641 61 +a960 1b 642 61 +a97b 1 642 61 +FUNC a97c 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, __true_type) +a97c c 182 28 +a988 1b 183 79 +a9a3 1 183 79 +FUNC a9a4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a9a4 c 214 28 +a9b0 23 218 79 +a9d3 1 218 79 +FUNC a9d4 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +a9d4 c 308 28 +a9e0 1b 310 79 +a9fb 1 310 79 +a9fc c 200 71 +aa08 19 201 71 +aa21 42 203 71 +aa63 15 205 71 +aa85 11 992 34 +aa96 c 993 34 +aaa2 15 995 34 +aab7 c 996 34 +aac3 4a 998 34 +ab0d f 1001 34 +ab1c 1c 998 34 +ab38 1a 1003 34 +ab52 5 1004 34 +ab57 1f 1007 34 +ab76 1c 1008 34 +ab92 19 1009 34 +abab 19 1010 34 +abc4 1a 1011 34 +abde a 1004 34 +abe8 11 1001 34 +abf9 15 1014 34 +ac0e 13 1028 34 +ac21 b 1016 34 +ac2c 9 1018 34 +ac35 19 1023 34 +ac4e 23 1024 34 +ac71 19 1025 34 +ac8a 1d 1021 34 +aca7 1a 1018 34 +acc1 b 1028 34 +accc b 1016 34 +acd7 1e 1028 34 +acf5 1 1028 34 +ad06 16 438 34 +ad1c 37 439 34 +ad53 1 439 34 +ad64 37 212 42 +ad9b 1 212 42 +ada8 8 616 61 +adb0 2 617 61 +adb2 8 618 61 +adba f 617 61 +adc9 5 619 61 +adda 4 641 61 +adde 1b 642 61 +adf9 1 642 61 +FUNC adfa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, __true_type) +adfa c 182 28 +ae06 1b 183 79 +ae21 1 183 79 +FUNC ae22 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +ae22 c 214 28 +ae2e 23 218 79 +ae51 1 218 79 +FUNC ae52 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +ae52 c 308 28 +ae5e 1b 310 79 +ae79 1 310 79 +ae7a c 200 71 +ae86 19 201 71 +ae9f 42 203 71 +aee1 15 205 71 +af03 11 992 34 +af14 c 993 34 +af20 15 995 34 +af35 c 996 34 +af41 4a 998 34 +af8b f 1001 34 +af9a 1c 998 34 +afb6 1a 1003 34 +afd0 5 1004 34 +afd5 1f 1007 34 +aff4 1c 1008 34 +b010 19 1009 34 +b029 19 1010 34 +b042 1a 1011 34 +b05c a 1004 34 +b066 11 1001 34 +b077 15 1014 34 +b08c 13 1028 34 +b09f b 1016 34 +b0aa 9 1018 34 +b0b3 19 1023 34 +b0cc 23 1024 34 +b0ef 19 1025 34 +b108 1d 1021 34 +b125 1a 1018 34 +b13f b 1028 34 +b14a b 1016 34 +b155 1e 1028 34 +b173 1 1028 34 +b184 16 438 34 +b19a 37 439 34 +b1d1 1 439 34 +b1e2 37 212 42 +b219 1 212 42 +b227 12 424 61 +b239 2e 425 61 +b267 13 426 61 +b286 4 440 61 +b28a 1b 443 61 +b2a5 1 443 61 +b2b4 56 482 61 +b316 4 514 61 +b31a 4 515 61 +b31e 1b 517 61 +b339 1 517 61 +b346 8 616 61 +b34e 2 617 61 +b350 12 618 61 +b362 16 617 61 +b378 5 619 61 +b37d 1 619 61 +b38a 4 641 61 +b38e 1b 642 61 +b3a9 1 642 61 +FUNC b3aa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, __true_type) +b3aa c 182 28 +b3b6 1b 183 79 +b3d1 1 183 79 +FUNC b3d2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +b3d2 c 214 28 +b3de 23 218 79 +b401 1 218 79 +FUNC b402 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +b402 c 308 28 +b40e 1b 310 79 +b429 1 310 79 +b436 8 616 61 +b43e 2 617 61 +b440 12 618 61 +b452 16 617 61 +b468 5 619 61 +b46d 1 619 61 +b47a 4 641 61 +b47e 1b 642 61 +b499 1 642 61 +FUNC b49a 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, __true_type) +b49a c 182 28 +b4a6 1b 183 79 +b4c1 1 183 79 +FUNC b4c2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +b4c2 c 214 28 +b4ce 23 218 79 +b4f1 1 218 79 +FUNC b4f2 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +b4f2 c 308 28 +b4fe 1b 310 79 +b519 1 310 79 +b526 22 300 61 +b548 11 301 61 +b559 1 301 61 +b566 4 315 61 +b56a 1b 317 61 +b585 1 317 61 +b592 1b 326 61 +b5ad 1 326 61 +b5ba 4 384 61 +b5be 4 385 61 +b5c2 1b 387 61 +b5dd 1 387 61 +b5ea 1b 74 79 +b605 1 74 79 +b612 23 113 79 +b635 1 113 79 +b642 1b 254 79 +b65d 1 254 79 +b66a 15 763 71 +b67f 40 766 71 +b6bf 3 768 71 +b6c2 2 773 71 +FUNC b6c4 124 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::reserve(unsigned long) +b6c4 13 69 29 +b6d7 15 71 78 +b6ec e 72 78 +b6fa 19 73 78 +b713 e 75 78 +b721 28 78 78 +b749 3e 79 78 +b787 30 81 78 +b7b7 8 84 78 +b7bf 11 85 78 +b7d0 18 86 78 +b7f5 33 335 61 +b834 4 384 61 +b838 4 385 61 +b83c 1b 387 61 +b857 1 387 61 +b864 1b 74 79 +b87f 1 74 79 +b88c 23 113 79 +b8af 1 113 79 +b8bc 1b 254 79 +b8d7 1 254 79 +b8e6 56 354 61 +b948 4 384 61 +b94c 4 385 61 +b950 1b 387 61 +b96b 1 387 61 +b978 1b 74 79 +b993 1 74 79 +b9a0 23 113 79 +b9c3 1 113 79 +b9d0 1b 254 79 +b9eb 1 254 79 +FUNC b9ec 46e 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +b9ec 14 311 29 +ba00 b 313 78 +ba0b 24 315 78 +ba2f 8 318 78 +ba37 23 319 78 +ba5a 15 320 78 +ba6f c 321 78 +ba7b 51 323 78 +bacc 14 327 78 +bae0 30 328 78 +bb10 35 330 78 +bb45 48 334 78 +bb8d 17 338 78 +bba4 43 339 78 +bbe7 14 342 78 +bbfb 1e 343 78 +bc19 e 348 78 +bc27 1e 349 78 +bc45 e 350 78 +bc53 1d 353 78 +bc70 8 354 78 +bc78 e 355 78 +bc86 27 357 78 +bcad 6 358 78 +bcb3 4d 361 78 +bd00 40 365 78 +bd40 18 367 78 +bd58 4d 368 78 +bda5 3e 379 78 +bde3 30 381 78 +be13 12 384 78 +be25 13 385 78 +be38 22 386 78 +FUNC be5a 2e 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +be5a c 657 29 +be66 22 658 71 +be94 15 580 34 +bea9 15 581 34 +bebe 37 582 34 +bef5 c 583 34 +bf01 1 583 34 +bf02 d 335 34 +bf0f 4e 337 34 +bf5d 4d 338 34 +bfaa d 134 42 +bfb7 65 135 42 +c028 22 300 61 +c04a 11 301 61 +c05b 1 301 61 +c068 4 315 61 +c06c 1b 317 61 +c087 1 317 61 +c094 1b 326 61 +c0af 1 326 61 +c0bc 4 384 61 +c0c0 4 385 61 +c0c4 1b 387 61 +c0df 1 387 61 +c0ec 1b 74 79 +c107 1 74 79 +c114 23 113 79 +c137 1 113 79 +c144 1b 254 79 +c15f 1 254 79 +c16c 15 763 71 +c181 40 766 71 +c1c1 3 768 71 +c1c4 2 773 71 +FUNC c1c6 124 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::reserve(unsigned long) +c1c6 13 69 29 +c1d9 15 71 78 +c1ee e 72 78 +c1fc 19 73 78 +c215 e 75 78 +c223 28 78 78 +c24b 3e 79 78 +c289 30 81 78 +c2b9 8 84 78 +c2c1 11 85 78 +c2d2 18 86 78 +c2f7 33 335 61 +c336 4 384 61 +c33a 4 385 61 +c33e 1b 387 61 +c359 1 387 61 +c366 1b 74 79 +c381 1 74 79 +c38e 23 113 79 +c3b1 1 113 79 +c3be 1b 254 79 +c3d9 1 254 79 +c3e8 56 354 61 +c44a 4 384 61 +c44e 4 385 61 +c452 1b 387 61 +c46d 1 387 61 +c47a 1b 74 79 +c495 1 74 79 +c4a2 23 113 79 +c4c5 1 113 79 +c4d2 1b 254 79 +c4ed 1 254 79 +FUNC c4ee 46e 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +c4ee 14 311 29 +c502 b 313 78 +c50d 24 315 78 +c531 8 318 78 +c539 23 319 78 +c55c 15 320 78 +c571 c 321 78 +c57d 51 323 78 +c5ce 14 327 78 +c5e2 30 328 78 +c612 35 330 78 +c647 48 334 78 +c68f 17 338 78 +c6a6 43 339 78 +c6e9 14 342 78 +c6fd 1e 343 78 +c71b e 348 78 +c729 1e 349 78 +c747 e 350 78 +c755 1d 353 78 +c772 8 354 78 +c77a e 355 78 +c788 27 357 78 +c7af 6 358 78 +c7b5 4d 361 78 +c802 40 365 78 +c842 18 367 78 +c85a 4d 368 78 +c8a7 3e 379 78 +c8e5 30 381 78 +c915 12 384 78 +c927 13 385 78 +c93a 22 386 78 +FUNC c95c 2e 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +c95c c 657 29 +c968 22 658 71 +c996 15 580 34 +c9ab 15 581 34 +c9c0 37 582 34 +c9f7 c 583 34 +ca03 1 583 34 +ca04 d 335 34 +ca11 4e 337 34 +ca5f 4d 338 34 +caac d 134 42 +cab9 65 135 42 +FUNC cb1e 44 0 dwarf2reader::CUFunctionInfoHandler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char) +cb1e 39 135 42 +cb57 5 102 30 +cb5c 6 103 30 +FUNC cb62 41 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&) +cb62 18 136 30 +cb7a 10 137 30 +cb8a 17 138 30 +cba1 2 139 30 +cba3 1 139 30 +FUNC cba4 2a5 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long) +cba4 2d 144 30 +cbd1 a 145 30 +cbdb 58 146 30 +cc33 35 147 30 +cc68 32 146 30 +cc9a 2a 147 30 +ccc4 82 152 30 +cd46 18 153 30 +cd5e 1c 152 30 +cd7a 2f 153 30 +cda9 e 154 30 +cdb7 28 155 30 +cddf 12 157 30 +cdf1 2 158 30 +cdf3 12 160 30 +ce05 2 161 30 +ce07 c 163 30 +ce13 2 164 30 +ce15 2c 166 30 +ce41 8 172 30 +ce49 1 172 30 +FUNC ce4a 19c 0 dwarf2reader::CULineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int) +ce4a 20 84 30 +ce6a 1c 85 30 +ce86 9c 87 30 +cf22 4f 89 30 +cf71 19 87 30 +cf8a 25 90 30 +cfaf 30 93 30 +cfdf 7 95 30 +FUNC cfe6 9f 0 dwarf2reader::CUFunctionInfoHandler::EndDIE(unsigned long long) +cfe6 19 174 30 +cfff 1c 175 30 +d01b 65 177 30 +d080 5 178 30 +d085 1 178 30 +FUNC d086 164 0 dwarf2reader::CUFunctionInfoHandler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list, std::allocator > > const&) +d086 20 111 30 +d0a6 1c 112 30 +d0c2 c 126 30 +d0ce 23 115 30 +d0f1 26 116 30 +d117 1a 117 30 +d131 d 118 30 +d13e 1b 119 30 +d159 5f 120 30 +d1b8 c 124 30 +d1c4 1c 115 30 +d1e0 3 126 30 +d1e3 7 129 30 +FUNC d1ea 73 0 dwarf2reader::CULineInfoHandler::DefineDir(std::string const&, unsigned int) +d1ea 13 52 30 +d1fd 45 54 30 +d242 15 55 30 +d257 6 56 30 +d25d 1 56 30 +FUNC d25e 23b 0 dwarf2reader::CULineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long) +d25e 2c 60 30 +d28a 45 62 30 +d2cf 2f 65 30 +d2fe 24 66 30 +d322 b 68 30 +d32d e 69 30 +d33b 19 71 30 +d354 17 72 30 +d36b 93 74 30 +d3fe 64 77 30 +d462 30 79 30 +d492 7 81 30 +d499 1 81 30 +d49a 14 38 30 +d4ae 36 40 30 +d4e4 41 43 30 +d525 41 44 30 +d566 67 45 30 +d5cd 10 46 30 +d5dd 13 45 30 +d5f0 15 47 30 +d605 e 48 30 +d613 3d 49 30 +d650 20 50 30 +d670 14 38 30 +d684 36 40 30 +d6ba 41 43 30 +d6fb 41 44 30 +d73c 67 45 30 +d7a3 10 46 30 +d7b3 13 45 30 +d7c6 15 47 30 +d7db e 48 30 +d7e9 3d 49 30 +d826 20 50 30 +d846 12 125 74 +d858 12 125 74 +d86a 13 55 32 +d87d 35 55 32 +d8b2 13 98 32 +d8c5 35 98 32 +d8fa c 35 32 +d906 d 35 32 +d913 1 35 32 +d914 d 22 32 +d921 40 22 32 +d961 1 22 32 +d962 c 89 70 +d96e 1e 90 70 +d998 14 208 34 +d9ac c 190 67 +d9b8 a 190 67 +d9c2 c 259 67 +d9ce 21 259 67 +d9ef 1 259 67 +FUNC d9f0 13 0 std::auto_ptr::operator->() const +d9f0 c 283 35 +d9fc 7 286 67 +da03 1 286 67 +da11 5c 104 68 +da6d 1 104 68 +FUNC da6e 28 0 bool std::operator==, std::allocator >(std::basic_string, std::allocator > const&, char const*) +da6e c 2139 37 +da7a 1c 2140 37 +FUNC da96 5d 0 std::basic_string, std::allocator > std::operator+, std::allocator >(std::basic_string, std::allocator > const&, char const*) +da96 d 2081 37 +daa3 12 2083 37 +dab5 1a 2084 37 +dacf 24 2085 37 +daf3 1 2085 37 +FUNC daf4 5d 0 std::basic_string, std::allocator > std::operator+, std::allocator >(std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&) +daf4 d 2044 37 +db01 12 2046 37 +db13 1a 2047 37 +db2d 24 2048 37 +db51 1 2048 37 +db52 c 84 70 +db5e 17 85 70 +db75 1 85 70 +FUNC db76 2d 0 std::pair std::make_pair(char const*, unsigned int) +db76 c 144 37 +db82 21 145 70 +dba3 1 145 70 +dba4 c 84 70 +dbb0 23 85 70 +dbd3 1 85 70 +FUNC dbd4 3c 0 std::pair > std::make_pair >(unsigned long long, std::pair) +dbd4 1c 144 37 +dbf0 20 145 70 +dc10 d 89 70 +dc1d 64 90 70 +dc81 1 90 70 +dc82 c 89 70 +dc8e 2a 90 70 +dcb8 c 84 70 +dcc4 1d 85 70 +dce1 1 85 70 +FUNC dce2 3c 0 std::pair std::make_pair(unsigned long long, dwarf2reader::FunctionInfo*) +dce2 1c 144 37 +dcfe 20 145 70 +dd2a a 190 34 +dd40 d 194 34 +dd4d 1 194 34 +dd4e c 603 72 +dd5a c 603 72 +FUNC dd66 2b 0 std::vector >::begin() const +dd66 c 342 39 +dd72 1f 343 71 +dd91 1 343 71 +FUNC dd92 2c 0 std::vector >::end() const +dd92 c 360 39 +dd9e 20 361 71 +ddca 5 666 72 +ddcf 1 666 72 +dddd 2b 759 72 +FUNC de08 3c 0 std::vector >::size() const +de08 c 402 39 +de14 30 403 71 +FUNC de44 2b 0 std::vector >::begin() const +de44 c 342 39 +de50 1f 343 71 +de6f 1 343 71 +FUNC de70 2c 0 std::vector >::end() const +de70 c 360 39 +de7c 20 361 71 +dea9 31 759 72 +FUNC deda 3c 0 std::vector >::size() const +deda c 402 39 +dee6 30 403 71 +df16 c 603 72 +df22 c 603 72 +FUNC df2e 26 0 std::vector >::end() +df2e c 351 39 +df3a 1a 352 71 +df60 7 614 72 +df67 1 614 72 +FUNC df68 13 0 std::vector >::max_size() const +df68 c 407 39 +df74 7 408 71 +df7b 1 408 71 +df88 5 666 72 +df8d 1 666 72 +df9a d 623 72 +dfa7 5 624 72 +FUNC dfac 23 0 std::vector >::begin() +dfac c 333 39 +dfb8 17 334 71 +dfcf 1 334 71 +dfd0 c 35 32 +dfdc 26 35 32 +e00f 5c 104 68 +e06b 1 104 68 +e078 7 614 72 +e07f 1 614 72 +FUNC e080 35 0 dwarf2reader::SourceFileInfo::operator=(dwarf2reader::SourceFileInfo const&) +e080 c 35 39 +e08c 29 35 32 +e0b5 1 35 32 +FUNC e0b6 13 0 std::vector >::max_size() const +e0b6 c 407 39 +e0c2 7 408 71 +e0c9 1 408 71 +e0d6 d 623 72 +e0e3 5 624 72 +FUNC e0e8 3c 0 std::vector >::_M_range_check(unsigned long) const +e0e8 13 515 39 +e0fb 15 517 71 +e110 14 518 71 +FUNC e124 3c 0 std::vector >::_M_range_check(unsigned long) const +e124 13 515 39 +e137 15 517 71 +e14c 14 518 71 +e16c 2a 654 72 +FUNC e196 42 0 std::vector >::operator[](unsigned long) +e196 c 494 39 +e1a2 36 495 71 +FUNC e1d8 32 0 std::vector >::at(unsigned long) +e1d8 c 534 39 +e1e4 12 536 71 +e1f6 14 537 71 +e216 32 654 72 +FUNC e248 42 0 std::vector >::operator[](unsigned long) +e248 c 494 39 +e254 36 495 71 +FUNC e28a 32 0 std::vector >::at(unsigned long) +e28a c 534 39 +e296 12 536 71 +e2a8 14 537 71 +FUNC e2bc 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_end() +e2bc c 472 40 +e2c8 8 473 40 +FUNC e2d0 11 0 std::_Select1st > >::operator()(std::pair > const&) const +e2d0 c 550 41 +e2dc 5 551 41 +e2e1 1 551 41 +FUNC e2e2 53 0 std::less::operator()(unsigned long long const&, unsigned long long const&) const +e2e2 c 226 41 +e2ee 47 227 41 +e335 1 227 41 +FUNC e336 20 0 std::_Rb_tree_iterator > >::operator==(std::_Rb_tree_iterator > > const&) const +e336 c 209 41 +e342 14 210 40 +e356 c 84 70 +e362 18 85 70 +FUNC e37a 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_end() +e37a c 472 41 +e386 8 473 40 +FUNC e38e 11 0 std::_Select1st >::operator()(std::pair const&) const +e38e c 550 41 +e39a 5 551 41 +e39f 1 551 41 +FUNC e3a0 20 0 std::_Rb_tree_iterator >::operator==(std::_Rb_tree_iterator > const&) const +e3a0 c 209 41 +e3ac 14 210 40 +e3c0 c 84 70 +e3cc 18 85 70 +e3e4 c 180 34 +e3f0 13 181 34 +e403 1 181 34 +e410 22 409 34 +e43e d 207 42 +e44b 1 207 42 +FUNC e44c 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +e44c d 699 42 +e459 28 700 72 +e481 1 700 72 +FUNC e482 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +e482 c 171 43 +e48e 2 173 73 +e490 1a 174 73 +e4aa 21 173 73 +e4cb 2 174 73 +e4cd 1 174 73 +FUNC e4ce 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +e4ce d 699 43 +e4db 28 700 72 +e503 1 700 72 +FUNC e504 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +e504 c 171 43 +e510 2 173 73 +e512 1a 174 73 +e52c 21 173 73 +e54d 2 174 73 +e54f 1 174 73 +FUNC e550 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_value(std::_Rb_tree_node > > const*) +e550 c 480 43 +e55c 8 481 40 +FUNC e564 28 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_key(std::_Rb_tree_node > > const*) +e564 c 484 43 +e570 1c 485 40 +FUNC e58c 25 0 std::_Rb_tree_iterator >::operator--() +e58c c 194 43 +e598 14 196 40 +e5ac 5 197 40 +e5b1 1 197 40 +FUNC e5b2 25 0 std::_Rb_tree_iterator > >::operator--() +e5b2 c 194 43 +e5be 14 196 40 +e5d2 5 197 40 +e5d7 1 197 40 +FUNC e5d8 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_value(std::_Rb_tree_node_base const*) +e5d8 c 504 43 +e5e4 8 505 40 +FUNC e5ec 28 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_key(std::_Rb_tree_node_base const*) +e5ec c 508 43 +e5f8 1c 509 40 +FUNC e614 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_value(std::_Rb_tree_node > const*) +e614 c 480 43 +e620 8 481 40 +FUNC e628 28 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_key(std::_Rb_tree_node > const*) +e628 c 484 43 +e634 1c 485 40 +FUNC e650 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_value(std::_Rb_tree_node_base const*) +e650 c 504 43 +e65c 8 505 40 +FUNC e664 28 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_key(std::_Rb_tree_node_base const*) +e664 c 508 43 +e670 1c 509 40 +e698 7 614 72 +e69f 1 614 72 +e6ac 7 98 68 +e6b3 1 98 68 +e6c0 1d 85 68 +e6dd 5 86 68 +e6e2 10 88 68 +FUNC e6f2 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +e6f2 c 116 43 +e6fe 1e 117 71 +e728 7 98 68 +e72f 1 98 68 +e73c 1d 85 68 +e759 5 86 68 +e75e 16 88 68 +FUNC e774 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +e774 c 116 43 +e780 1e 117 71 +e7aa 3a 104 68 +e7f0 2a 654 72 +FUNC e81a 42 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::operator[](unsigned long) const +e81a c 509 43 +e826 36 510 71 +FUNC e85c 4e 0 std::string* std::__copy_backward::copy_b(std::string*, std::string*, std::string*) +e85c c 408 61 +e868 14 411 61 +e87c 1e 412 61 +e89a b 411 61 +e8a5 5 413 61 +FUNC e8aa 2b 0 std::string* std::__copy_backward_aux(std::string*, std::string*, std::string*) +e8aa c 432 44 +e8b6 4 440 61 +e8ba 1b 443 61 +e8d5 1 443 61 +e8e4 56 482 61 +e946 4 514 61 +e94a 4 515 61 +e94e 1b 517 61 +e969 1 517 61 +FUNC e96a 69 0 void std::_Construct(std::string*, std::string const&) +e96a d 77 44 +e977 5c 81 73 +e9d3 1 81 73 +FUNC e9d4 54 0 dwarf2reader::SourceFileInfo* std::__copy_backward::copy_b(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*) +e9d4 c 408 61 +e9e0 1a 411 61 +e9fa 1e 412 61 +ea18 b 411 61 +ea23 5 413 61 +FUNC ea28 2b 0 dwarf2reader::SourceFileInfo* std::__copy_backward_aux(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*) +ea28 c 432 44 +ea34 4 440 61 +ea38 1b 443 61 +ea53 1 443 61 +ea62 56 482 61 +eac4 4 514 61 +eac8 4 515 61 +eacc 1b 517 61 +eae7 1 517 61 +FUNC eae8 69 0 void std::_Construct(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo const&) +eae8 d 77 44 +eaf5 5c 81 73 +eb51 1 81 73 +eb52 c 69 70 +eb5e 20 69 70 +eb7e c 69 70 +eb8a 2a 69 70 +ebc1 5c 104 68 +ec1d 1 104 68 +ec2a 15 523 34 +ec3f 79 525 34 +ecb8 21 529 34 +ecd9 1 529 34 +ece6 14 229 42 +ed06 7 98 68 +ed0d 1 98 68 +ed1a 1d 85 68 +ed37 5 86 68 +ed3c 10 88 68 +FUNC ed4c 29 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_get_node() +ed4c c 355 44 +ed58 1d 356 40 +ed75 1 356 40 +FUNC ed76 b6 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_create_node(std::pair > const&) +ed76 d 363 44 +ed83 e 365 40 +ed91 3c 367 40 +edcd b 373 40 +edd8 11 367 40 +ede9 b 368 40 +edf4 12 370 40 +ee06 b 371 40 +ee11 13 368 40 +ee24 8 373 40 +FUNC ee2c cd 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair > const&) +ee2c d 787 44 +ee39 15 789 40 +ee4e 5d 792 40 +eeab 24 796 40 +eecf f 798 40 +eede 1b 799 40 +eef9 1 799 40 +FUNC eefa 1ef 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::insert_unique(std::pair > const&) +eefa d 869 44 +ef07 e 871 40 +ef15 e 872 40 +ef23 4 873 40 +ef27 2 874 40 +ef29 6 876 40 +ef2f 35 877 40 +ef64 2a 878 40 +ef8e 6 874 40 +ef94 12 880 40 +efa6 a 881 40 +efb0 24 882 40 +efd4 51 883 40 +f025 b 885 40 +f030 36 886 40 +f066 4e 887 40 +f0b4 35 888 40 +f0e9 1 888 40 +FUNC f0ea 20 0 std::map, std::less, std::allocator > > >::insert(std::pair > const&) +f0ea c 359 45 +f0f6 14 360 45 +f116 7 98 68 +f11d 1 98 68 +f12a 1d 85 68 +f147 5 86 68 +f14c 1d 88 68 +f169 1 88 68 +FUNC f16a 29 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_get_node() +f16a c 355 45 +f176 1d 356 40 +f193 1 356 40 +FUNC f194 5f 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_create_node(std::pair const&) +f194 d 363 45 +f1a1 e 365 40 +f1af 3c 367 40 +f1eb 8 373 40 +f1f3 1 373 40 +FUNC f1f4 cd 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair const&) +f1f4 d 787 45 +f201 15 789 40 +f216 5d 792 40 +f273 24 796 40 +f297 f 798 40 +f2a6 1b 799 40 +f2c1 1 799 40 +FUNC f2c2 1ef 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::insert_unique(std::pair const&) +f2c2 d 869 45 +f2cf e 871 40 +f2dd e 872 40 +f2eb 4 873 40 +f2ef 2 874 40 +f2f1 6 876 40 +f2f7 35 877 40 +f32c 2a 878 40 +f356 6 874 40 +f35c 12 880 40 +f36e a 881 40 +f378 24 882 40 +f39c 51 883 40 +f3ed b 885 40 +f3f8 36 886 40 +f42e 4e 887 40 +f47c 35 888 40 +f4b1 1 888 40 +FUNC f4b2 20 0 std::map, std::allocator > >::insert(std::pair const&) +f4b2 c 359 45 +f4be 14 360 45 +FUNC f4d2 19 0 void std::_Destroy(std::string*) +f4d2 c 106 45 +f4de d 107 73 +f4eb 1 107 73 +FUNC f4ec 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) +f4ec c 119 45 +f4f8 2 121 73 +f4fa 13 122 73 +f50d 21 121 73 +f52e 2 122 73 +FUNC f530 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +f530 c 148 45 +f53c 1c 155 73 +f565 6 82 79 +f56b 2 85 79 +f56d 24 86 79 +f591 2c 85 79 +f5bd b 87 79 +f5c8 b 89 79 +f5d3 12 91 79 +f5e5 b 92 79 +f5f0 13 89 79 +f603 9 92 79 +f618 23 113 79 +f63b 1 113 79 +f648 1b 254 79 +f663 1 254 79 +FUNC f664 430 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, std::string const&) +f664 14 249 47 +f678 14 251 78 +f68c 22 253 78 +f6ae f 255 78 +f6bd 12 256 78 +f6cf 55 257 78 +f724 4b 260 78 +f76f e 264 78 +f77d 15 265 78 +f792 e 266 78 +f7a0 1d 271 78 +f7bd 8 272 78 +f7c5 e 273 78 +f7d3 27 275 78 +f7fa 6 276 78 +f800 55 279 78 +f855 25 284 78 +f87a b 285 78 +f885 4f 286 78 +f8d4 3 284 78 +f8d7 13 279 78 +f8ea e 286 78 +f8f8 4d 298 78 +f945 30 299 78 +f975 12 302 78 +f987 13 303 78 +f99a 23 304 78 +f9bd 3 298 78 +f9c0 13 286 78 +f9d3 b 292 78 +f9de 39 294 78 +fa17 23 295 78 +fa3a 8 296 78 +fa42 16 294 78 +fa58 3 296 78 +fa5b 19 292 78 +fa74 19 298 78 +fa8d 7 304 78 +FUNC fa94 70 0 std::vector >::push_back(std::string const&) +fa94 c 602 47 +faa0 10 604 71 +fab0 1e 606 71 +face 11 607 71 +fadf 25 610 71 +FUNC fb04 19 0 void std::_Destroy(dwarf2reader::SourceFileInfo*) +fb04 c 106 47 +fb10 d 107 73 +fb1d 1 107 73 +FUNC fb1e 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) +fb1e c 119 47 +fb2a 2 121 73 +fb2c 13 122 73 +fb3f 21 121 73 +fb60 2 122 73 +FUNC fb62 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +fb62 c 148 47 +fb6e 1c 155 73 +fb97 6 82 79 +fb9d 2 85 79 +fb9f 24 86 79 +fbc3 2c 85 79 +fbef b 87 79 +fbfa b 89 79 +fc05 12 91 79 +fc17 b 92 79 +fc22 13 89 79 +fc35 9 92 79 +fc4a 23 113 79 +fc6d 1 113 79 +fc7a 1b 254 79 +fc95 1 254 79 +FUNC fc96 43d 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, dwarf2reader::SourceFileInfo const&) +fc96 14 249 47 +fcaa 14 251 78 +fcbe 22 253 78 +fce0 f 255 78 +fcef 12 256 78 +fd01 55 257 78 +fd56 4b 260 78 +fda1 e 264 78 +fdaf 15 265 78 +fdc4 e 266 78 +fdd2 1d 271 78 +fdef 8 272 78 +fdf7 e 273 78 +fe05 27 275 78 +fe2c 6 276 78 +fe32 55 279 78 +fe87 25 284 78 +feac b 285 78 +feb7 4f 286 78 +ff06 3 284 78 +ff09 13 279 78 +ff1c e 286 78 +ff2a 4d 298 78 +ff77 36 299 78 +ffad 12 302 78 +ffbf 13 303 78 +ffd2 2a 304 78 +fffc 3 298 78 +ffff 13 286 78 +10012 b 292 78 +1001d 39 294 78 +10056 23 295 78 +10079 8 296 78 +10081 16 294 78 +10097 3 296 78 +1009a 19 292 78 +100b3 19 298 78 +100cc 7 304 78 +100d3 1 304 78 +FUNC 100d4 70 0 std::vector >::push_back(dwarf2reader::SourceFileInfo const&) +100d4 c 602 47 +100e0 10 604 71 +100f0 1e 606 71 +1010e 11 607 71 +1011f 25 610 71 +FUNC 10144 16c 0 Start +10144 17 610 71 +1015b 40 49 48 +1019b 6 51 48 +101a1 3f 53 48 +101e0 7 54 48 +101e7 5 55 48 +101ec 2a 58 48 +10216 61 61 48 +10277 7 62 48 +1027e 2 63 48 +10280 29 66 48 +102a9 7 67 48 +FUNC 102b0 108 0 Usage +102b0 19 70 48 +102c9 30 71 48 +102f9 29 73 48 +10322 30 74 48 +10352 30 75 48 +10382 30 76 48 +103b2 6 77 48 +FUNC 103b8 3af 0 SetupOptions +103b8 21 80 48 +103d9 8 82 48 +103e1 6 85 48 +103e7 10 86 48 +103f7 2e 88 48 +10425 2f 94 48 +10454 2a 91 48 +1047e 23 95 48 +104a1 3e 97 48 +104df 11 98 48 +104f0 7c 99 48 +1056c c 100 48 +10578 5 99 48 +1057d 3e 101 48 +105bb 11 102 48 +105cc 3e 103 48 +1060a 11 104 48 +1061b 37 106 48 +10652 b 107 48 +1065d c 108 48 +10669 b 113 48 +10674 c 114 48 +10680 14 119 48 +10694 30 120 48 +106c4 b 121 48 +106cf c 122 48 +106db 81 127 48 +1075c b 128 48 +10767 1 128 48 +FUNC 10768 a7 0 main +10768 13 131 48 +1077b 37 132 48 +107b2 1e 135 48 +107d0 e 136 48 +107de 8 137 48 +107e6 17 139 48 +107fd c 141 48 +10809 6 142 48 +1080f 1 142 48 +10810 c 47 49 +1081c 1a 48 49 +10836 2 49 49 +10838 c 47 49 +10844 1a 48 49 +1085e 2 49 49 +FUNC 10860 cb 0 google_breakpad::FileID::FileIdentifier(unsigned char*) +10860 f 51 49 +1086f 16 52 49 +10885 6 53 49 +1088b f 54 49 +1089a b 57 49 +108a5 7 62 49 +108ac 2 63 49 +108ae 1c 64 49 +108ca 32 63 49 +108fc b 67 49 +10907 12 68 49 +10919 10 70 49 +10929 2 71 49 +1092b 1 71 49 +FUNC 1092c f2 0 google_breakpad::FileID::MachoIdentifier(int, unsigned char*) +1092c 10 73 49 +1093c 15 74 49 +10951 20 76 49 +10971 f 77 49 +10980 20 79 49 +109a0 c 80 49 +109ac 69 82 49 +10a15 9 83 49 +FUNC 10a1e fb 0 google_breakpad::FileID::ConvertIdentifierToString(unsigned char const*, char*, int) +10a1e c 87 49 +10a2a 7 88 49 +10a31 c 89 49 +10a3d 15 90 49 +10a52 12 91 49 +10a64 18 93 49 +10a7c e 94 49 +10a8a 2b 96 49 +10ab5 2b 97 49 +10ae0 17 89 49 +10af7 20 101 49 +10b17 2 102 49 +10b19 1 102 49 +FUNC 10b1a 13 0 NXHostByteOrder +10b1a c 144 56 +10b26 5 147 56 +10b2b 2 153 56 +10b2d 1 153 56 +10b2e c 56 51 +10b3a 1a 57 51 +10b54 1e 58 51 +10b72 2 59 51 +10b74 c 56 51 +10b80 1a 57 51 +10b9a 1e 58 51 +10bb8 2 59 51 +10bba c 61 51 +10bc6 e 62 51 +10bd4 11 63 51 +10be5 2 64 51 +10be7 1 64 51 +10be8 c 61 51 +10bf4 e 62 51 +10c02 11 63 51 +10c13 2 64 51 +10c15 1 64 51 +FUNC 10c16 477 0 MacFileUtilities::MachoID::UpdateCRC(unsigned char*, unsigned long) +10c16 c 74 51 +10c22 11 82 51 +10c33 14 83 51 +10c47 5 86 51 +10c4c 9 87 51 +10c55 7 88 51 +10c5c 18b 90 51 +10de7 6 91 51 +10ded 14 89 51 +10e01 23 93 51 +10e24 23 94 51 +10e47 d 86 51 +10e54 f 98 51 +10e63 6 100 51 +10e69 18b 101 51 +10ff4 6 102 51 +10ffa c 99 51 +11006 13 105 51 +11019 8 106 51 +11021 10 104 51 +11031 23 108 51 +11054 23 109 51 +11077 14 110 51 +1108b 2 112 51 +1108d 1 112 51 +FUNC 1108e 2c 0 MacFileUtilities::MachoID::UpdateMD5(unsigned char*, unsigned long) +1108e c 114 51 +1109a 1e 115 51 +110b8 2 116 51 +FUNC 110ba 2c 0 MacFileUtilities::MachoID::UpdateSHA1(unsigned char*, unsigned long) +110ba c 118 51 +110c6 1e 119 51 +110e4 2 120 51 +FUNC 110e6 121 0 MacFileUtilities::MachoID::Update(MacFileUtilities::MachoWalker*, unsigned long, unsigned long) +110e6 f 122 51 +110f5 1b 123 51 +11110 e 129 51 +1111e 5 130 51 +11123 9 131 51 +1112c 7 132 51 +11133 a 133 51 +1113d 6 135 51 +11143 7 136 51 +1114a 35 139 51 +1117f 6c 142 51 +111eb 10 143 51 +111fb a 130 51 +11205 2 145 51 +11207 1 145 51 +FUNC 11208 cf 0 MacFileUtilities::MachoID::UUIDCommand(int, unsigned char*) +11208 14 147 51 +1121c 25 149 51 +11241 7 151 51 +11248 19 152 51 +11261 9 153 51 +1126a 8 157 51 +11272 1f 158 51 +11291 9 159 51 +1129a 36 162 51 +112d0 7 163 51 +112d7 1 163 51 +FUNC 112d8 224 0 MacFileUtilities::MachoID::IDCommand(int, unsigned char*) +112d8 15 165 51 +112ed 25 167 51 +11312 7 169 51 +11319 19 170 51 +11332 c 171 51 +1133e c 175 51 +1134a 6 180 51 +11350 7 181 51 +11357 9 182 51 +11360 9 183 51 +11369 28 185 51 +11391 33 186 51 +113c4 1e 185 51 +113e2 10 189 51 +113f2 10 190 51 +11402 10 191 51 +11412 d 192 51 +1141f 10 193 51 +1142f 10 194 51 +1143f 10 195 51 +1144f d 196 51 +1145c 17 197 51 +11473 17 198 51 +1148a 17 199 51 +114a1 14 200 51 +114b5 9 202 51 +114be 36 205 51 +114f4 8 206 51 +FUNC 114fc d1 0 MacFileUtilities::MachoID::Adler32(int) +114fc 14 208 51 +11510 25 209 51 +11535 27 210 51 +1155c d 211 51 +11569 19 213 51 +11582 9 214 51 +1158b 3b 216 51 +115c6 7 217 51 +115cd 1 217 51 +FUNC 115ce f8 0 MacFileUtilities::MachoID::MD5(int, unsigned char*) +115ce 14 219 51 +115e2 25 220 51 +11607 27 221 51 +1162e 19 223 51 +11647 19 224 51 +11660 9 225 51 +11669 17 227 51 +11680 9 228 51 +11689 36 231 51 +116bf 7 232 51 +FUNC 116c6 f8 0 MacFileUtilities::MachoID::SHA1(int, unsigned char*) +116c6 14 234 51 +116da 25 235 51 +116ff 27 236 51 +11726 19 238 51 +1173f 19 239 51 +11758 9 240 51 +11761 17 242 51 +11778 9 243 51 +11781 36 246 51 +117b7 7 247 51 +FUNC 117be 378 0 MacFileUtilities::MachoID::WalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) +117be 2b 251 51 +117e9 6 252 51 +117ef e 254 51 +117fd 38 257 51 +11835 f 258 51 +11844 9 260 51 +1184d 17 261 51 +11864 20 266 51 +11884 f 267 51 +11893 d 271 51 +118a0 c 273 51 +118ac 38 274 51 +118e4 f 275 51 +118f3 9 277 51 +118fc 1f 278 51 +1191b 14 282 51 +1192f 2b 283 51 +1195a d 285 51 +11967 19 273 51 +11980 e 287 51 +1198e 38 290 51 +119c6 f 291 51 +119d5 9 293 51 +119de 17 294 51 +119f5 20 299 51 +11a15 f 300 51 +11a24 d 304 51 +11a31 c 306 51 +11a3d 38 307 51 +11a75 f 308 51 +11a84 9 310 51 +11a8d 1f 311 51 +11aac 1a 315 51 +11ac6 39 316 51 +11aff d 318 51 +11b0c 11 306 51 +11b1d 10 323 51 +11b2d 9 324 51 +FUNC 11b36 95 0 MacFileUtilities::MachoID::UUIDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) +11b36 1e 328 51 +11b54 a 329 51 +11b5e 6 331 51 +11b64 2f 333 51 +11b93 9 335 51 +11b9c 6 337 51 +11ba2 14 338 51 +11bb6 9 340 51 +11bbf a 344 51 +11bc9 2 345 51 +11bcb 1 345 51 +FUNC 11bcc 95 0 MacFileUtilities::MachoID::IDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) +11bcc 1e 349 51 +11bea a 350 51 +11bf4 6 351 51 +11bfa 2f 353 51 +11c29 9 354 51 +11c32 6 356 51 +11c38 14 357 51 +11c4c 9 359 51 +11c55 a 363 51 +11c5f 2 364 51 +11c61 1 364 51 +FUNC 11c62 1c 0 _OSSwapInt32 +11c62 f 53 55 +11c71 8 55 55 +11c79 3 56 55 +11c7c 2 57 55 +FUNC 11c7e 19 0 NXSwapInt +11c7e f 52 56 +11c8d 8 54 56 +11c95 2 55 56 +11c97 1 55 56 +FUNC 11c98 13 0 NXHostByteOrder +11c98 c 144 56 +11ca4 5 147 56 +11ca9 2 153 56 +11cab 1 153 56 +11cac c 52 54 +11cb8 12 54 54 +11cca 1a 55 54 +11ce4 2 56 54 +11ce6 c 52 54 +11cf2 12 54 54 +11d04 1a 55 54 +11d1e 2 56 54 +11d20 c 58 54 +11d2c a 59 54 +11d36 d 60 54 +11d43 2 61 54 +11d45 1 61 54 +11d46 c 58 54 +11d52 a 59 54 +11d5c d 60 54 +11d69 2 61 54 +11d6b 1 61 54 +FUNC 11d6c 37 0 MacFileUtilities::MachoWalker::ValidateCPUType(int) +11d6c c 63 54 +11d78 6 66 54 +11d7e 8 67 54 +11d86 6 68 54 +11d8c b 69 54 +11d97 7 74 54 +11d9e 3 80 54 +11da1 2 81 54 +11da3 1 81 54 +FUNC 11da4 50 0 MacFileUtilities::MachoWalker::ReadBytes(void*, unsigned long, long long) +11da4 18 96 54 +11dbc 36 97 54 +11df2 2 98 54 +FUNC 11df4 73 0 MacFileUtilities::MachoWalker::CurrentHeader(mach_header_64*, long long*) +11df4 c 100 54 +11e00 a 101 54 +11e0a 37 102 54 +11e41 11 103 54 +11e52 9 104 54 +11e5b a 107 54 +11e65 2 108 54 +11e67 1 108 54 +FUNC 11e68 2a6 0 MacFileUtilities::MachoWalker::FindHeader(int, long long&) +11e68 c 110 54 +11e74 15 111 54 +11e89 31 114 54 +11eba c 115 54 +11ec6 10 117 54 +11ed6 4 120 54 +11eda 14 121 54 +11eee 4 122 54 +11ef2 11 129 54 +11f03 28 124 54 +11f2b c 126 54 +11f37 31 133 54 +11f68 c 134 54 +11f74 14 136 54 +11f88 b 137 54 +11f93 8 139 54 +11f9b c 140 54 +11fa7 10 142 54 +11fb7 c 143 54 +11fc3 10 146 54 +11fd3 31 148 54 +12004 c 149 54 +12010 f 151 54 +1201f 14 152 54 +12033 16 154 54 +12049 c 158 54 +12055 31 159 54 +12086 9 160 54 +1208f f 162 54 +1209e 1c 163 54 +120ba 8 165 54 +120c2 10 166 54 +120d2 9 167 54 +120db 16 170 54 +120f1 11 158 54 +12102 a 174 54 +1210c 2 175 54 +FUNC 1210e 109 0 MacFileUtilities::MachoWalker::WalkHeaderCore(long long, unsigned int, bool) +1210e 1e 224 54 +1212c c 225 54 +12138 2f 227 54 +12167 c 228 54 +12173 6 230 54 +12179 14 231 54 +1218d 5b 234 54 +121e8 12 237 54 +121fa 11 225 54 +1220b a 240 54 +12215 2 241 54 +12217 1 241 54 +FUNC 12218 10e 0 MacFileUtilities::MachoWalker::WalkHeader64AtOffset(long long) +12218 18 203 54 +12230 2f 205 54 +1225f c 206 54 +1226b e 208 54 +12279 6 209 54 +1227f 14 210 54 +12293 9 212 54 +1229c a 213 54 +122a6 f 214 54 +122b5 15 215 54 +122ca 2b 216 54 +122f5 a 217 54 +122ff a 218 54 +12309 11 219 54 +1231a a 220 54 +12324 2 221 54 +FUNC 12326 143 0 MacFileUtilities::MachoWalker::WalkHeaderAtOffset(long long) +12326 18 177 54 +1233e 2f 179 54 +1236d c 180 54 +12379 e 182 54 +12387 6 183 54 +1238d 14 184 54 +123a1 2e 189 54 +123cf 7 190 54 +123d6 9 192 54 +123df a 193 54 +123e9 f 194 54 +123f8 15 195 54 +1240d 2b 196 54 +12438 a 197 54 +12442 a 198 54 +1244c 11 199 54 +1245d a 200 54 +12467 2 201 54 +12469 1 201 54 +FUNC 1246a 99 0 MacFileUtilities::MachoWalker::WalkHeader(int) +1246a c 83 54 +12476 15 84 54 +1248b 1d 86 54 +124a8 d 87 54 +124b5 21 88 54 +124d6 21 90 54 +124f7 a 93 54 +12501 2 94 54 +12503 1 94 54 +FUNC 12504 1c 0 _OSSwapInt32 +12504 f 53 55 +12513 8 55 55 +1251b 3 56 55 +1251e 2 57 55 +FUNC 12520 2b 0 _OSSwapInt64 +12520 12 64 55 +12532 11 69 55 +12543 6 70 55 +12549 2 71 55 +1254b 1 71 55 +FUNC 1254c 19 0 NXSwapLong +1254c f 61 56 +1255b 8 63 56 +12563 2 64 56 +12565 1 64 56 +FUNC 12566 1f 0 NXSwapLongLong +12566 12 70 56 +12578 b 72 56 +12583 2 73 56 +12585 1 73 56 +FUNC 12586 32 0 breakpad_swap_uuid_command(breakpad_uuid_command*, NXByteOrder) +12586 c 37 57 +12592 11 39 57 +125a3 13 40 57 +125b6 2 41 57 +FUNC 125b8 da 0 breakpad_swap_segment_command_64(segment_command_64*, NXByteOrder) +125b8 c 44 57 +125c4 11 46 57 +125d5 13 47 57 +125e8 17 49 57 +125ff 17 50 57 +12616 17 51 57 +1262d 17 52 57 +12644 13 54 57 +12657 13 55 57 +1266a 13 56 57 +1267d 13 57 57 +12690 2 58 57 +FUNC 12692 a4 0 breakpad_swap_mach_header_64(mach_header_64*, NXByteOrder) +12692 c 61 57 +1269e 11 63 57 +126af 13 64 57 +126c2 13 65 57 +126d5 13 66 57 +126e8 13 67 57 +126fb 13 68 57 +1270e 13 69 57 +12721 13 70 57 +12734 2 71 57 +FUNC 12736 1d1 0 breakpad_swap_section_64(section_64*, unsigned int, NXByteOrder) +12736 d 75 57 +12743 c 77 57 +1274f 33 78 57 +12782 33 79 57 +127b5 2d 81 57 +127e2 2d 82 57 +1280f 2d 83 57 +1283c 2d 84 57 +12869 2d 85 57 +12896 2d 86 57 +128c3 2d 87 57 +128f0 11 77 57 +12901 6 89 57 +12907 1 89 57 +12908 12 9 58 +1291a 4f 11 58 +12969 2 12 58 +1296b 1 12 58 +1296c 12 9 58 +1297e 4f 11 58 +129cd 2 12 58 +129cf 1 12 58 +129d0 13 14 58 +129e3 2a 14 58 +12a0d 1 14 58 +12a0e 13 14 58 +12a21 2a 14 58 +12a4b 1 14 58 +12a4c 13 14 58 +12a5f 2a 14 58 +12a89 1 14 58 +FUNC 12a8a bb 0 dwarf2reader::ByteReader::SetOffsetSize(unsigned char) +12a8a 19 16 58 +12aa3 a 17 58 +12aad 48 18 58 +12af5 6 19 58 +12afb 23 20 58 +12b1e 21 22 58 +12b3f 6 24 58 +12b45 1 24 58 +FUNC 12b46 bb 0 dwarf2reader::ByteReader::SetAddressSize(unsigned char) +12b46 19 26 58 +12b5f a 27 58 +12b69 48 28 58 +12bb1 6 29 58 +12bb7 23 30 58 +12bda 21 32 58 +12bfb 6 34 58 +12c01 1 34 58 +FUNC 12c02 a2 0 dwarf2reader::ByteReader::ReadFourBytes(char const*) const +12c02 c 24 59 +12c0e c 25 64 +12c1a d 26 64 +12c27 f 27 64 +12c36 f 28 64 +12c45 b 29 64 +12c50 27 30 64 +12c77 2b 32 64 +12ca2 2 34 64 +FUNC 12ca4 40e 0 dwarf2reader::ByteReader::ReadEightBytes(char const*) const +12ca4 11 36 59 +12cb5 1a 37 64 +12ccf 1b 38 64 +12cea 1d 39 64 +12d07 1d 40 64 +12d24 1d 41 64 +12d41 1d 42 64 +12d5e 1d 43 64 +12d7b 1d 44 64 +12d98 f 45 64 +12da7 18f 47 64 +12f36 172 50 64 +130a8 a 52 64 +130b2 2 52 64 +FUNC 130b4 a6 0 ReadInitialLength +130b4 15 29 60 +130c9 18 30 60 +130e1 6 31 60 +130e7 d 35 60 +130f4 13 36 60 +13107 9 37 60 +13110 1a 38 60 +1312a 13 40 60 +1313d 9 41 60 +13146 12 43 60 +13158 2 44 60 +1315a 1f 47 60 +13179 65 50 60 +131de 1f 47 60 +131fd 65 50 60 +FUNC 13262 393 0 dwarf2reader::CompilationUnit::SkipAttribute(char const*, dwarf2reader::DwarfForm) +13262 14 133 60 +13276 82 136 60 +132f8 1f 139 60 +13317 a 140 60 +13321 21 141 60 +13342 c 147 60 +1334e e 151 60 +1335c e 155 60 +1336a e 159 60 +13378 27 162 60 +1339f 1c 166 60 +133bb 10 167 60 +133cb 1c 171 60 +133e7 10 172 60 +133f7 1e 175 60 +13415 56 180 60 +1346b d 181 60 +13478 1e 182 60 +13496 11 183 60 +134a7 1e 184 60 +134c5 24 189 60 +134e9 26 192 60 +1350f 23 195 60 +13532 22 198 60 +13554 15 199 60 +13569 1b 203 60 +13584 30 206 60 +135b4 30 208 60 +135e4 a 209 60 +135ee 7 210 60 +135f5 1 210 60 +FUNC 135f6 29b 0 dwarf2reader::CompilationUnit::ReadHeader() +135f6 14 217 60 +1360a 9 218 60 +13613 4e 221 60 +13661 17 223 60 +13678 a 224 60 +13682 f 225 60 +13691 4e 227 60 +136df 1e 228 60 +136fd 6 229 60 +13703 5e 231 60 +13761 1e 232 60 +1377f 18 233 60 +13797 4c 235 60 +137e3 1d 236 60 +13800 1c 237 60 +1381c 5 238 60 +13821 9 240 60 +1382a 60 245 60 +1388a 7 247 60 +13891 1 247 60 +FUNC 13892 a57 0 dwarf2reader::CompilationUnit::ProcessAttribute(unsigned long long, char const*, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm) +13892 24 299 60 +138b6 8a 302 60 +13940 1f 307 60 +1395f a 308 60 +13969 36 309 60 +1399f 5b 316 60 +139fa c 317 60 +13a06 5b 322 60 +13a61 e 323 60 +13a6f 55 328 60 +13ac4 e 329 60 +13ad2 55 334 60 +13b27 e 335 60 +13b35 6 338 60 +13b3b 9a 340 60 +13bd5 33 341 60 +13c08 25 340 60 +13c2d 5c 348 60 +13c89 10 349 60 +13c99 5c 354 60 +13cf5 10 355 60 +13d05 55 359 60 +13d5a 1e 360 60 +13d78 56 365 60 +13dce d 366 60 +13ddb 55 368 60 +13e30 1e 369 60 +13e4e 11 370 60 +13e5f 55 372 60 +13eb4 1e 373 60 +13ed2 29 378 60 +13efb 4a 380 60 +13f45 f 381 60 +13f54 29 385 60 +13f7d 4c 387 60 +13fc9 11 388 60 +13fda 1b 392 60 +13ff5 4c 394 60 +14041 11 395 60 +14052 22 399 60 +14074 4e 401 60 +140c2 15 402 60 +140d7 3c 406 60 +14113 1b 408 60 +1412e 54 409 60 +14182 f 411 60 +14191 9a 413 60 +1422b 24 414 60 +1424f 25 413 60 +14274 30 418 60 +142a4 30 420 60 +142d4 a 421 60 +142de b 422 60 +142e9 1 422 60 +142ea 1f 489 60 +14309 3a 491 60 +14343 a 492 60 +1434d 6 493 60 +14353 1 493 60 +14354 1f 489 60 +14373 3a 491 60 +143ad a 492 60 +143b7 6 493 60 +143bd 1 493 60 +FUNC 143be b5 0 dwarf2reader::CompilationUnit::ProcessDIE(unsigned long long, char const*, dwarf2reader::CompilationUnit::Abbrev const&) +143be 19 426 60 +143d7 13 427 60 +143ea 46 430 60 +14430 3a 427 60 +1446a 3 432 60 +1446d 6 433 60 +14473 1 433 60 +FUNC 14474 85 0 dwarf2reader::CompilationUnit::SkipDIE(char const*, dwarf2reader::CompilationUnit::Abbrev const&) +14474 c 122 60 +14480 13 123 60 +14493 27 126 60 +144ba 3a 123 60 +144f4 3 128 60 +144f7 2 129 60 +144f9 1 129 60 +FUNC 144fa be4 0 dwarf2reader::LineInfo::ProcessOneOpcode(dwarf2reader::ByteReader*, dwarf2reader::LineInfoHandler*, dwarf2reader::LineInfoHeader const&, char const*, dwarf2reader::LineStateMachine*, unsigned long*, unsigned long, bool*) +144fa 18 593 60 +14512 a 594 60 +1451c 18 596 60 +14534 8 597 60 +1453c 5 598 60 +14541 19 602 60 +1455a f 603 60 +14569 50 605 60 +145b9 46 607 60 +145ff e0 610 60 +146df 6 612 60 +146e5 22 615 60 +14707 22 616 60 +14729 7 617 60 +14730 b 618 60 +1473b f 619 60 +1474a 5a 623 60 +147a4 7 625 60 +147ab b 626 60 +147b6 f 627 60 +147c5 28 631 60 +147ed e 632 60 +147fb 144 635 60 +1493f 6 637 60 +14945 9e 640 60 +149e3 5 642 60 +149e8 22 644 60 +14a0a e 645 60 +14a18 1c 646 60 +14a34 2b 652 60 +14a5f b 653 60 +14a6a 22 658 60 +14a8c e 659 60 +14a9a 8 660 60 +14aa2 5 662 60 +14aa7 22 664 60 +14ac9 e 665 60 +14ad7 9 666 60 +14ae0 5 668 60 +14ae5 12 670 60 +14af7 5 672 60 +14afc 7 674 60 +14b03 5 676 60 +14b08 16 678 60 +14b1e 9 679 60 +14b27 d9 682 60 +14c00 6 684 60 +14c06 1f 687 60 +14c25 5 689 60 +14c2a 40 693 60 +14c6a d6 696 60 +14d40 6 698 60 +14d46 1c 701 60 +14d62 5 703 60 +14d67 1f 706 60 +14d86 d 707 60 +14d93 13 708 60 +14da6 26 710 60 +14dcc 5 711 60 +14dd1 50 713 60 +14e21 7 715 60 +14e28 b 716 60 +14e33 f 717 60 +14e42 18 725 60 +14e5a f 726 60 +14e69 5 728 60 +14e6e 6 730 60 +14e74 28 732 60 +14e9c d 733 60 +14ea9 22 735 60 +14ecb e 736 60 +14ed9 22 739 60 +14efb e 740 60 +14f09 22 743 60 +14f2b e 744 60 +14f39 a 746 60 +14f43 fd 748 60 +15040 a 758 60 +1504a 9 759 60 +15053 1c 761 60 +1506f d 762 60 +1507c e 763 60 +1508a 2e 759 60 +150b8 b 769 60 +150c3 10 770 60 +150d3 b 771 60 +FUNC 150de 14b 0 dwarf2reader::LineInfo::ReadLines() +150de e 773 60 +150ec 9 778 60 +150f5 17 782 60 +1510c 8 783 60 +15114 6 785 60 +1511a 9 787 60 +15123 5 788 60 +15128 19 789 60 +15141 5 790 60 +15146 4a 793 60 +15190 6 794 60 +15196 4a 796 60 +151e0 a 797 60 +151ea f 790 60 +151f9 15 788 60 +1520e 14 801 60 +15222 7 802 60 +15229 1 802 60 +FUNC 1522a 4fd 0 dwarf2reader::CompilationUnit::ReadAbbrevs() +1522a 18 60 60 +15242 e 61 60 +15250 58 65 60 +152a8 38 66 60 +152e0 44 65 60 +15324 2a 66 60 +1534e 45 68 60 +15393 16 69 60 +153a9 1d 75 60 +153c6 6 76 60 +153cc 40 77 60 +1540c b 80 60 +15417 1f 82 60 +15436 e 84 60 +15444 6 77 60 +1544a 1f 68 60 +15469 a 84 60 +15473 1d 79 60 +15490 6 86 60 +15496 a 87 60 +154a0 3d 89 60 +154dd 1f 90 60 +154fc a 91 60 +15506 6 92 60 +1550c 3d 94 60 +15549 1d 95 60 +15566 5 96 60 +1556b 3d 98 60 +155a8 1f 101 60 +155c7 a 102 60 +155d1 3d 104 60 +1560e 1f 105 60 +1562d a 106 60 +15637 c 107 60 +15643 6 111 60 +15649 6 112 60 +1564f 32 113 60 +15681 47 115 60 +156c8 30 116 60 +156f8 24 79 60 +1571c b 118 60 +15727 1 118 60 +FUNC 15728 5dc 0 dwarf2reader::LineInfo::ReadHeader() +15728 18 503 60 +15740 9 504 60 +15749 17 508 60 +15760 a 510 60 +1576a f 511 60 +15779 60 512 60 +157d9 44 516 60 +1581d 1e 518 60 +1583b 6 519 60 +15841 1e 521 60 +1585f 18 522 60 +15877 1d 524 60 +15894 5 525 60 +15899 20 527 60 +158b9 5 528 60 +158be c 530 60 +158ca 5 531 60 +158cf 1d 533 60 +158ec 5 534 60 +158f1 1d 536 60 +1590e 5 537 60 +15913 45 539 60 +15958 1f 540 60 +15977 19 541 60 +15990 15 542 60 +159a5 1f 539 60 +159c4 30 543 60 +159f4 5 544 60 +159f9 14 542 60 +15a0d e 548 60 +15a1b 7 549 60 +15a22 5 550 60 +15a27 6 551 60 +15a2d 8b 552 60 +15ab8 28 553 60 +15ae0 5 554 60 +15ae5 16 550 60 +15afb 25 552 60 +15b20 5 557 60 +15b25 e 560 60 +15b33 7 561 60 +15b3a 5 563 60 +15b3f 6 564 60 +15b45 28 565 60 +15b6d 22 567 60 +15b8f a 568 60 +15b99 22 570 60 +15bbb a 571 60 +15bc5 22 573 60 +15be7 a 574 60 +15bf1 ba 576 60 +15cab 5 577 60 +15cb0 16 563 60 +15cc6 25 576 60 +15ceb 5 580 60 +15cf0 9 582 60 +15cf9 b 583 60 +FUNC 15d04 3d 0 dwarf2reader::LineInfo::Start() +15d04 c 495 60 +15d10 b 496 60 +15d1b b 497 60 +15d26 19 498 60 +15d3f 2 499 60 +15d41 1 499 60 +FUNC 15d42 304 0 dwarf2reader::CompilationUnit::ProcessDIEs() +15d42 11 435 60 +15d53 9 436 60 +15d5c 9 441 60 +15d65 17 445 60 +15d7c 8 446 60 +15d84 6 448 60 +15d8a 6c 453 60 +15df6 8 455 60 +15dfe 16 453 60 +15e14 3 455 60 +15e17 2f 453 60 +15e46 29 458 60 +15e6f 22 460 60 +15e91 a 462 60 +15e9b a 465 60 +15ea5 1e 466 60 +15ec3 13 467 60 +15ed6 2b 468 60 +15f01 18 472 60 +15f19 9 473 60 +15f22 42 474 60 +15f64 1e 475 60 +15f82 2a 477 60 +15fac b 480 60 +15fb7 1e 481 60 +15fd5 26 483 60 +15ffb 1d 455 60 +16018 24 485 60 +1603c a 486 60 +FUNC 16046 35f 0 dwarf2reader::CompilationUnit::Start() +16046 18 249 60 +1605e 58 251 60 +160b6 35 252 60 +160eb 32 251 60 +1611d 2a 252 60 +16147 20 255 60 +16167 37 256 60 +1619e b 259 60 +161a9 f 264 60 +161b8 17 265 60 +161cf c 266 60 +161db a 268 60 +161e5 95 271 60 +1627a 11 276 60 +1628b b 279 60 +16296 58 282 60 +162ee 2f 283 60 +1631d 32 282 60 +1634f 14 284 60 +16363 1a 285 60 +1637d b 289 60 +16388 12 291 60 +1639a b 292 60 +163a5 1 292 60 +FUNC 163a6 3a 0 std::fill(unsigned char*, unsigned char*, unsigned char const&) +163a6 c 573 61 +163b2 9 576 61 +163bb 23 577 61 +163de 2 578 61 +FUNC 163e0 33 0 std::__deque_buf_size(unsigned long) +163e0 c 83 62 +163ec 27 84 62 +16413 1 84 62 +FUNC 16414 18 0 dwarf2reader::ByteReader::OffsetSize() const +16414 c 38 63 +16420 c 38 63 +FUNC 1642c 18 0 dwarf2reader::ByteReader::AddressSize() const +1642c c 41 63 +16438 c 41 63 +FUNC 16444 17 0 dwarf2reader::ByteReader::ReadOneByte(char const*) const +16444 c 10 64 +16450 9 11 64 +16459 2 12 64 +1645b 1 12 64 +FUNC 1645c 63 0 dwarf2reader::ByteReader::ReadTwoBytes(char const*) const +1645c c 14 64 +16468 d 15 64 +16475 e 16 64 +16483 b 17 64 +1648e 17 18 64 +164a5 18 20 64 +164bd 2 22 64 +164bf 1 22 64 +FUNC 164c0 98 0 dwarf2reader::ByteReader::ReadUnsignedLEB128(char const*, unsigned long*) const +164c0 e 59 64 +164ce e 60 64 +164dc 7 61 64 +164e3 7 62 64 +164ea e 66 64 +164f8 5 67 64 +164fd 38 69 64 +16535 6 71 64 +1653b 8 65 64 +16543 8 75 64 +1654b 6 77 64 +16551 7 78 64 +FUNC 16558 ee 0 dwarf2reader::ByteReader::ReadSignedLEB128(char const*, unsigned long*) const +16558 e 84 64 +16566 e 85 64 +16574 7 86 64 +1657b 7 87 64 +16582 e 91 64 +16590 5 92 64 +16595 44 93 64 +165d9 6 94 64 +165df 8 90 64 +165e7 14 97 64 +165fb 36 98 64 +16631 8 99 64 +16639 6 100 64 +1663f 7 101 64 +FUNC 16646 a2 0 dwarf2reader::ByteReader::ReadOffset(char const*) const +16646 13 103 64 +16659 3f 104 64 +16698 4a 105 64 +166e2 6 106 64 +FUNC 166e8 a2 0 dwarf2reader::ByteReader::ReadAddress(char const*) const +166e8 13 108 64 +166fb 3f 109 64 +1673a 4a 110 64 +16784 6 111 64 +FUNC 1678a 61 0 dwarf2reader::LineStateMachine::Reset(bool) +1678a 12 12 65 +1679c 9 13 65 +167a5 11 14 65 +167b6 11 15 65 +167c7 a 16 65 +167d1 a 17 65 +167db 7 18 65 +167e2 7 19 65 +167e9 2 20 65 +167eb 1 20 65 +FUNC 167ec 20 0 std::_List_const_iterator >::operator!=(std::_List_const_iterator > const&) const +167ec c 253 66 +167f8 14 254 66 +FUNC 1680c 25 0 std::_List_const_iterator >::operator++(int) +1680c c 226 66 +16818 8 228 66 +16820 c 229 66 +1682c 5 230 66 +16831 1 230 66 +FUNC 16832 16 0 std::_List_const_iterator >::operator->() const +16832 c 215 66 +1683e a 216 66 +16848 c 190 67 +16854 a 190 67 +FUNC 1685e 13 0 std::auto_ptr > > >::operator->() const +1685e c 283 67 +1686a 7 286 67 +16871 1 286 67 +16872 c 65 68 +1687e 2 65 68 +16880 c 97 69 +1688c d 97 69 +16899 1 97 69 +1689a c 99 69 +168a6 14 100 69 +168ba c 97 69 +168c6 d 97 69 +168d3 1 97 69 +168d4 c 84 70 +168e0 17 85 70 +168f7 1 85 70 +FUNC 168f8 2d 0 std::pair std::make_pair(dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm) +168f8 c 144 70 +16904 21 145 70 +16925 1 145 70 +16926 c 202 66 +16932 a 203 66 +FUNC 1693c 25 0 std::list, std::allocator > >::begin() const +1693c c 588 70 +16948 19 589 66 +16961 1 589 66 +FUNC 16962 23 0 std::list, std::allocator > >::end() const +16962 c 605 70 +1696e 17 606 66 +16985 1 606 66 +16986 c 65 68 +16992 2 65 68 +16994 c 72 68 +169a0 2 72 68 +169a2 c 97 69 +169ae d 97 69 +169bb 1 97 69 +169bc c 105 69 +169c8 d 105 69 +169d5 1 105 69 +169d6 c 105 69 +169e2 d 105 69 +169ef 1 105 69 +169f0 c 67 68 +169fc 2 67 68 +169fe c 99 69 +16a0a 14 100 69 +16a1e c 99 69 +16a2a 14 100 69 +16a3e c 129 62 +16a4a 30 131 62 +16a7a c 65 68 +16a86 2 65 68 +16a88 c 72 68 +16a94 2 72 68 +16a96 c 97 69 +16aa2 d 97 69 +16aaf 1 97 69 +16ab0 c 105 69 +16abc d 105 69 +16ac9 1 105 69 +16aca c 105 69 +16ad6 d 105 69 +16ae3 1 105 69 +16ae4 c 67 68 +16af0 2 67 68 +16af2 c 99 69 +16afe 14 100 69 +16b12 c 99 69 +16b1e 14 100 69 +FUNC 16b32 2b 0 std::_Vector_base >::get_allocator() const +16b32 10 93 71 +16b42 1b 94 71 +16b5d 1 94 71 +16b6a 7 614 72 +16b71 1 614 72 +16b72 c 80 71 +16b7e d 80 71 +16b8b 1 80 71 +16b98 2 107 68 +FUNC 16b9a 2d 0 void std::_Destroy >(unsigned char*, unsigned char*, std::allocator) +16b9a c 171 73 +16ba6 2 173 73 +16ba8 12 174 73 +16bba b 173 73 +16bc5 2 174 73 +16bc7 1 174 73 +16bc8 c 84 71 +16bd4 2f 85 71 +16c03 2 86 71 +16c05 1 86 71 +16c06 c 96 71 +16c12 12 97 71 +16c24 2 98 71 +FUNC 16c26 1f 0 std::_List_base, std::allocator > >::_M_init() +16c26 c 338 73 +16c32 8 340 66 +16c3a b 341 66 +16c45 1 341 66 +16c46 c 105 69 +16c52 d 105 69 +16c5f 1 105 69 +16c60 c 125 66 +16c6c a 126 66 +FUNC 16c76 25 0 std::list, std::allocator > >::begin() +16c76 c 579 73 +16c82 19 580 66 +16c9b 1 580 66 +FUNC 16c9c 23 0 std::list, std::allocator > >::end() +16c9c c 597 73 +16ca8 17 597 66 +16cbf 1 597 66 +16cc0 c 603 72 +16ccc c 603 72 +FUNC 16cd8 2b 0 std::vector >::begin() const +16cd8 c 342 73 +16ce4 1f 343 71 +16d03 1 343 71 +FUNC 16d04 2c 0 std::vector >::end() const +16d04 c 360 73 +16d10 20 361 71 +16d3c 5 666 72 +16d41 1 666 72 +16d4f 31 759 72 +FUNC 16d80 3c 0 std::vector >::size() const +16d80 c 402 73 +16d8c 30 403 71 +16dbc c 603 72 +16dc8 c 603 72 +FUNC 16dd4 23 0 std::vector >::begin() +16dd4 c 333 73 +16de0 17 334 71 +16df7 1 334 71 +16e04 33 654 72 +16e37 1 654 72 +FUNC 16e38 26 0 std::vector >::end() +16e38 c 351 73 +16e44 1a 352 71 +16e6a 7 614 72 +16e71 1 614 72 +FUNC 16e72 42 0 std::vector >::operator[](unsigned long) +16e72 c 494 73 +16e7e 36 495 71 +FUNC 16eb4 13 0 std::vector >::max_size() const +16eb4 c 407 73 +16ec0 7 408 71 +16ec7 1 408 71 +16ed4 5 666 72 +16ed9 1 666 72 +16ee6 d 623 72 +16ef3 5 624 72 +16ef8 c 382 62 +16f04 d 382 62 +16f11 1 382 62 +FUNC 16f12 2b 0 std::_Deque_base >::get_allocator() const +16f12 10 360 73 +16f22 1b 361 62 +16f3d 1 361 62 +FUNC 16f3e 2d 0 std::deque >::get_allocator() const +16f3e 10 764 73 +16f4e 1d 765 62 +16f6b 1 765 62 +FUNC 16f6c 13 0 std::_Deque_iterator::operator*() const +16f6c c 134 73 +16f78 7 135 62 +16f7f 1 135 62 +16f8c 2 107 68 +16f8e c 129 62 +16f9a 30 131 62 +FUNC 16fca 2c 0 std::deque >::end() const +16fca 10 799 73 +16fda 1c 800 62 +FUNC 16ff6 2c 0 std::deque >::begin() const +16ff6 10 781 73 +17006 1c 782 62 +FUNC 17022 2e 0 std::deque >::end() +17022 10 790 73 +17032 1e 791 62 +FUNC 17050 3c 0 std::vector >::_M_range_check(unsigned long) const +17050 13 515 73 +17063 15 517 71 +17078 14 518 71 +FUNC 1708c 32 0 std::vector >::at(unsigned long) +1708c c 534 73 +17098 12 536 71 +170aa 14 537 71 +170ca 2e 104 68 +170f8 c 84 71 +17104 2f 85 71 +17133 2 86 71 +17135 1 86 71 +17136 c 96 71 +17142 12 97 71 +17154 2 98 71 +17156 c 603 72 +17162 c 603 72 +FUNC 1716e 23 0 std::vector >::begin() +1716e c 333 73 +1717a 17 334 71 +17191 1 334 71 +1719e 27 654 72 +171c5 1 654 72 +FUNC 171c6 42 0 std::vector >::operator[](unsigned long) +171c6 c 494 73 +171d2 36 495 71 +FUNC 17208 26 0 std::vector >::end() +17208 c 351 73 +17214 1a 352 71 +1723a d 94 68 +17247 1 94 68 +FUNC 17248 2f 0 std::_Vector_base >::_M_deallocate(unsigned char*, unsigned long) +17248 c 120 73 +17254 6 122 71 +1725a 1d 123 71 +17277 1 123 71 +17278 c 108 71 +17284 3a 109 71 +172be c 188 71 +172ca 12 189 71 +172dc 2 190 71 +172de c 272 71 +172ea 4b 273 71 +17335 1 273 71 +17336 13 62 74 +17349 10 62 74 +17359 a 63 74 +17363 25 64 74 +17388 1a 66 74 +173a2 13 62 74 +173b5 10 62 74 +173c5 a 63 74 +173cf 25 64 74 +173f4 1a 66 74 +1740e c 188 71 +1741a 12 189 71 +1742c 2 190 71 +1743b 31 759 72 +1746c c 65 68 +17478 2 65 68 +1747a c 103 69 +17486 d 103 69 +17493 1 103 69 +FUNC 17494 2d 0 std::list, std::allocator > >::get_allocator() const +17494 10 570 74 +174a4 1d 571 66 +174c1 1 571 66 +174ce 2e 104 68 +FUNC 174fc 20 0 std::_List_iterator >::operator!=(std::_List_iterator > const&) const +174fc c 172 74 +17508 14 173 66 +FUNC 1751c 1d 0 std::_List_const_iterator >::operator++() +1751c c 219 74 +17528 c 221 66 +17534 5 222 66 +17539 1 222 66 +FUNC 1753a 1d 0 std::_List_iterator >::operator++() +1753a c 138 74 +17546 c 140 66 +17552 5 141 66 +17557 1 141 66 +FUNC 17558 16 0 std::_List_const_iterator >::operator*() const +17558 c 211 74 +17564 a 212 66 +FUNC 1756e 16 0 std::_List_iterator >::operator*() const +1756e c 130 74 +1757a a 131 66 +FUNC 17584 20 0 std::_List_const_iterator >::operator==(std::_List_const_iterator > const&) const +17584 c 249 74 +17590 14 250 66 +FUNC 175a4 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +175a4 d 699 74 +175b1 28 700 72 +175d9 1 700 72 +FUNC 175da 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +175da c 171 74 +175e6 2 173 73 +175e8 1a 174 73 +17602 21 173 73 +17623 2 174 73 +17625 1 174 73 +17626 c 127 62 +17632 29 127 62 +1765b 1 127 62 +1765c c 388 62 +17668 41 389 62 +176a9 2 390 62 +176ab 1 390 62 +176b8 d 94 68 +176c5 1 94 68 +FUNC 176c6 20 0 bool std::operator==(std::_Deque_iterator const&, std::_Deque_iterator const&) +176c6 c 243 74 +176d2 14 244 62 +FUNC 176e6 26 0 bool std::operator!=(std::_Deque_iterator const&, std::_Deque_iterator const&) +176e6 c 256 74 +176f2 1a 257 62 +FUNC 1770c 1a 0 std::_Deque_iterator::_S_buffer_size() +1770c c 106 74 +17718 e 107 62 +FUNC 17726 3e 0 std::_Deque_iterator::_M_set_node(unsigned long long**) +17726 d 229 74 +17733 9 231 62 +1773c b 232 62 +17747 1d 233 62 +FUNC 17764 50 0 std::_Deque_iterator::operator++() +17764 c 142 74 +17770 d 144 62 +1777d f 145 62 +1778c 18 147 62 +177a4 b 148 62 +177af 5 150 62 +FUNC 177b4 4b 0 void std::_Destroy, std::allocator >(std::_Deque_iterator, std::_Deque_iterator, std::allocator) +177b4 c 171 74 +177c0 2 173 73 +177c2 1a 174 73 +177dc 21 173 73 +177fd 2 174 73 +177ff 1 174 73 +FUNC 17800 50 0 std::_Deque_iterator::operator--() +17800 c 162 74 +1780c f 164 62 +1781b 18 166 62 +17833 b 167 62 +1783e d 169 62 +1784b 5 170 62 +FUNC 17850 39 0 std::deque >::back() +17850 c 988 74 +1785c 15 990 62 +17871 b 991 62 +1787c d 992 62 +17889 1 992 62 +FUNC 1788a 19 0 std::stack > >::top() +1788a c 163 75 +17896 d 166 75 +178a3 1 166 75 +FUNC 178a4 66 0 std::_Deque_iterator::difference_type std::operator-(std::_Deque_iterator const&, std::_Deque_iterator const&) +178a4 d 328 75 +178b1 59 333 62 +FUNC 1790a 26 0 std::deque >::size() const +1790a c 840 75 +17916 1a 841 62 +1793c 36 662 72 +1797e 23 650 72 +179a1 1 650 72 +179a2 c 67 68 +179ae 2 67 68 +179b0 c 99 69 +179bc 14 100 69 +179d0 c 303 66 +179dc 12 304 66 +179ee 2 305 66 +179f0 c 326 66 +179fc 2f 327 66 +17a2b d 328 66 +17a38 c 457 66 +17a44 14 458 66 +17a58 c 211 74 +17a64 2d 211 74 +17a91 1 211 74 +17a9e 7 98 68 +17aa5 1 98 68 +17ab2 1d 85 68 +17acf 5 86 68 +17ad4 17 88 68 +17aeb 1 88 68 +FUNC 17aec 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +17aec c 116 75 +17af8 1e 117 71 +17b22 d 94 68 +17b2f 1 94 68 +FUNC 17b30 34 0 std::_Deque_base >::_M_deallocate_node(unsigned long long*) +17b30 c 402 75 +17b3c 28 403 62 +FUNC 17b64 38 0 std::_Deque_base >::_M_destroy_nodes(unsigned long long**, unsigned long long**) +17b64 c 504 75 +17b70 8 506 62 +17b78 14 507 62 +17b8c e 506 62 +17b9a 2 507 62 +FUNC 17b9c 62 0 std::deque >::_M_pop_back_aux() +17b9c c 391 76 +17ba8 15 393 76 +17bbd 1b 394 76 +17bd8 f 395 76 +17be7 17 396 76 +FUNC 17bfe 4f 0 std::deque >::pop_back() +17bfe c 1081 76 +17c0a 10 1083 62 +17c1a f 1086 62 +17c29 17 1087 62 +17c40 d 1090 62 +17c4d 1 1090 62 +FUNC 17c4e 19 0 std::stack > >::pop() +17c4e c 205 76 +17c5a d 208 75 +17c67 1 208 75 +17c68 c 72 68 +17c74 2 72 68 +17c76 c 105 69 +17c82 d 105 69 +17c8f 1 105 69 +17c90 c 603 72 +17c9c c 603 72 +FUNC 17ca8 2b 0 std::vector >::begin() const +17ca8 c 342 76 +17cb4 1f 343 71 +17cd3 1 343 71 +FUNC 17cd4 2c 0 std::vector >::end() const +17cd4 c 360 76 +17ce0 20 361 71 +17d0c 5 666 72 +17d11 1 666 72 +17d1f 28 759 72 +17d47 1 759 72 +FUNC 17d48 3c 0 std::vector >::size() const +17d48 c 402 76 +17d54 30 403 71 +17d90 d 623 72 +17d9d 5 624 72 +17dae 5 666 72 +17db3 1 666 72 +FUNC 17db4 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +17db4 d 699 76 +17dc1 28 700 72 +17de9 1 700 72 +FUNC 17dea 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +17dea c 171 76 +17df6 2 173 73 +17df8 1a 174 73 +17e12 21 173 73 +17e33 2 174 73 +17e35 1 174 73 +17e43 28 759 72 +17e6b 1 759 72 +17e78 2a 662 72 +FUNC 17ea2 13 0 std::vector >::max_size() const +17ea2 c 407 76 +17eae 7 408 71 +17eb5 1 408 71 +17ec2 16 650 72 +17ee4 7 98 68 +17eeb 1 98 68 +17ef8 1d 85 68 +17f15 5 86 68 +17f1a 10 88 68 +FUNC 17f2a 29 0 std::_List_base, std::allocator > >::_M_get_node() +17f2a c 311 76 +17f36 1d 312 66 +17f53 1 312 66 +FUNC 17f54 5f 0 std::list, std::allocator > >::_M_create_node(std::pair const&) +17f54 d 435 76 +17f61 e 437 66 +17f6f 3c 440 66 +17fab 8 447 66 +17fb3 1 447 66 +FUNC 17fb4 35 0 std::list, std::allocator > >::_M_insert(std::_List_iterator >, std::pair const&) +17fb4 c 1149 76 +17fc0 15 1151 66 +17fd5 14 1152 66 +17fe9 1 1152 66 +FUNC 17fea 52 0 void std::list, std::allocator > >::_M_insert_dispatch > >(std::_List_iterator >, std::_List_const_iterator >, std::_List_const_iterator >, __false_type) +17fea c 1126 66 +17ff6 2 1128 66 +17ff8 21 1129 66 +18019 21 1128 66 +1803a 2 1129 66 +FUNC 1803c 36 0 void std::list, std::allocator > >::insert > >(std::_List_iterator >, std::_List_const_iterator >, std::_List_const_iterator >) +1803c c 838 66 +18048 2a 842 66 +18072 e 491 66 +18080 32 492 66 +180b2 64 493 66 +18116 c 211 74 +18122 3d 211 74 +1815f 1 211 74 +1816d 5c 104 68 +181c9 1 104 68 +FUNC 181ca 31 0 std::list, std::allocator > >::push_back(std::pair const&) +181ca c 772 76 +181d6 25 773 66 +181fb 1 773 66 +FUNC 181fc 69 0 void std::_Construct(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev const&) +181fc d 77 76 +18209 5c 81 73 +18265 1 81 73 +18272 7 98 68 +18279 1 98 68 +18286 1d 85 68 +182a3 5 86 68 +182a8 10 88 68 +182b8 c 65 68 +182c4 2 65 68 +182c6 c 103 69 +182d2 d 103 69 +182df 1 103 69 +FUNC 182e0 4d 0 std::_Deque_base >::_M_get_map_allocator() const +182e0 11 394 76 +182f1 3c 395 62 +1832d 1 395 62 +FUNC 1832e 75 0 std::_Deque_base >::_M_allocate_map(unsigned long) +1832e d 406 76 +1833b 68 407 62 +183a3 1 407 62 +FUNC 183a4 47 0 std::_Deque_base >::_M_deallocate_map(unsigned long long**, unsigned long) +183a4 c 410 76 +183b0 3b 411 62 +183eb 1 411 62 +183ec c 424 62 +183f8 9 426 62 +18401 22 428 62 +18423 2b 430 62 +1844e c 714 62 +1845a 70 715 62 +184ca c 111 75 +184d6 d 111 75 +184e3 1 111 75 +184e4 c 259 67 +184f0 26 259 67 +18522 7 98 68 +18529 1 98 68 +18536 1d 85 68 +18553 5 86 68 +18558 10 88 68 +FUNC 18568 33 0 std::_Deque_base >::_M_allocate_node() +18568 c 398 76 +18574 27 399 62 +1859b 1 399 62 +FUNC 1859c 82 0 std::_Deque_base >::_M_create_nodes(unsigned long long**, unsigned long long**) +1859c d 486 76 +185a9 8 491 62 +185b1 12 492 62 +185c3 13 491 62 +185d6 b 494 62 +185e1 19 496 62 +185fa b 497 62 +18605 13 494 62 +18618 6 497 62 +FUNC 1861e 17b 0 std::_Deque_base >::_M_initialize_map(unsigned long) +1861e d 447 76 +1862b 1e 450 62 +18649 2a 452 62 +18673 1c 454 62 +1868f 19 462 62 +186a8 c 463 62 +186b4 1e 466 62 +186d2 b 467 62 +186dd 1e 469 62 +186fb 9 470 62 +18704 a 471 62 +1870e b 472 62 +18719 13 467 62 +1872c 15 475 62 +18741 18 476 62 +18759 c 477 62 +18765 34 478 62 +18799 1 478 62 +1879a d 366 62 +187a7 12 367 62 +187b9 39 368 62 +187f2 c 645 62 +187fe 1c 646 62 +FUNC 1881a 4d 0 void std::__fill::fill<__gnu_cxx::__normal_iterator > >, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char const&) +1881a c 539 61 +18826 9 541 61 +1882f 2 542 61 +18831 13 543 61 +18844 21 542 61 +18865 2 543 61 +18867 1 543 61 +FUNC 18868 2b 0 void std::fill<__gnu_cxx::__normal_iterator > >, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char const&) +18868 c 560 76 +18874 4 567 61 +18878 1b 568 61 +18893 1 568 61 +FUNC 18894 6a 0 std::list, std::allocator > >::_M_erase(std::_List_iterator >) +18894 d 1157 76 +188a1 b 1159 66 +188ac 6 1160 66 +188b2 35 1161 66 +188e7 17 1162 66 +FUNC 188fe 37 0 std::list, std::allocator > >::erase(std::_List_iterator >) +188fe c 95 77 +1890a 14 97 77 +1891e 12 98 77 +18930 5 99 77 +18935 1 99 77 +FUNC 18936 3e 0 std::list, std::allocator > >::erase(std::_List_iterator >, std::_List_iterator >) +18936 c 883 77 +18942 2 885 66 +18944 15 886 66 +18959 16 885 66 +1896f 5 887 66 +FUNC 18974 129 0 std::list, std::allocator > >::operator=(std::list, std::allocator > > const&) +18974 e 120 77 +18982 c 122 77 +1898e e 124 77 +1899c e 125 77 +189aa e 126 77 +189b8 e 127 77 +189c6 2 128 77 +189c8 20 130 77 +189e8 5a 128 77 +18a42 16 131 77 +18a58 1b 132 77 +18a73 20 134 77 +18a93 a 136 77 +18a9d 1 136 77 +FUNC 18a9e 4c 0 dwarf2reader::CompilationUnit::Abbrev::operator=(dwarf2reader::CompilationUnit::Abbrev const&) +18a9e c 211 77 +18aaa 40 211 74 +FUNC 18aea 52 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy::copy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18aea c 280 61 +18af6 1a 283 61 +18b10 12 285 61 +18b22 4 286 61 +18b26 6 287 61 +18b2c b 283 61 +18b37 5 289 61 +FUNC 18b3c 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18b3c c 307 77 +18b48 4 315 61 +18b4c 1b 317 61 +18b67 1 317 61 +18b76 56 354 61 +18bd8 4 384 61 +18bdc 4 385 61 +18be0 1b 387 61 +18bfb 1 387 61 +FUNC 18bfc ac 0 std::vector >::erase(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +18bfc d 122 78 +18c09 26 124 78 +18c2f 43 125 78 +18c72 2e 126 78 +18ca0 8 127 78 +FUNC 18ca8 54 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward::copy_b(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18ca8 c 408 61 +18cb4 1a 411 61 +18cce 1e 412 61 +18cec b 411 61 +18cf7 5 413 61 +FUNC 18cfc 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18cfc c 432 78 +18d08 4 440 61 +18d0c 1b 443 61 +18d27 1 443 61 +18d36 56 482 61 +18d98 4 514 61 +18d9c 4 515 61 +18da0 1b 517 61 +18dbb 1 517 61 +FUNC 18dbc 4d 0 void std::__fill::fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +18dbc c 526 61 +18dc8 2 528 61 +18dca 1c 529 61 +18de6 21 528 61 +18e07 2 529 61 +18e09 1 529 61 +FUNC 18e0a 2b 0 void std::fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +18e0a c 560 78 +18e16 4 567 61 +18e1a 1b 568 61 +18e35 1 568 61 +FUNC 18e36 3f 0 unsigned char* std::__copy::copy(unsigned char const*, unsigned char const*, unsigned char*) +18e36 c 298 61 +18e42 22 300 61 +18e64 11 301 61 +18e75 1 301 61 +FUNC 18e76 2b 0 unsigned char* std::__copy_aux(unsigned char*, unsigned char*, unsigned char*) +18e76 c 307 78 +18e82 4 315 61 +18e86 1b 317 61 +18ea1 1 317 61 +18eb0 56 354 61 +18f12 4 384 61 +18f16 4 385 61 +18f1a 1b 387 61 +18f35 1 387 61 +FUNC 18f36 a0 0 std::vector >::erase(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +18f36 d 122 78 +18f43 26 124 78 +18f69 43 125 78 +18fac 22 126 78 +18fce 8 127 78 +18fe2 7 98 68 +18fe9 1 98 68 +18ff6 1d 85 68 +19013 5 86 68 +19018 d 88 68 +19025 1 88 68 +FUNC 19026 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +19026 c 116 78 +19032 1e 117 71 +1905c 1b 74 79 +19077 1 74 79 +19084 23 113 79 +190a7 1 113 79 +190b4 1b 254 79 +190cf 1 254 79 +FUNC 190d0 19 0 void std::_Destroy(dwarf2reader::CompilationUnit::Abbrev*) +190d0 c 106 79 +190dc d 107 73 +190e9 1 107 73 +FUNC 190ea 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) +190ea c 119 79 +190f6 2 121 73 +190f8 13 122 73 +1910b 21 121 73 +1912c 2 122 73 +FUNC 1912e 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +1912e c 148 79 +1913a 1c 155 73 +FUNC 19156 8d 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type) +19156 d 188 79 +19163 6 190 79 +19169 2 193 79 +1916b 1c 194 79 +19187 1b 193 79 +191a2 b 196 79 +191ad 12 198 79 +191bf b 199 79 +191ca 13 196 79 +191dd 6 199 79 +191e3 1 199 79 +FUNC 191e4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +191e4 c 214 79 +191f0 23 218 79 +19213 1 218 79 +FUNC 19214 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator) +19214 c 308 79 +19220 1b 310 79 +1923b 1 310 79 +19249 6 82 79 +1924f 2 85 79 +19251 24 86 79 +19275 2c 85 79 +192a1 b 87 79 +192ac b 89 79 +192b7 12 91 79 +192c9 b 92 79 +192d4 13 89 79 +192e7 9 92 79 +192fc 23 113 79 +1931f 1 113 79 +1932c 1b 254 79 +19347 1 254 79 +FUNC 19348 409 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +19348 14 249 79 +1935c 14 251 78 +19370 22 253 78 +19392 f 255 78 +193a1 12 256 78 +193b3 55 257 78 +19408 4b 260 78 +19453 e 264 78 +19461 15 265 78 +19476 e 266 78 +19484 1d 271 78 +194a1 8 272 78 +194a9 e 273 78 +194b7 27 275 78 +194de 6 276 78 +194e4 55 279 78 +19539 25 284 78 +1955e b 285 78 +19569 4f 286 78 +195b8 3 284 78 +195bb 13 279 78 +195ce e 286 78 +195dc 4d 298 78 +19629 36 299 78 +1965f 12 302 78 +19671 13 303 78 +19684 2e 304 78 +196b2 13 286 78 +196c5 b 292 78 +196d0 39 294 78 +19709 23 295 78 +1972c b 296 78 +19737 13 292 78 +1974a 7 304 78 +19751 1 304 78 +FUNC 19752 70 0 std::vector >::push_back(dwarf2reader::CompilationUnit::Abbrev const&) +19752 c 602 79 +1975e 10 604 71 +1976e 1e 606 71 +1978c 11 607 71 +1979d 25 610 71 +FUNC 197c2 50 0 unsigned char* std::__copy_backward::copy_b(unsigned char const*, unsigned char const*, unsigned char*) +197c2 d 422 61 +197cf f 424 61 +197de 24 425 61 +19802 10 426 61 +FUNC 19812 2b 0 unsigned char* std::__copy_backward_aux(unsigned char*, unsigned char*, unsigned char*) +19812 c 432 79 +1981e 4 440 61 +19822 1b 443 61 +1983d 1 443 61 +1984c 56 482 61 +198ae 4 514 61 +198b2 4 515 61 +198b6 1b 517 61 +198d1 1 517 61 +FUNC 198d2 32 0 unsigned char* std::fill_n(unsigned char*, unsigned long, unsigned char const&) +198d2 c 647 79 +198de 1e 649 61 +198fc 8 650 61 +FUNC 19904 27 0 void std::__uninitialized_fill_n_aux(unsigned char*, unsigned long, unsigned char const&, __true_type) +19904 c 182 79 +19910 1b 183 79 +1992b 1 183 79 +FUNC 1992c 2f 0 void std::uninitialized_fill_n(unsigned char*, unsigned long, unsigned char const&) +1992c c 214 79 +19938 23 218 79 +1995b 1 218 79 +FUNC 1995c 27 0 void std::__uninitialized_fill_n_a(unsigned char*, unsigned long, unsigned char const&, std::allocator) +1995c c 308 79 +19968 1b 310 79 +19983 1 310 79 +FUNC 19984 27 0 void std::__destroy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type) +19984 c 119 79 +19990 2 121 73 +19992 b 122 73 +1999d c 121 73 +199a9 2 122 73 +199ab 1 122 73 +FUNC 199ac 28 0 void std::_Destroy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +199ac c 148 79 +199b8 1c 155 73 +FUNC 199d4 88 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type) +199d4 d 80 79 +199e1 6 82 79 +199e7 2 85 79 +199e9 12 86 79 +199fb 12 85 79 +19a0d b 87 79 +19a18 b 89 79 +19a23 12 91 79 +19a35 b 92 79 +19a40 13 89 79 +19a53 9 92 79 +FUNC 19a5c 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +19a5c c 108 79 +19a68 23 113 79 +19a8b 1 113 79 +FUNC 19a8c 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) +19a8c c 252 79 +19a98 1b 254 79 +19ab3 1 254 79 +FUNC 19ab4 7e 0 void std::__uninitialized_fill_n_aux(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type) +19ab4 d 188 79 +19ac1 6 190 79 +19ac7 2 193 79 +19ac9 12 194 79 +19adb 16 193 79 +19af1 b 196 79 +19afc 12 198 79 +19b0e b 199 79 +19b19 13 196 79 +19b2c 6 199 79 +FUNC 19b32 2f 0 void std::uninitialized_fill_n(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +19b32 c 214 79 +19b3e 23 218 79 +19b61 1 218 79 +FUNC 19b62 27 0 void std::__uninitialized_fill_n_a(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator) +19b62 c 308 79 +19b6e 1b 310 79 +19b89 1 310 79 +FUNC 19b8a a5 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, __false_type) +19b8a d 80 79 +19b97 6 82 79 +19b9d 2 85 79 +19b9f 1a 86 79 +19bb9 27 85 79 +19be0 b 87 79 +19beb b 89 79 +19bf6 12 91 79 +19c08 b 92 79 +19c13 13 89 79 +19c26 9 92 79 +19c2f 1 92 79 +FUNC 19c30 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*) +19c30 c 108 79 +19c3c 23 113 79 +19c5f 1 113 79 +FUNC 19c60 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) +19c60 c 252 79 +19c6c 1b 254 79 +19c87 1 254 79 +FUNC 19c88 5f8 0 std::vector >::_M_fill_insert(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +19c88 15 311 79 +19c9d b 313 78 +19ca8 2a 315 78 +19cd2 12 318 78 +19ce4 23 319 78 +19d07 15 320 78 +19d1c c 321 78 +19d28 5a 323 78 +19d82 1c 327 78 +19d9e 35 328 78 +19dd3 16 323 78 +19de9 30 330 78 +19e19 10 343 78 +19e29 48 334 78 +19e71 21 338 78 +19e92 3d 339 78 +19ecf 13 334 78 +19ee2 b 339 78 +19eed 1c 342 78 +19f09 1e 343 78 +19f27 13 339 78 +19f3a 24 343 78 +19f5e e 348 78 +19f6c 1e 349 78 +19f8a e 350 78 +19f98 1d 353 78 +19fb5 8 354 78 +19fbd e 355 78 +19fcb 27 357 78 +19ff2 6 358 78 +19ff8 4d 361 78 +1a045 40 365 78 +1a085 18 367 78 +1a09d 44 368 78 +1a0e1 3 365 78 +1a0e4 19 361 78 +1a0fd 13 365 78 +1a110 e 368 78 +1a11e 3e 379 78 +1a15c 36 381 78 +1a192 12 384 78 +1a1a4 13 385 78 +1a1b7 2e 386 78 +1a1e5 e 368 78 +1a1f3 b 372 78 +1a1fe 39 374 78 +1a237 23 376 78 +1a25a b 377 78 +1a265 13 372 78 +1a278 8 386 78 +FUNC 1a280 2e 0 std::vector >::insert(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +1a280 c 657 79 +1a28c 22 658 71 +FUNC 1a2ae ab 0 std::vector >::resize(unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +1a2ae d 422 79 +1a2bb 15 424 71 +1a2d0 48 425 71 +1a318 41 427 71 +1a359 1 427 71 +FUNC 1a35a 63 0 std::vector >::resize(unsigned long) +1a35a d 441 79 +1a367 56 442 71 +1a3bd 1 442 71 +FUNC 1a3be 13 0 std::_Deque_iterator::operator*() const +1a3be c 134 79 +1a3ca 7 135 62 +1a3d1 1 135 62 +FUNC 1a3d2 3f 0 unsigned long long** std::__copy::copy(unsigned long long* const*, unsigned long long* const*, unsigned long long**) +1a3d2 c 298 61 +1a3de 22 300 61 +1a400 11 301 61 +1a411 1 301 61 +FUNC 1a412 2b 0 unsigned long long** std::__copy_aux(unsigned long long**, unsigned long long**, unsigned long long**) +1a412 c 307 79 +1a41e 4 315 61 +1a422 1b 317 61 +1a43d 1 317 61 +FUNC 1a43e 27 0 unsigned long long** std::__copy_normal::copy_n(unsigned long long**, unsigned long long**, unsigned long long**) +1a43e c 325 61 +1a44a 1b 326 61 +1a465 1 326 61 +FUNC 1a466 2f 0 unsigned long long** std::copy(unsigned long long**, unsigned long long**, unsigned long long**) +1a466 c 376 79 +1a472 4 384 61 +1a476 4 385 61 +1a47a 1b 387 61 +1a495 1 387 61 +FUNC 1a496 60 0 unsigned long long** std::__copy_backward::copy_b(unsigned long long* const*, unsigned long long* const*, unsigned long long**) +1a496 d 422 61 +1a4a3 12 424 61 +1a4b5 2e 425 61 +1a4e3 13 426 61 +FUNC 1a4f6 2b 0 unsigned long long** std::__copy_backward_aux(unsigned long long**, unsigned long long**, unsigned long long**) +1a4f6 c 432 79 +1a502 4 440 61 +1a506 1b 443 61 +1a521 1 443 61 +FUNC 1a522 27 0 unsigned long long** std::__copy_backward_normal::copy_b_n(unsigned long long**, unsigned long long**, unsigned long long**) +1a522 c 451 61 +1a52e 1b 452 61 +1a549 1 452 61 +FUNC 1a54a 2f 0 unsigned long long** std::copy_backward(unsigned long long**, unsigned long long**, unsigned long long**) +1a54a c 504 79 +1a556 4 514 61 +1a55a 4 515 61 +1a55e 1b 517 61 +1a579 1 517 61 +FUNC 1a57a 1df 0 std::deque >::_M_reallocate_map(unsigned long, bool) +1a57a 13 723 79 +1a58d 1b 726 76 +1a5a8 9 727 76 +1a5b1 13 730 76 +1a5c4 39 732 76 +1a5fd b 735 76 +1a608 27 736 76 +1a62f 2f 740 76 +1a65e 26 748 76 +1a684 15 750 76 +1a699 36 751 76 +1a6cf 22 753 76 +1a6f1 1e 756 76 +1a70f 8 758 76 +1a717 9 759 76 +1a720 15 762 76 +1a735 24 763 76 +1a759 1 763 76 +FUNC 1a75a 59 0 std::deque >::_M_reserve_map_at_back(unsigned long) +1a75a e 1443 79 +1a768 2a 1445 62 +1a792 21 1447 62 +1a7b3 1 1447 62 +FUNC 1a7b4 8c 0 std::deque >::_M_push_back_aux(unsigned long long const&) +1a7b4 c 345 79 +1a7c0 e 347 76 +1a7ce 13 348 76 +1a7e1 18 349 76 +1a7f9 1e 352 76 +1a817 1b 353 76 +1a832 c 355 76 +1a83e 2 360 76 +FUNC 1a840 62 0 std::deque >::push_back(unsigned long long const&) +1a840 c 1039 79 +1a84c 13 1041 62 +1a85f 1e 1044 62 +1a87d 11 1045 62 +1a88e 14 1048 62 +FUNC 1a8a2 20 0 std::stack > >::push(unsigned long long const&) +1a8a2 c 190 79 +1a8ae 14 191 75 +FUNC 1a8c2 27 0 unsigned char* std::__copy_normal::copy_n(unsigned char*, unsigned char*, unsigned char*) +1a8c2 c 325 61 +1a8ce 1b 326 61 +1a8e9 1 326 61 +FUNC 1a8ea 2f 0 unsigned char* std::copy(unsigned char*, unsigned char*, unsigned char*) +1a8ea c 376 79 +1a8f6 4 384 61 +1a8fa 4 385 61 +1a8fe 1b 387 61 +1a919 1 387 61 +FUNC 1a91a 27 0 unsigned char* std::__uninitialized_copy_aux(unsigned char*, unsigned char*, unsigned char*, __true_type) +1a91a c 73 79 +1a926 1b 74 79 +1a941 1 74 79 +FUNC 1a942 2f 0 unsigned char* std::uninitialized_copy(unsigned char*, unsigned char*, unsigned char*) +1a942 c 108 79 +1a94e 23 113 79 +1a971 1 113 79 +FUNC 1a972 27 0 unsigned char* std::__uninitialized_copy_a(unsigned char*, unsigned char*, unsigned char*, std::allocator) +1a972 c 252 79 +1a97e 1b 254 79 +1a999 1 254 79 +FUNC 1a99a 40 0 unsigned char* std::__copy_normal::copy_n<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1a99a d 334 61 +1a9a7 33 335 61 +FUNC 1a9da 2f 0 unsigned char* std::copy<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1a9da c 376 79 +1a9e6 4 384 61 +1a9ea 4 385 61 +1a9ee 1b 387 61 +1aa09 1 387 61 +FUNC 1aa0a 27 0 unsigned char* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*, __true_type) +1aa0a c 73 79 +1aa16 1b 74 79 +1aa31 1 74 79 +FUNC 1aa32 2f 0 unsigned char* std::uninitialized_copy<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1aa32 c 108 79 +1aa3e 23 113 79 +1aa61 1 113 79 +FUNC 1aa62 27 0 unsigned char* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator > >, unsigned char*, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*, std::allocator) +1aa62 c 252 79 +1aa6e 1b 254 79 +1aa89 1 254 79 +1aa96 9 616 61 +1aa9f 2 617 61 +1aaa1 13 618 61 +1aab4 16 617 61 +1aaca 5 619 61 +1aacf 1 619 61 +1aadc 4 641 61 +1aae0 1b 642 61 +1aafb 1 642 61 +FUNC 1aafc 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&, __true_type) +1aafc c 182 79 +1ab08 1b 183 79 +1ab23 1 183 79 +FUNC 1ab24 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) +1ab24 c 214 79 +1ab30 23 218 79 +1ab53 1 218 79 +FUNC 1ab54 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&, std::allocator) +1ab54 c 308 79 +1ab60 1b 310 79 +1ab7b 1 310 79 +FUNC 1ab7c 45a 0 std::vector >::_M_fill_insert(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) +1ab7c 14 311 79 +1ab90 b 313 78 +1ab9b 21 315 78 +1abbc 9 318 78 +1abc5 23 319 78 +1abe8 15 320 78 +1abfd c 321 78 +1ac09 4e 323 78 +1ac57 11 327 78 +1ac68 30 328 78 +1ac98 35 330 78 +1accd 48 334 78 +1ad15 14 338 78 +1ad29 43 339 78 +1ad6c 11 342 78 +1ad7d 1e 343 78 +1ad9b e 348 78 +1ada9 1e 349 78 +1adc7 e 350 78 +1add5 1d 353 78 +1adf2 8 354 78 +1adfa e 355 78 +1ae08 27 357 78 +1ae2f 6 358 78 +1ae35 4d 361 78 +1ae82 40 365 78 +1aec2 18 367 78 +1aeda 4d 368 78 +1af27 3e 379 78 +1af65 2d 381 78 +1af92 12 384 78 +1afa4 13 385 78 +1afb7 1f 386 78 +FUNC 1afd6 2e 0 std::vector >::insert(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) +1afd6 c 657 79 +1afe2 22 658 71 +FUNC 1b004 ab 0 std::vector >::resize(unsigned long, unsigned char const&) +1b004 d 422 79 +1b011 15 424 71 +1b026 48 425 71 +1b06e 41 427 71 +1b0af 1 427 71 +FUNC 1b0b0 2b 0 std::vector >::resize(unsigned long) +1b0b0 c 441 79 +1b0bc 1f 442 71 +1b0db 1 442 71 +FUNC 1b0dc 1a 0 std::_Deque_iterator::_S_buffer_size() +1b0dc c 106 79 +1b0e8 e 107 62 +FUNC 1b0f6 66 0 std::_Deque_iterator::difference_type std::operator-(std::_Deque_iterator const&, std::_Deque_iterator const&) +1b0f6 d 328 79 +1b103 59 333 62 +FUNC 1b15c 3e 0 std::_Deque_iterator::_M_set_node(unsigned long long**) +1b15c d 229 79 +1b169 9 231 62 +1b172 b 232 62 +1b17d 1d 233 62 +FUNC 1b19a 50 0 std::_Deque_iterator::operator++() +1b19a c 142 79 +1b1a6 d 144 62 +1b1b3 f 145 62 +1b1c2 18 147 62 +1b1da b 148 62 +1b1e5 5 150 62 +FUNC 1b1ea 84 0 std::_Deque_iterator std::__copy::copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b1ea e 280 61 +1b1f8 17 283 61 +1b20f 20 285 61 +1b22f b 286 61 +1b23a b 287 61 +1b245 b 283 61 +1b250 1e 289 61 +FUNC 1b26e 7e 0 std::_Deque_iterator std::__copy_aux, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b26e 11 307 79 +1b27f 4 315 61 +1b283 69 317 61 +FUNC 1b2ec 7a 0 std::_Deque_iterator std::__copy_normal::copy_n, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b2ec 11 325 61 +1b2fd 69 326 61 +FUNC 1b366 82 0 std::_Deque_iterator std::copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b366 11 376 79 +1b377 4 384 61 +1b37b 4 385 61 +1b37f 69 387 61 +FUNC 1b3e8 7a 0 std::_Deque_iterator std::__uninitialized_copy_aux, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator, __true_type) +1b3e8 11 73 79 +1b3f9 69 74 79 +FUNC 1b462 82 0 std::_Deque_iterator std::uninitialized_copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b462 11 108 79 +1b473 71 113 79 +FUNC 1b4e4 7a 0 std::_Deque_iterator std::__uninitialized_copy_a, std::_Deque_iterator, unsigned long long>(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator, std::allocator) +1b4e4 11 252 79 +1b4f5 69 254 79 +1b55e 10 679 62 +1b56e 64 680 62 +1b5d2 e8 681 62 +1b6ba c 143 75 +1b6c6 14 144 75 +1b6da 6 144 75 +FUNC 1b6e0 4d 0 __eprintf +1b6e0 6 1826 80 +1b6e6 3 1832 80 +1b6e9 c 1826 80 +1b6f5 29 1832 80 +1b71e a 1837 80 +1b728 5 1838 80 +1b72d e8d3 1838 80 diff --git a/src/lib/crashdump/gbreakpad/client/minidump_file_writer-inl.h b/src/lib/crashdump/gbreakpad/client/minidump_file_writer-inl.h new file mode 100644 index 0000000..0e12e00 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/minidump_file_writer-inl.h @@ -0,0 +1,97 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump_file_writer-inl.h: Minidump file writer implementation. +// +// See minidump_file_writer.h for documentation. + +#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__ +#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__ + +#include + +#include "client/minidump_file_writer.h" +#include "google_breakpad/common/minidump_size.h" + +namespace google_breakpad { + +template +inline bool TypedMDRVA::Allocate() { + allocation_state_ = SINGLE_OBJECT; + return UntypedMDRVA::Allocate(minidump_size::size()); +} + +template +inline bool TypedMDRVA::Allocate(size_t additional) { + allocation_state_ = SINGLE_OBJECT; + return UntypedMDRVA::Allocate(minidump_size::size() + additional); +} + +template +inline bool TypedMDRVA::AllocateArray(size_t count) { + assert(count); + allocation_state_ = ARRAY; + return UntypedMDRVA::Allocate(minidump_size::size() * count); +} + +template +inline bool TypedMDRVA::AllocateObjectAndArray(size_t count, + size_t length) { + assert(count && length); + allocation_state_ = SINGLE_OBJECT_WITH_ARRAY; + return UntypedMDRVA::Allocate(minidump_size::size() + count * length); +} + +template +inline bool TypedMDRVA::CopyIndex(unsigned int index, MDType *item) { + assert(allocation_state_ == ARRAY); + return writer_->Copy( + static_cast(position_ + index * minidump_size::size()), + item, minidump_size::size()); +} + +template +inline bool TypedMDRVA::CopyIndexAfterObject(unsigned int index, + const void *src, + size_t length) { + assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY); + return writer_->Copy( + static_cast(position_ + minidump_size::size() + + index * length), + src, length); +} + +template +inline bool TypedMDRVA::Flush() { + return writer_->Copy(position_, &data_, minidump_size::size()); +} + +} // namespace google_breakpad + +#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/client/minidump_file_writer.cc b/src/lib/crashdump/gbreakpad/client/minidump_file_writer.cc new file mode 100644 index 0000000..db30466 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/minidump_file_writer.cc @@ -0,0 +1,273 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump_file_writer.cc: Minidump file writer implementation. +// +// See minidump_file_writer.h for documentation. + +#include +#include +#include +#include +#include + +#include "client/minidump_file_writer-inl.h" +#include "common/linux/linux_libc_support.h" +#include "common/string_conversion.h" +#if __linux__ +#include "third_party/lss/linux_syscall_support.h" +#endif + +namespace google_breakpad { + +const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast(-1); + +MinidumpFileWriter::MinidumpFileWriter() : file_(-1), position_(0), size_(0) { +} + +MinidumpFileWriter::~MinidumpFileWriter() { + Close(); +} + +bool MinidumpFileWriter::Open(const char *path) { + assert(file_ == -1); +#if __linux__ + file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#else + file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#endif + + return file_ != -1; +} + +bool MinidumpFileWriter::Close() { + bool result = true; + + if (file_ != -1) { + if (-1 == ftruncate(file_, position_)) { + return false; + } +#if __linux__ + result = (sys_close(file_) == 0); +#else + result = (close(file_) == 0); +#endif + file_ = -1; + } + + return result; +} + +bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str, + unsigned int length, + TypedMDRVA *mdstring) { + bool result = true; + if (sizeof(wchar_t) == sizeof(u_int16_t)) { + // Shortcut if wchar_t is the same size as MDString's buffer + result = mdstring->Copy(str, mdstring->get()->length); + } else { + u_int16_t out[2]; + int out_idx = 0; + + // Copy the string character by character + while (length && result) { + UTF32ToUTF16Char(*str, out); + if (!out[0]) + return false; + + // Process one character at a time + --length; + ++str; + + // Append the one or two UTF-16 characters. The first one will be non- + // zero, but the second one may be zero, depending on the conversion from + // UTF-32. + int out_count = out[1] ? 2 : 1; + size_t out_size = sizeof(u_int16_t) * out_count; + result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); + out_idx += out_count; + } + } + return result; +} + +bool MinidumpFileWriter::CopyStringToMDString(const char *str, + unsigned int length, + TypedMDRVA *mdstring) { + bool result = true; + u_int16_t out[2]; + int out_idx = 0; + + // Copy the string character by character + while (length && result) { + int conversion_count = UTF8ToUTF16Char(str, length, out); + if (!conversion_count) + return false; + + // Move the pointer along based on the nubmer of converted characters + length -= conversion_count; + str += conversion_count; + + // Append the one or two UTF-16 characters + int out_count = out[1] ? 2 : 1; + size_t out_size = sizeof(u_int16_t) * out_count; + result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); + out_idx += out_count; + } + return result; +} + +template +bool MinidumpFileWriter::WriteStringCore(const CharType *str, + unsigned int length, + MDLocationDescriptor *location) { + assert(str); + assert(location); + // Calculate the mdstring length by either limiting to |length| as passed in + // or by finding the location of the NULL character. + unsigned int mdstring_length = 0; + if (!length) + length = INT_MAX; + for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) + ; + + // Allocate the string buffer + TypedMDRVA mdstring(this); + if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(u_int16_t))) + return false; + + // Set length excluding the NULL and copy the string + mdstring.get()->length = + static_cast(mdstring_length * sizeof(u_int16_t)); + bool result = CopyStringToMDString(str, mdstring_length, &mdstring); + + // NULL terminate + if (result) { + u_int16_t ch = 0; + result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); + + if (result) + *location = mdstring.location(); + } + + return result; +} + +bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length, + MDLocationDescriptor *location) { + return WriteStringCore(str, length, location); +} + +bool MinidumpFileWriter::WriteString(const char *str, unsigned int length, + MDLocationDescriptor *location) { + return WriteStringCore(str, length, location); +} + +bool MinidumpFileWriter::WriteMemory(const void *src, size_t size, + MDMemoryDescriptor *output) { + assert(src); + assert(output); + UntypedMDRVA mem(this); + + if (!mem.Allocate(size)) + return false; + if (!mem.Copy(src, mem.size())) + return false; + + output->start_of_memory_range = reinterpret_cast(src); + output->memory = mem.location(); + + return true; +} + +MDRVA MinidumpFileWriter::Allocate(size_t size) { + assert(size); + assert(file_ != -1); + size_t aligned_size = (size + 7) & ~7; // 64-bit alignment + + if (position_ + aligned_size > size_) { + size_t growth = aligned_size; + size_t minimal_growth = getpagesize(); + + // Ensure that the file grows by at least the size of a memory page + if (growth < minimal_growth) + growth = minimal_growth; + + size_t new_size = size_ + growth; + if (ftruncate(file_, new_size) != 0) + return kInvalidMDRVA; + + size_ = new_size; + } + + MDRVA current_position = position_; + position_ += static_cast(aligned_size); + + return current_position; +} + +bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { + assert(src); + assert(size); + assert(file_ != -1); + + // Ensure that the data will fit in the allocated space + if (static_cast(size + position) > size_) + return false; + + // Seek and write the data +#if __linux__ + if (sys_lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (sys_write(file_, src, size) == size) { +#else + if (lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (write(file_, src, size) == size) { +#endif + return true; + } + } + + return false; +} + +bool UntypedMDRVA::Allocate(size_t size) { + assert(size_ == 0); + size_ = size; + position_ = writer_->Allocate(size_); + return position_ != MinidumpFileWriter::kInvalidMDRVA; +} + +bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) { + assert(src); + assert(size); + assert(pos + size <= position_ + size_); + return writer_->Copy(pos, src, size); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/minidump_file_writer.h b/src/lib/crashdump/gbreakpad/client/minidump_file_writer.h new file mode 100644 index 0000000..21d4ce6 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/minidump_file_writer.h @@ -0,0 +1,251 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump_file_writer.h: Implements file-based minidump generation. It's +// intended to be used with the Google Breakpad open source crash handling +// project. + +#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__ +#define CLIENT_MINIDUMP_FILE_WRITER_H__ + +#include + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class UntypedMDRVA; +template class TypedMDRVA; + +// The user of this class can Open() a file and add minidump streams, data, and +// strings using the definitions in minidump_format.h. Since this class is +// expected to be used in a situation where the current process may be +// damaged, it will not allocate heap memory. +// Sample usage: +// MinidumpFileWriter writer; +// writer.Open("/tmp/minidump.dmp"); +// TypedMDRVA header(&writer_); +// header.Allocate(); +// header->get()->signature = MD_HEADER_SIGNATURE; +// : +// writer.Close(); +class MinidumpFileWriter { +public: + // Invalid MDRVA (Minidump Relative Virtual Address) + // returned on failed allocation + static const MDRVA kInvalidMDRVA; + + MinidumpFileWriter(); + ~MinidumpFileWriter(); + + // Open |path| as the destination of the minidump data. Any existing file + // will be overwritten. + // Return true on success, or false on failure + bool Open(const char *path); + + // Close the current file + // Return true on success, or false on failure + bool Close(); + + // Copy the contents of |str| to a MDString and write it to the file. + // |str| is expected to be either UTF-16 or UTF-32 depending on the size + // of wchar_t. + // Maximum |length| of characters to copy from |str|, or specify 0 to use the + // entire NULL terminated string. Copying will stop at the first NULL. + // |location| the allocated location + // Return true on success, or false on failure + bool WriteString(const wchar_t *str, unsigned int length, + MDLocationDescriptor *location); + + // Same as above, except with |str| as a UTF-8 string + bool WriteString(const char *str, unsigned int length, + MDLocationDescriptor *location); + + // Write |size| bytes starting at |src| into the current position. + // Return true on success and set |output| to position, or false on failure + bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output); + + // Copies |size| bytes from |src| to |position| + // Return true on success, or false on failure + bool Copy(MDRVA position, const void *src, ssize_t size); + + // Return the current position for writing to the minidump + inline MDRVA position() const { return position_; } + + private: + friend class UntypedMDRVA; + + // Allocates an area of |size| bytes. + // Returns the position of the allocation, or kInvalidMDRVA if it was + // unable to allocate the bytes. + MDRVA Allocate(size_t size); + + // The file descriptor for the output file + int file_; + + // Current position in buffer + MDRVA position_; + + // Current allocated size + size_t size_; + + // Copy |length| characters from |str| to |mdstring|. These are distinct + // because the underlying MDString is a UTF-16 based string. The wchar_t + // variant may need to create a MDString that has more characters than the + // source |str|, whereas the UTF-8 variant may coalesce characters to form + // a single UTF-16 character. + bool CopyStringToMDString(const wchar_t *str, unsigned int length, + TypedMDRVA *mdstring); + bool CopyStringToMDString(const char *str, unsigned int length, + TypedMDRVA *mdstring); + + // The common templated code for writing a string + template + bool WriteStringCore(const CharType *str, unsigned int length, + MDLocationDescriptor *location); +}; + +// Represents an untyped allocated chunk +class UntypedMDRVA { + public: + explicit UntypedMDRVA(MinidumpFileWriter *writer) + : writer_(writer), + position_(writer->position()), + size_(0) {} + + // Allocates |size| bytes. Must not call more than once. + // Return true on success, or false on failure + bool Allocate(size_t size); + + // Returns the current position or kInvalidMDRVA if allocation failed + inline MDRVA position() const { return position_; } + + // Number of bytes allocated + inline size_t size() const { return size_; } + + // Return size and position + inline MDLocationDescriptor location() const { + MDLocationDescriptor location = { static_cast(size_), + position_ }; + return location; + } + + // Copy |size| bytes starting at |src| into the minidump at |position| + // Return true on success, or false on failure + bool Copy(MDRVA position, const void *src, size_t size); + + // Copy |size| bytes from |src| to the current position + inline bool Copy(const void *src, size_t size) { + return Copy(position_, src, size); + } + + protected: + // Writer we associate with + MinidumpFileWriter *writer_; + + // Position of the start of the data + MDRVA position_; + + // Allocated size + size_t size_; +}; + +// Represents a Minidump object chunk. Additional memory can be allocated at +// the end of the object as a: +// - single allocation +// - Array of MDType objects +// - A MDType object followed by an array +template +class TypedMDRVA : public UntypedMDRVA { + public: + // Constructs an unallocated MDRVA + explicit TypedMDRVA(MinidumpFileWriter *writer) + : UntypedMDRVA(writer), + data_(), + allocation_state_(UNALLOCATED) {} + + inline ~TypedMDRVA() { + // Ensure that the data_ object is written out + if (allocation_state_ != ARRAY) + Flush(); + } + + // Address of object data_ of MDType. This is not declared const as the + // typical usage will be to access the underlying |data_| object as to + // alter its contents. + MDType *get() { return &data_; } + + // Allocates minidump_size::size() bytes. + // Must not call more than once. + // Return true on success, or false on failure + bool Allocate(); + + // Allocates minidump_size::size() + |additional| bytes. + // Must not call more than once. + // Return true on success, or false on failure + bool Allocate(size_t additional); + + // Allocate an array of |count| elements of MDType. + // Must not call more than once. + // Return true on success, or false on failure + bool AllocateArray(size_t count); + + // Allocate an array of |count| elements of |size| after object of MDType + // Must not call more than once. + // Return true on success, or false on failure + bool AllocateObjectAndArray(size_t count, size_t size); + + // Copy |item| to |index| + // Must have been allocated using AllocateArray(). + // Return true on success, or false on failure + bool CopyIndex(unsigned int index, MDType *item); + + // Copy |size| bytes starting at |str| to |index| + // Must have been allocated using AllocateObjectAndArray(). + // Return true on success, or false on failure + bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size); + + // Write data_ + bool Flush(); + + private: + enum AllocationState { + UNALLOCATED = 0, + SINGLE_OBJECT, + ARRAY, + SINGLE_OBJECT_WITH_ARRAY + }; + + MDType data_; + AllocationState allocation_state_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_MINIDUMP_FILE_WRITER_H__ diff --git a/src/lib/crashdump/gbreakpad/client/minidump_file_writer_unittest.cc b/src/lib/crashdump/gbreakpad/client/minidump_file_writer_unittest.cc new file mode 100644 index 0000000..08522fb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/minidump_file_writer_unittest.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Author: waylonis@google.com (Dan Waylonis) + +/* + g++ -I../ ../common/convert_UTF.c \ + ../common/string_conversion.cc \ + minidump_file_writer.cc \ + minidump_file_writer_unittest.cc \ + -o minidump_file_writer_unittest + */ + +#include +#include + +#include "minidump_file_writer-inl.h" + +using google_breakpad::MinidumpFileWriter; + +#define ASSERT_TRUE(cond) \ +if (!(cond)) { \ + fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ + return false; \ +} + +#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) +#define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2)) + +struct StringStructure { + unsigned long integer_value; + MDLocationDescriptor first_string; + MDLocationDescriptor second_string; +}; + +struct ArrayStructure { + unsigned char char_value; + unsigned short short_value; + unsigned long long_value; +}; + +typedef struct { + unsigned long count; + ArrayStructure array[0]; +} ObjectAndArrayStructure; + +static bool WriteFile(const char *path) { + MinidumpFileWriter writer; + if (writer.Open(path)) { + // Test a single structure + google_breakpad::TypedMDRVA strings(&writer); + ASSERT_TRUE(strings.Allocate()); + strings.get()->integer_value = 0xBEEF; + const char *first = "First String"; + ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string)); + const wchar_t *second = L"Second String"; + ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string)); + + // Test an array structure + google_breakpad::TypedMDRVA array(&writer); + unsigned int count = 10; + ASSERT_TRUE(array.AllocateArray(count)); + for (unsigned int i = 0; i < count; ++i) { + ArrayStructure local; + local.char_value = i; + local.short_value = i + 1; + local.long_value = i + 2; + ASSERT_TRUE(array.CopyIndex(i, &local)); + } + + // Test an object followed by an array + google_breakpad::TypedMDRVA obj_array(&writer); + ASSERT_TRUE(obj_array.AllocateObjectAndArray(count, + sizeof(ArrayStructure))); + obj_array.get()->count = count; + for (unsigned int i = 0; i < count; ++i) { + ArrayStructure local; + local.char_value = i; + local.short_value = i + 1; + local.long_value = i + 2; + ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local))); + } + } + + return writer.Close(); +} + +static bool CompareFile(const char *path) { + unsigned long expected[] = { +#if defined(__BIG_ENDIAN__) + 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000, + 0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069, + 0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064, + 0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002, + 0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005, + 0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008, + 0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b, + 0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003, + 0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006, + 0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009, + 0x0000000a, 0x0900000a, 0x0000000b, 0x00000000 +#else + 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, + 0x00000038, 0x00000000, 0x00000018, 0x00690046, + 0x00730072, 0x00200074, 0x00740053, 0x00690072, + 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, + 0x006f0063, 0x0064006e, 0x00530020, 0x00720074, + 0x006e0069, 0x00000067, 0x00011e00, 0x00000002, + 0x00021e01, 0x00000003, 0x00031e02, 0x00000004, + 0x00041e03, 0x00000005, 0x00051e04, 0x00000006, + 0x00061e05, 0x00000007, 0x00071e06, 0x00000008, + 0x00081e07, 0x00000009, 0x00091e08, 0x0000000a, + 0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00, + 0x00000002, 0x00021c01, 0x00000003, 0x00031c02, + 0x00000004, 0x00041c03, 0x00000005, 0x00051c04, + 0x00000006, 0x00061c05, 0x00000007, 0x00071c06, + 0x00000008, 0x00081c07, 0x00000009, 0x00091c08, + 0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000, +#endif + }; + size_t expected_byte_count = sizeof(expected); + int fd = open(path, O_RDONLY, 0600); + void *buffer = malloc(expected_byte_count); + ASSERT_NE(fd, -1); + ASSERT_TRUE(buffer); + ASSERT_EQ(read(fd, buffer, expected_byte_count), + static_cast(expected_byte_count)); + + char *b1, *b2; + b1 = reinterpret_cast(buffer); + b2 = reinterpret_cast(expected); + while (*b1 == *b2) { + b1++; + b2++; + } + + printf("%p\n", reinterpret_cast(b1 - (char*)buffer)); + + ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0); + return true; +} + +static bool RunTests() { + const char *path = "/tmp/minidump_file_writer_unittest.dmp"; + ASSERT_TRUE(WriteFile(path)); + ASSERT_TRUE(CompareFile(path)); + unlink(path); + return true; +} + +extern "C" int main(int argc, const char *argv[]) { + return RunTests() ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/client/windows/common/auto_critical_section.h b/src/lib/crashdump/gbreakpad/client/windows/common/auto_critical_section.h new file mode 100644 index 0000000..82c7b7f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/common/auto_critical_section.h @@ -0,0 +1,63 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ +#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ + +#include + +namespace google_breakpad { + +// Automatically enters the critical section in the constructor and leaves +// the critical section in the destructor. +class AutoCriticalSection { + public: + // Creates a new instance with the given critical section object + // and enters the critical section immediately. + explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs) { + assert(cs_); + EnterCriticalSection(cs_); + } + + // Destructor: leaves the critical section. + ~AutoCriticalSection() { + LeaveCriticalSection(cs_); + } + + private: + // Disable copy ctor and operator=. + AutoCriticalSection(const AutoCriticalSection&); + AutoCriticalSection& operator=(const AutoCriticalSection&); + + CRITICAL_SECTION* cs_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ diff --git a/src/lib/crashdump/gbreakpad/client/windows/common/ipc_protocol.h b/src/lib/crashdump/gbreakpad/client/windows/common/ipc_protocol.h new file mode 100644 index 0000000..b03c032 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/common/ipc_protocol.h @@ -0,0 +1,181 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ +#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ + +#include +#include +#include +#include +#include "common/windows/string_utils-inl.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Name/value pair for custom client information. +struct CustomInfoEntry { + // Maximum length for name and value for client custom info. + static const int kNameMaxLength = 64; + static const int kValueMaxLength = 64; + + CustomInfoEntry() { + // Putting name and value in initializer list makes VC++ show warning 4351. + set_name(NULL); + set_value(NULL); + } + + CustomInfoEntry(const wchar_t* name_arg, const wchar_t* value_arg) { + set_name(name_arg); + set_value(value_arg); + } + + void set_name(const wchar_t* name_arg) { + if (!name_arg) { + name[0] = L'\0'; + return; + } + WindowsStringUtils::safe_wcscpy(name, kNameMaxLength, name_arg); + } + + void set_value(const wchar_t* value_arg) { + if (!value_arg) { + value[0] = L'\0'; + return; + } + + WindowsStringUtils::safe_wcscpy(value, kValueMaxLength, value_arg); + } + + void set(const wchar_t* name_arg, const wchar_t* value_arg) { + set_name(name_arg); + set_value(value_arg); + } + + wchar_t name[kNameMaxLength]; + wchar_t value[kValueMaxLength]; +}; + +// Constants for the protocol between client and the server. + +// Tags sent with each message indicating the purpose of +// the message. +enum MessageTag { + MESSAGE_TAG_NONE = 0, + MESSAGE_TAG_REGISTRATION_REQUEST = 1, + MESSAGE_TAG_REGISTRATION_RESPONSE = 2, + MESSAGE_TAG_REGISTRATION_ACK = 3, + MESSAGE_TAG_UPLOAD_REQUEST = 4 +}; + +struct CustomClientInfo { + const CustomInfoEntry* entries; + size_t count; +}; + +// Message structure for IPC between crash client and crash server. +struct ProtocolMessage { + ProtocolMessage() + : tag(MESSAGE_TAG_NONE), + id(0), + dump_type(MiniDumpNormal), + thread_id(0), + exception_pointers(NULL), + assert_info(NULL), + custom_client_info(), + dump_request_handle(NULL), + dump_generated_handle(NULL), + server_alive_handle(NULL) { + } + + // Creates an instance with the given parameters. + ProtocolMessage(MessageTag arg_tag, + DWORD arg_id, + MINIDUMP_TYPE arg_dump_type, + DWORD* arg_thread_id, + EXCEPTION_POINTERS** arg_exception_pointers, + MDRawAssertionInfo* arg_assert_info, + const CustomClientInfo& custom_info, + HANDLE arg_dump_request_handle, + HANDLE arg_dump_generated_handle, + HANDLE arg_server_alive) + : tag(arg_tag), + id(arg_id), + dump_type(arg_dump_type), + thread_id(arg_thread_id), + exception_pointers(arg_exception_pointers), + assert_info(arg_assert_info), + custom_client_info(custom_info), + dump_request_handle(arg_dump_request_handle), + dump_generated_handle(arg_dump_generated_handle), + server_alive_handle(arg_server_alive) { + } + + // Tag in the message. + MessageTag tag; + + // The id for this message. This may be either a process id or a crash id + // depending on the type of message. + DWORD id; + + // Dump type requested. + MINIDUMP_TYPE dump_type; + + // Client thread id pointer. + DWORD* thread_id; + + // Exception information. + EXCEPTION_POINTERS** exception_pointers; + + // Assert information in case of an invalid parameter or + // pure call failure. + MDRawAssertionInfo* assert_info; + + // Custom client information. + CustomClientInfo custom_client_info; + + // Handle to signal the crash event. + HANDLE dump_request_handle; + + // Handle to check if server is done generating crash. + HANDLE dump_generated_handle; + + // Handle to a mutex that becomes signaled (WAIT_ABANDONED) + // if server process goes down. + HANDLE server_alive_handle; + + private: + // Disable copy ctor and operator=. + ProtocolMessage(const ProtocolMessage& msg); + ProtocolMessage& operator=(const ProtocolMessage& msg); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/ReadMe.txt b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/ReadMe.txt new file mode 100644 index 0000000..b54d0e1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/ReadMe.txt @@ -0,0 +1,58 @@ +========================================================================= + State machine transitions for the Crash Generation Server +========================================================================= + +========================================================================= + | + STATE | ACTIONS + | +========================================================================= + ERROR | Clean up resources used to serve clients. + | Always remain in ERROR state. +------------------------------------------------------------------------- + INITIAL | Connect to the pipe asynchronously. + | If connection is successfully queued up asynchronously, + | go into CONNECTING state. + | If connection is done synchronously, go into CONNECTED + | state. + | For any unexpected problems, go into ERROR state. +------------------------------------------------------------------------- + CONNECTING | Get the result of async connection request. + | If I/O is still incomplete, remain in the CONNECTING + | state. + | If connection is complete, go into CONNECTED state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + CONNECTED | Read from the pipe asynchronously. + | If read request is successfully queued up asynchronously, + | go into READING state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READING | Get the result of async read request. + | If read is done, go into READ_DONE state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READ_DONE | Register the client, prepare the reply and write the + | reply to the pipe asynchronously. + | If write request is successfully queued up asynchronously, + | go into WRITING state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + WRITING | Get the result of the async write request. + | If write is done, go into WRITE_DONE state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + WRITE_DONE | Read from the pipe asynchronously (for an ACK). + | If read request is successfully queued up asynchonously, + | go into READING_ACK state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READING_ACK | Get the result of the async read request. + | If read is done, perform action for successful client + | connection. + | Go into DISCONNECTING state. +------------------------------------------------------------------------- + DISCONNECTING | Disconnect from the pipe, reset the event and go into + | INITIAL state and signal the event again. If anything + | fails, go into ERROR state. +========================================================================= diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.cc b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.cc new file mode 100644 index 0000000..60bbac8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.cc @@ -0,0 +1,211 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/windows/crash_generation/client_info.h" +#include "client/windows/common/ipc_protocol.h" + +static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime"; +static const size_t kMaxCustomInfoEntries = 4096; + +namespace google_breakpad { + +ClientInfo::ClientInfo(CrashGenerationServer* crash_server, + DWORD pid, + MINIDUMP_TYPE dump_type, + DWORD* thread_id, + EXCEPTION_POINTERS** ex_info, + MDRawAssertionInfo* assert_info, + const CustomClientInfo& custom_client_info) + : crash_server_(crash_server), + pid_(pid), + dump_type_(dump_type), + ex_info_(ex_info), + assert_info_(assert_info), + custom_client_info_(custom_client_info), + thread_id_(thread_id), + process_handle_(NULL), + dump_requested_handle_(NULL), + dump_generated_handle_(NULL), + dump_request_wait_handle_(NULL), + process_exit_wait_handle_(NULL), + crash_id_(NULL) { + GetSystemTimeAsFileTime(&start_time_); +} + +bool ClientInfo::Initialize() { + process_handle_ = OpenProcess(GENERIC_ALL, FALSE, pid_); + if (!process_handle_) { + return false; + } + + // The crash_id will be the low order word of the process creation time. + FILETIME creation_time, exit_time, kernel_time, user_time; + if (GetProcessTimes(process_handle_, &creation_time, &exit_time, + &kernel_time, &user_time)) + crash_id_ = creation_time.dwLowDateTime; + + dump_requested_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + if (!dump_requested_handle_) { + return false; + } + + dump_generated_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + return dump_generated_handle_ != NULL; +} + +ClientInfo::~ClientInfo() { + if (dump_request_wait_handle_) { + // Wait for callbacks that might already be running to finish. + UnregisterWaitEx(dump_request_wait_handle_, INVALID_HANDLE_VALUE); + } + + if (process_exit_wait_handle_) { + // Wait for the callback that might already be running to finish. + UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE); + } + + if (process_handle_) { + CloseHandle(process_handle_); + } + + if (dump_requested_handle_) { + CloseHandle(dump_requested_handle_); + } + + if (dump_generated_handle_) { + CloseHandle(dump_generated_handle_); + } +} + +void ClientInfo::UnregisterWaits() { + if (dump_request_wait_handle_) { + UnregisterWait(dump_request_wait_handle_); + dump_request_wait_handle_ = NULL; + } + + if (process_exit_wait_handle_) { + UnregisterWait(process_exit_wait_handle_); + process_exit_wait_handle_ = NULL; + } +} + +bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const { + SIZE_T bytes_count = 0; + if (!ReadProcessMemory(process_handle_, + ex_info_, + ex_info, + sizeof(*ex_info), + &bytes_count)) { + return false; + } + + return bytes_count == sizeof(*ex_info); +} + +bool ClientInfo::GetClientThreadId(DWORD* thread_id) const { + SIZE_T bytes_count = 0; + if (!ReadProcessMemory(process_handle_, + thread_id_, + thread_id, + sizeof(*thread_id), + &bytes_count)) { + return false; + } + + return bytes_count == sizeof(*thread_id); +} + +void ClientInfo::SetProcessUptime() { + FILETIME now = {0}; + GetSystemTimeAsFileTime(&now); + + ULARGE_INTEGER time_start; + time_start.HighPart = start_time_.dwHighDateTime; + time_start.LowPart = start_time_.dwLowDateTime; + + ULARGE_INTEGER time_now; + time_now.HighPart = now.dwHighDateTime; + time_now.LowPart = now.dwLowDateTime; + + // Calculate the delay and convert it from 100-nanoseconds to milliseconds. + __int64 delay = (time_now.QuadPart - time_start.QuadPart) / 10 / 1000; + + // Convert it to a string. + wchar_t* value = custom_info_entries_.get()[custom_client_info_.count].value; + _i64tow_s(delay, value, CustomInfoEntry::kValueMaxLength, 10); +} + +bool ClientInfo::PopulateCustomInfo() { + if (custom_client_info_.count > kMaxCustomInfoEntries) + return false; + + SIZE_T bytes_count = 0; + SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count; + + // If the scoped array for custom info already has an array, it will be + // the same size as what we need. This is because the number of custom info + // entries is always the same. So allocate memory only if scoped array has + // a NULL pointer. + if (!custom_info_entries_.get()) { + // Allocate an extra entry for reporting uptime for the client process. + custom_info_entries_.reset( + new CustomInfoEntry[custom_client_info_.count + 1]); + // Use the last element in the array for uptime. + custom_info_entries_.get()[custom_client_info_.count].set_name( + kCustomInfoProcessUptimeName); + } + + if (!ReadProcessMemory(process_handle_, + custom_client_info_.entries, + custom_info_entries_.get(), + read_count, + &bytes_count)) { + return false; + } + + SetProcessUptime(); + return (bytes_count != read_count); +} + +CustomClientInfo ClientInfo::GetCustomInfo() const { + CustomClientInfo custom_info; + custom_info.entries = custom_info_entries_.get(); + // Add 1 to the count from the client process to account for extra entry for + // process uptime. + custom_info.count = custom_client_info_.count + 1; + return custom_info; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.h b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.h new file mode 100644 index 0000000..999e667 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/client_info.h @@ -0,0 +1,176 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ + +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "google_breakpad/common/minidump_format.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +class CrashGenerationServer; + +// Abstraction for a crash client process. +class ClientInfo { + public: + // Creates an instance with the given values. Gets the process + // handle for the given process id and creates necessary event + // objects. + ClientInfo(CrashGenerationServer* crash_server, + DWORD pid, + MINIDUMP_TYPE dump_type, + DWORD* thread_id, + EXCEPTION_POINTERS** ex_info, + MDRawAssertionInfo* assert_info, + const CustomClientInfo& custom_client_info); + + ~ClientInfo(); + + CrashGenerationServer* crash_server() const { return crash_server_; } + DWORD pid() const { return pid_; } + MINIDUMP_TYPE dump_type() const { return dump_type_; } + EXCEPTION_POINTERS** ex_info() const { return ex_info_; } + MDRawAssertionInfo* assert_info() const { return assert_info_; } + DWORD* thread_id() const { return thread_id_; } + HANDLE process_handle() const { return process_handle_; } + HANDLE dump_requested_handle() const { return dump_requested_handle_; } + HANDLE dump_generated_handle() const { return dump_generated_handle_; } + DWORD crash_id() const { return crash_id_; } + + HANDLE dump_request_wait_handle() const { + return dump_request_wait_handle_; + } + + void set_dump_request_wait_handle(HANDLE value) { + dump_request_wait_handle_ = value; + } + + HANDLE process_exit_wait_handle() const { + return process_exit_wait_handle_; + } + + void set_process_exit_wait_handle(HANDLE value) { + process_exit_wait_handle_ = value; + } + + // Unregister all waits for the client. + void UnregisterWaits(); + + bool Initialize(); + bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const; + bool GetClientThreadId(DWORD* thread_id) const; + + // Reads the custom information from the client process address space. + bool PopulateCustomInfo(); + + // Returns the client custom information. + CustomClientInfo GetCustomInfo() const; + + private: + // Calcualtes the uptime for the client process, converts it to a string and + // stores it in the last entry of client custom info. + void SetProcessUptime(); + + // Crash generation server. + CrashGenerationServer* crash_server_; + + // Client process ID. + DWORD pid_; + + // Dump type requested by the client. + MINIDUMP_TYPE dump_type_; + + // Address of an EXCEPTION_POINTERS* variable in the client + // process address space that will point to an instance of + // EXCEPTION_POINTERS containing information about crash. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + EXCEPTION_POINTERS** ex_info_; + + // Address of an instance of MDRawAssertionInfo in the client + // process address space that will contain information about + // non-exception related crashes like invalid parameter assertion + // failures and pure calls. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + MDRawAssertionInfo* assert_info_; + + // Custom information about the client. + CustomClientInfo custom_client_info_; + + // Contains the custom client info entries read from the client process + // memory. This will be populated only if the method GetClientCustomInfo + // is called. + scoped_array custom_info_entries_; + + // Address of a variable in the client process address space that + // will contain the thread id of the crashing client thread. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + DWORD* thread_id_; + + // Client process handle. + HANDLE process_handle_; + + // Dump request event handle. + HANDLE dump_requested_handle_; + + // Dump generated event handle. + HANDLE dump_generated_handle_; + + // Wait handle for dump request event. + HANDLE dump_request_wait_handle_; + + // Wait handle for process exit event. + HANDLE process_exit_wait_handle_; + + // Time when the client process started. It is used to determine the uptime + // for the client process when it signals a crash. + FILETIME start_time_; + + // The crash id which can be used to request an upload. This will be the + // value of the low order dword of the process creation time for the process + // being dumped. + DWORD crash_id_; + + // Disallow copy ctor and operator=. + ClientInfo(const ClientInfo& client_info); + ClientInfo& operator=(const ClientInfo& client_info); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.cc b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.cc new file mode 100644 index 0000000..ffbcaf2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.cc @@ -0,0 +1,345 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/windows/crash_generation/crash_generation_client.h" +#include +#include +#include "client/windows/common/ipc_protocol.h" + +namespace google_breakpad { + +const int kPipeBusyWaitTimeoutMs = 2000; + +#ifdef _DEBUG +const DWORD kWaitForServerTimeoutMs = INFINITE; +#else +const DWORD kWaitForServerTimeoutMs = 15000; +#endif + +const int kPipeConnectMaxAttempts = 2; + +const DWORD kPipeDesiredAccess = FILE_READ_DATA | + FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES; + +const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | + SECURITY_SQOS_PRESENT; + +const DWORD kPipeMode = PIPE_READMODE_MESSAGE; + +const size_t kWaitEventCount = 2; + +// This function is orphan for production code. It can be used +// for debugging to help repro some scenarios like the client +// is slow in writing to the pipe after connecting, the client +// is slow in reading from the pipe after writing, etc. The parameter +// overlapped below is not used and it is present to match the signature +// of this function to TransactNamedPipe Win32 API. Uncomment if needed +// for debugging. +/** +static bool TransactNamedPipeDebugHelper(HANDLE pipe, + const void* in_buffer, + DWORD in_size, + void* out_buffer, + DWORD out_size, + DWORD* bytes_count, + LPOVERLAPPED) { + // Uncomment the next sleep to create a gap before writing + // to pipe. + // Sleep(5000); + + if (!WriteFile(pipe, + in_buffer, + in_size, + bytes_count, + NULL)) { + return false; + } + + // Uncomment the next sleep to create a gap between write + // and read. + // Sleep(5000); + + return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE; +} +**/ + +CrashGenerationClient::CrashGenerationClient( + const wchar_t* pipe_name, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info) + : pipe_name_(pipe_name), + dump_type_(dump_type), + thread_id_(0), + server_process_id_(0), + crash_event_(NULL), + crash_generated_(NULL), + server_alive_(NULL), + exception_pointers_(NULL), + custom_info_() { + memset(&assert_info_, 0, sizeof(assert_info_)); + if (custom_info) { + custom_info_ = *custom_info; + } +} + +CrashGenerationClient::~CrashGenerationClient() { + if (crash_event_) { + CloseHandle(crash_event_); + } + + if (crash_generated_) { + CloseHandle(crash_generated_); + } + + if (server_alive_) { + CloseHandle(server_alive_); + } +} + +// Performs the registration step with the server process. +// The registration step involves communicating with the server +// via a named pipe. The client sends the following pieces of +// data to the server: +// +// * Message tag indicating the client is requesting registration. +// * Process id of the client process. +// * Address of a DWORD variable in the client address space +// that will contain the thread id of the client thread that +// caused the crash. +// * Address of a EXCEPTION_POINTERS* variable in the client +// address space that will point to an instance of EXCEPTION_POINTERS +// when the crash happens. +// * Address of an instance of MDRawAssertionInfo that will contain +// relevant information in case of non-exception crashes like assertion +// failures and pure calls. +// +// In return the client expects the following information from the server: +// +// * Message tag indicating successful registration. +// * Server process id. +// * Handle to an object that client can signal to request dump +// generation from the server. +// * Handle to an object that client can wait on after requesting +// dump generation for the server to finish dump generation. +// * Handle to a mutex object that client can wait on to make sure +// server is still alive. +// +// If any step of the expected behavior mentioned above fails, the +// registration step is not considered successful and hence out-of-process +// dump generation service is not available. +// +// Returns true if the registration is successful; false otherwise. +bool CrashGenerationClient::Register() { + HANDLE pipe = ConnectToServer(); + if (!pipe) { + return false; + } + + bool success = RegisterClient(pipe); + CloseHandle(pipe); + return success; +} + +bool CrashGenerationClient::RequestUpload(DWORD crash_id) { + HANDLE pipe = ConnectToServer(); + if (!pipe) { + return false; + } + + CustomClientInfo custom_info = {NULL, 0}; + ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id, + static_cast(NULL), NULL, NULL, NULL, + custom_info, NULL, NULL, NULL); + DWORD bytes_count = 0; + bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0; + + CloseHandle(pipe); + return success; +} + +HANDLE CrashGenerationClient::ConnectToServer() { + HANDLE pipe = ConnectToPipe(pipe_name_.c_str(), + kPipeDesiredAccess, + kPipeFlagsAndAttributes); + if (!pipe) { + return NULL; + } + + DWORD mode = kPipeMode; + if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) { + CloseHandle(pipe); + pipe = NULL; + } + + return pipe; +} + +bool CrashGenerationClient::RegisterClient(HANDLE pipe) { + ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST, + GetCurrentProcessId(), + dump_type_, + &thread_id_, + &exception_pointers_, + &assert_info_, + custom_info_, + NULL, + NULL, + NULL); + ProtocolMessage reply; + DWORD bytes_count = 0; + // The call to TransactNamedPipe below can be changed to a call + // to TransactNamedPipeDebugHelper to help repro some scenarios. + // For details see comments for TransactNamedPipeDebugHelper. + if (!TransactNamedPipe(pipe, + &msg, + sizeof(msg), + &reply, + sizeof(ProtocolMessage), + &bytes_count, + NULL)) { + return false; + } + + if (!ValidateResponse(reply)) { + return false; + } + + ProtocolMessage ack_msg; + ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK; + + if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) { + return false; + } + crash_event_ = reply.dump_request_handle; + crash_generated_ = reply.dump_generated_handle; + server_alive_ = reply.server_alive_handle; + server_process_id_ = reply.id; + + return true; +} + +HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, + DWORD pipe_access, + DWORD flags_attrs) { + for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { + HANDLE pipe = CreateFile(pipe_name, + pipe_access, + 0, + NULL, + OPEN_EXISTING, + flags_attrs, + NULL); + if (pipe != INVALID_HANDLE_VALUE) { + return pipe; + } + + // Cannot continue retrying if error is something other than + // ERROR_PIPE_BUSY. + if (GetLastError() != ERROR_PIPE_BUSY) { + break; + } + + // Cannot continue retrying if wait on pipe fails. + if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { + break; + } + } + + return NULL; +} + +bool CrashGenerationClient::ValidateResponse( + const ProtocolMessage& msg) const { + return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) && + (msg.id != 0) && + (msg.dump_request_handle != NULL) && + (msg.dump_generated_handle != NULL) && + (msg.server_alive_handle != NULL); +} + +bool CrashGenerationClient::IsRegistered() const { + return crash_event_ != NULL; +} + +bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info, + MDRawAssertionInfo* assert_info) { + if (!IsRegistered()) { + return false; + } + + exception_pointers_ = ex_info; + thread_id_ = GetCurrentThreadId(); + + if (assert_info) { + memcpy(&assert_info_, assert_info, sizeof(assert_info_)); + } else { + memset(&assert_info_, 0, sizeof(assert_info_)); + } + + return SignalCrashEventAndWait(); +} + +bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) { + return RequestDump(ex_info, NULL); +} + +bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) { + return RequestDump(NULL, assert_info); +} + +bool CrashGenerationClient::SignalCrashEventAndWait() { + assert(crash_event_); + assert(crash_generated_); + assert(server_alive_); + + // Reset the dump generated event before signaling the crash + // event so that the server can set the dump generated event + // once it is done generating the event. + if (!ResetEvent(crash_generated_)) { + return false; + } + + if (!SetEvent(crash_event_)) { + return false; + } + + HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_}; + + DWORD result = WaitForMultipleObjects(kWaitEventCount, + wait_handles, + FALSE, + kWaitForServerTimeoutMs); + + // Crash dump was successfully generated only if the server + // signaled the crash generated event. + return result == WAIT_OBJECT_0; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.h b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.h new file mode 100644 index 0000000..85a0456 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_client.h @@ -0,0 +1,166 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include +#include +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +struct CustomClientInfo; + +// Abstraction of client-side implementation of out of process +// crash generation. +// +// The process that desires to have out-of-process crash dump +// generation service can use this class in the following way: +// +// * Create an instance. +// * Call Register method so that the client tries to register +// with the server process and check the return value. If +// registration is not successful, out-of-process crash dump +// generation will not be available +// * Request dump generation by calling either of the two +// overloaded RequestDump methods - one in case of exceptions +// and the other in case of assertion failures +// +// Note that it is the responsibility of the client code of +// this class to set the unhandled exception filter with the +// system by calling the SetUnhandledExceptionFilter function +// and the client code should explicitly request dump generation. +class CrashGenerationClient { + public: + CrashGenerationClient(const wchar_t* pipe_name, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info); + + ~CrashGenerationClient(); + + // Registers the client process with the crash server. + // + // Returns true if the registration is successful; false otherwise. + bool Register(); + + // Requests the crash server to upload a previous dump with the + // given crash id. + bool RequestUpload(DWORD crash_id); + + bool RequestDump(EXCEPTION_POINTERS* ex_info, + MDRawAssertionInfo* assert_info); + + // Requests the crash server to generate a dump with the given + // exception information. + // + // Returns true if the dump was successful; false otherwise. Note that + // if the registration step was not performed or it was not successful, + // false will be returned. + bool RequestDump(EXCEPTION_POINTERS* ex_info); + + // Requests the crash server to generate a dump with the given + // assertion information. + // + // Returns true if the dump was successful; false otherwise. Note that + // if the registration step was not performed or it was not successful, + // false will be returned. + bool RequestDump(MDRawAssertionInfo* assert_info); + + private: + // Connects to the appropriate pipe and sets the pipe handle state. + // + // Returns the pipe handle if everything goes well; otherwise Returns NULL. + HANDLE ConnectToServer(); + + // Performs a handshake with the server over the given pipe which should be + // already connected to the server. + // + // Returns true if handshake with the server was successful; false otherwise. + bool RegisterClient(HANDLE pipe); + + // Validates the given server response. + bool ValidateResponse(const ProtocolMessage& msg) const; + + // Returns true if the registration step succeeded; false otherwise. + bool IsRegistered() const; + + // Connects to the given named pipe with given parameters. + // + // Returns true if the connection is successful; false otherwise. + HANDLE ConnectToPipe(const wchar_t* pipe_name, + DWORD pipe_access, + DWORD flags_attrs); + + // Signals the crash event and wait for the server to generate crash. + bool SignalCrashEventAndWait(); + + // Pipe name to use to talk to server. + std::wstring pipe_name_; + + // Custom client information + CustomClientInfo custom_info_; + + // Type of dump to generate. + MINIDUMP_TYPE dump_type_; + + // Event to signal in case of a crash. + HANDLE crash_event_; + + // Handle to wait on after signaling a crash for the server + // to finish generating crash dump. + HANDLE crash_generated_; + + // Handle to a mutex that will become signaled with WAIT_ABANDONED + // if the server process goes down. + HANDLE server_alive_; + + // Server process id. + DWORD server_process_id_; + + // Id of the thread that caused the crash. + DWORD thread_id_; + + // Exception pointers for an exception crash. + EXCEPTION_POINTERS* exception_pointers_; + + // Assertion info for an invalid parameter or pure call crash. + MDRawAssertionInfo assert_info_; + + // Disable copy ctor and operator=. + CrashGenerationClient(const CrashGenerationClient& crash_client); + CrashGenerationClient& operator=(const CrashGenerationClient& crash_client); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.cc b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.cc new file mode 100644 index 0000000..794742d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.cc @@ -0,0 +1,893 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/windows/crash_generation/crash_generation_server.h" +#include +#include +#include +#include "client/windows/common/auto_critical_section.h" +#include "processor/scoped_ptr.h" + +#include "client/windows/crash_generation/client_info.h" + +namespace google_breakpad { + +// Output buffer size. +static const size_t kOutBufferSize = 64; + +// Input buffer size. +static const size_t kInBufferSize = 64; + +// Access flags for the client on the dump request event. +static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE; + +// Access flags for the client on the dump generated event. +static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE | + SYNCHRONIZE; + +// Access flags for the client on the mutex. +static const DWORD kMutexAccess = SYNCHRONIZE; + +// Attribute flags for the pipe. +static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE | + PIPE_ACCESS_DUPLEX | + FILE_FLAG_OVERLAPPED; + +// Mode for the pipe. +static const DWORD kPipeMode = PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT; + +// For pipe I/O, execute the callback in the wait thread itself, +// since the callback does very little work. The callback executes +// the code for one of the states of the server state machine and +// the code for all of the states perform async I/O and hence +// finish very quickly. +static const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD; + +// Dump request threads will, most likely, generate dumps. That may +// take some time to finish, so specify WT_EXECUTELONGFUNCTION flag. +static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD | + WT_EXECUTELONGFUNCTION; + +// Maximum delay during server shutdown if some work items +// are still executing. +static const int kShutdownDelayMs = 10000; + +// Interval for each sleep during server shutdown. +static const int kShutdownSleepIntervalMs = 5; + +static bool IsClientRequestValid(const ProtocolMessage& msg) { + return msg.tag == MESSAGE_TAG_UPLOAD_REQUEST || + (msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST && + msg.id != 0 && + msg.thread_id != NULL && + msg.exception_pointers != NULL && + msg.assert_info != NULL); +} + +CrashGenerationServer::CrashGenerationServer( + const std::wstring& pipe_name, + SECURITY_ATTRIBUTES* pipe_sec_attrs, + OnClientConnectedCallback connect_callback, + void* connect_context, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitedCallback exit_callback, + void* exit_context, + OnClientUploadRequestCallback upload_request_callback, + void* upload_context, + bool generate_dumps, + const std::wstring* dump_path) + : pipe_name_(pipe_name), + pipe_sec_attrs_(pipe_sec_attrs), + pipe_(NULL), + pipe_wait_handle_(NULL), + server_alive_handle_(NULL), + connect_callback_(connect_callback), + connect_context_(connect_context), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + upload_request_callback_(upload_request_callback), + upload_context_(upload_context), + generate_dumps_(generate_dumps), + dump_generator_(NULL), + server_state_(IPC_SERVER_STATE_UNINITIALIZED), + shutting_down_(false), + overlapped_(), + client_info_(NULL), + cleanup_item_count_(0) { + InitializeCriticalSection(&clients_sync_); + + if (dump_path) { + dump_generator_.reset(new MinidumpGenerator(*dump_path)); + } +} + +CrashGenerationServer::~CrashGenerationServer() { + // Indicate to existing threads that server is shutting down. + shutting_down_ = true; + + // Even if there are no current worker threads running, it is possible that + // an I/O request is pending on the pipe right now but not yet done. In fact, + // it's very likely this is the case unless we are in an ERROR state. If we + // don't wait for the pending I/O to be done, then when the I/O completes, + // it may write to invalid memory. AppVerifier will flag this problem too. + // So we disconnect from the pipe and then wait for the server to get into + // error state so that the pending I/O will fail and get cleared. + DisconnectNamedPipe(pipe_); + int num_tries = 100; + while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) { + Sleep(10); + } + + // Unregister wait on the pipe. + if (pipe_wait_handle_) { + // Wait for already executing callbacks to finish. + UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE); + } + + // Close the pipe to avoid further client connections. + if (pipe_) { + CloseHandle(pipe_); + } + + // Request all ClientInfo objects to unregister all waits. + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&clients_sync_); + + std::list::iterator iter; + for (iter = clients_.begin(); iter != clients_.end(); ++iter) { + ClientInfo* client_info = *iter; + client_info->UnregisterWaits(); + } + } + + // Now that all waits have been unregistered, wait for some time + // for all pending work items to finish. + int total_wait = 0; + while (cleanup_item_count_ > 0) { + Sleep(kShutdownSleepIntervalMs); + + total_wait += kShutdownSleepIntervalMs; + + if (total_wait >= kShutdownDelayMs) { + break; + } + } + + // Clean up all the ClientInfo objects. + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&clients_sync_); + + std::list::iterator iter; + for (iter = clients_.begin(); iter != clients_.end(); ++iter) { + ClientInfo* client_info = *iter; + delete client_info; + } + } + + if (server_alive_handle_) { + // Release the mutex before closing the handle so that clients requesting + // dumps wait for a long time for the server to generate a dump. + ReleaseMutex(server_alive_handle_); + CloseHandle(server_alive_handle_); + } + + if (overlapped_.hEvent) { + CloseHandle(overlapped_.hEvent); + } + + DeleteCriticalSection(&clients_sync_); +} + +bool CrashGenerationServer::Start() { + if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) { + return false; + } + + server_state_ = IPC_SERVER_STATE_INITIAL; + + server_alive_handle_ = CreateMutex(NULL, TRUE, NULL); + if (!server_alive_handle_) { + return false; + } + + // Event to signal the client connection and pipe reads and writes. + overlapped_.hEvent = CreateEvent(NULL, // Security descriptor. + TRUE, // Manual reset. + FALSE, // Initially signaled. + NULL); // Name. + if (!overlapped_.hEvent) { + return false; + } + + // Register a callback with the thread pool for the client connection. + if (!RegisterWaitForSingleObject(&pipe_wait_handle_, + overlapped_.hEvent, + OnPipeConnected, + this, + INFINITE, + kPipeIOThreadFlags)) { + return false; + } + + pipe_ = CreateNamedPipe(pipe_name_.c_str(), + kPipeAttr, + kPipeMode, + 1, + kOutBufferSize, + kInBufferSize, + 0, + pipe_sec_attrs_); + if (pipe_ == INVALID_HANDLE_VALUE) { + return false; + } + + // Kick-start the state machine. This will initiate an asynchronous wait + // for client connections. + HandleInitialState(); + + // If we are in error state, it's because we failed to start listening. + return server_state_ != IPC_SERVER_STATE_ERROR; +} + +// If the server thread serving clients ever gets into the +// ERROR state, reset the event, close the pipe and remain +// in the error state forever. Error state means something +// that we didn't account for has happened, and it's dangerous +// to do anything unknowingly. +void CrashGenerationServer::HandleErrorState() { + assert(server_state_ == IPC_SERVER_STATE_ERROR); + + // If the server is shutting down anyway, don't clean up + // here since shut down process will clean up. + if (shutting_down_) { + return; + } + + if (pipe_wait_handle_) { + UnregisterWait(pipe_wait_handle_); + pipe_wait_handle_ = NULL; + } + + if (pipe_) { + CloseHandle(pipe_); + pipe_ = NULL; + } + + if (overlapped_.hEvent) { + CloseHandle(overlapped_.hEvent); + overlapped_.hEvent = NULL; + } +} + +// When the server thread serving clients is in the INITIAL state, +// try to connect to the pipe asynchronously. If the connection +// finishes synchronously, directly go into the CONNECTED state; +// otherwise go into the CONNECTING state. For any problems, go +// into the ERROR state. +void CrashGenerationServer::HandleInitialState() { + assert(server_state_ == IPC_SERVER_STATE_INITIAL); + + if (!ResetEvent(overlapped_.hEvent)) { + EnterErrorState(); + return; + } + + bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + // From MSDN, it is not clear that when ConnectNamedPipe is used + // in an overlapped mode, will it ever return non-zero value, and + // if so, in what cases. + assert(!success); + + switch (error_code) { + case ERROR_IO_PENDING: + EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING); + break; + + case ERROR_PIPE_CONNECTED: + EnterStateImmediately(IPC_SERVER_STATE_CONNECTED); + break; + + default: + EnterErrorState(); + break; + } +} + +// When the server thread serving the clients is in the CONNECTING state, +// try to get the result of the asynchronous connection request using +// the OVERLAPPED object. If the result indicates the connection is done, +// go into the CONNECTED state. If the result indicates I/O is still +// INCOMPLETE, remain in the CONNECTING state. For any problems, +// go into the DISCONNECTING state. +void CrashGenerationServer::HandleConnectingState() { + assert(server_state_ == IPC_SERVER_STATE_CONNECTING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success) { + EnterStateImmediately(IPC_SERVER_STATE_CONNECTED); + } else if (error_code != ERROR_IO_INCOMPLETE) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } else { + // remain in CONNECTING state + } +} + +// When the server thread serving the clients is in the CONNECTED state, +// try to issue an asynchronous read from the pipe. If read completes +// synchronously or if I/O is pending then go into the READING state. +// For any problems, go into the DISCONNECTING state. +void CrashGenerationServer::HandleConnectedState() { + assert(server_state_ == IPC_SERVER_STATE_CONNECTED); + + DWORD bytes_count = 0; + memset(&msg_, 0, sizeof(msg_)); + bool success = ReadFile(pipe_, + &msg_, + sizeof(msg_), + &bytes_count, + &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + // Note that the asynchronous read issued above can finish before the + // code below executes. But, it is okay to change state after issuing + // the asynchronous read. This is because even if the asynchronous read + // is done, the callback for it would not be executed until the current + // thread finishes its execution. + if (success || error_code == ERROR_IO_PENDING) { + EnterStateWhenSignaled(IPC_SERVER_STATE_READING); + } else { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } +} + +// When the server thread serving the clients is in the READING state, +// try to get the result of the async read. If async read is done, +// go into the READ_DONE state. For any problems, go into the +// DISCONNECTING state. +void CrashGenerationServer::HandleReadingState() { + assert(server_state_ == IPC_SERVER_STATE_READING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success && bytes_count == sizeof(ProtocolMessage)) { + EnterStateImmediately(IPC_SERVER_STATE_READ_DONE); + } else { + // We should never get an I/O incomplete since we should not execute this + // unless the Read has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + assert(error_code != ERROR_IO_INCOMPLETE); + + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } +} + +// When the server thread serving the client is in the READ_DONE state, +// validate the client's request message, register the client by +// creating appropriate objects and prepare the response. Then try to +// write the response to the pipe asynchronously. If that succeeds, +// go into the WRITING state. For any problems, go into the DISCONNECTING +// state. +void CrashGenerationServer::HandleReadDoneState() { + assert(server_state_ == IPC_SERVER_STATE_READ_DONE); + + if (!IsClientRequestValid(msg_)) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + if (msg_.tag == MESSAGE_TAG_UPLOAD_REQUEST) { + if (upload_request_callback_) + upload_request_callback_(upload_context_, msg_.id); + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + scoped_ptr client_info( + new ClientInfo(this, + msg_.id, + msg_.dump_type, + msg_.thread_id, + msg_.exception_pointers, + msg_.assert_info, + msg_.custom_client_info)); + + if (!client_info->Initialize()) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + // Issues an asynchronous WriteFile call if successful. + // Iff successful, assigns ownership of the client_info pointer to the server + // instance, in which case we must be sure not to free it in this function. + if (!RespondToClient(client_info.get())) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + client_info_ = client_info.release(); + + // Note that the asynchronous write issued by RespondToClient function + // can finish before the code below executes. But it is okay to change + // state after issuing the asynchronous write. This is because even if + // the asynchronous write is done, the callback for it would not be + // executed until the current thread finishes its execution. + EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING); +} + +// When the server thread serving the clients is in the WRITING state, +// try to get the result of the async write. If the async write is done, +// go into the WRITE_DONE state. For any problems, go into the +// DISONNECTING state. +void CrashGenerationServer::HandleWritingState() { + assert(server_state_ == IPC_SERVER_STATE_WRITING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success) { + EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE); + return; + } + + // We should never get an I/O incomplete since we should not execute this + // unless the Write has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + assert(error_code != ERROR_IO_INCOMPLETE); + + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); +} + +// When the server thread serving the clients is in the WRITE_DONE state, +// try to issue an async read on the pipe. If the read completes synchronously +// or if I/O is still pending then go into the READING_ACK state. For any +// issues, go into the DISCONNECTING state. +void CrashGenerationServer::HandleWriteDoneState() { + assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE); + + DWORD bytes_count = 0; + bool success = ReadFile(pipe_, + &msg_, + sizeof(msg_), + &bytes_count, + &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success) { + EnterStateImmediately(IPC_SERVER_STATE_READING_ACK); + } else if (error_code == ERROR_IO_PENDING) { + EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK); + } else { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } +} + +// When the server thread serving the clients is in the READING_ACK state, +// try to get result of async read. Go into the DISCONNECTING state. +void CrashGenerationServer::HandleReadingAckState() { + assert(server_state_ == IPC_SERVER_STATE_READING_ACK); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success) { + // The connection handshake with the client is now complete; perform + // the callback. + if (connect_callback_) { + connect_callback_(connect_context_, client_info_); + } + } else { + // We should never get an I/O incomplete since we should not execute this + // unless the Read has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + assert(error_code != ERROR_IO_INCOMPLETE); + } + + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); +} + +// When the server thread serving the client is in the DISCONNECTING state, +// disconnect from the pipe and reset the event. If anything fails, go into +// the ERROR state. If it goes well, go into the INITIAL state and set the +// event to start all over again. +void CrashGenerationServer::HandleDisconnectingState() { + assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING); + + // Done serving the client. + client_info_ = NULL; + + overlapped_.Internal = NULL; + overlapped_.InternalHigh = NULL; + overlapped_.Offset = 0; + overlapped_.OffsetHigh = 0; + overlapped_.Pointer = NULL; + + if (!ResetEvent(overlapped_.hEvent)) { + EnterErrorState(); + return; + } + + if (!DisconnectNamedPipe(pipe_)) { + EnterErrorState(); + return; + } + + // If the server is shutting down do not connect to the + // next client. + if (shutting_down_) { + return; + } + + EnterStateImmediately(IPC_SERVER_STATE_INITIAL); +} + +void CrashGenerationServer::EnterErrorState() { + SetEvent(overlapped_.hEvent); + server_state_ = IPC_SERVER_STATE_ERROR; +} + +void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) { + server_state_ = state; +} + +void CrashGenerationServer::EnterStateImmediately(IPCServerState state) { + server_state_ = state; + + if (!SetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_ERROR; + } +} + +bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info, + ProtocolMessage* reply) const { + reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE; + reply->id = GetCurrentProcessId(); + + if (CreateClientHandles(client_info, reply)) { + return true; + } + + if (reply->dump_request_handle) { + CloseHandle(reply->dump_request_handle); + } + + if (reply->dump_generated_handle) { + CloseHandle(reply->dump_generated_handle); + } + + if (reply->server_alive_handle) { + CloseHandle(reply->server_alive_handle); + } + + return false; +} + +bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info, + ProtocolMessage* reply) const { + HANDLE current_process = GetCurrentProcess(); + if (!DuplicateHandle(current_process, + client_info.dump_requested_handle(), + client_info.process_handle(), + &reply->dump_request_handle, + kDumpRequestEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, + client_info.dump_generated_handle(), + client_info.process_handle(), + &reply->dump_generated_handle, + kDumpGeneratedEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, + server_alive_handle_, + client_info.process_handle(), + &reply->server_alive_handle, + kMutexAccess, + FALSE, + 0)) { + return false; + } + + return true; +} + +bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) { + ProtocolMessage reply; + if (!PrepareReply(*client_info, &reply)) { + return false; + } + + DWORD bytes_count = 0; + bool success = WriteFile(pipe_, + &reply, + sizeof(reply), + &bytes_count, + &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (!success && error_code != ERROR_IO_PENDING) { + return false; + } + + // Takes over ownership of client_info. We MUST return true if AddClient + // succeeds. + if (!AddClient(client_info)) { + return false; + } + + return true; +} + +// The server thread servicing the clients runs this method. The method +// implements the state machine described in ReadMe.txt along with the +// helper methods HandleXXXState. +void CrashGenerationServer::HandleConnectionRequest() { + // If we are shutting doen then get into ERROR state, reset the event so more + // workers don't run and return immediately. + if (shutting_down_) { + server_state_ = IPC_SERVER_STATE_ERROR; + ResetEvent(overlapped_.hEvent); + return; + } + + switch (server_state_) { + case IPC_SERVER_STATE_ERROR: + HandleErrorState(); + break; + + case IPC_SERVER_STATE_INITIAL: + HandleInitialState(); + break; + + case IPC_SERVER_STATE_CONNECTING: + HandleConnectingState(); + break; + + case IPC_SERVER_STATE_CONNECTED: + HandleConnectedState(); + break; + + case IPC_SERVER_STATE_READING: + HandleReadingState(); + break; + + case IPC_SERVER_STATE_READ_DONE: + HandleReadDoneState(); + break; + + case IPC_SERVER_STATE_WRITING: + HandleWritingState(); + break; + + case IPC_SERVER_STATE_WRITE_DONE: + HandleWriteDoneState(); + break; + + case IPC_SERVER_STATE_READING_ACK: + HandleReadingAckState(); + break; + + case IPC_SERVER_STATE_DISCONNECTING: + HandleDisconnectingState(); + break; + + default: + assert(false); + // This indicates that we added one more state without + // adding handling code. + server_state_ = IPC_SERVER_STATE_ERROR; + break; + } +} + +bool CrashGenerationServer::AddClient(ClientInfo* client_info) { + HANDLE request_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&request_wait_handle, + client_info->dump_requested_handle(), + OnDumpRequest, + client_info, + INFINITE, + kDumpRequestThreadFlags)) { + return false; + } + + client_info->set_dump_request_wait_handle(request_wait_handle); + + // OnClientEnd will be called when the client process terminates. + HANDLE process_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&process_wait_handle, + client_info->process_handle(), + OnClientEnd, + client_info, + INFINITE, + WT_EXECUTEONLYONCE)) { + return false; + } + + client_info->set_process_exit_wait_handle(process_wait_handle); + + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&clients_sync_); + clients_.push_back(client_info); + } + + return true; +} + +// static +void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) { + assert(context); + + CrashGenerationServer* obj = + reinterpret_cast(context); + obj->HandleConnectionRequest(); +} + +// static +void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + client_info->PopulateCustomInfo(); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + crash_server->HandleDumpRequest(*client_info); + + ResetEvent(client_info->dump_requested_handle()); +} + +// static +void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + + client_info->UnregisterWaits(); + InterlockedIncrement(&crash_server->cleanup_item_count_); + + if (!QueueUserWorkItem(CleanupClient, context, WT_EXECUTEDEFAULT)) { + InterlockedDecrement(&crash_server->cleanup_item_count_); + } +} + +// static +DWORD WINAPI CrashGenerationServer::CleanupClient(void* context) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + + if (crash_server->exit_callback_) { + crash_server->exit_callback_(crash_server->exit_context_, client_info); + } + + crash_server->DoCleanup(client_info); + + InterlockedDecrement(&crash_server->cleanup_item_count_); + return 0; +} + +void CrashGenerationServer::DoCleanup(ClientInfo* client_info) { + assert(client_info); + + // Start a new scope to release lock automatically. + { + AutoCriticalSection lock(&clients_sync_); + clients_.remove(client_info); + } + + delete client_info; +} + +void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) { + // Generate the dump only if it's explicitly requested by the + // server application; otherwise the server might want to generate + // dump in the callback. + std::wstring dump_path; + if (generate_dumps_) { + if (!GenerateDump(client_info, &dump_path)) { + return; + } + } + + if (dump_callback_) { + std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path; + dump_callback_(dump_context_, &client_info, ptr_dump_path); + } + + SetEvent(client_info.dump_generated_handle()); +} + +bool CrashGenerationServer::GenerateDump(const ClientInfo& client, + std::wstring* dump_path) { + assert(client.pid() != 0); + assert(client.process_handle()); + + // We have to get the address of EXCEPTION_INFORMATION from + // the client process address space. + EXCEPTION_POINTERS* client_ex_info = NULL; + if (!client.GetClientExceptionInfo(&client_ex_info)) { + return false; + } + + DWORD client_thread_id = 0; + if (!client.GetClientThreadId(&client_thread_id)) { + return false; + } + + return dump_generator_->WriteMinidump(client.process_handle(), + client.pid(), + client_thread_id, + GetCurrentThreadId(), + client_ex_info, + client.assert_info(), + client.dump_type(), + true, + dump_path); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.h b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.h new file mode 100644 index 0000000..2f0a222 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/crash_generation_server.h @@ -0,0 +1,299 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ + +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/crash_generation/minidump_generator.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { +class ClientInfo; + +// Abstraction for server side implementation of out-of-process crash +// generation protocol for Windows platform only. It generates Windows +// minidump files for client processes that request dump generation. When +// the server is requested to start listening for clients (by calling the +// Start method), it creates a named pipe and waits for the clients to +// register. In response, it hands them event handles that the client can +// signal to request dump generation. When the clients request dump +// generation in this way, the server generates Windows minidump files. +class CrashGenerationServer { + public: + typedef void (*OnClientConnectedCallback)(void* context, + const ClientInfo* client_info); + + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo* client_info, + const std::wstring* file_path); + + typedef void (*OnClientExitedCallback)(void* context, + const ClientInfo* client_info); + + typedef void (*OnClientUploadRequestCallback)(void* context, + const DWORD crash_id); + + // Creates an instance with the given parameters. + // + // Parameter pipe_name: Name of the Windows named pipe + // Parameter pipe_sec_attrs Security attributes to set on the pipe. Pass + // NULL to use default security on the pipe. By default, the pipe created + // allows Local System, Administrators and the Creator full control and + // the Everyone group read access on the pipe. + // Parameter connect_callback: Callback for a new client connection. + // Parameter connect_context: Context for client connection callback. + // Parameter crash_callback: Callback for a client crash dump request. + // Parameter crash_context: Context for client crash dump request callback. + // Parameter exit_callback: Callback for client process exit. + // Parameter exit_context: Context for client exit callback. + // Parameter generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly in the + // crash dump request callback. In that case, false can be passed for this + // parameter. + // Parameter dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const std::wstring& pipe_name, + SECURITY_ATTRIBUTES* pipe_sec_attrs, + OnClientConnectedCallback connect_callback, + void* connect_context, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitedCallback exit_callback, + void* exit_context, + OnClientUploadRequestCallback upload_request_callback, + void* upload_context, + bool generate_dumps, + const std::wstring* dump_path); + + ~CrashGenerationServer(); + + // Performs initialization steps needed to start listening to clients. Upon + // successful return clients may connect to this server's pipe. + // + // Returns true if initialization is successful; false otherwise. + bool Start(); + + private: + // Various states the client can be in during the handshake with + // the server. + enum IPCServerState { + // Server starts in this state. + IPC_SERVER_STATE_UNINITIALIZED, + + // Server is in error state and it cannot serve any clients. + IPC_SERVER_STATE_ERROR, + + // Server starts in this state. + IPC_SERVER_STATE_INITIAL, + + // Server has issued an async connect to the pipe and it is waiting + // for the connection to be established. + IPC_SERVER_STATE_CONNECTING, + + // Server is connected successfully. + IPC_SERVER_STATE_CONNECTED, + + // Server has issued an async read from the pipe and it is waiting for + // the read to finish. + IPC_SERVER_STATE_READING, + + // Server is done reading from the pipe. + IPC_SERVER_STATE_READ_DONE, + + // Server has issued an async write to the pipe and it is waiting for + // the write to finish. + IPC_SERVER_STATE_WRITING, + + // Server is done writing to the pipe. + IPC_SERVER_STATE_WRITE_DONE, + + // Server has issued an async read from the pipe for an ack and it + // is waiting for the read to finish. + IPC_SERVER_STATE_READING_ACK, + + // Server is done writing to the pipe and it is now ready to disconnect + // and reconnect. + IPC_SERVER_STATE_DISCONNECTING + }; + + // + // Helper methods to handle various server IPC states. + // + void HandleErrorState(); + void HandleInitialState(); + void HandleConnectingState(); + void HandleConnectedState(); + void HandleReadingState(); + void HandleReadDoneState(); + void HandleWritingState(); + void HandleWriteDoneState(); + void HandleReadingAckState(); + void HandleDisconnectingState(); + + // Prepares reply for a client from the given parameters. + bool PrepareReply(const ClientInfo& client_info, + ProtocolMessage* reply) const; + + // Duplicates various handles in the ClientInfo object for the client + // process and stores them in the given ProtocolMessage instance. If + // creating any handle fails, ProtocolMessage will contain the handles + // already created successfully, which should be closed by the caller. + bool CreateClientHandles(const ClientInfo& client_info, + ProtocolMessage* reply) const; + + // Response to the given client. Return true if all steps of + // responding to the client succeed, false otherwise. + bool RespondToClient(ClientInfo* client_info); + + // Handles a connection request from the client. + void HandleConnectionRequest(); + + // Handles a dump request from the client. + void HandleDumpRequest(const ClientInfo& client_info); + + // Callback for pipe connected event. + static void CALLBACK OnPipeConnected(void* context, BOOLEAN timer_or_wait); + + // Callback for a dump request. + static void CALLBACK OnDumpRequest(void* context, BOOLEAN timer_or_wait); + + // Callback for client process exit event. + static void CALLBACK OnClientEnd(void* context, BOOLEAN timer_or_wait); + + // Releases resources for a client. + static DWORD WINAPI CleanupClient(void* context); + + // Cleans up for the given client. + void DoCleanup(ClientInfo* client_info); + + // Adds the given client to the list of registered clients. + bool AddClient(ClientInfo* client_info); + + // Generates dump for the given client. + bool GenerateDump(const ClientInfo& client, std::wstring* dump_path); + + // Puts the server in a permanent error state and sets a signal such that + // the state will be immediately entered after the current state transition + // is complete. + void EnterErrorState(); + + // Puts the server in the specified state and sets a signal such that the + // state is immediately entered after the current state transition is + // complete. + void EnterStateImmediately(IPCServerState state); + + // Puts the server in the specified state. No signal will be set, so the state + // transition will only occur when signaled manually or by completion of an + // asynchronous IO operation. + void EnterStateWhenSignaled(IPCServerState state); + + // Sync object for thread-safe access to the shared list of clients. + CRITICAL_SECTION clients_sync_; + + // List of clients. + std::list clients_; + + // Pipe name. + std::wstring pipe_name_; + + // Pipe security attributes + SECURITY_ATTRIBUTES* pipe_sec_attrs_; + + // Handle to the pipe used for handshake with clients. + HANDLE pipe_; + + // Pipe wait handle. + HANDLE pipe_wait_handle_; + + // Handle to server-alive mutex. + HANDLE server_alive_handle_; + + // Callback for a successful client connection. + OnClientConnectedCallback connect_callback_; + + // Context for client connected callback. + void* connect_context_; + + // Callback for a client dump request. + OnClientDumpRequestCallback dump_callback_; + + // Context for client dump request callback. + void* dump_context_; + + // Callback for client process exit. + OnClientExitedCallback exit_callback_; + + // Context for client process exit callback. + void* exit_context_; + + // Callback for upload request. + OnClientUploadRequestCallback upload_request_callback_; + + // Context for upload request callback. + void* upload_context_; + + // Whether to generate dumps. + bool generate_dumps_; + + // Instance of a mini dump generator. + scoped_ptr dump_generator_; + + // State of the server in performing the IPC with the client. + // Note that since we restrict the pipe to one instance, we + // only need to keep one state of the server. Otherwise, server + // would have one state per client it is talking to. + volatile IPCServerState server_state_; + + // Whether the server is shutting down. + volatile bool shutting_down_; + + // Overlapped instance for async I/O on the pipe. + OVERLAPPED overlapped_; + + // Message object used in IPC with the client. + ProtocolMessage msg_; + + // Client Info for the client that's connecting to the server. + ClientInfo* client_info_; + + // Count of clean-up work items that are currently running or are + // already queued to run. + volatile LONG cleanup_item_count_; + + // Disable copy ctor and operator=. + CrashGenerationServer(const CrashGenerationServer& crash_server); + CrashGenerationServer& operator=(const CrashGenerationServer& crash_server); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.cc b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.cc new file mode 100644 index 0000000..37bd55e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.cc @@ -0,0 +1,309 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "client/windows/crash_generation/minidump_generator.h" +#include +#include "client/windows/common/auto_critical_section.h" +#include "common/windows/guid_string.h" + +using std::wstring; + +namespace google_breakpad { + +MinidumpGenerator::MinidumpGenerator(const wstring& dump_path) + : dbghelp_module_(NULL), + rpcrt4_module_(NULL), + dump_path_(dump_path), + write_dump_(NULL), + create_uuid_(NULL) { + InitializeCriticalSection(&module_load_sync_); + InitializeCriticalSection(&get_proc_address_sync_); +} + +MinidumpGenerator::~MinidumpGenerator() { + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + + if (rpcrt4_module_) { + FreeLibrary(rpcrt4_module_); + } + + DeleteCriticalSection(&get_proc_address_sync_); + DeleteCriticalSection(&module_load_sync_); +} + +bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, + DWORD process_id, + DWORD thread_id, + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + MINIDUMP_TYPE dump_type, + bool is_client_pointers, + wstring* dump_path) { + // Just call the full WriteMinidump with NULL as the full_dump_path. + return this->WriteMinidump(process_handle, process_id, thread_id, + requesting_thread_id, exception_pointers, + assert_info, dump_type, is_client_pointers, + dump_path, NULL); +} + +bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, + DWORD process_id, + DWORD thread_id, + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + MINIDUMP_TYPE dump_type, + bool is_client_pointers, + wstring* dump_path, + wstring* full_dump_path) { + MiniDumpWriteDumpType write_dump = GetWriteDump(); + if (!write_dump) { + return false; + } + + wstring dump_file_path; + if (!GenerateDumpFilePath(&dump_file_path)) { + return false; + } + + // If the client requests a full memory dump, we will write a normal mini + // dump and a full memory dump. Both dump files use the same uuid as file + // name prefix. + bool full_memory_dump = (dump_type & MiniDumpWithFullMemory) != 0; + wstring full_dump_file_path; + if (full_memory_dump) { + full_dump_file_path.assign(dump_file_path); + full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp + full_dump_file_path.append(TEXT("-full.dmp")); + } + + HANDLE dump_file = CreateFile(dump_file_path.c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (dump_file == INVALID_HANDLE_VALUE) { + return false; + } + + HANDLE full_dump_file = INVALID_HANDLE_VALUE; + if (full_memory_dump) { + full_dump_file = CreateFile(full_dump_file_path.c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (full_dump_file == INVALID_HANDLE_VALUE) { + CloseHandle(dump_file); + return false; + } + } + + MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL; + MINIDUMP_EXCEPTION_INFORMATION dump_exception_info; + + // Setup the exception information object only if it's a dump + // due to an exception. + if (exception_pointers) { + dump_exception_pointers = &dump_exception_info; + dump_exception_info.ThreadId = thread_id; + dump_exception_info.ExceptionPointers = exception_pointers; + dump_exception_info.ClientPointers = is_client_pointers; + } + + // Add an MDRawBreakpadInfo stream to the minidump, to provide additional + // information about the exception handler to the Breakpad processor. + // The information will help the processor determine which threads are + // relevant. The Breakpad processor does not require this information but + // can function better with Breakpad-generated dumps when it is present. + // The native debugger is not harmed by the presence of this information. + MDRawBreakpadInfo breakpad_info = {0}; + if (!is_client_pointers) { + // Set the dump thread id and requesting thread id only in case of + // in-process dump generation. + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = thread_id; + breakpad_info.requesting_thread_id = requesting_thread_id; + } + + // Leave room in user_stream_array for a possible assertion info stream. + MINIDUMP_USER_STREAM user_stream_array[2]; + user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[0].BufferSize = sizeof(breakpad_info); + user_stream_array[0].Buffer = &breakpad_info; + + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 1; + user_streams.UserStreamArray = user_stream_array; + + MDRawAssertionInfo* actual_assert_info = assert_info; + MDRawAssertionInfo client_assert_info = {0}; + + if (assert_info) { + // If the assertion info object lives in the client process, + // read the memory of the client process. + if (is_client_pointers) { + SIZE_T bytes_read = 0; + if (!ReadProcessMemory(process_handle, + assert_info, + &client_assert_info, + sizeof(client_assert_info), + &bytes_read)) { + CloseHandle(dump_file); + if (full_dump_file != INVALID_HANDLE_VALUE) + CloseHandle(full_dump_file); + return false; + } + + if (bytes_read != sizeof(client_assert_info)) { + CloseHandle(dump_file); + if (full_dump_file != INVALID_HANDLE_VALUE) + CloseHandle(full_dump_file); + return false; + } + + actual_assert_info = &client_assert_info; + } + + user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[1].Buffer = actual_assert_info; + ++user_streams.UserStreamCount; + } + + bool result_minidump = write_dump( + process_handle, + process_id, + dump_file, + static_cast((dump_type & (~MiniDumpWithFullMemory)) + | MiniDumpNormal), + exception_pointers ? &dump_exception_info : NULL, + &user_streams, + NULL) != FALSE; + + bool result_full_memory = true; + if (full_memory_dump) { + result_full_memory = write_dump( + process_handle, + process_id, + full_dump_file, + static_cast(dump_type & (~MiniDumpNormal)), + exception_pointers ? &dump_exception_info : NULL, + &user_streams, + NULL) != FALSE; + } + + bool result = result_minidump && result_full_memory; + + CloseHandle(dump_file); + if (full_dump_file != INVALID_HANDLE_VALUE) + CloseHandle(full_dump_file); + + // Store the path of the dump file in the out parameter if dump generation + // succeeded. + if (result && dump_path) { + *dump_path = dump_file_path; + } + if (result && full_memory_dump && full_dump_path) { + *full_dump_path = full_dump_file_path; + } + + return result; +} + +HMODULE MinidumpGenerator::GetDbghelpModule() { + AutoCriticalSection lock(&module_load_sync_); + if (!dbghelp_module_) { + dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll")); + } + + return dbghelp_module_; +} + +MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() { + AutoCriticalSection lock(&get_proc_address_sync_); + if (!write_dump_) { + HMODULE module = GetDbghelpModule(); + if (module) { + FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump"); + write_dump_ = reinterpret_cast(proc); + } + } + + return write_dump_; +} + +HMODULE MinidumpGenerator::GetRpcrt4Module() { + AutoCriticalSection lock(&module_load_sync_); + if (!rpcrt4_module_) { + rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll")); + } + + return rpcrt4_module_; +} + +MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() { + AutoCriticalSection lock(&module_load_sync_); + if (!create_uuid_) { + HMODULE module = GetRpcrt4Module(); + if (module) { + FARPROC proc = GetProcAddress(module, "UuidCreate"); + create_uuid_ = reinterpret_cast(proc); + } + } + + return create_uuid_; +} + +bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) { + UUID id = {0}; + + UuidCreateType create_uuid = GetCreateUuid(); + if (!create_uuid) { + return false; + } + + create_uuid(&id); + wstring id_str = GUIDString::GUIDToWString(&id); + + *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp"); + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.h b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.h new file mode 100644 index 0000000..5f9e4b5 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/crash_generation/minidump_generator.h @@ -0,0 +1,135 @@ +// Copyright (c) 2008, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_ +#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_ + +#include +#include +#include +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Abstraction for various objects and operations needed to generate +// minidump on Windows. This abstraction is useful to hide all the gory +// details for minidump generation and provide a clean interface to +// the clients to generate minidumps. +class MinidumpGenerator { + public: + // Creates an instance with the given dump path. + explicit MinidumpGenerator(const std::wstring& dump_path); + + ~MinidumpGenerator(); + + // Writes the minidump with the given parameters. Stores the + // dump file path in the dump_path parameter if dump generation + // succeeds. + bool WriteMinidump(HANDLE process_handle, + DWORD process_id, + DWORD thread_id, + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + MINIDUMP_TYPE dump_type, + bool is_client_pointers, + std::wstring* dump_path); + + // Writes the minidump with the given parameters. Stores the dump file + // path in the dump_path (and full_dump_path) parameter if dump + // generation succeeds. full_dump_path and dump_path can be NULL. + bool WriteMinidump(HANDLE process_handle, + DWORD process_id, + DWORD thread_id, + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + MINIDUMP_TYPE dump_type, + bool is_client_pointers, + std::wstring* dump_path, + std::wstring* full_dump_path); + + private: + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI* MiniDumpWriteDumpType)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // Function pointer type for UuidCreate, which is looked up dynamically. + typedef RPC_STATUS (RPC_ENTRY* UuidCreateType)(UUID* Uuid); + + // Loads the appropriate DLL lazily in a thread safe way. + HMODULE GetDbghelpModule(); + + // Loads the appropriate DLL and gets a pointer to the MiniDumpWriteDump + // function lazily and in a thread-safe manner. + MiniDumpWriteDumpType GetWriteDump(); + + // Loads the appropriate DLL lazily in a thread safe way. + HMODULE GetRpcrt4Module(); + + // Loads the appropriate DLL and gets a pointer to the UuidCreate + // function lazily and in a thread-safe manner. + UuidCreateType GetCreateUuid(); + + // Returns the path for the file to write dump to. + bool GenerateDumpFilePath(std::wstring* file_path); + + // Handle to dynamically loaded DbgHelp.dll. + HMODULE dbghelp_module_; + + // Pointer to the MiniDumpWriteDump function. + MiniDumpWriteDumpType write_dump_; + + // Handle to dynamically loaded rpcrt4.dll. + HMODULE rpcrt4_module_; + + // Pointer to the UuidCreate function. + UuidCreateType create_uuid_; + + // Folder path to store dump files. + std::wstring dump_path_; + + // Critical section to sychronize action of loading modules dynamically. + CRITICAL_SECTION module_load_sync_; + + // Critical section to synchronize action of dynamically getting function + // addresses from modules. + CRITICAL_SECTION get_proc_address_sync_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_ diff --git a/src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.cc b/src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.cc new file mode 100644 index 0000000..08c5fb5 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.cc @@ -0,0 +1,895 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/windows/string_utils-inl.h" + +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/handler/exception_handler.h" +#include "common/windows/guid_string.h" + +namespace google_breakpad { + +static const int kWaitForHandlerThreadMs = 60000; +static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; + +// This is passed as the context to the MinidumpWriteDump callback. +typedef struct { + ULONG64 memory_base; + ULONG memory_size; + bool finished; +} MinidumpCallbackContext; + +vector* ExceptionHandler::handler_stack_ = NULL; +LONG ExceptionHandler::handler_stack_index_ = 0; +CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_; +volatile LONG ExceptionHandler::instance_count_ = 0; + +ExceptionHandler::ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + dump_type, + pipe_name, + custom_info); +} + +ExceptionHandler::ExceptionHandler(const wstring &dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + MiniDumpNormal, + NULL, + NULL); +} + +void ExceptionHandler::Initialize(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info) { + LONG instance_count = InterlockedIncrement(&instance_count_); + filter_ = filter; + callback_ = callback; + callback_context_ = callback_context; + dump_path_c_ = NULL; + next_minidump_id_c_ = NULL; + next_minidump_path_c_ = NULL; + dbghelp_module_ = NULL; + minidump_write_dump_ = NULL; + dump_type_ = dump_type; + rpcrt4_module_ = NULL; + uuid_create_ = NULL; + handler_types_ = handler_types; + previous_filter_ = NULL; +#if _MSC_VER >= 1400 // MSVC 2005/8 + previous_iph_ = NULL; +#endif // _MSC_VER >= 1400 + previous_pch_ = NULL; + handler_thread_ = NULL; + is_shutdown_ = false; + handler_start_semaphore_ = NULL; + handler_finish_semaphore_ = NULL; + requesting_thread_id_ = 0; + exception_info_ = NULL; + assertion_ = NULL; + handler_return_value_ = false; + handle_debug_exceptions_ = false; + + // Attempt to use out-of-process if user has specified pipe name. + if (pipe_name != NULL) { + scoped_ptr client( + new CrashGenerationClient(pipe_name, + dump_type_, + custom_info)); + + // If successful in registering with the monitoring process, + // there is no need to setup in-process crash generation. + if (client->Register()) { + crash_generation_client_.reset(client.release()); + } + } + + if (!IsOutOfProcess()) { + // Either client did not ask for out-of-process crash generation + // or registration with the server process failed. In either case, + // setup to do in-process crash generation. + + // Set synchronization primitives and the handler thread. Each + // ExceptionHandler object gets its own handler thread because that's the + // only way to reliably guarantee sufficient stack space in an exception, + // and it allows an easy way to get a snapshot of the requesting thread's + // context outside of an exception. + InitializeCriticalSection(&handler_critical_section_); + handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + assert(handler_start_semaphore_ != NULL); + + handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + assert(handler_finish_semaphore_ != NULL); + + // Don't attempt to create the thread if we could not create the semaphores. + if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) { + DWORD thread_id; + handler_thread_ = CreateThread(NULL, // lpThreadAttributes + kExceptionHandlerThreadInitialStackSize, + ExceptionHandlerThreadMain, + this, // lpParameter + 0, // dwCreationFlags + &thread_id); + assert(handler_thread_ != NULL); + } + + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); + if (dbghelp_module_) { + minidump_write_dump_ = reinterpret_cast( + GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + } + + // Load this library dynamically to not affect existing projects. Most + // projects don't link against this directly, it's usually dynamically + // loaded by dependent code. + rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll"); + if (rpcrt4_module_) { + uuid_create_ = reinterpret_cast( + GetProcAddress(rpcrt4_module_, "UuidCreate")); + } + + // set_dump_path calls UpdateNextID. This sets up all of the path and id + // strings, and their equivalent c_str pointers. + set_dump_path(dump_path); + } + + // There is a race condition here. If the first instance has not yet + // initialized the critical section, the second (and later) instances may + // try to use uninitialized critical section object. The feature of multiple + // instances in one module is not used much, so leave it as is for now. + // One way to solve this in the current design (that is, keeping the static + // handler stack) is to use spin locks with volatile bools to synchronize + // the handler stack. This works only if the compiler guarantees to generate + // cache coherent code for volatile. + // TODO(munjal): Fix this in a better way by changing the design if possible. + + // Lazy initialization of the handler_stack_critical_section_ + if (instance_count == 1) { + InitializeCriticalSection(&handler_stack_critical_section_); + } + + if (handler_types != HANDLER_NONE) { + EnterCriticalSection(&handler_stack_critical_section_); + + // The first time an ExceptionHandler that installs a handler is + // created, set up the handler stack. + if (!handler_stack_) { + handler_stack_ = new vector(); + } + handler_stack_->push_back(this); + + if (handler_types & HANDLER_EXCEPTION) + previous_filter_ = SetUnhandledExceptionFilter(HandleException); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + if (handler_types & HANDLER_INVALID_PARAMETER) + previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter); +#endif // _MSC_VER >= 1400 + + if (handler_types & HANDLER_PURECALL) + previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); + + LeaveCriticalSection(&handler_stack_critical_section_); + } +} + +ExceptionHandler::~ExceptionHandler() { + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + + if (rpcrt4_module_) { + FreeLibrary(rpcrt4_module_); + } + + if (handler_types_ != HANDLER_NONE) { + EnterCriticalSection(&handler_stack_critical_section_); + + if (handler_types_ & HANDLER_EXCEPTION) + SetUnhandledExceptionFilter(previous_filter_); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + if (handler_types_ & HANDLER_INVALID_PARAMETER) + _set_invalid_parameter_handler(previous_iph_); +#endif // _MSC_VER >= 1400 + + if (handler_types_ & HANDLER_PURECALL) + _set_purecall_handler(previous_pch_); + + if (handler_stack_->back() == this) { + handler_stack_->pop_back(); + } else { + // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the + // system's application event log. + fprintf(stderr, "warning: removing Breakpad handler out of order\n"); + vector::iterator iterator = handler_stack_->begin(); + while (iterator != handler_stack_->end()) { + if (*iterator == this) { + iterator = handler_stack_->erase(iterator); + } else { + ++iterator; + } + } + } + + if (handler_stack_->empty()) { + // When destroying the last ExceptionHandler that installed a handler, + // clean up the handler stack. + delete handler_stack_; + handler_stack_ = NULL; + } + + LeaveCriticalSection(&handler_stack_critical_section_); + } + + // Some of the objects were only initialized if out of process + // registration was not done. + if (!IsOutOfProcess()) { +#ifdef BREAKPAD_NO_TERMINATE_THREAD + // Clean up the handler thread and synchronization primitives. The handler + // thread is either waiting on the semaphore to handle a crash or it is + // handling a crash. Coming out of the wait is fast but wait more in the + // eventuality a crash is handled. This compilation option results in a + // deadlock if the exception handler is destroyed while executing code + // inside DllMain. + is_shutdown_ = true; + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); + WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs); +#else + TerminateThread(handler_thread_, 1); +#endif // BREAKPAD_NO_TERMINATE_THREAD + + CloseHandle(handler_thread_); + handler_thread_ = NULL; + DeleteCriticalSection(&handler_critical_section_); + CloseHandle(handler_start_semaphore_); + CloseHandle(handler_finish_semaphore_); + } + + // There is a race condition in the code below: if this instance is + // deleting the static critical section and a new instance of the class + // is created, then there is a possibility that the critical section be + // initialized while the same critical section is being deleted. Given the + // usage pattern for the code, this race condition is unlikely to hit, but it + // is a race condition nonetheless. + if (InterlockedDecrement(&instance_count_) == 0) { + DeleteCriticalSection(&handler_stack_critical_section_); + } +} + +bool ExceptionHandler::RequestUpload(DWORD crash_id) { + return crash_generation_client_->RequestUpload(crash_id); +} + +// static +DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) { + ExceptionHandler* self = reinterpret_cast(lpParameter); + assert(self); + assert(self->handler_start_semaphore_ != NULL); + assert(self->handler_finish_semaphore_ != NULL); + + while (true) { + if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) == + WAIT_OBJECT_0) { + // Perform the requested action. + if (self->is_shutdown_) { + // The instance of the exception handler is being destroyed. + break; + } else { + self->handler_return_value_ = + self->WriteMinidumpWithException(self->requesting_thread_id_, + self->exception_info_, + self->assertion_); + } + + // Allow the requesting thread to proceed. + ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL); + } + } + + // This statement is not reached when the thread is unconditionally + // terminated by the ExceptionHandler destructor. + return 0; +} + +// HandleException and HandleInvalidParameter must create an +// AutoExceptionHandler object to maintain static state and to determine which +// ExceptionHandler instance to use. The constructor locates the correct +// instance, and makes it available through get_handler(). The destructor +// restores the state in effect prior to allocating the AutoExceptionHandler. +class AutoExceptionHandler { + public: + AutoExceptionHandler() { + // Increment handler_stack_index_ so that if another Breakpad handler is + // registered using this same HandleException function, and it needs to be + // called while this handler is running (either because this handler + // declines to handle the exception, or an exception occurs during + // handling), HandleException will find the appropriate ExceptionHandler + // object in handler_stack_ to deliver the exception to. + // + // Because handler_stack_ is addressed in reverse (as |size - index|), + // preincrementing handler_stack_index_ avoids needing to subtract 1 from + // the argument to |at|. + // + // The index is maintained instead of popping elements off of the handler + // stack and pushing them at the end of this method. This avoids ruining + // the order of elements in the stack in the event that some other thread + // decides to manipulate the handler stack (such as creating a new + // ExceptionHandler object) while an exception is being handled. + EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_); + handler_ = ExceptionHandler::handler_stack_->at( + ExceptionHandler::handler_stack_->size() - + ++ExceptionHandler::handler_stack_index_); + + // In case another exception occurs while this handler is doing its thing, + // it should be delivered to the previous filter. + SetUnhandledExceptionFilter(handler_->previous_filter_); +#if _MSC_VER >= 1400 // MSVC 2005/8 + _set_invalid_parameter_handler(handler_->previous_iph_); +#endif // _MSC_VER >= 1400 + _set_purecall_handler(handler_->previous_pch_); + } + + ~AutoExceptionHandler() { + // Put things back the way they were before entering this handler. + SetUnhandledExceptionFilter(ExceptionHandler::HandleException); +#if _MSC_VER >= 1400 // MSVC 2005/8 + _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter); +#endif // _MSC_VER >= 1400 + _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall); + + --ExceptionHandler::handler_stack_index_; + LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_); + } + + ExceptionHandler* get_handler() const { return handler_; } + + private: + ExceptionHandler* handler_; +}; + +// static +LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) { + AutoExceptionHandler auto_exception_handler; + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); + + // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This + // logic will short-circuit before calling WriteMinidumpOnHandlerThread, + // allowing something else to handle the breakpoint without incurring the + // overhead transitioning to and from the handler thread. This behavior + // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions. + DWORD code = exinfo->ExceptionRecord->ExceptionCode; + LONG action; + bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) || + (code == EXCEPTION_SINGLE_STEP); + + bool success = false; + + if (!is_debug_exception || + current_handler->get_handle_debug_exceptions()) { + // If out-of-proc crash handler client is available, we have to use that + // to generate dump and we cannot fall back on in-proc dump generation + // because we never prepared for an in-proc dump generation + + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + exinfo, + NULL); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL); + } + } + + // The handler fully handled the exception. Returning + // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually + // results in the application being terminated. + // + // Note: If the application was launched from within the Cygwin + // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the + // application to be restarted. + if (success) { + action = EXCEPTION_EXECUTE_HANDLER; + } else { + // There was an exception, it was a breakpoint or something else ignored + // above, or it was passed to the handler, which decided not to handle it. + // This could be because the filter callback didn't want it, because + // minidump writing failed for some reason, or because the post-minidump + // callback function indicated failure. Give the previous handler a + // chance to do something with the exception. If there is no previous + // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger + // or native "crashed" dialog to handle the exception. + if (current_handler->previous_filter_) { + action = current_handler->previous_filter_(exinfo); + } else { + action = EXCEPTION_CONTINUE_SEARCH; + } + } + + return action; +} + +#if _MSC_VER >= 1400 // MSVC 2005/8 +// static +void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t reserved) { + // This is an invalid parameter, not an exception. It's safe to play with + // sprintf here. + AutoExceptionHandler auto_exception_handler; + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); + + MDRawAssertionInfo assertion; + memset(&assertion, 0, sizeof(assertion)); + _snwprintf_s(reinterpret_cast(assertion.expression), + sizeof(assertion.expression) / sizeof(assertion.expression[0]), + _TRUNCATE, L"%s", expression); + _snwprintf_s(reinterpret_cast(assertion.function), + sizeof(assertion.function) / sizeof(assertion.function[0]), + _TRUNCATE, L"%s", function); + _snwprintf_s(reinterpret_cast(assertion.file), + sizeof(assertion.file) / sizeof(assertion.file[0]), + _TRUNCATE, L"%s", file); + assertion.line = line; + assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER; + + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + + exception_record.ExceptionCode = STATUS_INVALID_PARAMETER; + + // We store pointers to the the expression and function strings, + // and the line as exception parameters to make them easy to + // access by the developer on the far side. + exception_record.NumberParameters = 3; + exception_record.ExceptionInformation[0] = + reinterpret_cast(&assertion.expression); + exception_record.ExceptionInformation[1] = + reinterpret_cast(&assertion.file); + exception_record.ExceptionInformation[2] = assertion.line; + + bool success = false; + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + &exception_ptrs, + &assertion); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, + &assertion); + } + + if (!success) { + if (current_handler->previous_iph_) { + // The handler didn't fully handle the exception. Give it to the + // previous invalid parameter handler. + current_handler->previous_iph_(expression, + function, + file, + line, + reserved); + } else { + // If there's no previous handler, pass the exception back in to the + // invalid parameter handler's core. That's the routine that called this + // function, but now, since this function is no longer registered (and in + // fact, no function at all is registered), this will result in the + // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson. + // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes + // more information through. In non-debug builds, it is not available, + // so fall back to using _invalid_parameter_noinfo. See invarg.c in the + // CRT source. +#ifdef _DEBUG + _invalid_parameter(expression, function, file, line, reserved); +#else // _DEBUG + _invalid_parameter_noinfo(); +#endif // _DEBUG + } + } + + // The handler either took care of the invalid parameter problem itself, + // or passed it on to another handler. "Swallow" it by exiting, paralleling + // the behavior of "swallowing" exceptions. + exit(0); +} +#endif // _MSC_VER >= 1400 + +// static +void ExceptionHandler::HandlePureVirtualCall() { + // This is an pure virtual function call, not an exception. It's safe to + // play with sprintf here. + AutoExceptionHandler auto_exception_handler; + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); + + MDRawAssertionInfo assertion; + memset(&assertion, 0, sizeof(assertion)); + assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL; + + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; + + // We store pointers to the the expression and function strings, + // and the line as exception parameters to make them easy to + // access by the developer on the far side. + exception_record.NumberParameters = 3; + exception_record.ExceptionInformation[0] = + reinterpret_cast(&assertion.expression); + exception_record.ExceptionInformation[1] = + reinterpret_cast(&assertion.file); + exception_record.ExceptionInformation[2] = assertion.line; + + bool success = false; + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + &exception_ptrs, + &assertion); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, + &assertion); + } + + if (!success) { + if (current_handler->previous_pch_) { + // The handler didn't fully handle the exception. Give it to the + // previous purecall handler. + current_handler->previous_pch_(); + } else { + // If there's no previous handler, return and let _purecall handle it. + // This will just put up an assertion dialog. + return; + } + } + + // The handler either took care of the invalid parameter problem itself, + // or passed it on to another handler. "Swallow" it by exiting, paralleling + // the behavior of "swallowing" exceptions. + exit(0); +} + +bool ExceptionHandler::WriteMinidumpOnHandlerThread( + EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) { + EnterCriticalSection(&handler_critical_section_); + + // There isn't much we can do if the handler thread + // was not successfully created. + if (handler_thread_ == NULL) { + LeaveCriticalSection(&handler_critical_section_); + return false; + } + + // The handler thread should only be created when the semaphores are valid. + assert(handler_start_semaphore_ != NULL); + assert(handler_finish_semaphore_ != NULL); + + // Set up data to be passed in to the handler thread. + requesting_thread_id_ = GetCurrentThreadId(); + exception_info_ = exinfo; + assertion_ = assertion; + + // This causes the handler thread to call WriteMinidumpWithException. + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); + + // Wait until WriteMinidumpWithException is done and collect its return value. + WaitForSingleObject(handler_finish_semaphore_, INFINITE); + bool status = handler_return_value_; + + // Clean up. + requesting_thread_id_ = 0; + exception_info_ = NULL; + assertion_ = NULL; + + LeaveCriticalSection(&handler_critical_section_); + + return status; +} + +bool ExceptionHandler::WriteMinidump() { + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; + + return WriteMinidumpForException(&exception_ptrs); +} + +bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) { + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (IsOutOfProcess()) { + return WriteMinidumpWithException(GetCurrentThreadId(), + exinfo, + NULL); + } + + bool success = WriteMinidumpOnHandlerThread(exinfo, NULL); + UpdateNextID(); + return success; +} + +// static +bool ExceptionHandler::WriteMinidump(const wstring &dump_path, + MinidumpCallback callback, + void* callback_context) { + ExceptionHandler handler(dump_path, NULL, callback, callback_context, + HANDLER_NONE); + return handler.WriteMinidump(); +} + +bool ExceptionHandler::WriteMinidumpWithException( + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion) { + // Give user code a chance to approve or prevent writing a minidump. If the + // filter returns false, don't handle the exception at all. If this method + // was called as a result of an exception, returning false will cause + // HandleException to call any previous handler or return + // EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear + // as though this handler were not present at all. + if (filter_ && !filter_(callback_context_, exinfo, assertion)) { + return false; + } + + bool success = false; + if (IsOutOfProcess()) { + success = crash_generation_client_->RequestDump(exinfo, assertion); + } else { + if (minidump_write_dump_) { + HANDLE dump_file = CreateFile(next_minidump_path_c_, + GENERIC_WRITE, + 0, // no sharing + NULL, + CREATE_NEW, // fail if exists + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION except_info; + except_info.ThreadId = requesting_thread_id; + except_info.ExceptionPointers = exinfo; + except_info.ClientPointers = FALSE; + + // Add an MDRawBreakpadInfo stream to the minidump, to provide + // additional information about the exception handler to the Breakpad + // processor. The information will help the processor determine which + // threads are relevant. The Breakpad processor does not require this + // information but can function better with Breakpad-generated dumps + // when it is present. The native debugger is not harmed by the + // presence of this information. + MDRawBreakpadInfo breakpad_info; + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = GetCurrentThreadId(); + breakpad_info.requesting_thread_id = requesting_thread_id; + + // Leave room in user_stream_array for a possible assertion info stream. + MINIDUMP_USER_STREAM user_stream_array[2]; + user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[0].BufferSize = sizeof(breakpad_info); + user_stream_array[0].Buffer = &breakpad_info; + + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 1; + user_streams.UserStreamArray = user_stream_array; + + if (assertion) { + user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[1].Buffer = assertion; + ++user_streams.UserStreamCount; + } + + MINIDUMP_CALLBACK_INFORMATION callback; + MinidumpCallbackContext context; + MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL; + // Older versions of DbgHelp.dll don't correctly put the memory around + // the faulting instruction pointer into the minidump. This + // callback will ensure that it gets included. + if (exinfo) { + // Find a memory region of 256 bytes centered on the + // faulting instruction pointer. + const ULONG64 instruction_pointer = +#if defined(_M_IX86) + exinfo->ContextRecord->Eip; +#elif defined(_M_AMD64) + exinfo->ContextRecord->Rip; +#else +#error Unsupported platform +#endif + + MEMORY_BASIC_INFORMATION info; + if (VirtualQuery(reinterpret_cast(instruction_pointer), + &info, + sizeof(MEMORY_BASIC_INFORMATION)) != 0 && + info.State == MEM_COMMIT) { + // Attempt to get 128 bytes before and after the instruction + // pointer, but settle for whatever's available up to the + // boundaries of the memory region. + const ULONG64 kIPMemorySize = 256; + context.memory_base = + (std::max)(reinterpret_cast(info.BaseAddress), + instruction_pointer - (kIPMemorySize / 2)); + ULONG64 end_of_range = + (std::min)(instruction_pointer + (kIPMemorySize / 2), + reinterpret_cast(info.BaseAddress) + + info.RegionSize); + context.memory_size = + static_cast(end_of_range - context.memory_base); + + context.finished = false; + callback.CallbackRoutine = MinidumpWriteDumpCallback; + callback.CallbackParam = reinterpret_cast(&context); + callback_pointer = &callback; + } + } + + // The explicit comparison to TRUE avoids a warning (C4800). + success = (minidump_write_dump_(GetCurrentProcess(), + GetCurrentProcessId(), + dump_file, + dump_type_, + exinfo ? &except_info : NULL, + &user_streams, + callback_pointer) == TRUE); + + CloseHandle(dump_file); + } + } + } + + if (callback_) { + // TODO(munjal): In case of out-of-process dump generation, both + // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process + // scenario, the server process ends up creating the dump path and dump + // id so they are not known to the client. + success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_, + exinfo, assertion, success); + } + + return success; +} + +// static +BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output) { + switch (callback_input->CallbackType) { + case MemoryCallback: { + MinidumpCallbackContext* callback_context = + reinterpret_cast(context); + if (callback_context->finished) + return FALSE; + + // Include the specified memory region. + callback_output->MemoryBase = callback_context->memory_base; + callback_output->MemorySize = callback_context->memory_size; + callback_context->finished = true; + return TRUE; + } + + // Include all modules. + case IncludeModuleCallback: + case ModuleCallback: + return TRUE; + + // Include all threads. + case IncludeThreadCallback: + case ThreadCallback: + return TRUE; + + // Stop receiving cancel callbacks. + case CancelCallback: + callback_output->CheckCancel = FALSE; + callback_output->Cancel = FALSE; + return TRUE; + } + // Ignore other callback types. + return FALSE; +} + +void ExceptionHandler::UpdateNextID() { + assert(uuid_create_); + UUID id = {0}; + if (uuid_create_) { + uuid_create_(&id); + } + next_minidump_id_ = GUIDString::GUIDToWString(&id); + next_minidump_id_c_ = next_minidump_id_.c_str(); + + wchar_t minidump_path[MAX_PATH]; + swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp", + dump_path_c_, next_minidump_id_c_); + + // remove when VC++7.1 is no longer supported + minidump_path[MAX_PATH - 1] = L'\0'; + + next_minidump_path_ = minidump_path; + next_minidump_path_c_ = next_minidump_path_.c_str(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.h b/src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.h new file mode 100644 index 0000000..6c5ee76 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/client/windows/handler/exception_handler.h @@ -0,0 +1,425 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the unqiue id of the minidump. The caller can use this +// id to collect and write additional application state, and to launch an +// external crash-reporting application. +// +// It is important that creation and destruction of ExceptionHandler objects +// be nested cleanly, when using install_handler = true. +// Avoid the following pattern: +// ExceptionHandler *e = new ExceptionHandler(...); +// ExceptionHandler *f = new ExceptionHandler(...); +// delete e; +// This will put the exception filter stack into an inconsistent state. + +#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include +#include +#include + +#pragma warning( push ) +// Disable exception handler warnings. +#pragma warning( disable : 4530 ) + +#include +#include + +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/crash_generation/crash_generation_client.h" +#include "google_breakpad/common/minidump_format.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +using std::vector; +using std::wstring; + +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. exinfo points to the + // exception record, if any; assertion points to assertion information, + // if any. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, + // Breakpad will immediately report the exception as unhandled without + // writing a minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void* context, EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); + + // A callback function to run after the minidump has been written. + // minidump_id is a unique id for the dump, so the minidump + // file is \.dmp. context is the parameter supplied + // by the user as callback_context when the handler was created. exinfo + // points to the exception record, or NULL if no exception occurred. + // succeeded indicates whether a minidump file was successfully written. + // assertion points to information about an assertion if the handler was + // invoked by an assertion. + // + // If an exception occurred and the callback returns true, Breakpad will treat + // the exception as fully-handled, suppressing any other handlers from being + // notified of the exception. If the callback returns false, Breakpad will + // treat the exception as unhandled, and allow another handler to handle it. + // If there are no other handlers, Breakpad will report the exception to the + // system as unhandled, allowing a debugger or native crash dialog the + // opportunity to handle the exception. Most callback implementations + // should normally return the value of |succeeded|, or when they wish to + // not report an exception of handled, false. Callbacks will rarely want to + // return true directly (unless |succeeded| is true). + // + // For out-of-process dump generation, dump path and minidump ID will always + // be NULL. In case of out-of-process dump generation, the dump path and + // minidump id are controlled by the server process and are not communicated + // back to the crashing process. + typedef bool (*MinidumpCallback)(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded); + + // HandlerType specifies which types of handlers should be installed, if + // any. Use HANDLER_NONE for an ExceptionHandler that remains idle, + // without catching any failures on its own. This type of handler may + // still be triggered by calling WriteMinidump. Otherwise, use a + // combination of the other HANDLER_ values, or HANDLER_ALL to install + // all handlers. + enum HandlerType { + HANDLER_NONE = 0, + HANDLER_EXCEPTION = 1 << 0, // SetUnhandledExceptionFilter + HANDLER_INVALID_PARAMETER = 1 << 1, // _set_invalid_parameter_handler + HANDLER_PURECALL = 1 << 2, // _set_purecall_handler + HANDLER_ALL = HANDLER_EXCEPTION | + HANDLER_INVALID_PARAMETER | + HANDLER_PURECALL + }; + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Before writing a minidump, the optional filter callback will be called. + // Its return value determines whether or not Breakpad should write a + // minidump. Minidump files will be written to dump_path, and the optional + // callback is called after writing the dump file, as described above. + // handler_types specifies the types of handlers that should be installed. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types); + + // Creates a new ExcetpionHandler instance that can attempt to perform + // out-of-process dump generation if pipe_name is not NULL. If pipe_name is + // NULL, or if out-of-process dump generation registration step fails, + // in-process dump generation will be used. This also allows specifying + // the dump type to generate. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info); + + ~ExceptionHandler(); + + // Get and set the minidump path. + wstring dump_path() const { return dump_path_; } + void set_dump_path(const wstring &dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. + } + + // Requests that a previously reported crash be uploaded. + bool RequestUpload(DWORD crash_id); + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump(); + + // Writes a minidump immediately, with the user-supplied exception + // information. + bool WriteMinidumpForException(EXCEPTION_POINTERS* exinfo); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const wstring &dump_path, + MinidumpCallback callback, void* callback_context); + + // Get the thread ID of the thread requesting the dump (either the exception + // thread or any other thread that called WriteMinidump directly). This + // may be useful if you want to include additional thread state in your + // dumps. + DWORD get_requesting_thread_id() const { return requesting_thread_id_; } + + // Controls behavior of EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP. + bool get_handle_debug_exceptions() const { return handle_debug_exceptions_; } + void set_handle_debug_exceptions(bool handle_debug_exceptions) { + handle_debug_exceptions_ = handle_debug_exceptions; + } + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; } + + private: + friend class AutoExceptionHandler; + + // Initializes the instance with given values. + void Initialize(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info); + + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI *MiniDumpWriteDump_type)( + HANDLE hProcess, + DWORD dwPid, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // Function pointer type for UuidCreate, which is looked up dynamically. + typedef RPC_STATUS (RPC_ENTRY *UuidCreate_type)(UUID* Uuid); + + // Runs the main loop for the exception handler thread. + static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter); + + // Called on the exception thread when an unhandled exception occurs. + // Signals the exception handler thread to handle the exception. + static LONG WINAPI HandleException(EXCEPTION_POINTERS* exinfo); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + // This function will be called by some CRT functions when they detect + // that they were passed an invalid parameter. Note that in _DEBUG builds, + // the CRT may display an assertion dialog before calling this function, + // and the function will not be called unless the assertion dialog is + // dismissed by clicking "Ignore." + static void HandleInvalidParameter(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t reserved); +#endif // _MSC_VER >= 1400 + + // This function will be called by the CRT when a pure virtual + // function is called. + static void HandlePureVirtualCall(); + + // This is called on the exception thread or on another thread that + // the user wishes to produce a dump from. It calls + // WriteMinidumpWithException on the handler thread, avoiding stack + // overflows and inconsistent dumps due to writing the dump from + // the exception thread. If the dump is requested as a result of an + // exception, exinfo contains exception information, otherwise, it + // is NULL. If the dump is requested as a result of an assertion + // (such as an invalid parameter being passed to a CRT function), + // assertion contains data about the assertion, otherwise, it is NULL. + bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); + + // This function does the actual writing of a minidump. It is called + // on the handler thread. requesting_thread_id is the ID of the thread + // that requested the dump. If the dump is requested as a result of + // an exception, exinfo contains exception information, otherwise, + // it is NULL. + bool WriteMinidumpWithException(DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); + + // This function is used as a callback when calling MinidumpWriteDump, + // in order to add additional memory regions to the dump. + static BOOL CALLBACK MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output); + + // Generates a new ID and stores it in next_minidump_id_, and stores the + // path of the next minidump to be written in next_minidump_path_. + void UpdateNextID(); + + FilterCallback filter_; + MinidumpCallback callback_; + void* callback_context_; + + scoped_ptr crash_generation_client_; + + // The directory in which a minidump will be written, set by the dump_path + // argument to the constructor, or set_dump_path. + wstring dump_path_; + + // The basename of the next minidump to be written, without the extension. + wstring next_minidump_id_; + + // The full pathname of the next minidump to be written, including the file + // extension. + wstring next_minidump_path_; + + // Pointers to C-string representations of the above. These are set when + // the above wstring versions are set in order to avoid calling c_str during + // an exception, as c_str may attempt to allocate heap memory. These + // pointers are not owned by the ExceptionHandler object, but their lifetimes + // should be equivalent to the lifetimes of the associated wstring, provided + // that the wstrings are not altered. + const wchar_t* dump_path_c_; + const wchar_t* next_minidump_id_c_; + const wchar_t* next_minidump_path_c_; + + HMODULE dbghelp_module_; + MiniDumpWriteDump_type minidump_write_dump_; + MINIDUMP_TYPE dump_type_; + + HMODULE rpcrt4_module_; + UuidCreate_type uuid_create_; + + // Tracks the handler types that were installed according to the + // handler_types constructor argument. + int handler_types_; + + // When installed_handler_ is true, previous_filter_ is the unhandled + // exception filter that was set prior to installing ExceptionHandler as + // the unhandled exception filter and pointing it to |this|. NULL indicates + // that there is no previous unhandled exception filter. + LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + // Beginning in VC 8, the CRT provides an invalid parameter handler that will + // be called when some CRT functions are passed invalid parameters. In + // earlier CRTs, the same conditions would cause unexpected behavior or + // crashes. + _invalid_parameter_handler previous_iph_; +#endif // _MSC_VER >= 1400 + + // The CRT allows you to override the default handler for pure + // virtual function calls. + _purecall_handler previous_pch_; + + // The exception handler thread. + HANDLE handler_thread_; + + // True if the exception handler is being destroyed. + // Starting with MSVC 2005, Visual C has stronger guarantees on volatile vars. + // It has release semantics on write and acquire semantics on reads. + // See the msdn documentation. + volatile bool is_shutdown_; + + // The critical section enforcing the requirement that only one exception be + // handled by a handler at a time. + CRITICAL_SECTION handler_critical_section_; + + // Semaphores used to move exception handling between the exception thread + // and the handler thread. handler_start_semaphore_ is signalled by the + // exception thread to wake up the handler thread when an exception occurs. + // handler_finish_semaphore_ is signalled by the handler thread to wake up + // the exception thread when handling is complete. + HANDLE handler_start_semaphore_; + HANDLE handler_finish_semaphore_; + + // The next 2 fields contain data passed from the requesting thread to + // the handler thread. + + // The thread ID of the thread requesting the dump (either the exception + // thread or any other thread that called WriteMinidump directly). + DWORD requesting_thread_id_; + + // The exception info passed to the exception handler on the exception + // thread, if an exception occurred. NULL for user-requested dumps. + EXCEPTION_POINTERS* exception_info_; + + // If the handler is invoked due to an assertion, this will contain a + // pointer to the assertion information. It is NULL at other times. + MDRawAssertionInfo* assertion_; + + // The return value of the handler, passed from the handler thread back to + // the requesting thread. + bool handler_return_value_; + + // If true, the handler will intercept EXCEPTION_BREAKPOINT and + // EXCEPTION_SINGLE_STEP exceptions. Leave this false (the default) + // to not interfere with debuggers. + bool handle_debug_exceptions_; + + // A stack of ExceptionHandler objects that have installed unhandled + // exception filters. This vector is used by HandleException to determine + // which ExceptionHandler object to route an exception to. When an + // ExceptionHandler is created with install_handler true, it will append + // itself to this list. + static vector* handler_stack_; + + // The index of the ExceptionHandler in handler_stack_ that will handle the + // next exception. Note that 0 means the last entry in handler_stack_, 1 + // means the next-to-last entry, and so on. This is used by HandleException + // to support multiple stacked Breakpad handlers. + static LONG handler_stack_index_; + + // handler_stack_critical_section_ guards operations on handler_stack_ and + // handler_stack_index_. The critical section is initialized by the + // first instance of the class and destroyed by the last instance of it. + static CRITICAL_SECTION handler_stack_critical_section_; + + // The number of instances of this class. + volatile static LONG instance_count_; + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler &); + void operator=(const ExceptionHandler &); +}; + +} // namespace google_breakpad + +#pragma warning( pop ) + +#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/basictypes.h b/src/lib/crashdump/gbreakpad/common/basictypes.h new file mode 100644 index 0000000..694f702 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/basictypes.h @@ -0,0 +1,39 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_BASICTYPES_H_ +#define COMMON_BASICTYPES_H_ + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#endif // COMMON_BASICTYPES_H_ diff --git a/src/lib/crashdump/gbreakpad/common/byte_cursor.h b/src/lib/crashdump/gbreakpad/common/byte_cursor.h new file mode 100644 index 0000000..24ffce7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/byte_cursor.h @@ -0,0 +1,263 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// byte_cursor.h: Classes for parsing values from a buffer of bytes. +// The ByteCursor class provides a convenient interface for reading +// fixed-size integers of arbitrary endianness, being thorough about +// checking for buffer overruns. + +#ifndef COMMON_BYTE_CURSOR_H_ +#define COMMON_BYTE_CURSOR_H_ + +#include +#include +#include +#include +#include + +namespace google_breakpad { + +// A buffer holding a series of bytes. +struct ByteBuffer { + ByteBuffer() : start(0), end(0) { } + ByteBuffer(const uint8_t *set_start, size_t set_size) + : start(set_start), end(set_start + set_size) { } + ~ByteBuffer() { }; + + // Equality operators. Useful in unit tests, and when we're using + // ByteBuffers to refer to regions of a larger buffer. + bool operator==(const ByteBuffer &that) const { + return start == that.start && end == that.end; + } + bool operator!=(const ByteBuffer &that) const { + return start != that.start || end != that.end; + } + + // Not C++ style guide compliant, but this definitely belongs here. + size_t Size() const { + assert(start <= end); + return end - start; + } + + const uint8_t *start, *end; +}; + +// A cursor pointing into a ByteBuffer that can parse numbers of various +// widths and representations, strings, and data blocks, advancing through +// the buffer as it goes. All ByteCursor operations check that accesses +// haven't gone beyond the end of the enclosing ByteBuffer. +class ByteCursor { + public: + // Create a cursor reading bytes from the start of BUFFER. By default, the + // cursor reads multi-byte values in little-endian form. + ByteCursor(const ByteBuffer *buffer, bool big_endian = false) + : buffer_(buffer), here_(buffer->start), + big_endian_(big_endian), complete_(true) { } + + // Accessor and setter for this cursor's endianness flag. + bool big_endian() const { return big_endian_; } + void set_big_endian(bool big_endian) { big_endian_ = big_endian; } + + // Accessor and setter for this cursor's current position. The setter + // returns a reference to this cursor. + const uint8_t *here() const { return here_; } + ByteCursor &set_here(const uint8_t *here) { + assert(buffer_->start <= here && here <= buffer_->end); + here_ = here; + return *this; + } + + // Return the number of bytes available to read at the cursor. + size_t Available() const { return size_t(buffer_->end - here_); } + + // Return true if this cursor is at the end of its buffer. + bool AtEnd() const { return Available() == 0; } + + // When used as a boolean value this cursor converts to true if all + // prior reads have been completed, or false if we ran off the end + // of the buffer. + operator bool() const { return complete_; } + + // Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true, + // unsigned otherwise, using the cursor's established endianness, and set + // *RESULT to the number. If we read off the end of our buffer, clear + // this cursor's complete_ flag, and store a dummy value in *RESULT. + // Return a reference to this cursor. + template + ByteCursor &Read(size_t size, bool is_signed, T *result) { + if (CheckAvailable(size)) { + T v = 0; + if (big_endian_) { + for (size_t i = 0; i < size; i++) + v = (v << 8) + here_[i]; + } else { + // This loop condition looks weird, but size_t is unsigned, so + // decrementing i after it is zero yields the largest size_t value. + for (size_t i = size - 1; i < size; i--) + v = (v << 8) + here_[i]; + } + if (is_signed && size < sizeof(T)) { + size_t sign_bit = (T)1 << (size * 8 - 1); + v = (v ^ sign_bit) - sign_bit; + } + here_ += size; + *result = v; + } else { + *result = (T) 0xdeadbeef; + } + return *this; + } + + // Read an integer, using the cursor's established endianness and + // *RESULT's size and signedness, and set *RESULT to the number. If we + // read off the end of our buffer, clear this cursor's complete_ flag. + // Return a reference to this cursor. + template + ByteCursor &operator>>(T &result) { + bool T_is_signed = (T)-1 < 0; + return Read(sizeof(T), T_is_signed, &result); + } + + // Copy the SIZE bytes at the cursor to BUFFER, and advance this + // cursor to the end of them. If we read off the end of our buffer, + // clear this cursor's complete_ flag, and set *POINTER to NULL. + // Return a reference to this cursor. + ByteCursor &Read(uint8_t *buffer, size_t size) { + if (CheckAvailable(size)) { + memcpy(buffer, here_, size); + here_ += size; + } + return *this; + } + + // Set STR to a copy of the '\0'-terminated string at the cursor. If the + // byte buffer does not contain a terminating zero, clear this cursor's + // complete_ flag, and set STR to the empty string. Return a reference to + // this cursor. + ByteCursor &CString(std::string *str) { + const uint8_t *end + = static_cast(memchr(here_, '\0', Available())); + if (end) { + str->assign(reinterpret_cast(here_), end - here_); + here_ = end + 1; + } else { + str->clear(); + here_ = buffer_->end; + complete_ = false; + } + return *this; + } + + // Like CString(STR), but extract the string from a fixed-width buffer + // LIMIT bytes long, which may or may not contain a terminating '\0' + // byte. Specifically: + // + // - If there are not LIMIT bytes available at the cursor, clear the + // cursor's complete_ flag and set STR to the empty string. + // + // - Otherwise, if the LIMIT bytes at the cursor contain any '\0' + // characters, set *STR to a copy of the bytes before the first '\0', + // and advance the cursor by LIMIT bytes. + // + // - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the + // cursor by LIMIT bytes. + ByteCursor &CString(std::string *str, size_t limit) { + if (CheckAvailable(limit)) { + const uint8_t *end + = static_cast(memchr(here_, '\0', limit)); + if (end) + str->assign(reinterpret_cast(here_), end - here_); + else + str->assign(reinterpret_cast(here_), limit); + here_ += limit; + } else { + str->clear(); + } + return *this; + } + + // Set *POINTER to point to the SIZE bytes at the cursor, and advance + // this cursor to the end of them. If SIZE is omitted, don't move the + // cursor. If we read off the end of our buffer, clear this cursor's + // complete_ flag, and set *POINTER to NULL. Return a reference to this + // cursor. + ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) { + if (CheckAvailable(size)) { + *pointer = here_; + here_ += size; + } else { + *pointer = NULL; + } + return *this; + } + + // Skip SIZE bytes at the cursor. If doing so would advance us off + // the end of our buffer, clear this cursor's complete_ flag, and + // set *POINTER to NULL. Return a reference to this cursor. + ByteCursor &Skip(size_t size) { + if (CheckAvailable(size)) + here_ += size; + return *this; + } + + private: + // If there are at least SIZE bytes available to read from the buffer, + // return true. Otherwise, set here_ to the end of the buffer, set + // complete_ to false, and return false. + bool CheckAvailable(size_t size) { + if (Available() >= size) { + return true; + } else { + here_ = buffer_->end; + complete_ = false; + return false; + } + } + + // The buffer we're reading bytes from. + const ByteBuffer *buffer_; + + // The next byte within buffer_ that we'll read. + const uint8_t *here_; + + // True if we should read numbers in big-endian form; false if we + // should read in little-endian form. + bool big_endian_; + + // True if we've been able to read all we've been asked to. + bool complete_; +}; + +} // namespace google_breakpad + +#endif // COMMON_BYTE_CURSOR_H_ diff --git a/src/lib/crashdump/gbreakpad/common/byte_cursor_unittest.cc b/src/lib/crashdump/gbreakpad/common/byte_cursor_unittest.cc new file mode 100644 index 0000000..0ab4770 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/byte_cursor_unittest.cc @@ -0,0 +1,776 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer +// and google_breakpad::ByteCursor. + +#include + +#include + +#include "common/byte_cursor.h" +#include "breakpad_googletest_includes.h" + +using google_breakpad::ByteBuffer; +using google_breakpad::ByteCursor; +using std::string; + +TEST(Buffer, SizeOfNothing) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + EXPECT_EQ(0U, buffer.Size()); +} + +TEST(Buffer, SizeOfSomething) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + EXPECT_EQ(10U, buffer.Size()); +} + +TEST(Extent, AvailableEmpty) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + EXPECT_EQ(0U, cursor.Available()); +} + +TEST(Extent, AtEndEmpty) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + EXPECT_TRUE(cursor.AtEnd()); +} + +TEST(Extent, AsBoolEmpty) { + uint8_t data[1]; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + EXPECT_TRUE(cursor); +} + +TEST(Extent, AvailableSome) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + EXPECT_EQ(10U, cursor.Available()); +} + +TEST(Extent, AtEndSome) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + EXPECT_FALSE(cursor.AtEnd()); + EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd()); +} + +TEST(Extent, AsBoolSome) { + uint8_t data[10]; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + EXPECT_TRUE(cursor); + EXPECT_TRUE(cursor.Skip(sizeof(data))); + EXPECT_FALSE(cursor.Skip(1)); +} + +TEST(Extent, Cursor) { + uint8_t data[] = { 0xf7, + 0x9f, 0xbe, + 0x67, 0xfb, 0xd3, 0x58, + 0x6f, 0x36, 0xde, 0xd1, + 0x2a, 0x2a, 0x2a }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t a; + uint16_t b; + uint32_t c; + uint32_t d; + uint8_t stars[3]; + + EXPECT_EQ(data + 0U, cursor.here()); + + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(data + 1U, cursor.here()); + + EXPECT_TRUE(cursor >> b); + EXPECT_EQ(data + 3U, cursor.here()); + + EXPECT_TRUE(cursor >> c); + EXPECT_EQ(data + 7U, cursor.here()); + + EXPECT_TRUE(cursor.Skip(4)); + EXPECT_EQ(data + 11U, cursor.here()); + + EXPECT_TRUE(cursor.Read(stars, 3)); + EXPECT_EQ(data + 14U, cursor.here()); + + EXPECT_FALSE(cursor >> d); + EXPECT_EQ(data + 14U, cursor.here()); +} + +TEST(Extent, SetOffset) { + uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t a, b, c, d, e; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0x5cU, a); + EXPECT_EQ(data + 1U, cursor.here()); + EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1) + >> d >> e); + EXPECT_EQ(0x79U, b); + EXPECT_EQ(0xd5U, c); + EXPECT_EQ(0x79U, d); + EXPECT_EQ(0x8cU, e); + EXPECT_EQ(data + 3U, cursor.here()); +} + +TEST(BigEndian, Signed1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, true, &a) + .Read(1, true, &b) + .Read(1, true, &c) + .Read(1, true, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(-0x80, c); + EXPECT_EQ(-1, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, true, &e)); +} + +TEST(BigEndian, Signed2) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff, + 0x80, 0x00, 0x80, 0x80, 0xff, 0xff, + 0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + int a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, true, &a) + .Read(2, true, &b) + .Read(2, true, &c) + .Read(2, true, &d) + .Read(2, true, &e) + .Read(2, true, &f) + .Read(2, true, &g) + .Read(2, true, &h) + .Read(2, true, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(-0x8000, d); + EXPECT_EQ(-0x7f80, e); + EXPECT_EQ(-1, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(-0x7544, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, true, &j)); +} + +TEST(BigEndian, Signed4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0xb6, 0xb1, 0xff, 0xef, + 0x19, 0x6a, 0xca, 0x46 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, true, &a) + .Read(4, true, &b) + .Read(4, true, &c) + .Read(4, true, &d) + .Read(4, true, &e) + .Read(4, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(-0x80000000LL, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int32_t) 0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, true, &g)); +} + +TEST(BigEndian, Signed8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, + 0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, true, &a) + .Read(8, true, &b) + .Read(8, true, &c) + .Read(8, true, &d) + .Read(8, true, &e) + .Read(8, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffffffffffffLL, b); + EXPECT_EQ(-0x7fffffffffffffffLL - 1, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4LL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, true, &g)); +} + +TEST(BigEndian, Unsigned1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int32_t a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, false, &a) + .Read(1, false, &b) + .Read(1, false, &c) + .Read(1, false, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(0x80, c); + EXPECT_EQ(0xff, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, false, &e)); +} + +TEST(BigEndian, Unsigned2) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff, + 0x80, 0x00, 0x80, 0x80, 0xff, 0xff, + 0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + int64_t a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, false, &a) + .Read(2, false, &b) + .Read(2, false, &c) + .Read(2, false, &d) + .Read(2, false, &e) + .Read(2, false, &f) + .Read(2, false, &g) + .Read(2, false, &h) + .Read(2, false, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(0x8000, d); + EXPECT_EQ(0x8080, e); + EXPECT_EQ(0xffff, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(0x8abc, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, false, &j)); +} + +TEST(BigEndian, Unsigned4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, + 0xb6, 0xb1, 0xff, 0xef, + 0x19, 0x6a, 0xca, 0x46 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, false, &a) + .Read(4, false, &b) + .Read(4, false, &c) + .Read(4, false, &d) + .Read(4, false, &e) + .Read(4, false, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(0x80000000, c); + EXPECT_EQ(0xffffffff, d); + EXPECT_EQ(0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, false, &g)); +} + +TEST(BigEndian, Unsigned8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, + 0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, true); + uint64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, false, &a) + .Read(8, false, &b) + .Read(8, false, &c) + .Read(8, false, &d) + .Read(8, false, &e) + .Read(8, false, &f)); + EXPECT_EQ(0U, a); + EXPECT_EQ(0x7fffffffffffffffULL, b); + EXPECT_EQ(0x8000000000000000ULL, c); + EXPECT_EQ(0xffffffffffffffffULL, d); + EXPECT_EQ(0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4ULL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, false, &g)); +} + +TEST(LittleEndian, Signed1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, true, &a) + .Read(1, true, &b) + .Read(1, true, &c) + .Read(1, true, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(-0x80, c); + EXPECT_EQ(-1, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, true, &e)); +} + +TEST(LittleEndian, Signed2) { + uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f, + 0x00, 0x80, 0x80, 0x80, 0xff, 0xff, + 0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, false); + int32_t a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, true, &a) + .Read(2, true, &b) + .Read(2, true, &c) + .Read(2, true, &d) + .Read(2, true, &e) + .Read(2, true, &f) + .Read(2, true, &g) + .Read(2, true, &h) + .Read(2, true, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(-0x8000, d); + EXPECT_EQ(-0x7f80, e); + EXPECT_EQ(-1, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(-0x7544, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, true, &j)); +} + +TEST(LittleEndian, Signed4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xb1, 0xb6, + 0x46, 0xca, 0x6a, 0x19 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, true, &a) + .Read(4, true, &b) + .Read(4, true, &c) + .Read(4, true, &d) + .Read(4, true, &e) + .Read(4, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(-0x80000000LL, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int32_t) 0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, true, &g)); +} + +TEST(LittleEndian, Signed8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, + 0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer, false); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, true, &a) + .Read(8, true, &b) + .Read(8, true, &c) + .Read(8, true, &d) + .Read(8, true, &e) + .Read(8, true, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffffffffffffLL, b); + EXPECT_EQ(-0x7fffffffffffffffLL - 1, c); + EXPECT_EQ(-1, d); + EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4LL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, true, &g)); +} + +TEST(LittleEndian, Unsigned1) { + uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a, b, c, d, e; + ASSERT_TRUE(cursor + .Read(1, false, &a) + .Read(1, false, &b) + .Read(1, false, &c) + .Read(1, false, &d)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7f, b); + EXPECT_EQ(0x80, c); + EXPECT_EQ(0xff, d); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(1, false, &e)); +} + +TEST(LittleEndian, Unsigned2) { + uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f, + 0x00, 0x80, 0x80, 0x80, 0xff, 0xff, + 0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a, b, c, d, e, f, g, h, i, j; + ASSERT_TRUE(cursor + .Read(2, false, &a) + .Read(2, false, &b) + .Read(2, false, &c) + .Read(2, false, &d) + .Read(2, false, &e) + .Read(2, false, &f) + .Read(2, false, &g) + .Read(2, false, &h) + .Read(2, false, &i)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x80, b); + EXPECT_EQ(0x7fff, c); + EXPECT_EQ(0x8000, d); + EXPECT_EQ(0x8080, e); + EXPECT_EQ(0xffff, f); + EXPECT_EQ(0x39f1, g); + EXPECT_EQ(0x8abc, h); + EXPECT_EQ(0x5aec, i); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(2, false, &j)); +} + +TEST(LittleEndian, Unsigned4) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, + 0xef, 0xff, 0xb1, 0xb6, + 0x46, 0xca, 0x6a, 0x19 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(4, false, &a) + .Read(4, false, &b) + .Read(4, false, &c) + .Read(4, false, &d) + .Read(4, false, &e) + .Read(4, false, &f)); + EXPECT_EQ(0, a); + EXPECT_EQ(0x7fffffff, b); + EXPECT_EQ(0x80000000, c); + EXPECT_EQ(0xffffffff, d); + EXPECT_EQ(0xb6b1ffef, e); + EXPECT_EQ(0x196aca46, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(4, false, &g)); +} + +TEST(LittleEndian, Unsigned8) { + uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, + 0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint64_t a, b, c, d, e, f, g; + ASSERT_TRUE(cursor + .Read(8, false, &a) + .Read(8, false, &b) + .Read(8, false, &c) + .Read(8, false, &d) + .Read(8, false, &e) + .Read(8, false, &f)); + EXPECT_EQ(0U, a); + EXPECT_EQ(0x7fffffffffffffffULL, b); + EXPECT_EQ(0x8000000000000000ULL, c); + EXPECT_EQ(0xffffffffffffffffULL, d); + EXPECT_EQ(0x9320d5e9d2d5879cULL, e); + EXPECT_EQ(0x4e4249d27f8414a4ULL, f); + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor.Read(8, false, &g)); +} + +TEST(Extractor, Signed1) { + uint8_t data[] = { 0xfd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int8_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(-3, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Signed2) { + uint8_t data[] = { 0x13, 0xcd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int16_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(-13037, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Signed4) { + uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + int32_t a; + // For some reason, G++ 4.4.1 complains: + // warning: array subscript is above array bounds + // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but + // I'm not able to see how such a reference would occur. + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(-380377902, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Unsigned1) { + uint8_t data[] = { 0xfd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint8_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0xfd, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Unsigned2) { + uint8_t data[] = { 0x13, 0xcd }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint16_t a; + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0xcd13, a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Unsigned4) { + uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + uint32_t a; + // For some reason, G++ 4.4.1 complains: + // warning: array subscript is above array bounds + // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but + // I'm not able to see how such a reference would occur. + EXPECT_TRUE(cursor >> a); + EXPECT_EQ(0xe953e4d2, a); + EXPECT_FALSE(cursor >> a); + EXPECT_FALSE(cursor >> a); +} + +TEST(Extractor, Mixed) { + uint8_t data[] = { 0x42, + 0x25, 0x0b, + 0x3d, 0x25, 0xed, 0x2a, + 0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf, + 0xd8, + 0x22, 0xa5, + 0x3a, 0x02, 0x6a, 0xd7, + 0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + cursor.set_big_endian(true); + + uint8_t a; + uint16_t b; + uint32_t c; + uint64_t d; + int8_t e; + int16_t f; + int32_t g; + int64_t h; + int z; + EXPECT_FALSE(cursor.AtEnd()); + EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h); + EXPECT_EQ(0x42U, a); + EXPECT_EQ(0x250bU, b); + EXPECT_EQ(0x3d25ed2aU, c); + EXPECT_EQ(0xec169e14615b2ccfULL, d); + EXPECT_EQ(-40, e); + EXPECT_EQ(0x22a5, f); + EXPECT_EQ(0x3a026ad7, g); + EXPECT_EQ(-7842405714468937530LL, h); + + EXPECT_TRUE(cursor.AtEnd()); + EXPECT_FALSE(cursor >> z); +} + +TEST(Strings, Zero) { + uint8_t data[] = { 0xa6 }; + ByteBuffer buffer(data, 0); + ByteCursor cursor(&buffer); + + uint8_t received[1]; + received[0] = 0xc2; + EXPECT_TRUE(cursor.Read(received, 0)); + EXPECT_EQ(0xc2U, received[0]); +} + +TEST(Strings, Some) { + uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed }; + EXPECT_TRUE(cursor.Skip(2).Read(received, 5)); + uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed }; + EXPECT_TRUE(memcmp(received, expected, 7) == 0); +} + +TEST(Strings, TooMuch) { + uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + uint8_t received1[3]; + uint8_t received2[3]; + uint8_t received3[3]; + EXPECT_FALSE(cursor + .Read(received1, 3) + .Read(received2, 3) + .Read(received3, 3)); + uint8_t expected1[3] = { 0x5d, 0x31, 0x09 }; + uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c }; + + EXPECT_TRUE(memcmp(received1, expected1, 3) == 0); + EXPECT_TRUE(memcmp(received2, expected2, 3) == 0); +} + +TEST(Strings, PointTo) { + uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 }; + ByteBuffer buffer(data, sizeof(data)); + ByteCursor cursor(&buffer); + + const uint8_t *received1; + const uint8_t *received2; + const uint8_t *received3; + const uint8_t *received4; + EXPECT_FALSE(cursor + .PointTo(&received1, 3) + .PointTo(&received2, 3) + .PointTo(&received3) + .PointTo(&received4, 3)); + EXPECT_EQ(data + 0, received1); + EXPECT_EQ(data + 3, received2); + EXPECT_EQ(data + 6, received3); + EXPECT_EQ(NULL, received4); +} + +TEST(Strings, CString) { + uint8_t data[] = "abc\0\0foo"; + ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0' + ByteCursor cursor(&buffer); + + string a, b, c; + EXPECT_TRUE(cursor.CString(&a).CString(&b)); + EXPECT_EQ("abc", a); + EXPECT_EQ("", b); + EXPECT_FALSE(cursor.CString(&c)); + EXPECT_EQ("", c); + EXPECT_TRUE(cursor.AtEnd()); +} + +TEST(Strings, CStringLimit) { + uint8_t data[] = "abcdef\0\0foobar"; + ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0' + ByteCursor cursor(&buffer); + + string a, b, c, d, e; + + EXPECT_TRUE(cursor.CString(&a, 3)); + EXPECT_EQ("abc", a); + + EXPECT_TRUE(cursor.CString(&b, 0)); + EXPECT_EQ("", b); + + EXPECT_TRUE(cursor.CString(&c, 6)); + EXPECT_EQ("def", c); + + EXPECT_TRUE(cursor.CString(&d, 4)); + EXPECT_EQ("ooba", d); + + EXPECT_FALSE(cursor.CString(&e, 4)); + EXPECT_EQ("", e); + + EXPECT_TRUE(cursor.AtEnd()); +} + +// uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 }; +// ByteBuffer buffer(data, sizeof(data)); diff --git a/src/lib/crashdump/gbreakpad/common/convert_UTF.c b/src/lib/crashdump/gbreakpad/common/convert_UTF.c new file mode 100644 index 0000000..80178d3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/convert_UTF.c @@ -0,0 +1,533 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + +Conversions between UTF32, UTF-16, and UTF-8. Source code file. +Author: Mark E. Davis, 1994. +Rev History: Rick McGowan, fixes & updates May 2001. +Sept 2001: fixed const & error conditions per +mods suggested by S. Parent & A. Lillich. +June 2002: Tim Dodd added detection and handling of incomplete +source sequences, enhanced error detection, added casts +to eliminate compiler warnings. +July 2003: slight mods to back out aggressive FFFE detection. +Jan 2004: updated switches in from-UTF8 conversions. +Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. + +See the header file "ConvertUTF.h" for complete documentation. + +------------------------------------------------------------------------ */ + + +#include "convert_UTF.h" +#ifdef CVTUTF_DEBUG +#include +#endif + +static const int halfShift = 10; /* used for shifting by 10 bits */ + +static const UTF32 halfBase = 0x0010000UL; +static const UTF32 halfMask = 0x3FFUL; + +#define UNI_SUR_HIGH_START (UTF32)0xD800 +#define UNI_SUR_HIGH_END (UTF32)0xDBFF +#define UNI_SUR_LOW_START (UTF32)0xDC00 +#define UNI_SUR_LOW_END (UTF32)0xDFFF +#define false 0 +#define true 1 + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + if (target >= targetEnd) { + result = targetExhausted; break; + } + ch = *source++; + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_LEGAL_UTF32) { + if (flags == strictConversion) { + result = sourceIllegal; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + --source; /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF32* target = *targetStart; + UTF32 ch, ch2; + while (source < sourceEnd) { + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + if (target >= targetEnd) { + source = oldSource; /* Back up source pointer! */ + result = targetExhausted; break; + } + *target++ = ch; + } + *sourceStart = source; + *targetStart = target; +#ifdef CVTUTF_DEBUG + if (result == sourceIllegal) { + fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); + fflush(stderr); + } +#endif + return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is + * left as-is for anyone who may want to do such conversion, which was + * allowed in earlier algorithms. + */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,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,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, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* + * Magic values subtracted from a buffer value during UTF8 conversion. + * This table contains as many values as there might be trailing bytes + * in a UTF-8 sequence. + */ +static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; + +/* + * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed + * into the first byte, depending on how many bytes follow. There are + * as many entries in this table as there are UTF-8 sequence types. + * (I.e., one byte sequence, two byte... etc.). Remember that sequencs + * for *legal* UTF-8 will be 4 or fewer bytes total. + */ +static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + +/* --------------------------------------------------------------------- */ + +/* The interface converts a whole buffer to avoid function-call overhead. +* Constants have been gathered. Loops & conditionals have been removed as +* much as possible for efficiency, in favor of drop-through switches. +* (See "Note A" at the bottom of the file for equivalent code.) +* If your compiler supports it, the "isLegalUTF8" call can be turned +* into an inline function. +*/ + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF16* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ + ch = *source++; + /* If we have a surrogate pair, convert to UTF32 first. */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { + /* If the 16 bits following the high surrogate are in the source buffer... */ + if (source < sourceEnd) { + UTF32 ch2 = *source; + /* If it's a low surrogate, convert to UTF32. */ + if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { + ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + + (ch2 - UNI_SUR_LOW_START) + halfBase; + ++source; + } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } else { /* We don't have the 16 bits following the high surrogate. */ + --source; /* return to the high surrogate */ + result = sourceExhausted; + break; + } + } else if (flags == strictConversion) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* Figure out how many bytes the result will require */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch < (UTF32)0x110000) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + } + + target += bytesToWrite; + if (target > targetEnd) { + source = oldSource; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * If not calling this from ConvertUTF8to*, then the length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns false. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ + +static Boolean isLegalUTF8(const UTF8 *source, int length) { + UTF8 a; + const UTF8 *srcptr = source+length; + switch (length) { + default: return false; + /* Everything else falls through when "true"... */ + case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 2: if ((a = (*--srcptr)) > 0xBF) return false; + + switch (*source) { + /* no fall-through in this inner switch */ + case 0xE0: if (a < 0xA0) return false; break; + case 0xED: if (a > 0x9F) return false; break; + case 0xF0: if (a < 0x90) return false; break; + case 0xF4: if (a > 0x8F) return false; break; + default: if (a < 0x80) return false; + } + + case 1: if (*source >= 0x80 && *source < 0xC2) return false; + } + if (*source > 0xF4) return false; + return true; +} + +/* --------------------------------------------------------------------- */ + +/* + * Exported function to return whether a UTF-8 sequence is legal or not. + * This is not used here; it's just exported. + */ +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { + int length = trailingBytesForUTF8[*source]+1; + if (source+length > sourceEnd) { + return false; + } + return isLegalUTF8(source, length); +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF16* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = (UTF16)ch; /* normal case */ + } + } else if (ch > UNI_MAX_UTF16) { + if (flags == strictConversion) { + result = sourceIllegal; + source -= (extraBytesToRead+1); /* return to the start */ + break; /* Bail out; shouldn't continue */ + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + /* target is a character in range 0xFFFF - 0x10FFFF. */ + if (target + 1 >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up source pointer! */ + result = targetExhausted; break; + } + ch -= halfBase; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); + *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); + } + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF32* source = *sourceStart; + UTF8* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch; + unsigned short bytesToWrite = 0; + const UTF32 byteMask = 0xBF; + const UTF32 byteMark = 0x80; + ch = *source++; + if (flags == strictConversion ) { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + --source; /* return to the illegal value itself */ + result = sourceIllegal; + break; + } + } + /* + * Figure out how many bytes the result will require. Turn any + * illegally large UTF32 things (> Plane 17) into replacement chars. + */ + if (ch < (UTF32)0x80) { bytesToWrite = 1; + } else if (ch < (UTF32)0x800) { bytesToWrite = 2; + } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; + } else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; + } else { bytesToWrite = 3; + ch = UNI_REPLACEMENT_CHAR; + result = sourceIllegal; + } + + target += bytesToWrite; + if (target > targetEnd) { + --source; /* Back up source pointer! */ + target -= bytesToWrite; result = targetExhausted; break; + } + switch (bytesToWrite) { /* note: everything falls through. */ + case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; + case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); + } + target += bytesToWrite; + } +*sourceStart = source; +*targetStart = target; +return result; +} + +/* --------------------------------------------------------------------- */ + +ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { + ConversionResult result = conversionOK; + const UTF8* source = *sourceStart; + UTF32* target = *targetStart; + while (source < sourceEnd) { + UTF32 ch = 0; + unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; + if (source + extraBytesToRead >= sourceEnd) { + result = sourceExhausted; break; + } + /* Do this check whether lenient or strict */ + if (! isLegalUTF8(source, extraBytesToRead+1)) { + result = sourceIllegal; + break; + } + /* + * The cases all fall through. See "Note A" below. + */ + switch (extraBytesToRead) { + case 5: ch += *source++; ch <<= 6; + case 4: ch += *source++; ch <<= 6; + case 3: ch += *source++; ch <<= 6; + case 2: ch += *source++; ch <<= 6; + case 1: ch += *source++; ch <<= 6; + case 0: ch += *source++; + } + ch -= offsetsFromUTF8[extraBytesToRead]; + + if (target >= targetEnd) { + source -= (extraBytesToRead+1); /* Back up the source pointer! */ + result = targetExhausted; break; + } + if (ch <= UNI_MAX_LEGAL_UTF32) { + /* + * UTF-16 surrogate values are illegal in UTF-32, and anything + * over Plane 17 (> 0x10FFFF) is illegal. + */ + if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { + if (flags == strictConversion) { + source -= (extraBytesToRead+1); /* return to the illegal value itself */ + result = sourceIllegal; + break; + } else { + *target++ = UNI_REPLACEMENT_CHAR; + } + } else { + *target++ = ch; + } + } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ + result = sourceIllegal; + *target++ = UNI_REPLACEMENT_CHAR; + } + } + *sourceStart = source; + *targetStart = target; + return result; +} + +/* --------------------------------------------------------------------- + +Note A. +The fall-through switches in UTF-8 reading code save a +temp variable, some decrements & conditionals. The switches +are equivalent to the following loop: +{ + int tmpBytesToRead = extraBytesToRead+1; + do { + ch += *source++; + --tmpBytesToRead; + if (tmpBytesToRead) ch <<= 6; + } while (tmpBytesToRead > 0); +} +In UTF-8 writing code, the switches on "bytesToWrite" are +similarly unrolled loops. + +--------------------------------------------------------------------- */ diff --git a/src/lib/crashdump/gbreakpad/common/convert_UTF.h b/src/lib/crashdump/gbreakpad/common/convert_UTF.h new file mode 100644 index 0000000..b1556de --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/convert_UTF.h @@ -0,0 +1,143 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + +Conversions between UTF32, UTF-16, and UTF-8. Header file. + +Several funtions are included here, forming a complete set of +conversions between the three formats. UTF-7 is not included +here, but is handled in a separate source file. + +Each of these routines takes pointers to input buffers and output +buffers. The input buffers are const. + +Each routine converts the text between *sourceStart and sourceEnd, +putting the result into the buffer between *targetStart and +targetEnd. Note: the end pointers are *after* the last item: e.g. +*(sourceEnd - 1) is the last item. + +The return result indicates whether the conversion was successful, +and if not, whether the problem was in the source or target buffers. +(Only the first encountered problem is indicated.) + +After the conversion, *sourceStart and *targetStart are both +updated to point to the end of last text successfully converted in +the respective buffers. + +Input parameters: +sourceStart - pointer to a pointer to the source buffer. +The contents of this are modified on return so that +it points at the next thing to be converted. +targetStart - similarly, pointer to pointer to the target buffer. +sourceEnd, targetEnd - respectively pointers to the ends of the +two buffers, for overflow checking only. + +These conversion functions take a ConversionFlags argument. When this +flag is set to strict, both irregular sequences and isolated surrogates +will cause an error. When the flag is set to lenient, both irregular +sequences and isolated surrogates are converted. + +Whether the flag is strict or lenient, all illegal sequences will cause +an error return. This includes sequences such as: , , +or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code +must check for illegal sequences. + +When the flag is set to lenient, characters over 0x10FFFF are converted +to the replacement character; otherwise (when the flag is set to strict) +they constitute an error. + +Output parameters: +The value "sourceIllegal" is returned from some routines if the input +sequence is malformed. When "sourceIllegal" is returned, the source +value will point to the illegal value that caused the problem. E.g., +in UTF-8 when a sequence is malformed, it points to the start of the +malformed sequence. + +Author: Mark E. Davis, 1994. +Rev History: Rick McGowan, fixes & updates May 2001. +Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- +The following 4 definitions are compiler-specific. +The C standard does not guarantee that wchar_t has at least +16 bits, so wchar_t is no less portable than unsigned short! +All should be unsigned values to avoid sign extension during +bit mask & shift operations. +------------------------------------------------------------------------ */ + +typedef unsigned long UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +/* This is for C++ and does no harm in C */ +#ifdef __cplusplus +extern "C" { +#endif + +ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +#ifdef __cplusplus +} +#endif + +/* --------------------------------------------------------------------- */ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/bytereader-inl.h b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader-inl.h new file mode 100644 index 0000000..3c16708 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader-inl.h @@ -0,0 +1,175 @@ +// Copyright 2006 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 UTIL_DEBUGINFO_BYTEREADER_INL_H__ +#define UTIL_DEBUGINFO_BYTEREADER_INL_H__ + +#include "common/dwarf/bytereader.h" + +#include + +namespace dwarf2reader { + +inline uint8 ByteReader::ReadOneByte(const char* buffer) const { + return buffer[0]; +} + +inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint16 buffer0 = buffer[0]; + const uint16 buffer1 = buffer[1]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8; + } else { + return buffer1 | buffer0 << 8; + } +} + +inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint32 buffer0 = buffer[0]; + const uint32 buffer1 = buffer[1]; + const uint32 buffer2 = buffer[2]; + const uint32 buffer3 = buffer[3]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; + } else { + return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24; + } +} + +inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint64 buffer0 = buffer[0]; + const uint64 buffer1 = buffer[1]; + const uint64 buffer2 = buffer[2]; + const uint64 buffer3 = buffer[3]; + const uint64 buffer4 = buffer[4]; + const uint64 buffer5 = buffer[5]; + const uint64 buffer6 = buffer[6]; + const uint64 buffer7 = buffer[7]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | + buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; + } else { + return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 | + buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56; + } +} + +// Read an unsigned LEB128 number. Each byte contains 7 bits of +// information, plus one bit saying whether the number continues or +// not. + +inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer, + size_t* len) const { + uint64 result = 0; + size_t num_read = 0; + unsigned int shift = 0; + unsigned char byte; + + do { + byte = *buffer++; + num_read++; + + result |= (static_cast(byte & 0x7f)) << shift; + + shift += 7; + + } while (byte & 0x80); + + *len = num_read; + + return result; +} + +// Read a signed LEB128 number. These are like regular LEB128 +// numbers, except the last byte may have a sign bit set. + +inline int64 ByteReader::ReadSignedLEB128(const char* buffer, + size_t* len) const { + int64 result = 0; + unsigned int shift = 0; + size_t num_read = 0; + unsigned char byte; + + do { + byte = *buffer++; + num_read++; + result |= (static_cast(byte & 0x7f) << shift); + shift += 7; + } while (byte & 0x80); + + if ((shift < 8 * sizeof (result)) && (byte & 0x40)) + result |= -((static_cast(1)) << shift); + *len = num_read; + return result; +} + +inline uint64 ByteReader::ReadOffset(const char* buffer) const { + assert(this->offset_reader_); + return (this->*offset_reader_)(buffer); +} + +inline uint64 ByteReader::ReadAddress(const char* buffer) const { + assert(this->address_reader_); + return (this->*address_reader_)(buffer); +} + +inline void ByteReader::SetCFIDataBase(uint64 section_base, + const char *buffer_base) { + section_base_ = section_base; + buffer_base_ = buffer_base; + have_section_base_ = true; +} + +inline void ByteReader::SetTextBase(uint64 text_base) { + text_base_ = text_base; + have_text_base_ = true; +} + +inline void ByteReader::SetDataBase(uint64 data_base) { + data_base_ = data_base; + have_data_base_ = true; +} + +inline void ByteReader::SetFunctionBase(uint64 function_base) { + function_base_ = function_base; + have_function_base_ = true; +} + +inline void ByteReader::ClearFunctionBase() { + have_function_base_ = false; +} + +} // namespace dwarf2reader + +#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/bytereader.cc b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader.cc new file mode 100644 index 0000000..6802026 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" + +namespace dwarf2reader { + +ByteReader::ByteReader(enum Endianness endian) + :offset_reader_(NULL), address_reader_(NULL), endian_(endian), + address_size_(0), offset_size_(0), + have_section_base_(), have_text_base_(), have_data_base_(), + have_function_base_() { } + +ByteReader::~ByteReader() { } + +void ByteReader::SetOffsetSize(uint8 size) { + offset_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->offset_reader_ = &ByteReader::ReadFourBytes; + } else { + this->offset_reader_ = &ByteReader::ReadEightBytes; + } +} + +void ByteReader::SetAddressSize(uint8 size) { + address_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->address_reader_ = &ByteReader::ReadFourBytes; + } else { + this->address_reader_ = &ByteReader::ReadEightBytes; + } +} + +uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) { + const uint64 initial_length = ReadFourBytes(start); + start += 4; + + // In DWARF2/3, if the initial length is all 1 bits, then the offset + // size is 8 and we need to read the next 8 bytes for the real length. + if (initial_length == 0xffffffff) { + SetOffsetSize(8); + *len = 12; + return ReadOffset(start); + } else { + SetOffsetSize(4); + *len = 4; + } + return initial_length; +} + +bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const { + if (encoding == DW_EH_PE_omit) return true; + if (encoding == DW_EH_PE_aligned) return true; + if ((encoding & 0x7) > DW_EH_PE_udata8) + return false; + if ((encoding & 0x70) > DW_EH_PE_funcrel) + return false; + return true; +} + +bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const { + switch (encoding & 0x70) { + case DW_EH_PE_absptr: return true; + case DW_EH_PE_pcrel: return have_section_base_; + case DW_EH_PE_textrel: return have_text_base_; + case DW_EH_PE_datarel: return have_data_base_; + case DW_EH_PE_funcrel: return have_function_base_; + default: return false; + } +} + +uint64 ByteReader::ReadEncodedPointer(const char *buffer, + DwarfPointerEncoding encoding, + size_t *len) const { + // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't + // see it here. + assert(encoding != DW_EH_PE_omit); + + // The Linux Standards Base 4.0 does not make this clear, but the + // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c) + // agree that aligned pointers are always absolute, machine-sized, + // machine-signed pointers. + if (encoding == DW_EH_PE_aligned) { + assert(have_section_base_); + + // We don't need to align BUFFER in *our* address space. Rather, we + // need to find the next position in our buffer that would be aligned + // when the .eh_frame section the buffer contains is loaded into the + // program's memory. So align assuming that buffer_base_ gets loaded at + // address section_base_, where section_base_ itself may or may not be + // aligned. + + // First, find the offset to START from the closest prior aligned + // address. + uint64 skew = section_base_ & (AddressSize() - 1); + // Now find the offset from that aligned address to buffer. + uint64 offset = skew + (buffer - buffer_base_); + // Round up to the next boundary. + uint64 aligned = (offset + AddressSize() - 1) & -AddressSize(); + // Convert back to a pointer. + const char *aligned_buffer = buffer_base_ + (aligned - skew); + // Finally, store the length and actually fetch the pointer. + *len = aligned_buffer - buffer + AddressSize(); + return ReadAddress(aligned_buffer); + } + + // Extract the value first, ignoring whether it's a pointer or an + // offset relative to some base. + uint64 offset; + switch (encoding & 0x0f) { + case DW_EH_PE_absptr: + // DW_EH_PE_absptr is weird, as it is used as a meaningful value for + // both the high and low nybble of encoding bytes. When it appears in + // the high nybble, it means that the pointer is absolute, not an + // offset from some base address. When it appears in the low nybble, + // as here, it means that the pointer is stored as a normal + // machine-sized and machine-signed address. A low nybble of + // DW_EH_PE_absptr does not imply that the pointer is absolute; it is + // correct for us to treat the value as an offset from a base address + // if the upper nybble is not DW_EH_PE_absptr. + offset = ReadAddress(buffer); + *len = AddressSize(); + break; + + case DW_EH_PE_uleb128: + offset = ReadUnsignedLEB128(buffer, len); + break; + + case DW_EH_PE_udata2: + offset = ReadTwoBytes(buffer); + *len = 2; + break; + + case DW_EH_PE_udata4: + offset = ReadFourBytes(buffer); + *len = 4; + break; + + case DW_EH_PE_udata8: + offset = ReadEightBytes(buffer); + *len = 8; + break; + + case DW_EH_PE_sleb128: + offset = ReadSignedLEB128(buffer, len); + break; + + case DW_EH_PE_sdata2: + offset = ReadTwoBytes(buffer); + // Sign-extend from 16 bits. + offset = (offset ^ 0x8000) - 0x8000; + *len = 2; + break; + + case DW_EH_PE_sdata4: + offset = ReadFourBytes(buffer); + // Sign-extend from 32 bits. + offset = (offset ^ 0x80000000ULL) - 0x80000000ULL; + *len = 4; + break; + + case DW_EH_PE_sdata8: + // No need to sign-extend; this is the full width of our type. + offset = ReadEightBytes(buffer); + *len = 8; + break; + + default: + abort(); + } + + // Find the appropriate base address. + uint64 base; + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + base = 0; + break; + + case DW_EH_PE_pcrel: + assert(have_section_base_); + base = section_base_ + (buffer - buffer_base_); + break; + + case DW_EH_PE_textrel: + assert(have_text_base_); + base = text_base_; + break; + + case DW_EH_PE_datarel: + assert(have_data_base_); + base = data_base_; + break; + + case DW_EH_PE_funcrel: + assert(have_function_base_); + base = function_base_; + break; + + default: + abort(); + } + + uint64 pointer = base + offset; + + // Remove inappropriate upper bits. + if (AddressSize() == 4) + pointer = pointer & 0xffffffff; + else + assert(AddressSize() == sizeof(uint64)); + + return pointer; +} + +} // namespace dwarf2reader diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/bytereader.h b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader.h new file mode 100644 index 0000000..e389427 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader.h @@ -0,0 +1,310 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_DWARF_BYTEREADER_H__ +#define COMMON_DWARF_BYTEREADER_H__ + +#include +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" + +namespace dwarf2reader { + +// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN +// because it conflicts with a macro +enum Endianness { + ENDIANNESS_BIG, + ENDIANNESS_LITTLE +}; + +// A ByteReader knows how to read single- and multi-byte values of +// various endiannesses, sizes, and encodings, as used in DWARF +// debugging information and Linux C++ exception handling data. +class ByteReader { + public: + // Construct a ByteReader capable of reading one-, two-, four-, and + // eight-byte values according to ENDIANNESS, absolute machine-sized + // addresses, DWARF-style "initial length" values, signed and + // unsigned LEB128 numbers, and Linux C++ exception handling data's + // encoded pointers. + explicit ByteReader(enum Endianness endianness); + virtual ~ByteReader(); + + // Read a single byte from BUFFER and return it as an unsigned 8 bit + // number. + uint8 ReadOneByte(const char* buffer) const; + + // Read two bytes from BUFFER and return them as an unsigned 16 bit + // number, using this ByteReader's endianness. + uint16 ReadTwoBytes(const char* buffer) const; + + // Read four bytes from BUFFER and return them as an unsigned 32 bit + // number, using this ByteReader's endianness. This function returns + // a uint64 so that it is compatible with ReadAddress and + // ReadOffset. The number it returns will never be outside the range + // of an unsigned 32 bit integer. + uint64 ReadFourBytes(const char* buffer) const; + + // Read eight bytes from BUFFER and return them as an unsigned 64 + // bit number, using this ByteReader's endianness. + uint64 ReadEightBytes(const char* buffer) const; + + // Read an unsigned LEB128 (Little Endian Base 128) number from + // BUFFER and return it as an unsigned 64 bit integer. Set LEN to + // the number of bytes read. + // + // The unsigned LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between 0 and 0x7f, then its unsigned LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | + // 0x80, followed by the unsigned LEB128 representation of N / + // 128, rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const; + + // Read a signed LEB128 number from BUFFER and return it as an + // signed 64 bit integer. Set LEN to the number of bytes read. + // + // The signed LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between -0x40 and 0x3f, then its signed LEB128 + // representation is a single byte whose value is N in two's + // complement. + // + // - Otherwise, its signed LEB128 representation is (N & 0x7f) | + // 0x80, followed by the signed LEB128 representation of N / 128, + // rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + int64 ReadSignedLEB128(const char* buffer, size_t* len) const; + + // Indicate that addresses on this architecture are SIZE bytes long. SIZE + // must be either 4 or 8. (DWARF allows addresses to be any number of + // bytes in length from 1 to 255, but we only support 32- and 64-bit + // addresses at the moment.) You must call this before using the + // ReadAddress member function. + // + // For data in a .debug_info section, or something that .debug_info + // refers to like line number or macro data, the compilation unit + // header's address_size field indicates the address size to use. Call + // frame information doesn't indicate its address size (a shortcoming of + // the spec); you must supply the appropriate size based on the + // architecture of the target machine. + void SetAddressSize(uint8 size); + + // Return the current address size, in bytes. This is either 4, + // indicating 32-bit addresses, or 8, indicating 64-bit addresses. + uint8 AddressSize() const { return address_size_; } + + // Read an address from BUFFER and return it as an unsigned 64 bit + // integer, respecting this ByteReader's endianness and address size. You + // must call SetAddressSize before calling this function. + uint64 ReadAddress(const char* buffer) const; + + // DWARF actually defines two slightly different formats: 32-bit DWARF + // and 64-bit DWARF. This is *not* related to the size of registers or + // addresses on the target machine; it refers only to the size of section + // offsets and data lengths appearing in the DWARF data. One only needs + // 64-bit DWARF when the debugging data itself is larger than 4GiB. + // 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the + // debugging data itself is very large. + // + // DWARF information identifies itself as 32-bit or 64-bit DWARF: each + // compilation unit and call frame information entry begins with an + // "initial length" field, which, in addition to giving the length of the + // data, also indicates the size of section offsets and lengths appearing + // in that data. The ReadInitialLength member function, below, reads an + // initial length and sets the ByteReader's offset size as a side effect. + // Thus, in the normal process of reading DWARF data, the appropriate + // offset size is set automatically. So, you should only need to call + // SetOffsetSize if you are using the same ByteReader to jump from the + // midst of one block of DWARF data into another. + + // Read a DWARF "initial length" field from START, and return it as + // an unsigned 64 bit integer, respecting this ByteReader's + // endianness. Set *LEN to the length of the initial length in + // bytes, either four or twelve. As a side effect, set this + // ByteReader's offset size to either 4 (if we see a 32-bit DWARF + // initial length) or 8 (if we see a 64-bit DWARF initial length). + // + // A DWARF initial length is either: + // + // - a byte count stored as an unsigned 32-bit value less than + // 0xffffff00, indicating that the data whose length is being + // measured uses the 32-bit DWARF format, or + // + // - The 32-bit value 0xffffffff, followed by a 64-bit byte count, + // indicating that the data whose length is being measured uses + // the 64-bit DWARF format. + uint64 ReadInitialLength(const char* start, size_t* len); + + // Read an offset from BUFFER and return it as an unsigned 64 bit + // integer, respecting the ByteReader's endianness. In 32-bit DWARF, the + // offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes + // long. You must call ReadInitialLength or SetOffsetSize before calling + // this function; see the comments above for details. + uint64 ReadOffset(const char* buffer) const; + + // Return the current offset size, in bytes. + // A return value of 4 indicates that we are reading 32-bit DWARF. + // A return value of 8 indicates that we are reading 64-bit DWARF. + uint8 OffsetSize() const { return offset_size_; } + + // Indicate that section offsets and lengths are SIZE bytes long. SIZE + // must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF). + // Usually, you should not call this function yourself; instead, let a + // call to ReadInitialLength establish the data's offset size + // automatically. + void SetOffsetSize(uint8 size); + + // The Linux C++ ABI uses a variant of DWARF call frame information + // for exception handling. This data is included in the program's + // address space as the ".eh_frame" section, and intepreted at + // runtime to walk the stack, find exception handlers, and run + // cleanup code. The format is mostly the same as DWARF CFI, with + // some adjustments made to provide the additional + // exception-handling data, and to make the data easier to work with + // in memory --- for example, to allow it to be placed in read-only + // memory even when describing position-independent code. + // + // In particular, exception handling data can select a number of + // different encodings for pointers that appear in the data, as + // described by the DwarfPointerEncoding enum. There are actually + // four axes(!) to the encoding: + // + // - The pointer size: pointers can be 2, 4, or 8 bytes long, or use + // the DWARF LEB128 encoding. + // + // - The pointer's signedness: pointers can be signed or unsigned. + // + // - The pointer's base address: the data stored in the exception + // handling data can be the actual address (that is, an absolute + // pointer), or relative to one of a number of different base + // addreses --- including that of the encoded pointer itself, for + // a form of "pc-relative" addressing. + // + // - The pointer may be indirect: it may be the address where the + // true pointer is stored. (This is used to refer to things via + // global offset table entries, program linkage table entries, or + // other tricks used in position-independent code.) + // + // There are also two options that fall outside that matrix + // altogether: the pointer may be omitted, or it may have padding to + // align it on an appropriate address boundary. (That last option + // may seem like it should be just another axis, but it is not.) + + // Indicate that the exception handling data is loaded starting at + // SECTION_BASE, and that the start of its buffer in our own memory + // is BUFFER_BASE. This allows us to find the address that a given + // byte in our buffer would have when loaded into the program the + // data describes. We need this to resolve DW_EH_PE_pcrel pointers. + void SetCFIDataBase(uint64 section_base, const char *buffer_base); + + // Indicate that the base address of the program's ".text" section + // is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers. + void SetTextBase(uint64 text_base); + + // Indicate that the base address for DW_EH_PE_datarel pointers is + // DATA_BASE. The proper value depends on the ABI; it is usually the + // address of the global offset table, held in a designated register in + // position-independent code. You will need to look at the startup code + // for the target system to be sure. I tried; my eyes bled. + void SetDataBase(uint64 data_base); + + // Indicate that the base address for the FDE we are processing is + // FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel + // pointers. (This encoding does not seem to be used by the GNU + // toolchain.) + void SetFunctionBase(uint64 function_base); + + // Indicate that we are no longer processing any FDE, so any use of + // a DW_EH_PE_funcrel encoding is an error. + void ClearFunctionBase(); + + // Return true if ENCODING is a valid pointer encoding. + bool ValidEncoding(DwarfPointerEncoding encoding) const; + + // Return true if we have all the information we need to read a + // pointer that uses ENCODING. This checks that the appropriate + // SetFooBase function for ENCODING has been called. + bool UsableEncoding(DwarfPointerEncoding encoding) const; + + // Read an encoded pointer from BUFFER using ENCODING; return the + // absolute address it represents, and set *LEN to the pointer's + // length in bytes, including any padding for aligned pointers. + // + // This function calls 'abort' if ENCODING is invalid or refers to a + // base address this reader hasn't been given, so you should check + // with ValidEncoding and UsableEncoding first if you would rather + // die in a more helpful way. + uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding, + size_t *len) const; + + private: + + // Function pointer type for our address and offset readers. + typedef uint64 (ByteReader::*AddressReader)(const char*) const; + + // Read an offset from BUFFER and return it as an unsigned 64 bit + // integer. DWARF2/3 define offsets as either 4 or 8 bytes, + // generally depending on the amount of DWARF2/3 info present. + // This function pointer gets set by SetOffsetSize. + AddressReader offset_reader_; + + // Read an address from BUFFER and return it as an unsigned 64 bit + // integer. DWARF2/3 allow addresses to be any size from 0-255 + // bytes currently. Internally we support 4 and 8 byte addresses, + // and will CHECK on anything else. + // This function pointer gets set by SetAddressSize. + AddressReader address_reader_; + + Endianness endian_; + uint8 address_size_; + uint8 offset_size_; + + // Base addresses for Linux C++ exception handling data's encoded pointers. + bool have_section_base_, have_text_base_, have_data_base_; + bool have_function_base_; + uint64 section_base_, text_base_, data_base_, function_base_; + const char *buffer_base_; +}; + +} // namespace dwarf2reader + +#endif // COMMON_DWARF_BYTEREADER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/bytereader_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader_unittest.cc new file mode 100644 index 0000000..d839dbe --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/bytereader_unittest.cc @@ -0,0 +1,697 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader + +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/cfi_assembler.h" + +using dwarf2reader::ByteReader; +using dwarf2reader::DwarfPointerEncoding; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using google_breakpad::CFISection; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; +using std::string; +using testing::Test; + +struct ReaderFixture { + string contents; + size_t pointer_size; +}; + +class Reader: public ReaderFixture, public Test { }; +class ReaderDeathTest: public ReaderFixture, public Test { }; + +TEST_F(Reader, SimpleConstructor) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + CFISection section(kBigEndian, 4); + section + .D8(0xc0) + .D16(0xcf0d) + .D32(0x96fdd219) + .D64(0xbbf55fef0825f117ULL) + .ULEB128(0xa0927048ba8121afULL) + .LEB128(-0x4f337badf4483f83LL) + .D32(0xfec319c9); + ASSERT_TRUE(section.GetContents(&contents)); + const char *data = contents.data(); + EXPECT_EQ(0xc0U, reader.ReadOneByte(data)); + EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1)); + EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3)); + EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7)); + size_t leb128_size; + EXPECT_EQ(0xa0927048ba8121afULL, + reader.ReadUnsignedLEB128(data + 15, &leb128_size)); + EXPECT_EQ(10U, leb128_size); + EXPECT_EQ(-0x4f337badf4483f83LL, + reader.ReadSignedLEB128(data + 25, &leb128_size)); + EXPECT_EQ(10U, leb128_size); + EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35)); +} + +TEST_F(Reader, ValidEncodings) { + ByteReader reader(ENDIANNESS_LITTLE); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_funcrel))); + + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0))); +} + +TEST_F(ReaderDeathTest, DW_EH_PE_omit) { + static const char data[1] = { 42 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit, + &pointer_size), + "encoding != DW_EH_PE_omit"); +} + +TEST_F(Reader, DW_EH_PE_absptr4) { + static const char data[] = { 0x27, 0x57, 0xea, 0x40 }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x40ea5727U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_absptr8) { + static const char data[] = { + 0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50 + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x010598c240ea5727ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_uleb128) { + static const char data[] = { 0x81, 0x84, 0x4c }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x130201U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128, + &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata2) { + static const char data[] = { 0xf4, 0x8d }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_EQ(0xf48dU, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2, + &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata4) { + static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(8); + EXPECT_EQ(0xa5628f8b, + reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata8Addr8) { + static const char data[] = { + 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x8fed199f69047304ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata8Addr4) { + static const char data[] = { + 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x69047304ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sleb128) { + static const char data[] = { 0x42, 0xff, 0xfb, 0x73 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_EQ(-0x030201U & 0xffffffff, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128, + &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata2) { + static const char data[] = { 0xb9, 0xbf }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0xffffffffffffbfb9ULL, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2, + &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata4) { + static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0xffffffffadc2b8f2ULL, + reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata8) { + static const char data[] = { + 0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87 + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x87269b0ce0795766ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_pcrel) { + static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel + | dwarf2reader::DW_EH_PE_absptr); + reader.SetCFIDataBase(0x89951377, data); + EXPECT_EQ(0x89951377 + 3 + 0x14c8c402, + reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_textrel) { + static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + reader.SetTextBase(0xb91beaf0); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_sdata2); + EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff, + reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_datarel) { + static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(8); + reader.SetDataBase(0xbef308bd25ce74f0ULL); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_sleb128); + EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL, + reader.ReadEncodedPointer(data + 2, encoding, &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_funcrel) { + static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + reader.SetFunctionBase(0x823c3520); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel + | dwarf2reader::DW_EH_PE_udata2); + EXPECT_EQ(0x823c3520 + 0xd148, + reader.ReadEncodedPointer(data + 5, encoding, &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST(UsableBase, CFI) { + static const char data[1] = { 0x42 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetCFIDataBase(0xb31cbd20, data); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Text) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetTextBase(0xa899ccb9); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Data) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetDataBase(0xf7b10bcd); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Function) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, ClearFunction) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + reader.ClearFunctionBase(); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +struct AlignedFixture { + AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); } + static const char data[10]; + ByteReader reader; + size_t pointer_size; +}; + +const char AlignedFixture::data[10] = { + 0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b +}; + +class Aligned: public AlignedFixture, public Test { }; + +TEST_F(Aligned, DW_EH_PE_aligned0) { + reader.SetCFIDataBase(0xb440305c, data); + EXPECT_EQ(0xfe6e93d8U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned1) { + reader.SetCFIDataBase(0xb440305d, data); + EXPECT_EQ(0xd834d51cU, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(7U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned2) { + reader.SetCFIDataBase(0xb440305e, data); + EXPECT_EQ(0x93d834d5U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(6U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned3) { + reader.SetCFIDataBase(0xb440305f, data); + EXPECT_EQ(0x6e93d834U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned11) { + reader.SetCFIDataBase(0xb4403061, data); + EXPECT_EQ(0xd834d51cU, + reader.ReadEncodedPointer(data + 1, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(6U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned30) { + reader.SetCFIDataBase(0xb4403063, data); + EXPECT_EQ(0x6e93d834U, + reader.ReadEncodedPointer(data + 1, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned23) { + reader.SetCFIDataBase(0xb4403062, data); + EXPECT_EQ(0x1cd3ac2bU, + reader.ReadEncodedPointer(data + 3, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(7U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned03) { + reader.SetCFIDataBase(0xb4403064, data); + EXPECT_EQ(0x34d51cd3U, + reader.ReadEncodedPointer(data + 3, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.cc b/src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.cc new file mode 100644 index 0000000..a6a5aca --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.cc @@ -0,0 +1,198 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// cfi_assembler.cc: Implementation of google_breakpad::CFISection class. +// See cfi_assembler.h for details. + +#include "common/dwarf/cfi_assembler.h" + +#include +#include + +namespace google_breakpad { + +using dwarf2reader::DwarfPointerEncoding; + +CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor, + int data_alignment_factor, + unsigned return_address_register, + u_int8_t version, + const string &augmentation, + bool dwarf64) { + assert(!entry_length_); + entry_length_ = new PendingLength(); + in_fde_ = false; + + if (dwarf64) { + D32(kDwarf64InitialLengthMarker); + D64(entry_length_->length); + entry_length_->start = Here(); + D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier); + } else { + D32(entry_length_->length); + entry_length_->start = Here(); + D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier); + } + D8(version); + AppendCString(augmentation); + ULEB128(code_alignment_factor); + LEB128(data_alignment_factor); + if (version == 1) + D8(return_address_register); + else + ULEB128(return_address_register); + return *this; +} + +CFISection &CFISection::FDEHeader(Label cie_pointer, + u_int64_t initial_location, + u_int64_t address_range, + bool dwarf64) { + assert(!entry_length_); + entry_length_ = new PendingLength(); + in_fde_ = true; + fde_start_address_ = initial_location; + + if (dwarf64) { + D32(0xffffffff); + D64(entry_length_->length); + entry_length_->start = Here(); + if (eh_frame_) + D64(Here() - cie_pointer); + else + D64(cie_pointer); + } else { + D32(entry_length_->length); + entry_length_->start = Here(); + if (eh_frame_) + D32(Here() - cie_pointer); + else + D32(cie_pointer); + } + EncodedPointer(initial_location); + // The FDE length in an .eh_frame section uses the same encoding as the + // initial location, but ignores the base address (selected by the upper + // nybble of the encoding), as it's a length, not an address that can be + // made relative. + EncodedPointer(address_range, + DwarfPointerEncoding(pointer_encoding_ & 0x0f)); + return *this; +} + +CFISection &CFISection::FinishEntry() { + assert(entry_length_); + Align(address_size_, dwarf2reader::DW_CFA_nop); + entry_length_->length = Here() - entry_length_->start; + delete entry_length_; + entry_length_ = NULL; + in_fde_ = false; + return *this; +} + +CFISection &CFISection::EncodedPointer(u_int64_t address, + DwarfPointerEncoding encoding, + const EncodedPointerBases &bases) { + // Omitted data is extremely easy to emit. + if (encoding == dwarf2reader::DW_EH_PE_omit) + return *this; + + // If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume + // that ADDRESS is the address at which the pointer is stored --- in + // other words, that bit has no effect on how we write the pointer. + encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect); + + // Find the base address to which this pointer is relative. The upper + // nybble of the encoding specifies this. + u_int64_t base; + switch (encoding & 0xf0) { + case dwarf2reader::DW_EH_PE_absptr: base = 0; break; + case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break; + case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break; + case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break; + case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break; + case dwarf2reader::DW_EH_PE_aligned: base = 0; break; + default: abort(); + }; + + // Make ADDRESS relative. Yes, this is appropriate even for "absptr" + // values; see gcc/unwind-pe.h. + address -= base; + + // Align the pointer, if required. + if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned) + Align(AddressSize()); + + // Append ADDRESS to this section in the appropriate form. For the + // fixed-width forms, we don't need to differentiate between signed and + // unsigned encodings, because ADDRESS has already been extended to 64 + // bits before it was passed to us. + switch (encoding & 0x0f) { + case dwarf2reader::DW_EH_PE_absptr: + Address(address); + break; + + case dwarf2reader::DW_EH_PE_uleb128: + ULEB128(address); + break; + + case dwarf2reader::DW_EH_PE_sleb128: + LEB128(address); + break; + + case dwarf2reader::DW_EH_PE_udata2: + case dwarf2reader::DW_EH_PE_sdata2: + D16(address); + break; + + case dwarf2reader::DW_EH_PE_udata4: + case dwarf2reader::DW_EH_PE_sdata4: + D32(address); + break; + + case dwarf2reader::DW_EH_PE_udata8: + case dwarf2reader::DW_EH_PE_sdata8: + D64(address); + break; + + default: + abort(); + } + + return *this; +}; + +const u_int32_t CFISection::kDwarf64InitialLengthMarker; +const u_int32_t CFISection::kDwarf32CIEIdentifier; +const u_int64_t CFISection::kDwarf64CIEIdentifier; +const u_int32_t CFISection::kEHFrame32CIEIdentifier; +const u_int64_t CFISection::kEHFrame64CIEIdentifier; + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.h b/src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.h new file mode 100644 index 0000000..3f30503 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/cfi_assembler.h @@ -0,0 +1,269 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// cfi_assembler.h: Define CFISection, a class for creating properly +// (and improperly) formatted DWARF CFI data for unit tests. + +#ifndef PROCESSOR_CFI_ASSEMBLER_H_ +#define PROCESSOR_CFI_ASSEMBLER_H_ + +#include + +#include "common/dwarf/dwarf2enums.h" +#include "common/test_assembler.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using dwarf2reader::DwarfPointerEncoding; +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using std::string; + +class CFISection: public Section { + public: + + // CFI augmentation strings beginning with 'z', defined by the + // Linux/IA-64 C++ ABI, can specify interesting encodings for + // addresses appearing in FDE headers and call frame instructions (and + // for additional fields whose presence the augmentation string + // specifies). In particular, pointers can be specified to be relative + // to various base address: the start of the .text section, the + // location holding the address itself, and so on. These allow the + // frame data to be position-independent even when they live in + // write-protected pages. These variants are specified at the + // following two URLs: + // + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html + // + // CFISection leaves the production of well-formed 'z'-augmented CIEs and + // FDEs to the user, but does provide EncodedPointer, to emit + // properly-encoded addresses for a given pointer encoding. + // EncodedPointer uses an instance of this structure to find the base + // addresses it should use; you can establish a default for all encoded + // pointers appended to this section with SetEncodedPointerBases. + struct EncodedPointerBases { + EncodedPointerBases() : cfi(), text(), data() { } + + // The starting address of this CFI section in memory, for + // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data + // that has is loaded into the program's address space. + u_int64_t cfi; + + // The starting address of this file's .text section, for DW_EH_PE_textrel. + u_int64_t text; + + // The starting address of this file's .got or .eh_frame_hdr section, + // for DW_EH_PE_datarel. + u_int64_t data; + }; + + // Create a CFISection whose endianness is ENDIANNESS, and where + // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is + // true, use the .eh_frame format, as described by the Linux + // Standards Base Core Specification, instead of the DWARF CFI + // format. + CFISection(Endianness endianness, size_t address_size, + bool eh_frame = false) + : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), + pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), + encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { + // The 'start', 'Here', and 'Mark' members of a CFISection all refer + // to section offsets. + start() = 0; + } + + // Return this CFISection's address size. + size_t AddressSize() const { return address_size_; } + + // Return true if this CFISection uses the .eh_frame format, or + // false if it contains ordinary DWARF CFI data. + bool ContainsEHFrame() const { return eh_frame_; } + + // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer. + void SetPointerEncoding(DwarfPointerEncoding encoding) { + pointer_encoding_ = encoding; + } + + // Use the addresses in BASES as the base addresses for encoded + // pointers in subsequent calls to FDEHeader or EncodedPointer. + // This function makes a copy of BASES. + void SetEncodedPointerBases(const EncodedPointerBases &bases) { + encoded_pointer_bases_ = bases; + } + + // Append a Common Information Entry header to this section with the + // given values. If dwarf64 is true, use the 64-bit DWARF initial + // length format for the CIE's initial length. Return a reference to + // this section. You should call FinishEntry after writing the last + // instruction for the CIE. + // + // Before calling this function, you will typically want to use Mark + // or Here to make a label to pass to FDEHeader that refers to this + // CIE's position in the section. + CFISection &CIEHeader(u_int64_t code_alignment_factor, + int data_alignment_factor, + unsigned return_address_register, + u_int8_t version = 3, + const string &augmentation = "", + bool dwarf64 = false); + + // Append a Frame Description Entry header to this section with the + // given values. If dwarf64 is true, use the 64-bit DWARF initial + // length format for the CIE's initial length. Return a reference to + // this section. You should call FinishEntry after writing the last + // instruction for the CIE. + // + // This function doesn't support entries that are longer than + // 0xffffff00 bytes. (The "initial length" is always a 32-bit + // value.) Nor does it support .debug_frame sections longer than + // 0xffffff00 bytes. + CFISection &FDEHeader(Label cie_pointer, + u_int64_t initial_location, + u_int64_t address_range, + bool dwarf64 = false); + + // Note the current position as the end of the last CIE or FDE we + // started, after padding with DW_CFA_nops for alignment. This + // defines the label representing the entry's length, cited in the + // entry's header. Return a reference to this section. + CFISection &FinishEntry(); + + // Append the contents of BLOCK as a DW_FORM_block value: an + // unsigned LEB128 length, followed by that many bytes of data. + CFISection &Block(const string &block) { + ULEB128(block.size()); + Append(block); + return *this; + } + + // Append ADDRESS to this section, in the appropriate size and + // endianness. Return a reference to this section. + CFISection &Address(u_int64_t address) { + Section::Append(endianness(), address_size_, address); + return *this; + } + CFISection &Address(Label address) { + Section::Append(endianness(), address_size_, address); + return *this; + } + + // Append ADDRESS to this section, using ENCODING and BASES. ENCODING + // defaults to this section's default encoding, established by + // SetPointerEncoding. BASES defaults to this section's bases, set by + // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the + // encoding, assume that ADDRESS is where the true address is stored. + // Return a reference to this section. + // + // (C++ doesn't let me use default arguments here, because I want to + // refer to members of *this in the default argument expression.) + CFISection &EncodedPointer(u_int64_t address) { + return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); + } + CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding) { + return EncodedPointer(address, encoding, encoded_pointer_bases_); + } + CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding, + const EncodedPointerBases &bases); + + // Restate some member functions, to keep chaining working nicely. + CFISection &Mark(Label *label) { Section::Mark(label); return *this; } + CFISection &D8(u_int8_t v) { Section::D8(v); return *this; } + CFISection &D16(u_int16_t v) { Section::D16(v); return *this; } + CFISection &D16(Label v) { Section::D16(v); return *this; } + CFISection &D32(u_int32_t v) { Section::D32(v); return *this; } + CFISection &D32(const Label &v) { Section::D32(v); return *this; } + CFISection &D64(u_int64_t v) { Section::D64(v); return *this; } + CFISection &D64(const Label &v) { Section::D64(v); return *this; } + CFISection &LEB128(long long v) { Section::LEB128(v); return *this; } + CFISection &ULEB128(u_int64_t v) { Section::ULEB128(v); return *this; } + + private: + // A length value that we've appended to the section, but is not yet + // known. LENGTH is the appended value; START is a label referring + // to the start of the data whose length was cited. + struct PendingLength { + Label length; + Label start; + }; + + // Constants used in CFI/.eh_frame data: + + // If the first four bytes of an "initial length" are this constant, then + // the data uses the 64-bit DWARF format, and the length itself is the + // subsequent eight bytes. + static const u_int32_t kDwarf64InitialLengthMarker = 0xffffffffU; + + // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data. + static const u_int32_t kDwarf32CIEIdentifier = ~(u_int32_t)0; + static const u_int64_t kDwarf64CIEIdentifier = ~(u_int64_t)0; + static const u_int32_t kEHFrame32CIEIdentifier = 0; + static const u_int64_t kEHFrame64CIEIdentifier = 0; + + // The size of a machine address for the data in this section. + size_t address_size_; + + // If true, we are generating a Linux .eh_frame section, instead of + // a standard DWARF .debug_frame section. + bool eh_frame_; + + // The encoding to use for FDE pointers. + DwarfPointerEncoding pointer_encoding_; + + // The base addresses to use when emitting encoded pointers. + EncodedPointerBases encoded_pointer_bases_; + + // The length value for the current entry. + // + // Oddly, this must be dynamically allocated. Labels never get new + // values; they only acquire constraints on the value they already + // have, or assert if you assign them something incompatible. So + // each header needs truly fresh Label objects to cite in their + // headers and track their positions. The alternative is explicit + // destructor invocation and a placement new. Ick. + PendingLength *entry_length_; + + // True if we are currently emitting an FDE --- that is, we have + // called FDEHeader but have not yet called FinishEntry. + bool in_fde_; + + // If in_fde_ is true, this is its starting address. We use this for + // emitting DW_EH_PE_funcrel pointers. + u_int64_t fde_start_address_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_CFI_ASSEMBLER_H_ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.cc b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.cc new file mode 100644 index 0000000..1639954 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class. +// See dwarf2diehandler.h for details. + +#include "common/dwarf/dwarf2diehandler.h" + +#include + +namespace dwarf2reader { + +DIEDispatcher::~DIEDispatcher() { + while (!die_handlers_.empty()) { + HandlerStack &entry = die_handlers_.top(); + if (entry.handler_ != root_handler_) + delete entry.handler_; + die_handlers_.pop(); + } +} + +bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { + return root_handler_->StartCompilationUnit(offset, address_size, + offset_size, cu_length, + dwarf_version); +} + +bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { + // The stack entry for the parent of this DIE, if there is one. + HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top(); + + // Does this call indicate that we're done receiving the parent's + // attributes' values? If so, call its EndAttributes member function. + if (parent && parent->handler_ && !parent->reported_attributes_end_) { + parent->reported_attributes_end_ = true; + if (!parent->handler_->EndAttributes()) { + // Finish off this handler now. and edit *PARENT to indicate that + // we don't want to visit any of the children. + parent->handler_->Finish(); + if (parent->handler_ != root_handler_) + delete parent->handler_; + parent->handler_ = NULL; + return false; + } + } + + // Find a handler for this DIE. + DIEHandler *handler; + if (parent) { + if (parent->handler_) + // Ask the parent to find a handler. + handler = parent->handler_->FindChildHandler(offset, tag, attrs); + else + // No parent handler means we're not interested in any of our + // children. + handler = NULL; + } else { + // This is the root DIE. For a non-root DIE, the parent's handler + // decides whether to visit it, but the root DIE has no parent + // handler, so we have a special method on the root DIE handler + // itself to decide. + if (root_handler_->StartRootDIE(offset, tag, attrs)) + handler = root_handler_; + else + handler = NULL; + } + + // Push a handler stack entry for this new handler. As an + // optimization, we don't push NULL-handler entries on top of other + // NULL-handler entries; we just let the oldest such entry stand for + // the whole subtree. + if (handler || !parent || parent->handler_) { + HandlerStack entry; + entry.offset_ = offset; + entry.handler_ = handler; + entry.reported_attributes_end_ = false; + die_handlers_.push(entry); + } + + return handler != NULL; +} + +void DIEDispatcher::EndDIE(uint64 offset) { + assert(!die_handlers_.empty()); + HandlerStack *entry = &die_handlers_.top(); + if (entry->handler_) { + // This entry had better be the handler for this DIE. + assert(entry->offset_ == offset); + // If a DIE has no children, this EndDIE call indicates that we're + // done receiving its attributes' values. + if (!entry->reported_attributes_end_) + entry->handler_->EndAttributes(); // Ignore return value: no children. + entry->handler_->Finish(); + if (entry->handler_ != root_handler_) + delete entry->handler_; + } else { + // If this DIE is within a tree we're ignoring, then don't pop the + // handler stack: that entry stands for the whole tree. + if (entry->offset_ != offset) + return; + } + die_handlers_.pop(); +} + +void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeUnsigned(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSigned(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeReference(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeBuffer(attr, form, data, len); +} + +void DIEDispatcher::ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeString(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSignature(attr, form, signature); +} + +} // namespace dwarf2reader diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.h b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.h new file mode 100644 index 0000000..5d899bf --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler.h @@ -0,0 +1,365 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf2reader::CompilationUnit is a simple and direct parser for +// DWARF data, but its handler interface is not convenient to use. In +// particular: +// +// - CompilationUnit calls Dwarf2Handler's member functions to report +// every attribute's value, regardless of what sort of DIE it is. +// As a result, the ProcessAttributeX functions end up looking like +// this: +// +// switch (parent_die_tag) { +// case DW_TAG_x: +// switch (attribute_name) { +// case DW_AT_y: +// handle attribute y of DIE type x +// ... +// } break; +// ... +// } +// +// In C++ it's much nicer to use virtual function dispatch to find +// the right code for a given case than to switch on the DIE tag +// like this. +// +// - Processing different kinds of DIEs requires different sets of +// data: lexical block DIEs have start and end addresses, but struct +// type DIEs don't. It would be nice to be able to have separate +// handler classes for separate kinds of DIEs, each with the members +// appropriate to its role, instead of having one handler class that +// needs to hold data for every DIE type. +// +// - There should be a separate instance of the appropriate handler +// class for each DIE, instead of a single object with tables +// tracking all the dies in the compilation unit. +// +// - It's not convenient to take some action after all a DIE's +// attributes have been seen, but before visiting any of its +// children. The only indication you have that a DIE's attribute +// list is complete is that you get either a StartDIE or an EndDIE +// call. +// +// - It's not convenient to make use of the tree structure of the +// DIEs. Skipping all the children of a given die requires +// maintaining state and returning false from StartDIE until we get +// an EndDIE call with the appropriate offset. +// +// This interface tries to take care of all that. (You're shocked, I'm sure.) +// +// Using the classes here, you provide an initial handler for the root +// DIE of the compilation unit. Each handler receives its DIE's +// attributes, and provides fresh handler objects for children of +// interest, if any. The three classes are: +// +// - DIEHandler: the base class for your DIE-type-specific handler +// classes. +// +// - RootDIEHandler: derived from DIEHandler, the base class for your +// root DIE handler class. +// +// - DIEDispatcher: derived from Dwarf2Handler, an instance of this +// invokes your DIE-type-specific handler objects. +// +// In detail: +// +// - Define handler classes specialized for the DIE types you're +// interested in. These handler classes must inherit from +// DIEHandler. Thus: +// +// class My_DW_TAG_X_Handler: public DIEHandler { ... }; +// class My_DW_TAG_Y_Handler: public DIEHandler { ... }; +// +// DIEHandler subclasses needn't correspond exactly to single DIE +// types, as shown here; the point is that you can have several +// different classes appropriate to different kinds of DIEs. +// +// - In particular, define a handler class for the compilation +// unit's root DIE, that inherits from RootDIEHandler: +// +// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... }; +// +// RootDIEHandler inherits from DIEHandler, adding a few additional +// member functions for examining the compilation unit as a whole, +// and other quirks of rootness. +// +// - Then, create a DIEDispatcher instance, passing it an instance of +// your root DIE handler class, and use that DIEDispatcher as the +// dwarf2reader::CompilationUnit's handler: +// +// My_DW_TAG_compile_unit_Handler root_die_handler(...); +// DIEDispatcher die_dispatcher(&root_die_handler); +// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher); +// +// Here, 'die_dispatcher' acts as a shim between 'reader' and the +// various DIE-specific handlers you have defined. +// +// - When you call reader.Start(), die_dispatcher behaves as follows, +// starting with your root die handler and the compilation unit's +// root DIE: +// +// - It calls the handler's ProcessAttributeX member functions for +// each of the DIE's attributes. +// +// - It calls the handler's EndAttributes member function. This +// should return true if any of the DIE's children should be +// visited, in which case: +// +// - For each of the DIE's children, die_dispatcher calls the +// DIE's handler's FindChildHandler member function. If that +// returns a pointer to a DIEHandler instance, then +// die_dispatcher uses that handler to process the child, using +// this procedure recursively. Alternatively, if +// FindChildHandler returns NULL, die_dispatcher ignores that +// child and its descendants. +// +// - When die_dispatcher has finished processing all the DIE's +// children, it invokes the handler's Finish() member function, +// and destroys the handler. (As a special case, it doesn't +// destroy the root DIE handler.) +// +// This allows the code for handling a particular kind of DIE to be +// gathered together in a single class, makes it easy to skip all the +// children or individual children of a particular DIE, and provides +// appropriate parental context for each die. + +#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__ +#define COMMON_DWARF_DWARF2DIEHANDLER_H__ + +#include + +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/dwarf2reader.h" + +namespace dwarf2reader { + +// A base class for handlers for specific DIE types. The series of +// calls made on a DIE handler is as follows: +// +// - for each attribute of the DIE: +// - ProcessAttributeX() +// - EndAttributes() +// - if that returned true, then for each child: +// - FindChildHandler() +// - if that returns a non-NULL pointer to a new handler: +// - recurse, with the new handler and the child die +// - Finish() +// - destruction +class DIEHandler { + public: + DIEHandler() { } + virtual ~DIEHandler() { } + + // When we visit a DIE, we first use these member functions to + // report the DIE's attributes and their values. These have the + // same restrictions as the corresponding member functions of + // dwarf2reader::Dwarf2Handler. + // + // Since DWARF does not specify in what order attributes must + // appear, avoid making decisions in these functions that would be + // affected by the presence of other attributes. The EndAttributes + // function is a more appropriate place for such work, as all the + // DIE's attributes have been seen at that point. + // + // The default definitions ignore the values they are passed. + virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + virtual void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { } + virtual void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len) { } + virtual void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data) { } + virtual void ProcessAttributeSignature(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signture) { } + + // Once we have reported all the DIE's attributes' values, we call + // this member function. If it returns false, we skip all the DIE's + // children. If it returns true, we call FindChildHandler on each + // child. If that returns a handler object, we use that to visit + // the child; otherwise, we skip the child. + // + // This is a good place to make decisions that depend on more than + // one attribute. DWARF does not specify in what order attributes + // must appear, so only when the EndAttributes function is called + // does the handler have a complete picture of the DIE's attributes. + // + // The default definition elects to ignore the DIE's children. + // You'll need to override this if you override FindChildHandler, + // but at least the default behavior isn't to pass the children to + // FindChildHandler, which then ignores them all. + virtual bool EndAttributes() { return false; } + + // If EndAttributes returns true to indicate that some of the DIE's + // children might be of interest, then we apply this function to + // each of the DIE's children. If it returns a handler object, then + // we use that to visit the child DIE. If it returns NULL, we skip + // that child DIE (and all its descendants). + // + // OFFSET is the offset of the child; TAG indicates what kind of DIE + // it is; and ATTRS is the list of attributes the DIE will have, and + // their forms (their values are not provided). + // + // The default definition skips all children. + virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs) { + return NULL; + } + + // When we are done processing a DIE, we call this member function. + // This happens after the EndAttributes call, all FindChildHandler + // calls (if any), and all operations on the children themselves (if + // any). We call Finish on every handler --- even if EndAttributes + // returns false. + virtual void Finish() { }; +}; + +// A subclass of DIEHandler, with additional kludges for handling the +// compilation unit's root die. +class RootDIEHandler: public DIEHandler { + public: + RootDIEHandler() { } + virtual ~RootDIEHandler() { } + + // We pass the values reported via Dwarf2Handler::StartCompilationUnit + // to this member function, and skip the entire compilation unit if it + // returns false. So the root DIE handler is actually also + // responsible for handling the compilation unit metadata. + // The default definition always visits the compilation unit. + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { return true; } + + // For the root DIE handler only, we pass the offset, tag and + // attributes of the compilation unit's root DIE. This is the only + // way the root DIE handler can find the root DIE's tag. If this + // function returns true, we will visit the root DIE using the usual + // DIEHandler methods; otherwise, we skip the entire compilation + // unit. + // + // The default definition elects to visit the root DIE. + virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { return true; } +}; + +class DIEDispatcher: public Dwarf2Handler { + public: + // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for + // the compilation unit's root die, as described for the DIEHandler + // class. + DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { } + // Destroying a DIEDispatcher destroys all active handler objects + // except the root handler. + ~DIEDispatcher(); + bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + bool StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs); + void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len); + void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string &data); + void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature); + void EndDIE(uint64 offset); + + private: + + // The type of a handler stack entry. This includes some fields + // which don't really need to be on the stack --- they could just be + // single data members of DIEDispatcher --- but putting them here + // makes it easier to see that the code is correct. + struct HandlerStack { + // The offset of the DIE for this handler stack entry. + uint64 offset_; + + // The handler object interested in this DIE's attributes and + // children. If NULL, we're not interested in either. + DIEHandler *handler_; + + // Have we reported the end of this DIE's attributes to the handler? + bool reported_attributes_end_; + }; + + // Stack of DIE attribute handlers. At StartDIE(D), the top of the + // stack is the handler of D's parent, whom we may ask for a handler + // for D itself. At EndDIE(D), the top of the stack is D's handler. + // Special cases: + // + // - Before we've seen the compilation unit's root DIE, the stack is + // empty; we'll call root_handler_'s special member functions, and + // perhaps push root_handler_ on the stack to look at the root's + // immediate children. + // + // - When we decide to ignore a subtree, we only push an entry on + // the stack for the root of the tree being ignored, rather than + // pushing lots of stack entries with handler_ set to NULL. + std::stack die_handlers_; + + // The root handler. We don't push it on die_handlers_ until we + // actually get the StartDIE call for the root. + RootDIEHandler *root_handler_; +}; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler_unittest.cc new file mode 100644 index 0000000..186b951 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2diehandler_unittest.cc @@ -0,0 +1,579 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. + +#include +#include + +#include "breakpad_googletest_includes.h" + +#include "common/dwarf/dwarf2diehandler.h" + +using std::make_pair; +using std::string; + +using ::testing::_; +using ::testing::ContainerEq; +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::Sequence; +using ::testing::StrEq; + +using dwarf2reader::AttributeList; +using dwarf2reader::DIEDispatcher; +using dwarf2reader::DIEHandler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfTag; +using dwarf2reader::RootDIEHandler; + +class MockDIEHandler: public DIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const char *, uint64)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, + const AttributeList &)); + MOCK_METHOD0(Finish, void()); +}; + +class MockRootDIEHandler: public RootDIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const char *, uint64)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, + const AttributeList &)); + MOCK_METHOD0(Finish, void()); + MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8)); + MOCK_METHOD3(StartRootDIE, bool(uint64, DwarfTag, const AttributeList &)); +}; + +// If the handler elects to skip the compilation unit, the dispatcher +// should tell the reader so. +TEST(Dwarf2DIEHandler, SkipCompilationUnit) { + Sequence s; + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)); +} + +// If the handler elects to skip the root DIE, the dispatcher should +// tell the reader so. +TEST(Dwarf2DIEHandler, SkipRootDIE) { + Sequence s; + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList mock_attribute_list; + mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string)); + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02, + 0xb00febffa76e2b2bLL, 0x5c)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6, + ContainerEq(mock_attribute_list))) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL, + 0xf4, 0x02, + 0xb00febffa76e2b2bLL, 0x5c)); + EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, + (DwarfTag) 0xb4f98da6, + mock_attribute_list)); + die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); +} + +// If the handler elects to skip the root DIE's children, the +// dispatcher should tell the reader so --- and avoid deleting the +// root handler. +TEST(Dwarf2DIEHandler, SkipRootDIEChildren) { + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList mock_attribute_list; + + { + InSequence s; + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0, + 0x09f8bf0767f91675LL, 0xdb)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6, + ContainerEq(mock_attribute_list))) + .WillOnce(Return(true)); + // Please don't tell me about my children. + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(false)); + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL, + 0x26, 0xa0, + 0x09f8bf0767f91675LL, 0xdb)); + EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, + (DwarfTag) 0xb4f98da6, + mock_attribute_list)); + EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL, + (DwarfTag) 0xc3a17bba, + mock_attribute_list)); + die_dispatcher.EndDIE(0x435150ceedccda18LL); + die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); +} + +// The dispatcher should pass attribute values through to the die +// handler accurately. +TEST(Dwarf2DIEHandler, PassAttributeValues) { + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList mock_attribute_list; + mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string)); + const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca, + 0xcf, 0xa8, 0x84, 0xa7, 0x18 }; + string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d"; + + // Set expectations. + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, 0x66)) + .WillOnce(Return(true)); + + // We'll like the root DIE. + EXPECT_CALL(mock_root_handler, + StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c, + ContainerEq(mock_attribute_list))) + .WillOnce(Return(true)); + + // Expect some attribute values. + EXPECT_CALL(mock_root_handler, + ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed, + (DwarfForm) 0x424f1468, + 0xa592571997facda1ULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSigned((DwarfAttribute) 0x43694dc9, + (DwarfForm) 0xf6f78901L, + 0x92602a4e3bf1f446LL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeReference((DwarfAttribute) 0x4033e8cL, + (DwarfForm) 0xf66fbe0bL, + 0x50fddef44734fdecULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af, + (DwarfForm) 0xe99a539a, + buffer, sizeof(buffer))) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeString((DwarfAttribute) 0x310ed065, + (DwarfForm) 0x15762fec, + StrEq(str))) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSignature((DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _)) + .Times(0); + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + + // Drive the dispatcher. + + // Report the CU header. + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)); + // Report the root DIE. + EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL, + (DwarfTag) 0x9829445c, + mock_attribute_list)); + + // Report some attribute values. + die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x1cc0bfed, + (DwarfForm) 0x424f1468, + 0xa592571997facda1ULL); + die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x43694dc9, + (DwarfForm) 0xf6f78901, + 0x92602a4e3bf1f446LL); + die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x4033e8c, + (DwarfForm) 0xf66fbe0b, + 0x50fddef44734fdecULL); + die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x25d7e0af, + (DwarfForm) 0xe99a539a, + buffer, sizeof(buffer)); + die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x310ed065, + (DwarfForm) 0x15762fec, + str); + die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL); + + // Finish the root DIE (and thus the CU). + die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); +} + +TEST(Dwarf2DIEHandler, FindAndSkipChildren) { + MockRootDIEHandler mock_root_handler; + MockDIEHandler *mock_child1_handler = new(MockDIEHandler); + MockDIEHandler *mock_child3_handler = new(MockDIEHandler); + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList root_attribute_list; + root_attribute_list.push_back(make_pair((DwarfAttribute) 0xb01185df, + (DwarfForm) 0xbc97cee8)); + AttributeList child1_attribute_list; + child1_attribute_list.push_back(make_pair((DwarfAttribute) 0x41014e43, + (DwarfForm) 0x63155f4c)); + AttributeList grandchild1_attribute_list; + grandchild1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf72f823c, + (DwarfForm) 0x0ff6a201)); + AttributeList greatgrandchild1_attribute_list; + greatgrandchild1_attribute_list + .push_back(make_pair((DwarfAttribute) 0xbe66e5f0, (DwarfForm) 0xb4b24ff7)); + AttributeList child2_attribute_list; + child1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf22df14c, + (DwarfForm) 0x20676e7d)); + AttributeList child3_attribute_list; + child3_attribute_list.push_back(make_pair((DwarfAttribute) 0xe8bf1201, + (DwarfForm) 0x53a5b7a8)); + + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, + 0x47dd3c764275a216LL, 0xa5)) + .WillOnce(Return(true)); + + // Root DIE. + { + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59, + ContainerEq(root_attribute_list))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSigned((DwarfAttribute) 0xf779a642, + (DwarfForm) 0x2cb63027, + 0x18e744661769d08fLL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + + // First child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x149f644f8116fe8cLL, + (DwarfTag) 0xac2cbd8c, + ContainerEq(child1_attribute_list))) + .WillOnce(Return(mock_child1_handler)); + { + EXPECT_CALL(*mock_child1_handler, + ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65, + (DwarfForm) 0xe4f64c41, + 0x1b04e5444a55fe67LL)) + .WillOnce(Return()); + EXPECT_CALL(*mock_child1_handler, EndAttributes()) + .WillOnce(Return(false)); + // Skip first grandchild DIE and first great-grandchild DIE. + EXPECT_CALL(*mock_child1_handler, Finish()) + .WillOnce(Return()); + } + + // Second child DIE. Root handler will decline to return a handler + // for this child. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x97412be24875de9dLL, + (DwarfTag) 0x505a068b, + ContainerEq(child2_attribute_list))) + .WillOnce(Return((DIEHandler *) NULL)); + + // Third child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x753c964c8ab538aeLL, + (DwarfTag) 0x8c22970e, + ContainerEq(child3_attribute_list))) + .WillOnce(Return(mock_child3_handler)); + { + EXPECT_CALL(*mock_child3_handler, + ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL)) + .WillOnce(Return()); + EXPECT_CALL(*mock_child3_handler, EndAttributes()) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_child3_handler, Finish()) + .WillOnce(Return()); + } + + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + } + + + // Drive the dispatcher. + + // Report the CU header. + EXPECT_TRUE(die_dispatcher + .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, + 0x47dd3c764275a216LL, 0xa5)); + // Report the root DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL, + (DwarfTag) 0xf5d60c59, + root_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL, + (DwarfAttribute) 0xf779a642, + (DwarfForm) 0x2cb63027, + 0x18e744661769d08fLL); + + // First child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL, + (DwarfTag) 0xac2cbd8c, + child1_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL, + (DwarfAttribute) 0xa6fd6f65, + (DwarfForm) 0xe4f64c41, + 0x1b04e5444a55fe67LL); + + // First grandchild DIE. Will be skipped. + { + EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL, + (DwarfTag) 0x22f05a15, + grandchild1_attribute_list)); + // First great-grandchild DIE. Will be skipped without being + // mentioned to any handler. + { + EXPECT_FALSE(die_dispatcher + .StartDIE(0xb3076285d25cac25LL, + (DwarfTag) 0xcff4061b, + greatgrandchild1_attribute_list)); + die_dispatcher.EndDIE(0xb3076285d25cac25LL); + } + die_dispatcher.EndDIE(0xd68de1ee0bd29419LL); + } + die_dispatcher.EndDIE(0x149f644f8116fe8cLL); + } + + // Second child DIE. Root handler will decline to find a handler for it. + { + EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL, + (DwarfTag) 0x505a068b, + child2_attribute_list)); + die_dispatcher.EndDIE(0x97412be24875de9dLL); + } + + // Third child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL, + (DwarfTag) 0x8c22970e, + child3_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL, + (DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL); + die_dispatcher.EndDIE(0x753c964c8ab538aeLL); + } + + // Finish the root DIE (and thus the CU). + die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL); + } +} + +// The DIEDispatcher destructor is supposed to delete all handlers on +// the stack, except for the root. +TEST(Dwarf2DIEHandler, FreeHandlersOnStack) { + MockRootDIEHandler mock_root_handler; + MockDIEHandler *mock_child_handler = new(MockDIEHandler); + MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler); + AttributeList empty_attribute_list; + + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, + 0x76d392ff393ddda2LL, 0xbf)) + .WillOnce(Return(true)); + + // Root DIE. + { + EXPECT_CALL(mock_root_handler, + StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361, + ContainerEq(empty_attribute_list))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + + // Child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x058f09240c5fc8c9LL, + (DwarfTag) 0x898bf0d0, + ContainerEq(empty_attribute_list))) + .WillOnce(Return(mock_child_handler)); + { + EXPECT_CALL(*mock_child_handler, EndAttributes()) + .WillOnce(Return(true)); + + // Grandchild DIE. + EXPECT_CALL(*mock_child_handler, + FindChildHandler(0x32dc00c9945dc0c8LL, + (DwarfTag) 0x2802d007, + ContainerEq(empty_attribute_list))) + .WillOnce(Return(mock_grandchild_handler)); + { + EXPECT_CALL(*mock_grandchild_handler, + ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL)) + .WillOnce(Return()); + + // At this point, we abandon the traversal, so none of the + // usual stuff should get called. + EXPECT_CALL(*mock_grandchild_handler, EndAttributes()) + .Times(0); + EXPECT_CALL(*mock_grandchild_handler, Finish()) + .Times(0); + } + + EXPECT_CALL(*mock_child_handler, Finish()) + .Times(0); + } + + EXPECT_CALL(mock_root_handler, Finish()) + .Times(0); + } + } + + // The dispatcher. + DIEDispatcher die_dispatcher(&mock_root_handler); + + // Report the CU header. + EXPECT_TRUE(die_dispatcher + .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, + 0x76d392ff393ddda2LL, 0xbf)); + // Report the root DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL, + (DwarfTag) 0x98980361, + empty_attribute_list)); + + // Child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL, + (DwarfTag) 0x898bf0d0, + empty_attribute_list)); + + // Grandchild DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL, + (DwarfTag) 0x2802d007, + empty_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL, + (DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL); + + // Stop the traversal abruptly, so that there will still be + // handlers on the stack when the dispatcher is destructed. + + // No EndDIE call... + } + // No EndDIE call... + } + // No EndDIE call... + } +} diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2enums.h b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2enums.h new file mode 100644 index 0000000..5565d66 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2enums.h @@ -0,0 +1,650 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_DWARF_DWARF2ENUMS_H__ +#define COMMON_DWARF_DWARF2ENUMS_H__ + +namespace dwarf2reader { + +// These enums do not follow the google3 style only because they are +// known universally (specs, other implementations) by the names in +// exactly this capitalization. +// Tag names and codes. +enum DwarfTag { + DW_TAG_padding = 0x00, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_param = 0x2f, + DW_TAG_template_value_param = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + // DWARF 3. + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + // SGI/MIPS Extensions. + DW_TAG_MIPS_loop = 0x4081, + // HP extensions. See: + // ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz + DW_TAG_HP_array_descriptor = 0x4090, + // GNU extensions. + DW_TAG_format_label = 0x4101, // For FORTRAN 77 and Fortran 90. + DW_TAG_function_template = 0x4102, // For C++. + DW_TAG_class_template = 0x4103, // For C++. + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105, + // Extensions for UPC. See: http://upc.gwu.edu/~upc. + DW_TAG_upc_shared_type = 0x8765, + DW_TAG_upc_strict_type = 0x8766, + DW_TAG_upc_relaxed_type = 0x8767, + // PGI (STMicroelectronics) extensions. No documentation available. + DW_TAG_PGI_kanji_type = 0xA000, + DW_TAG_PGI_interface_block = 0xA020 +}; + + +enum DwarfHasChild { + DW_children_no = 0, + DW_children_yes = 1 +}; + +// Form names and codes. +enum DwarfForm { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + + // Added in DWARF 4: + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20 +}; + +// Attribute names and codes +enum DwarfAttribute { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_subscr_data = 0x0a, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_element_list = 0x0f, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_member = 0x14, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_stride_size = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_items = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + // DWARF 3 values. + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + // SGI/MIPS extensions. + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + // HP extensions. + DW_AT_HP_block_index = 0x2000, + DW_AT_HP_unmodifiable = 0x2001, // Same as DW_AT_MIPS_fde. + DW_AT_HP_actuals_stmt_list = 0x2010, + DW_AT_HP_proc_per_section = 0x2011, + DW_AT_HP_raw_data_ptr = 0x2012, + DW_AT_HP_pass_by_reference = 0x2013, + DW_AT_HP_opt_level = 0x2014, + DW_AT_HP_prof_version_id = 0x2015, + DW_AT_HP_opt_flags = 0x2016, + DW_AT_HP_cold_region_low_pc = 0x2017, + DW_AT_HP_cold_region_high_pc = 0x2018, + DW_AT_HP_all_variables_modifiable = 0x2019, + DW_AT_HP_linkage_name = 0x201a, + DW_AT_HP_prof_flags = 0x201b, // In comp unit of procs_info for -g. + // GNU extensions. + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + // VMS extensions. + DW_AT_VMS_rtnbeg_pd_address = 0x2201, + // UPC extension. + DW_AT_upc_threads_scaled = 0x3210, + // PGI (STMicroelectronics) extensions. + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02 +}; + + +// Line number opcodes. +enum DwarfLineNumberOps { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + // DWARF 3. + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 +}; + +// Line number extended opcodes. +enum DwarfLineNumberExtendedOps { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3, + // HP extensions. + DW_LNE_HP_negate_is_UV_update = 0x11, + DW_LNE_HP_push_context = 0x12, + DW_LNE_HP_pop_context = 0x13, + DW_LNE_HP_set_file_line_column = 0x14, + DW_LNE_HP_set_routine_name = 0x15, + DW_LNE_HP_set_sequence = 0x16, + DW_LNE_HP_negate_post_semantics = 0x17, + DW_LNE_HP_negate_function_exit = 0x18, + DW_LNE_HP_negate_front_end_logical = 0x19, + DW_LNE_HP_define_proc = 0x20 +}; + +// Type encoding names and codes +enum DwarfEncoding { + DW_ATE_address =0x1, + DW_ATE_boolean =0x2, + DW_ATE_complex_float =0x3, + DW_ATE_float =0x4, + DW_ATE_signed =0x5, + DW_ATE_signed_char =0x6, + DW_ATE_unsigned =0x7, + DW_ATE_unsigned_char =0x8, + // DWARF3/DWARF3f + DW_ATE_imaginary_float =0x9, + DW_ATE_packed_decimal =0xa, + DW_ATE_numeric_string =0xb, + DW_ATE_edited =0xc, + DW_ATE_signed_fixed =0xd, + DW_ATE_unsigned_fixed =0xe, + DW_ATE_decimal_float =0xf, + DW_ATE_lo_user =0x80, + DW_ATE_hi_user =0xff +}; + +// Location virtual machine opcodes +enum DwarfOpcode { + DW_OP_addr =0x03, + DW_OP_deref =0x06, + DW_OP_const1u =0x08, + DW_OP_const1s =0x09, + DW_OP_const2u =0x0a, + DW_OP_const2s =0x0b, + DW_OP_const4u =0x0c, + DW_OP_const4s =0x0d, + DW_OP_const8u =0x0e, + DW_OP_const8s =0x0f, + DW_OP_constu =0x10, + DW_OP_consts =0x11, + DW_OP_dup =0x12, + DW_OP_drop =0x13, + DW_OP_over =0x14, + DW_OP_pick =0x15, + DW_OP_swap =0x16, + DW_OP_rot =0x17, + DW_OP_xderef =0x18, + DW_OP_abs =0x19, + DW_OP_and =0x1a, + DW_OP_div =0x1b, + DW_OP_minus =0x1c, + DW_OP_mod =0x1d, + DW_OP_mul =0x1e, + DW_OP_neg =0x1f, + DW_OP_not =0x20, + DW_OP_or =0x21, + DW_OP_plus =0x22, + DW_OP_plus_uconst =0x23, + DW_OP_shl =0x24, + DW_OP_shr =0x25, + DW_OP_shra =0x26, + DW_OP_xor =0x27, + DW_OP_bra =0x28, + DW_OP_eq =0x29, + DW_OP_ge =0x2a, + DW_OP_gt =0x2b, + DW_OP_le =0x2c, + DW_OP_lt =0x2d, + DW_OP_ne =0x2e, + DW_OP_skip =0x2f, + DW_OP_lit0 =0x30, + DW_OP_lit1 =0x31, + DW_OP_lit2 =0x32, + DW_OP_lit3 =0x33, + DW_OP_lit4 =0x34, + DW_OP_lit5 =0x35, + DW_OP_lit6 =0x36, + DW_OP_lit7 =0x37, + DW_OP_lit8 =0x38, + DW_OP_lit9 =0x39, + DW_OP_lit10 =0x3a, + DW_OP_lit11 =0x3b, + DW_OP_lit12 =0x3c, + DW_OP_lit13 =0x3d, + DW_OP_lit14 =0x3e, + DW_OP_lit15 =0x3f, + DW_OP_lit16 =0x40, + DW_OP_lit17 =0x41, + DW_OP_lit18 =0x42, + DW_OP_lit19 =0x43, + DW_OP_lit20 =0x44, + DW_OP_lit21 =0x45, + DW_OP_lit22 =0x46, + DW_OP_lit23 =0x47, + DW_OP_lit24 =0x48, + DW_OP_lit25 =0x49, + DW_OP_lit26 =0x4a, + DW_OP_lit27 =0x4b, + DW_OP_lit28 =0x4c, + DW_OP_lit29 =0x4d, + DW_OP_lit30 =0x4e, + DW_OP_lit31 =0x4f, + DW_OP_reg0 =0x50, + DW_OP_reg1 =0x51, + DW_OP_reg2 =0x52, + DW_OP_reg3 =0x53, + DW_OP_reg4 =0x54, + DW_OP_reg5 =0x55, + DW_OP_reg6 =0x56, + DW_OP_reg7 =0x57, + DW_OP_reg8 =0x58, + DW_OP_reg9 =0x59, + DW_OP_reg10 =0x5a, + DW_OP_reg11 =0x5b, + DW_OP_reg12 =0x5c, + DW_OP_reg13 =0x5d, + DW_OP_reg14 =0x5e, + DW_OP_reg15 =0x5f, + DW_OP_reg16 =0x60, + DW_OP_reg17 =0x61, + DW_OP_reg18 =0x62, + DW_OP_reg19 =0x63, + DW_OP_reg20 =0x64, + DW_OP_reg21 =0x65, + DW_OP_reg22 =0x66, + DW_OP_reg23 =0x67, + DW_OP_reg24 =0x68, + DW_OP_reg25 =0x69, + DW_OP_reg26 =0x6a, + DW_OP_reg27 =0x6b, + DW_OP_reg28 =0x6c, + DW_OP_reg29 =0x6d, + DW_OP_reg30 =0x6e, + DW_OP_reg31 =0x6f, + DW_OP_breg0 =0x70, + DW_OP_breg1 =0x71, + DW_OP_breg2 =0x72, + DW_OP_breg3 =0x73, + DW_OP_breg4 =0x74, + DW_OP_breg5 =0x75, + DW_OP_breg6 =0x76, + DW_OP_breg7 =0x77, + DW_OP_breg8 =0x78, + DW_OP_breg9 =0x79, + DW_OP_breg10 =0x7a, + DW_OP_breg11 =0x7b, + DW_OP_breg12 =0x7c, + DW_OP_breg13 =0x7d, + DW_OP_breg14 =0x7e, + DW_OP_breg15 =0x7f, + DW_OP_breg16 =0x80, + DW_OP_breg17 =0x81, + DW_OP_breg18 =0x82, + DW_OP_breg19 =0x83, + DW_OP_breg20 =0x84, + DW_OP_breg21 =0x85, + DW_OP_breg22 =0x86, + DW_OP_breg23 =0x87, + DW_OP_breg24 =0x88, + DW_OP_breg25 =0x89, + DW_OP_breg26 =0x8a, + DW_OP_breg27 =0x8b, + DW_OP_breg28 =0x8c, + DW_OP_breg29 =0x8d, + DW_OP_breg30 =0x8e, + DW_OP_breg31 =0x8f, + DW_OP_regX =0x90, + DW_OP_fbreg =0x91, + DW_OP_bregX =0x92, + DW_OP_piece =0x93, + DW_OP_deref_size =0x94, + DW_OP_xderef_size =0x95, + DW_OP_nop =0x96, + // DWARF3/DWARF3f + DW_OP_push_object_address =0x97, + DW_OP_call2 =0x98, + DW_OP_call4 =0x99, + DW_OP_call_ref =0x9a, + DW_OP_form_tls_address =0x9b, + DW_OP_call_frame_cfa =0x9c, + DW_OP_bit_piece =0x9d, + DW_OP_lo_user =0xe0, + DW_OP_hi_user =0xff, + // GNU extensions + DW_OP_GNU_push_tls_address =0xe0 +}; + +// Source languages. These are values for DW_AT_language. +enum DwarfLanguage + { + DW_LANG_none =0x0000, + DW_LANG_C89 =0x0001, + DW_LANG_C =0x0002, + DW_LANG_Ada83 =0x0003, + DW_LANG_C_plus_plus =0x0004, + DW_LANG_Cobol74 =0x0005, + DW_LANG_Cobol85 =0x0006, + DW_LANG_Fortran77 =0x0007, + DW_LANG_Fortran90 =0x0008, + DW_LANG_Pascal83 =0x0009, + DW_LANG_Modula2 =0x000a, + DW_LANG_Java =0x000b, + DW_LANG_C99 =0x000c, + DW_LANG_Ada95 =0x000d, + DW_LANG_Fortran95 =0x000e, + DW_LANG_PLI =0x000f, + DW_LANG_ObjC =0x0010, + DW_LANG_ObjC_plus_plus =0x0011, + DW_LANG_UPC =0x0012, + DW_LANG_D =0x0013, + // Implementation-defined language code range. + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, + + // Extensions. + + // MIPS assembly language. The GNU toolchain uses this for all + // assembly languages, since there's no generic DW_LANG_ value for that. + // See include/dwarf2.h in the binutils, gdb, or gcc source trees. + DW_LANG_Mips_Assembler =0x8001, + DW_LANG_Upc =0x8765 // Unified Parallel C + }; + +// Inline codes. These are values for DW_AT_inline. +enum DwarfInline { + DW_INL_not_inlined =0x0, + DW_INL_inlined =0x1, + DW_INL_declared_not_inlined =0x2, + DW_INL_declared_inlined =0x3 +}; + +// Call Frame Info instructions. +enum DwarfCFI + { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + // Opcodes in this range are reserved for user extensions. + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + // SGI/MIPS specific. + DW_CFA_MIPS_advance_loc8 = 0x1d, + + // GNU extensions. + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f + }; + +// Exception handling 'z' augmentation letters. +enum DwarfZAugmentationCodes { + // If the CFI augmentation string begins with 'z', then the CIE and FDE + // have an augmentation data area just before the instructions, whose + // contents are determined by the subsequent augmentation letters. + DW_Z_augmentation_start = 'z', + + // If this letter is present in a 'z' augmentation string, the CIE + // augmentation data includes a pointer encoding, and the FDE + // augmentation data includes a language-specific data area pointer, + // represented using that encoding. + DW_Z_has_LSDA = 'L', + + // If this letter is present in a 'z' augmentation string, the CIE + // augmentation data includes a pointer encoding, followed by a pointer + // to a personality routine, represented using that encoding. + DW_Z_has_personality_routine = 'P', + + // If this letter is present in a 'z' augmentation string, the CIE + // augmentation data includes a pointer encoding describing how the FDE's + // initial location, address range, and DW_CFA_set_loc operands are + // encoded. + DW_Z_has_FDE_address_encoding = 'R', + + // If this letter is present in a 'z' augmentation string, then code + // addresses covered by FDEs that cite this CIE are signal delivery + // trampolines. Return addresses of frames in trampolines should not be + // adjusted as described in section 6.4.4 of the DWARF 3 spec. + DW_Z_is_signal_trampoline = 'S' +}; + +// Exception handling frame description pointer formats, as described +// by the Linux Standard Base Core Specification 4.0, section 11.5, +// DWARF Extensions. +enum DwarfPointerEncoding + { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_omit = 0xff, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + + // The GNU toolchain sources define this enum value as well, + // simply to help classify the lower nybble values into signed and + // unsigned groups. + DW_EH_PE_signed = 0x08, + + // This is not documented in LSB 4.0, but it is used in both the + // Linux and OS X toolchains. It can be added to any other + // encoding (except DW_EH_PE_aligned), and indicates that the + // encoded value represents the address at which the true address + // is stored, not the true address itself. + DW_EH_PE_indirect = 0x80 + }; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_DWARF2ENUMS_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.cc b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.cc new file mode 100644 index 0000000..4356646 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.cc @@ -0,0 +1,2338 @@ +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// CFI reader author: Jim Blandy + +// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit, +// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details. + +#include "common/dwarf/dwarf2reader.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/line_state_machine.h" + +namespace dwarf2reader { + +CompilationUnit::CompilationUnit(const SectionMap& sections, uint64 offset, + ByteReader* reader, Dwarf2Handler* handler) + : offset_from_section_start_(offset), reader_(reader), + sections_(sections), handler_(handler), abbrevs_(NULL), + string_buffer_(NULL), string_buffer_length_(0) {} + +// Read a DWARF2/3 abbreviation section. +// Each abbrev consists of a abbreviation number, a tag, a byte +// specifying whether the tag has children, and a list of +// attribute/form pairs. +// The list of forms is terminated by a 0 for the attribute, and a +// zero for the form. The entire abbreviation section is terminated +// by a zero for the code. + +void CompilationUnit::ReadAbbrevs() { + if (abbrevs_) + return; + + // First get the debug_abbrev section. ".debug_abbrev" is the name + // recommended in the DWARF spec, and used on Linux; + // "__debug_abbrev" is the name used in Mac OS X Mach-O files. + SectionMap::const_iterator iter = sections_.find(".debug_abbrev"); + if (iter == sections_.end()) + iter = sections_.find("__debug_abbrev"); + assert(iter != sections_.end()); + + abbrevs_ = new std::vector; + abbrevs_->resize(1); + + // The only way to check whether we are reading over the end of the + // buffer would be to first compute the size of the leb128 data by + // reading it, then go back and read it again. + const char* abbrev_start = iter->second.first + + header_.abbrev_offset; + const char* abbrevptr = abbrev_start; +#ifndef NDEBUG + const uint64 abbrev_length = iter->second.second - header_.abbrev_offset; +#endif + + while (1) { + CompilationUnit::Abbrev abbrev; + size_t len; + const uint64 number = reader_->ReadUnsignedLEB128(abbrevptr, &len); + + if (number == 0) + break; + abbrev.number = number; + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint64 tag = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + abbrev.tag = static_cast(tag); + + assert(abbrevptr < abbrev_start + abbrev_length); + abbrev.has_children = reader_->ReadOneByte(abbrevptr); + abbrevptr += 1; + + assert(abbrevptr < abbrev_start + abbrev_length); + + while (1) { + const uint64 nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint64 formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + if (nametemp == 0 && formtemp == 0) + break; + + const enum DwarfAttribute name = + static_cast(nametemp); + const enum DwarfForm form = static_cast(formtemp); + abbrev.attributes.push_back(std::make_pair(name, form)); + } + assert(abbrev.number == abbrevs_->size()); + abbrevs_->push_back(abbrev); + } +} + +// Skips a single DIE's attributes. +const char* CompilationUnit::SkipDIE(const char* start, + const Abbrev& abbrev) { + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = SkipAttribute(start, i->second); + } + return start; +} + +// Skips a single attribute form's data. +const char* CompilationUnit::SkipAttribute(const char* start, + enum DwarfForm form) { + size_t len; + + switch (form) { + case DW_FORM_indirect: + form = static_cast(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return SkipAttribute(start, form); + + case DW_FORM_flag_present: + return start; + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + return start + 1; + case DW_FORM_ref2: + case DW_FORM_data2: + return start + 2; + case DW_FORM_ref4: + case DW_FORM_data4: + return start + 4; + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + return start + 8; + case DW_FORM_string: + return start + strlen(start) + 1; + case DW_FORM_udata: + case DW_FORM_ref_udata: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + + case DW_FORM_sdata: + reader_->ReadSignedLEB128(start, &len); + return start + len; + case DW_FORM_addr: + return start + reader_->AddressSize(); + case DW_FORM_ref_addr: + // DWARF2 and 3 differ on whether ref_addr is address size or + // offset size. + assert(header_.version == 2 || header_.version == 3); + if (header_.version == 2) { + return start + reader_->AddressSize(); + } else if (header_.version == 3) { + return start + reader_->OffsetSize(); + } + + case DW_FORM_block1: + return start + 1 + reader_->ReadOneByte(start); + case DW_FORM_block2: + return start + 2 + reader_->ReadTwoBytes(start); + case DW_FORM_block4: + return start + 4 + reader_->ReadFourBytes(start); + case DW_FORM_block: + case DW_FORM_exprloc: { + uint64 size = reader_->ReadUnsignedLEB128(start, &len); + return start + size + len; + } + case DW_FORM_strp: + case DW_FORM_sec_offset: + return start + reader_->OffsetSize(); + } + fprintf(stderr,"Unhandled form type"); + return NULL; +} + +// Read a DWARF2/3 header. +// The header is variable length in DWARF3 (and DWARF2 as extended by +// most compilers), and consists of an length field, a version number, +// the offset in the .debug_abbrev section for our abbrevs, and an +// address size. +void CompilationUnit::ReadHeader() { + const char* headerptr = buffer_; + size_t initial_length_size; + + assert(headerptr + 4 < buffer_ + buffer_length_); + const uint64 initial_length + = reader_->ReadInitialLength(headerptr, &initial_length_size); + headerptr += initial_length_size; + header_.length = initial_length; + + assert(headerptr + 2 < buffer_ + buffer_length_); + header_.version = reader_->ReadTwoBytes(headerptr); + headerptr += 2; + + assert(headerptr + reader_->OffsetSize() < buffer_ + buffer_length_); + header_.abbrev_offset = reader_->ReadOffset(headerptr); + headerptr += reader_->OffsetSize(); + + assert(headerptr + 1 < buffer_ + buffer_length_); + header_.address_size = reader_->ReadOneByte(headerptr); + reader_->SetAddressSize(header_.address_size); + headerptr += 1; + + after_header_ = headerptr; + + // This check ensures that we don't have to do checking during the + // reading of DIEs. header_.length does not include the size of the + // initial length. + assert(buffer_ + initial_length_size + header_.length <= + buffer_ + buffer_length_); +} + +uint64 CompilationUnit::Start() { + // First get the debug_info section. ".debug_info" is the name + // recommended in the DWARF spec, and used on Linux; "__debug_info" + // is the name used in Mac OS X Mach-O files. + SectionMap::const_iterator iter = sections_.find(".debug_info"); + if (iter == sections_.end()) + iter = sections_.find("__debug_info"); + assert(iter != sections_.end()); + + // Set up our buffer + buffer_ = iter->second.first + offset_from_section_start_; + buffer_length_ = iter->second.second - offset_from_section_start_; + + // Read the header + ReadHeader(); + + // Figure out the real length from the end of the initial length to + // the end of the compilation unit, since that is the value we + // return. + uint64 ourlength = header_.length; + if (reader_->OffsetSize() == 8) + ourlength += 12; + else + ourlength += 4; + + // See if the user wants this compilation unit, and if not, just return. + if (!handler_->StartCompilationUnit(offset_from_section_start_, + reader_->AddressSize(), + reader_->OffsetSize(), + header_.length, + header_.version)) + return ourlength; + + // Otherwise, continue by reading our abbreviation entries. + ReadAbbrevs(); + + // Set the string section if we have one. ".debug_str" is the name + // recommended in the DWARF spec, and used on Linux; "__debug_str" + // is the name used in Mac OS X Mach-O files. + iter = sections_.find(".debug_str"); + if (iter == sections_.end()) + iter = sections_.find("__debug_str"); + if (iter != sections_.end()) { + string_buffer_ = iter->second.first; + string_buffer_length_ = iter->second.second; + } + + // Now that we have our abbreviations, start processing DIE's. + ProcessDIEs(); + + return ourlength; +} + +// If one really wanted, you could merge SkipAttribute and +// ProcessAttribute +// This is all boring data manipulation and calling of the handler. +const char* CompilationUnit::ProcessAttribute( + uint64 dieoffset, const char* start, enum DwarfAttribute attr, + enum DwarfForm form) { + size_t len; + + switch (form) { + // DW_FORM_indirect is never used because it is such a space + // waster. + case DW_FORM_indirect: + form = static_cast(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return ProcessAttribute(dieoffset, start, attr, form); + + case DW_FORM_flag_present: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1); + return start; + case DW_FORM_data1: + case DW_FORM_flag: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOneByte(start)); + return start + 1; + case DW_FORM_data2: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadTwoBytes(start)); + return start + 2; + case DW_FORM_data4: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadFourBytes(start)); + return start + 4; + case DW_FORM_data8: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; + case DW_FORM_string: { + const char* str = start; + handler_->ProcessAttributeString(dieoffset, attr, form, + str); + return start + strlen(str) + 1; + } + case DW_FORM_udata: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, + &len)); + return start + len; + + case DW_FORM_sdata: + handler_->ProcessAttributeSigned(dieoffset, attr, form, + reader_->ReadSignedLEB128(start, &len)); + return start + len; + case DW_FORM_addr: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadAddress(start)); + return start + reader_->AddressSize(); + case DW_FORM_sec_offset: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); + + case DW_FORM_ref1: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadOneByte(start) + + offset_from_section_start_); + return start + 1; + case DW_FORM_ref2: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadTwoBytes(start) + + offset_from_section_start_); + return start + 2; + case DW_FORM_ref4: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadFourBytes(start) + + offset_from_section_start_); + return start + 4; + case DW_FORM_ref8: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadEightBytes(start) + + offset_from_section_start_); + return start + 8; + case DW_FORM_ref_udata: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, + &len) + + offset_from_section_start_); + return start + len; + case DW_FORM_ref_addr: + // DWARF2 and 3 differ on whether ref_addr is address size or + // offset size. + assert(header_.version == 2 || header_.version == 3); + if (header_.version == 2) { + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadAddress(start)); + return start + reader_->AddressSize(); + } else if (header_.version == 3) { + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); + } + break; + case DW_FORM_ref_sig8: + handler_->ProcessAttributeSignature(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; + + case DW_FORM_block1: { + uint64 datalen = reader_->ReadOneByte(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, + datalen); + return start + 1 + datalen; + } + case DW_FORM_block2: { + uint64 datalen = reader_->ReadTwoBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, + datalen); + return start + 2 + datalen; + } + case DW_FORM_block4: { + uint64 datalen = reader_->ReadFourBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4, + datalen); + return start + 4 + datalen; + } + case DW_FORM_block: + case DW_FORM_exprloc: { + uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, + datalen); + return start + datalen + len; + } + case DW_FORM_strp: { + assert(string_buffer_ != NULL); + + const uint64 offset = reader_->ReadOffset(start); + assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_); + + const char* str = string_buffer_ + offset; + handler_->ProcessAttributeString(dieoffset, attr, form, + str); + return start + reader_->OffsetSize(); + } + } + fprintf(stderr, "Unhandled form type\n"); + return NULL; +} + +const char* CompilationUnit::ProcessDIE(uint64 dieoffset, + const char* start, + const Abbrev& abbrev) { + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = ProcessAttribute(dieoffset, start, i->first, i->second); + } + return start; +} + +void CompilationUnit::ProcessDIEs() { + const char* dieptr = after_header_; + size_t len; + + // lengthstart is the place the length field is based on. + // It is the point in the header after the initial length field + const char* lengthstart = buffer_; + + // In 64 bit dwarf, the initial length is 12 bytes, because of the + // 0xffffffff at the start. + if (reader_->OffsetSize() == 8) + lengthstart += 12; + else + lengthstart += 4; + + std::stack die_stack; + + while (dieptr < (lengthstart + header_.length)) { + // We give the user the absolute offset from the beginning of + // debug_info, since they need it to deal with ref_addr forms. + uint64 absolute_offset = (dieptr - buffer_) + offset_from_section_start_; + + uint64 abbrev_num = reader_->ReadUnsignedLEB128(dieptr, &len); + + dieptr += len; + + // Abbrev == 0 represents the end of a list of children, or padding + // at the end of the compilation unit. + if (abbrev_num == 0) { + if (die_stack.size() == 0) + // If it is padding, then we are done with the compilation unit's DIEs. + return; + const uint64 offset = die_stack.top(); + die_stack.pop(); + handler_->EndDIE(offset); + continue; + } + + const Abbrev& abbrev = abbrevs_->at(static_cast(abbrev_num)); + const enum DwarfTag tag = abbrev.tag; + if (!handler_->StartDIE(absolute_offset, tag, abbrev.attributes)) { + dieptr = SkipDIE(dieptr, abbrev); + } else { + dieptr = ProcessDIE(absolute_offset, dieptr, abbrev); + } + + if (abbrev.has_children) { + die_stack.push(absolute_offset); + } else { + handler_->EndDIE(absolute_offset); + } + } +} + +LineInfo::LineInfo(const char* buffer, uint64 buffer_length, + ByteReader* reader, LineInfoHandler* handler): + handler_(handler), reader_(reader), buffer_(buffer), + buffer_length_(buffer_length) { + header_.std_opcode_lengths = NULL; +} + +uint64 LineInfo::Start() { + ReadHeader(); + ReadLines(); + return after_header_ - buffer_; +} + +// The header for a debug_line section is mildly complicated, because +// the line info is very tightly encoded. +void LineInfo::ReadHeader() { + const char* lineptr = buffer_; + size_t initial_length_size; + + const uint64 initial_length + = reader_->ReadInitialLength(lineptr, &initial_length_size); + + lineptr += initial_length_size; + header_.total_length = initial_length; + assert(buffer_ + initial_length_size + header_.total_length <= + buffer_ + buffer_length_); + + // Address size *must* be set by CU ahead of time. + assert(reader_->AddressSize() != 0); + + header_.version = reader_->ReadTwoBytes(lineptr); + lineptr += 2; + + header_.prologue_length = reader_->ReadOffset(lineptr); + lineptr += reader_->OffsetSize(); + + header_.min_insn_length = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.default_is_stmt = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.line_base = *reinterpret_cast(lineptr); + lineptr += 1; + + header_.line_range = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.opcode_base = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.std_opcode_lengths = new std::vector; + header_.std_opcode_lengths->resize(header_.opcode_base + 1); + (*header_.std_opcode_lengths)[0] = 0; + for (int i = 1; i < header_.opcode_base; i++) { + (*header_.std_opcode_lengths)[i] = reader_->ReadOneByte(lineptr); + lineptr += 1; + } + + // It is legal for the directory entry table to be empty. + if (*lineptr) { + uint32 dirindex = 1; + while (*lineptr) { + const char* dirname = lineptr; + handler_->DefineDir(dirname, dirindex); + lineptr += strlen(dirname) + 1; + dirindex++; + } + } + lineptr++; + + // It is also legal for the file entry table to be empty. + if (*lineptr) { + uint32 fileindex = 1; + size_t len; + while (*lineptr) { + const char* filename = lineptr; + lineptr += strlen(filename) + 1; + + uint64 dirindex = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64 mod_time = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64 filelength = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + handler_->DefineFile(filename, fileindex, static_cast(dirindex), + mod_time, filelength); + fileindex++; + } + } + lineptr++; + + after_header_ = lineptr; +} + +/* static */ +bool LineInfo::ProcessOneOpcode(ByteReader* reader, + LineInfoHandler* handler, + const struct LineInfoHeader &header, + const char* start, + struct LineStateMachine* lsm, + size_t* len, + uintptr pc, + bool *lsm_passes_pc) { + size_t oplen = 0; + size_t templen; + uint8 opcode = reader->ReadOneByte(start); + oplen++; + start++; + + // If the opcode is great than the opcode_base, it is a special + // opcode. Most line programs consist mainly of special opcodes. + if (opcode >= header.opcode_base) { + opcode -= header.opcode_base; + const int64 advance_address = (opcode / header.line_range) + * header.min_insn_length; + const int32 advance_line = (opcode % header.line_range) + + header.line_base; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + lsm->line_num += advance_line; + lsm->basic_block = true; + *len = oplen; + return true; + } + + // Otherwise, we have the regular opcodes + switch (opcode) { + case DW_LNS_copy: { + lsm->basic_block = false; + *len = oplen; + return true; + } + + case DW_LNS_advance_pc: { + uint64 advance_address = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && lsm->address <= pc && + pc < lsm->address + header.min_insn_length * advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += header.min_insn_length * advance_address; + } + break; + case DW_LNS_advance_line: { + const int64 advance_line = reader->ReadSignedLEB128(start, &templen); + oplen += templen; + lsm->line_num += static_cast(advance_line); + + // With gcc 4.2.1, we can get the line_no here for the first time + // since DW_LNS_advance_line is called after DW_LNE_set_address is + // called. So we check if the lsm passes "pc" here, not in + // DW_LNE_set_address. + if (lsm_passes_pc && lsm->address == pc) { + *lsm_passes_pc = true; + } + } + break; + case DW_LNS_set_file: { + const uint64 fileno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->file_num = static_cast(fileno); + } + break; + case DW_LNS_set_column: { + const uint64 colno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->column_num = static_cast(colno); + } + break; + case DW_LNS_negate_stmt: { + lsm->is_stmt = !lsm->is_stmt; + } + break; + case DW_LNS_set_basic_block: { + lsm->basic_block = true; + } + break; + case DW_LNS_fixed_advance_pc: { + const uint16 advance_address = reader->ReadTwoBytes(start); + oplen += 2; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + } + break; + case DW_LNS_const_add_pc: { + const int64 advance_address = header.min_insn_length + * ((255 - header.opcode_base) + / header.line_range); + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + } + break; + case DW_LNS_extended_op: { + const uint64 extended_op_len = reader->ReadUnsignedLEB128(start, + &templen); + start += templen; + oplen += templen + extended_op_len; + + const uint64 extended_op = reader->ReadOneByte(start); + start++; + + switch (extended_op) { + case DW_LNE_end_sequence: { + lsm->end_sequence = true; + *len = oplen; + return true; + } + break; + case DW_LNE_set_address: { + // With gcc 4.2.1, we cannot tell the line_no here since + // DW_LNE_set_address is called before DW_LNS_advance_line is + // called. So we do not check if the lsm passes "pc" here. See + // also the comment in DW_LNS_advance_line. + uint64 address = reader->ReadAddress(start); + lsm->address = address; + } + break; + case DW_LNE_define_file: { + const char* filename = start; + + templen = strlen(filename) + 1; + start += templen; + + uint64 dirindex = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + const uint64 mod_time = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + const uint64 filelength = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + if (handler) { + handler->DefineFile(filename, -1, static_cast(dirindex), + mod_time, filelength); + } + } + break; + } + } + break; + + default: { + // Ignore unknown opcode silently + if (header.std_opcode_lengths) { + for (int i = 0; i < (*header.std_opcode_lengths)[opcode]; i++) { + reader->ReadUnsignedLEB128(start, &templen); + start += templen; + oplen += templen; + } + } + } + break; + } + *len = oplen; + return false; +} + +void LineInfo::ReadLines() { + struct LineStateMachine lsm; + + // lengthstart is the place the length field is based on. + // It is the point in the header after the initial length field + const char* lengthstart = buffer_; + + // In 64 bit dwarf, the initial length is 12 bytes, because of the + // 0xffffffff at the start. + if (reader_->OffsetSize() == 8) + lengthstart += 12; + else + lengthstart += 4; + + const char* lineptr = after_header_; + lsm.Reset(header_.default_is_stmt); + + // The LineInfoHandler interface expects each line's length along + // with its address, but DWARF only provides addresses (sans + // length), and an end-of-sequence address; one infers the length + // from the next address. So we report a line only when we get the + // next line's address, or the end-of-sequence address. + bool have_pending_line = false; + uint64 pending_address = 0; + uint32 pending_file_num = 0, pending_line_num = 0, pending_column_num = 0; + + while (lineptr < lengthstart + header_.total_length) { + size_t oplength; + bool add_row = ProcessOneOpcode(reader_, handler_, header_, + lineptr, &lsm, &oplength, (uintptr)-1, + NULL); + if (add_row) { + if (have_pending_line) + handler_->AddLine(pending_address, lsm.address - pending_address, + pending_file_num, pending_line_num, + pending_column_num); + if (lsm.end_sequence) { + lsm.Reset(header_.default_is_stmt); + have_pending_line = false; + } else { + pending_address = lsm.address; + pending_file_num = lsm.file_num; + pending_line_num = lsm.line_num; + pending_column_num = lsm.column_num; + have_pending_line = true; + } + } + lineptr += oplength; + } + + after_header_ = lengthstart + header_.total_length; +} + +// A DWARF rule for recovering the address or value of a register, or +// computing the canonical frame address. There is one subclass of this for +// each '*Rule' member function in CallFrameInfo::Handler. +// +// It's annoying that we have to handle Rules using pointers (because +// the concrete instances can have an arbitrary size). They're small, +// so it would be much nicer if we could just handle them by value +// instead of fretting about ownership and destruction. +// +// It seems like all these could simply be instances of std::tr1::bind, +// except that we need instances to be EqualityComparable, too. +// +// This could logically be nested within State, but then the qualified names +// get horrendous. +class CallFrameInfo::Rule { + public: + virtual ~Rule() { } + + // Tell HANDLER that, at ADDRESS in the program, REGISTER can be + // recovered using this rule. If REGISTER is kCFARegister, then this rule + // describes how to compute the canonical frame address. Return what the + // HANDLER member function returned. + virtual bool Handle(Handler *handler, + uint64 address, int register) const = 0; + + // Equality on rules. We use these to decide which rules we need + // to report after a DW_CFA_restore_state instruction. + virtual bool operator==(const Rule &rhs) const = 0; + + bool operator!=(const Rule &rhs) const { return ! (*this == rhs); } + + // Return a pointer to a copy of this rule. + virtual Rule *Copy() const = 0; + + // If this is a base+offset rule, change its base register to REG. + // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.) + virtual void SetBaseRegister(unsigned reg) { } + + // If this is a base+offset rule, change its offset to OFFSET. Otherwise, + // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.) + virtual void SetOffset(long long offset) { } +}; + +// Rule: the value the register had in the caller cannot be recovered. +class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule { + public: + UndefinedRule() { } + ~UndefinedRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->UndefinedRule(address, reg); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const UndefinedRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs != NULL); + } + Rule *Copy() const { return new UndefinedRule(*this); } +}; + +// Rule: the register's value is the same as that it had in the caller. +class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule { + public: + SameValueRule() { } + ~SameValueRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->SameValueRule(address, reg); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const SameValueRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs != NULL); + } + Rule *Copy() const { return new SameValueRule(*this); } +}; + +// Rule: the register is saved at OFFSET from BASE_REGISTER. BASE_REGISTER +// may be CallFrameInfo::Handler::kCFARegister. +class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule { + public: + OffsetRule(int base_register, long offset) + : base_register_(base_register), offset_(offset) { } + ~OffsetRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->OffsetRule(address, reg, base_register_, offset_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const OffsetRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && + base_register_ == our_rhs->base_register_ && + offset_ == our_rhs->offset_); + } + Rule *Copy() const { return new OffsetRule(*this); } + // We don't actually need SetBaseRegister or SetOffset here, since they + // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it + // doesn't make sense to use OffsetRule for computing the CFA: it + // computes the address at which a register is saved, not a value. + private: + int base_register_; + long offset_; +}; + +// Rule: the value the register had in the caller is the value of +// BASE_REGISTER plus offset. BASE_REGISTER may be +// CallFrameInfo::Handler::kCFARegister. +class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule { + public: + ValOffsetRule(int base_register, long offset) + : base_register_(base_register), offset_(offset) { } + ~ValOffsetRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ValOffsetRule(address, reg, base_register_, offset_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const ValOffsetRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && + base_register_ == our_rhs->base_register_ && + offset_ == our_rhs->offset_); + } + Rule *Copy() const { return new ValOffsetRule(*this); } + void SetBaseRegister(unsigned reg) { base_register_ = reg; } + void SetOffset(long long offset) { offset_ = offset; } + private: + int base_register_; + long offset_; +}; + +// Rule: the register has been saved in another register REGISTER_NUMBER_. +class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule { + public: + explicit RegisterRule(int register_number) + : register_number_(register_number) { } + ~RegisterRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->RegisterRule(address, reg, register_number_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const RegisterRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && register_number_ == our_rhs->register_number_); + } + Rule *Copy() const { return new RegisterRule(*this); } + private: + int register_number_; +}; + +// Rule: EXPRESSION evaluates to the address at which the register is saved. +class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule { + public: + explicit ExpressionRule(const std::string &expression) + : expression_(expression) { } + ~ExpressionRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ExpressionRule(address, reg, expression_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const ExpressionRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && expression_ == our_rhs->expression_); + } + Rule *Copy() const { return new ExpressionRule(*this); } + private: + std::string expression_; +}; + +// Rule: EXPRESSION evaluates to the address at which the register is saved. +class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule { + public: + explicit ValExpressionRule(const std::string &expression) + : expression_(expression) { } + ~ValExpressionRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ValExpressionRule(address, reg, expression_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is allowed by the Google C++ Style Guide, if the use has + // been carefully considered; cheap RTTI-like workarounds are forbidden. + const ValExpressionRule *our_rhs = + dynamic_cast(&rhs); + return (our_rhs && expression_ == our_rhs->expression_); + } + Rule *Copy() const { return new ValExpressionRule(*this); } + private: + std::string expression_; +}; + +// A map from register numbers to rules. +class CallFrameInfo::RuleMap { + public: + RuleMap() : cfa_rule_(NULL) { } + RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; } + ~RuleMap() { Clear(); } + + RuleMap &operator=(const RuleMap &rhs); + + // Set the rule for computing the CFA to RULE. Take ownership of RULE. + void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; } + + // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains + // ownership of the rule. We use this for DW_CFA_def_cfa_offset and + // DW_CFA_def_cfa_register, and for detecting references to the CFA before + // a rule for it has been established. + Rule *CFARule() const { return cfa_rule_; } + + // Return the rule for REG, or NULL if there is none. The caller takes + // ownership of the result. + Rule *RegisterRule(int reg) const; + + // Set the rule for computing REG to RULE. Take ownership of RULE. + void SetRegisterRule(int reg, Rule *rule); + + // Make all the appropriate calls to HANDLER as if we were changing from + // this RuleMap to NEW_RULES at ADDRESS. We use this to implement + // DW_CFA_restore_state, where lots of rules can change simultaneously. + // Return true if all handlers returned true; otherwise, return false. + bool HandleTransitionTo(Handler *handler, uint64 address, + const RuleMap &new_rules) const; + + private: + // A map from register numbers to Rules. + typedef std::map RuleByNumber; + + // Remove all register rules and clear cfa_rule_. + void Clear(); + + // The rule for computing the canonical frame address. This RuleMap owns + // this rule. + Rule *cfa_rule_; + + // A map from register numbers to postfix expressions to recover + // their values. This RuleMap owns the Rules the map refers to. + RuleByNumber registers_; +}; + +CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) { + Clear(); + // Since each map owns the rules it refers to, assignment must copy them. + if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy(); + for (RuleByNumber::const_iterator it = rhs.registers_.begin(); + it != rhs.registers_.end(); it++) + registers_[it->first] = it->second->Copy(); + return *this; +} + +CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const { + assert(reg != Handler::kCFARegister); + RuleByNumber::const_iterator it = registers_.find(reg); + if (it != registers_.end()) + return it->second->Copy(); + else + return NULL; +} + +void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) { + assert(reg != Handler::kCFARegister); + assert(rule); + Rule **slot = ®isters_[reg]; + delete *slot; + *slot = rule; +} + +bool CallFrameInfo::RuleMap::HandleTransitionTo( + Handler *handler, + uint64 address, + const RuleMap &new_rules) const { + // Transition from cfa_rule_ to new_rules.cfa_rule_. + if (cfa_rule_ && new_rules.cfa_rule_) { + if (*cfa_rule_ != *new_rules.cfa_rule_ && + !new_rules.cfa_rule_->Handle(handler, address, + Handler::kCFARegister)) + return false; + } else if (cfa_rule_) { + // this RuleMap has a CFA rule but new_rules doesn't. + // CallFrameInfo::Handler has no way to handle this --- and shouldn't; + // it's garbage input. The instruction interpreter should have + // detected this and warned, so take no action here. + } else if (new_rules.cfa_rule_) { + // This shouldn't be possible: NEW_RULES is some prior state, and + // there's no way to remove entries. + assert(0); + } else { + // Both CFA rules are empty. No action needed. + } + + // Traverse the two maps in order by register number, and report + // whatever differences we find. + RuleByNumber::const_iterator old_it = registers_.begin(); + RuleByNumber::const_iterator new_it = new_rules.registers_.begin(); + while (old_it != registers_.end() && new_it != new_rules.registers_.end()) { + if (old_it->first < new_it->first) { + // This RuleMap has an entry for old_it->first, but NEW_RULES + // doesn't. + // + // This isn't really the right thing to do, but since CFI generally + // only mentions callee-saves registers, and GCC's convention for + // callee-saves registers is that they are unchanged, it's a good + // approximation. + if (!handler->SameValueRule(address, old_it->first)) + return false; + old_it++; + } else if (old_it->first > new_it->first) { + // NEW_RULES has entry for new_it->first, but this RuleMap + // doesn't. This shouldn't be possible: NEW_RULES is some prior + // state, and there's no way to remove entries. + assert(0); + } else { + // Both maps have an entry for this register. Report the new + // rule if it is different. + if (*old_it->second != *new_it->second && + !new_it->second->Handle(handler, address, new_it->first)) + return false; + new_it++, old_it++; + } + } + // Finish off entries from this RuleMap with no counterparts in new_rules. + while (old_it != registers_.end()) { + if (!handler->SameValueRule(address, old_it->first)) + return false; + old_it++; + } + // Since we only make transitions from a rule set to some previously + // saved rule set, and we can only add rules to the map, NEW_RULES + // must have fewer rules than *this. + assert(new_it == new_rules.registers_.end()); + + return true; +} + +// Remove all register rules and clear cfa_rule_. +void CallFrameInfo::RuleMap::Clear() { + delete cfa_rule_; + cfa_rule_ = NULL; + for (RuleByNumber::iterator it = registers_.begin(); + it != registers_.end(); it++) + delete it->second; + registers_.clear(); +} + +// The state of the call frame information interpreter as it processes +// instructions from a CIE and FDE. +class CallFrameInfo::State { + public: + // Create a call frame information interpreter state with the given + // reporter, reader, handler, and initial call frame info address. + State(ByteReader *reader, Handler *handler, Reporter *reporter, + uint64 address) + : reader_(reader), handler_(handler), reporter_(reporter), + address_(address), entry_(NULL), cursor_(NULL) { } + + // Interpret instructions from CIE, save the resulting rule set for + // DW_CFA_restore instructions, and return true. On error, report + // the problem to reporter_ and return false. + bool InterpretCIE(const CIE &cie); + + // Interpret instructions from FDE, and return true. On error, + // report the problem to reporter_ and return false. + bool InterpretFDE(const FDE &fde); + + private: + // The operands of a CFI instruction, for ParseOperands. + struct Operands { + unsigned register_number; // A register number. + uint64 offset; // An offset or address. + long signed_offset; // A signed offset. + std::string expression; // A DWARF expression. + }; + + // Parse CFI instruction operands from STATE's instruction stream as + // described by FORMAT. On success, populate OPERANDS with the + // results, and return true. On failure, report the problem and + // return false. + // + // Each character of FORMAT should be one of the following: + // + // 'r' unsigned LEB128 register number (OPERANDS->register_number) + // 'o' unsigned LEB128 offset (OPERANDS->offset) + // 's' signed LEB128 offset (OPERANDS->signed_offset) + // 'a' machine-size address (OPERANDS->offset) + // (If the CIE has a 'z' augmentation string, 'a' uses the + // encoding specified by the 'R' argument.) + // '1' a one-byte offset (OPERANDS->offset) + // '2' a two-byte offset (OPERANDS->offset) + // '4' a four-byte offset (OPERANDS->offset) + // '8' an eight-byte offset (OPERANDS->offset) + // 'e' a DW_FORM_block holding a (OPERANDS->expression) + // DWARF expression + bool ParseOperands(const char *format, Operands *operands); + + // Interpret one CFI instruction from STATE's instruction stream, update + // STATE, report any rule changes to handler_, and return true. On + // failure, report the problem and return false. + bool DoInstruction(); + + // The following Do* member functions are subroutines of DoInstruction, + // factoring out the actual work of operations that have several + // different encodings. + + // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and + // return true. On failure, report and return false. (Used for + // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.) + bool DoDefCFA(unsigned base_register, long offset); + + // Change the offset of the CFA rule to OFFSET, and return true. On + // failure, report and return false. (Subroutine for + // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.) + bool DoDefCFAOffset(long offset); + + // Specify that REG can be recovered using RULE, and return true. On + // failure, report and return false. + bool DoRule(unsigned reg, Rule *rule); + + // Specify that REG can be found at OFFSET from the CFA, and return true. + // On failure, report and return false. (Subroutine for DW_CFA_offset, + // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.) + bool DoOffset(unsigned reg, long offset); + + // Specify that the caller's value for REG is the CFA plus OFFSET, + // and return true. On failure, report and return false. (Subroutine + // for DW_CFA_val_offset and DW_CFA_val_offset_sf.) + bool DoValOffset(unsigned reg, long offset); + + // Restore REG to the rule established in the CIE, and return true. On + // failure, report and return false. (Subroutine for DW_CFA_restore and + // DW_CFA_restore_extended.) + bool DoRestore(unsigned reg); + + // Return the section offset of the instruction at cursor. For use + // in error messages. + uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); } + + // Report that entry_ is incomplete, and return false. For brevity. + bool ReportIncomplete() { + reporter_->Incomplete(entry_->offset, entry_->kind); + return false; + } + + // For reading multi-byte values with the appropriate endianness. + ByteReader *reader_; + + // The handler to which we should report the data we find. + Handler *handler_; + + // For reporting problems in the info we're parsing. + Reporter *reporter_; + + // The code address to which the next instruction in the stream applies. + uint64 address_; + + // The entry whose instructions we are currently processing. This is + // first a CIE, and then an FDE. + const Entry *entry_; + + // The next instruction to process. + const char *cursor_; + + // The current set of rules. + RuleMap rules_; + + // The set of rules established by the CIE, used by DW_CFA_restore + // and DW_CFA_restore_extended. We set this after interpreting the + // CIE's instructions. + RuleMap cie_rules_; + + // A stack of saved states, for DW_CFA_remember_state and + // DW_CFA_restore_state. + std::stack saved_rules_; +}; + +bool CallFrameInfo::State::InterpretCIE(const CIE &cie) { + entry_ = &cie; + cursor_ = entry_->instructions; + while (cursor_ < entry_->end) + if (!DoInstruction()) + return false; + // Note the rules established by the CIE, for use by DW_CFA_restore + // and DW_CFA_restore_extended. + cie_rules_ = rules_; + return true; +} + +bool CallFrameInfo::State::InterpretFDE(const FDE &fde) { + entry_ = &fde; + cursor_ = entry_->instructions; + while (cursor_ < entry_->end) + if (!DoInstruction()) + return false; + return true; +} + +bool CallFrameInfo::State::ParseOperands(const char *format, + Operands *operands) { + size_t len; + const char *operand; + + for (operand = format; *operand; operand++) { + size_t bytes_left = entry_->end - cursor_; + switch (*operand) { + case 'r': + operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 'o': + operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 's': + operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 'a': + operands->offset = + reader_->ReadEncodedPointer(cursor_, entry_->cie->pointer_encoding, + &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case '1': + if (1 > bytes_left) return ReportIncomplete(); + operands->offset = static_cast(*cursor_++); + break; + + case '2': + if (2 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadTwoBytes(cursor_); + cursor_ += 2; + break; + + case '4': + if (4 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadFourBytes(cursor_); + cursor_ += 4; + break; + + case '8': + if (8 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadEightBytes(cursor_); + cursor_ += 8; + break; + + case 'e': { + size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left || expression_length > bytes_left - len) + return ReportIncomplete(); + cursor_ += len; + operands->expression = std::string(cursor_, expression_length); + cursor_ += expression_length; + break; + } + + default: + assert(0); + } + } + + return true; +} + +bool CallFrameInfo::State::DoInstruction() { + CIE *cie = entry_->cie; + Operands ops; + + // Our entry's kind should have been set by now. + assert(entry_->kind != kUnknown); + + // We shouldn't have been invoked unless there were more + // instructions to parse. + assert(cursor_ < entry_->end); + + unsigned opcode = *cursor_++; + if ((opcode & 0xc0) != 0) { + switch (opcode & 0xc0) { + // Advance the address. + case DW_CFA_advance_loc: { + size_t code_offset = opcode & 0x3f; + address_ += code_offset * cie->code_alignment_factor; + break; + } + + // Find a register at an offset from the CFA. + case DW_CFA_offset: + if (!ParseOperands("o", &ops) || + !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor)) + return false; + break; + + // Restore the rule established for a register by the CIE. + case DW_CFA_restore: + if (!DoRestore(opcode & 0x3f)) return false; + break; + + // The 'if' above should have excluded this possibility. + default: + assert(0); + } + + // Return here, so the big switch below won't be indented. + return true; + } + + switch (opcode) { + // Set the address. + case DW_CFA_set_loc: + if (!ParseOperands("a", &ops)) return false; + address_ = ops.offset; + break; + + // Advance the address. + case DW_CFA_advance_loc1: + if (!ParseOperands("1", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_advance_loc2: + if (!ParseOperands("2", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_advance_loc4: + if (!ParseOperands("4", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_MIPS_advance_loc8: + if (!ParseOperands("8", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Compute the CFA by adding an offset to a register. + case DW_CFA_def_cfa: + if (!ParseOperands("ro", &ops) || + !DoDefCFA(ops.register_number, ops.offset)) + return false; + break; + + // Compute the CFA by adding an offset to a register. + case DW_CFA_def_cfa_sf: + if (!ParseOperands("rs", &ops) || + !DoDefCFA(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // Change the base register used to compute the CFA. + case DW_CFA_def_cfa_register: { + Rule *cfa_rule = rules_.CFARule(); + if (!cfa_rule) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + if (!ParseOperands("r", &ops)) return false; + cfa_rule->SetBaseRegister(ops.register_number); + if (!cfa_rule->Handle(handler_, address_, + Handler::kCFARegister)) + return false; + break; + } + + // Change the offset used to compute the CFA. + case DW_CFA_def_cfa_offset: + if (!ParseOperands("o", &ops) || + !DoDefCFAOffset(ops.offset)) + return false; + break; + + // Change the offset used to compute the CFA. + case DW_CFA_def_cfa_offset_sf: + if (!ParseOperands("s", &ops) || + !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // Specify an expression whose value is the CFA. + case DW_CFA_def_cfa_expression: { + if (!ParseOperands("e", &ops)) + return false; + Rule *rule = new ValExpressionRule(ops.expression); + rules_.SetCFARule(rule); + if (!rule->Handle(handler_, address_, + Handler::kCFARegister)) + return false; + break; + } + + // The register's value cannot be recovered. + case DW_CFA_undefined: { + if (!ParseOperands("r", &ops) || + !DoRule(ops.register_number, new UndefinedRule())) + return false; + break; + } + + // The register's value is unchanged from its value in the caller. + case DW_CFA_same_value: { + if (!ParseOperands("r", &ops) || + !DoRule(ops.register_number, new SameValueRule())) + return false; + break; + } + + // Find a register at an offset from the CFA. + case DW_CFA_offset_extended: + if (!ParseOperands("ro", &ops) || + !DoOffset(ops.register_number, + ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register is saved at an offset from the CFA. + case DW_CFA_offset_extended_sf: + if (!ParseOperands("rs", &ops) || + !DoOffset(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // The register is saved at an offset from the CFA. + case DW_CFA_GNU_negative_offset_extended: + if (!ParseOperands("ro", &ops) || + !DoOffset(ops.register_number, + -ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register's value is the sum of the CFA plus an offset. + case DW_CFA_val_offset: + if (!ParseOperands("ro", &ops) || + !DoValOffset(ops.register_number, + ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register's value is the sum of the CFA plus an offset. + case DW_CFA_val_offset_sf: + if (!ParseOperands("rs", &ops) || + !DoValOffset(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // The register has been saved in another register. + case DW_CFA_register: { + if (!ParseOperands("ro", &ops) || + !DoRule(ops.register_number, new RegisterRule(ops.offset))) + return false; + break; + } + + // An expression yields the address at which the register is saved. + case DW_CFA_expression: { + if (!ParseOperands("re", &ops) || + !DoRule(ops.register_number, new ExpressionRule(ops.expression))) + return false; + break; + } + + // An expression yields the caller's value for the register. + case DW_CFA_val_expression: { + if (!ParseOperands("re", &ops) || + !DoRule(ops.register_number, new ValExpressionRule(ops.expression))) + return false; + break; + } + + // Restore the rule established for a register by the CIE. + case DW_CFA_restore_extended: + if (!ParseOperands("r", &ops) || + !DoRestore( ops.register_number)) + return false; + break; + + // Save the current set of rules on a stack. + case DW_CFA_remember_state: + saved_rules_.push(rules_); + break; + + // Pop the current set of rules off the stack. + case DW_CFA_restore_state: { + if (saved_rules_.empty()) { + reporter_->EmptyStateStack(entry_->offset, entry_->kind, + CursorOffset()); + return false; + } + const RuleMap &new_rules = saved_rules_.top(); + if (rules_.CFARule() && !new_rules.CFARule()) { + reporter_->ClearingCFARule(entry_->offset, entry_->kind, + CursorOffset()); + return false; + } + rules_.HandleTransitionTo(handler_, address_, new_rules); + rules_ = new_rules; + saved_rules_.pop(); + break; + } + + // No operation. (Padding instruction.) + case DW_CFA_nop: + break; + + // A SPARC register window save: Registers 8 through 15 (%o0-%o7) + // are saved in registers 24 through 31 (%i0-%i7), and registers + // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets + // (0-15 * the register size). The register numbers must be + // hard-coded. A GNU extension, and not a pretty one. + case DW_CFA_GNU_window_save: { + // Save %o0-%o7 in %i0-%i7. + for (int i = 8; i < 16; i++) + if (!DoRule(i, new RegisterRule(i + 16))) + return false; + // Save %l0-%l7 and %i0-%i7 at the CFA. + for (int i = 16; i < 32; i++) + // Assume that the byte reader's address size is the same as + // the architecture's register size. !@#%*^ hilarious. + if (!DoRule(i, new OffsetRule(Handler::kCFARegister, + (i - 16) * reader_->AddressSize()))) + return false; + break; + } + + // I'm not sure what this is. GDB doesn't use it for unwinding. + case DW_CFA_GNU_args_size: + if (!ParseOperands("o", &ops)) return false; + break; + + // An opcode we don't recognize. + default: { + reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + } + + return true; +} + +bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) { + Rule *rule = new ValOffsetRule(base_register, offset); + rules_.SetCFARule(rule); + return rule->Handle(handler_, address_, + Handler::kCFARegister); +} + +bool CallFrameInfo::State::DoDefCFAOffset(long offset) { + Rule *cfa_rule = rules_.CFARule(); + if (!cfa_rule) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + cfa_rule->SetOffset(offset); + return cfa_rule->Handle(handler_, address_, + Handler::kCFARegister); +} + +bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) { + rules_.SetRegisterRule(reg, rule); + return rule->Handle(handler_, address_, reg); +} + +bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) { + if (!rules_.CFARule()) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + return DoRule(reg, + new OffsetRule(Handler::kCFARegister, offset)); +} + +bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) { + if (!rules_.CFARule()) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + return DoRule(reg, + new ValOffsetRule(Handler::kCFARegister, offset)); +} + +bool CallFrameInfo::State::DoRestore(unsigned reg) { + // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE. + if (entry_->kind == kCIE) { + reporter_->RestoreInCIE(entry_->offset, CursorOffset()); + return false; + } + Rule *rule = cie_rules_.RegisterRule(reg); + if (!rule) { + // This isn't really the right thing to do, but since CFI generally + // only mentions callee-saves registers, and GCC's convention for + // callee-saves registers is that they are unchanged, it's a good + // approximation. + rule = new SameValueRule(); + } + return DoRule(reg, rule); +} + +bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) { + const char *buffer_end = buffer_ + buffer_length_; + + // Initialize enough of ENTRY for use in error reporting. + entry->offset = cursor - buffer_; + entry->start = cursor; + entry->kind = kUnknown; + entry->end = NULL; + + // Read the initial length. This sets reader_'s offset size. + size_t length_size; + uint64 length = reader_->ReadInitialLength(cursor, &length_size); + if (length_size > size_t(buffer_end - cursor)) + return ReportIncomplete(entry); + cursor += length_size; + + // In a .eh_frame section, a length of zero marks the end of the series + // of entries. + if (length == 0 && eh_frame_) { + entry->kind = kTerminator; + entry->end = cursor; + return true; + } + + // Validate the length. + if (length > size_t(buffer_end - cursor)) + return ReportIncomplete(entry); + + // The length is the number of bytes after the initial length field; + // we have that position handy at this point, so compute the end + // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine, + // and the length didn't fit in a size_t, we would have rejected it + // above.) + entry->end = cursor + length; + + // Parse the next field: either the offset of a CIE or a CIE id. + size_t offset_size = reader_->OffsetSize(); + if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry); + entry->id = reader_->ReadOffset(cursor); + + // Don't advance cursor past id field yet; in .eh_frame data we need + // the id's position to compute the section offset of an FDE's CIE. + + // Now we can decide what kind of entry this is. + if (eh_frame_) { + // In .eh_frame data, an ID of zero marks the entry as a CIE, and + // anything else is an offset from the id field of the FDE to the start + // of the CIE. + if (entry->id == 0) { + entry->kind = kCIE; + } else { + entry->kind = kFDE; + // Turn the offset from the id into an offset from the buffer's start. + entry->id = (cursor - buffer_) - entry->id; + } + } else { + // In DWARF CFI data, an ID of ~0 (of the appropriate width, given the + // offset size for the entry) marks the entry as a CIE, and anything + // else is the offset of the CIE from the beginning of the section. + if (offset_size == 4) + entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE; + else { + assert(offset_size == 8); + entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE; + } + } + + // Now advance cursor past the id. + cursor += offset_size; + + // The fields specific to this kind of entry start here. + entry->fields = cursor; + + entry->cie = NULL; + + return true; +} + +bool CallFrameInfo::ReadCIEFields(CIE *cie) { + const char *cursor = cie->fields; + size_t len; + + assert(cie->kind == kCIE); + + // Prepare for early exit. + cie->version = 0; + cie->augmentation.clear(); + cie->code_alignment_factor = 0; + cie->data_alignment_factor = 0; + cie->return_address_register = 0; + cie->has_z_augmentation = false; + cie->pointer_encoding = DW_EH_PE_absptr; + cie->instructions = 0; + + // Parse the version number. + if (cie->end - cursor < 1) + return ReportIncomplete(cie); + cie->version = reader_->ReadOneByte(cursor); + cursor++; + + // If we don't recognize the version, we can't parse any more fields of the + // CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a + // version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well; + // the difference between those versions seems to be the same as for + // .debug_frame. + if (cie->version < 1 || cie->version > 3) { + reporter_->UnrecognizedVersion(cie->offset, cie->version); + return false; + } + + const char *augmentation_start = cursor; + const void *augmentation_end = + memchr(augmentation_start, '\0', cie->end - augmentation_start); + if (! augmentation_end) return ReportIncomplete(cie); + cursor = static_cast(augmentation_end); + cie->augmentation = std::string(augmentation_start, + cursor - augmentation_start); + // Skip the terminating '\0'. + cursor++; + + // Is this CFI augmented? + if (!cie->augmentation.empty()) { + // Is it an augmentation we recognize? + if (cie->augmentation[0] == DW_Z_augmentation_start) { + // Linux C++ ABI 'z' augmentation, used for exception handling data. + cie->has_z_augmentation = true; + } else { + // Not an augmentation we recognize. Augmentations can have arbitrary + // effects on the form of rest of the content, so we have to give up. + reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); + return false; + } + } + + // Parse the code alignment factor. + cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + + // Parse the data alignment factor. + cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + + // Parse the return address register. This is a ubyte in version 1, and + // a ULEB128 in version 3. + if (cie->version == 1) { + if (cursor >= cie->end) return ReportIncomplete(cie); + cie->return_address_register = uint8(*cursor++); + } else { + cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + } + + // If we have a 'z' augmentation string, find the augmentation data and + // use the augmentation string to parse it. + if (cie->has_z_augmentation) { + uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len + data_size) + return ReportIncomplete(cie); + cursor += len; + const char *data = cursor; + cursor += data_size; + const char *data_end = cursor; + + cie->has_z_lsda = false; + cie->has_z_personality = false; + cie->has_z_signal_frame = false; + + // Walk the augmentation string, and extract values from the + // augmentation data as the string directs. + for (size_t i = 1; i < cie->augmentation.size(); i++) { + switch (cie->augmentation[i]) { + case DW_Z_has_LSDA: + // The CIE's augmentation data holds the language-specific data + // area pointer's encoding, and the FDE's augmentation data holds + // the pointer itself. + cie->has_z_lsda = true; + // Fetch the LSDA encoding from the augmentation data. + if (data >= data_end) return ReportIncomplete(cie); + cie->lsda_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->lsda_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, cie->lsda_encoding); + return false; + } + // Don't check if the encoding is usable here --- we haven't + // read the FDE's fields yet, so we're not prepared for + // DW_EH_PE_funcrel, although that's a fine encoding for the + // LSDA to use, since it appears in the FDE. + break; + + case DW_Z_has_personality_routine: + // The CIE's augmentation data holds the personality routine + // pointer's encoding, followed by the pointer itself. + cie->has_z_personality = true; + // Fetch the personality routine pointer's encoding from the + // augmentation data. + if (data >= data_end) return ReportIncomplete(cie); + cie->personality_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->personality_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, + cie->personality_encoding); + return false; + } + if (!reader_->UsableEncoding(cie->personality_encoding)) { + reporter_->UnusablePointerEncoding(cie->offset, + cie->personality_encoding); + return false; + } + // Fetch the personality routine's pointer itself from the data. + cie->personality_address = + reader_->ReadEncodedPointer(data, cie->personality_encoding, + &len); + if (len > size_t(data_end - data)) + return ReportIncomplete(cie); + data += len; + break; + + case DW_Z_has_FDE_address_encoding: + // The CIE's augmentation data holds the pointer encoding to use + // for addresses in the FDE. + if (data >= data_end) return ReportIncomplete(cie); + cie->pointer_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->pointer_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, + cie->pointer_encoding); + return false; + } + if (!reader_->UsableEncoding(cie->pointer_encoding)) { + reporter_->UnusablePointerEncoding(cie->offset, + cie->pointer_encoding); + return false; + } + break; + + case DW_Z_is_signal_trampoline: + // Frames using this CIE are signal delivery frames. + cie->has_z_signal_frame = true; + break; + + default: + // An augmentation we don't recognize. + reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); + return false; + } + } + } + + // The CIE's instructions start here. + cie->instructions = cursor; + + return true; +} + +bool CallFrameInfo::ReadFDEFields(FDE *fde) { + const char *cursor = fde->fields; + size_t size; + + fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding, + &size); + if (size > size_t(fde->end - cursor)) + return ReportIncomplete(fde); + cursor += size; + reader_->SetFunctionBase(fde->address); + + // For the length, we strip off the upper nybble of the encoding used for + // the starting address. + DwarfPointerEncoding length_encoding = + DwarfPointerEncoding(fde->cie->pointer_encoding & 0x0f); + fde->size = reader_->ReadEncodedPointer(cursor, length_encoding, &size); + if (size > size_t(fde->end - cursor)) + return ReportIncomplete(fde); + cursor += size; + + // If the CIE has a 'z' augmentation string, then augmentation data + // appears here. + if (fde->cie->has_z_augmentation) { + uint64_t data_size = reader_->ReadUnsignedLEB128(cursor, &size); + if (size_t(fde->end - cursor) < size + data_size) + return ReportIncomplete(fde); + cursor += size; + + // In the abstract, we should walk the augmentation string, and extract + // items from the FDE's augmentation data as we encounter augmentation + // string characters that specify their presence: the ordering of items + // in the augmentation string determines the arrangement of values in + // the augmentation data. + // + // In practice, there's only ever one value in FDE augmentation data + // that we support --- the LSDA pointer --- and we have to bail if we + // see any unrecognized augmentation string characters. So if there is + // anything here at all, we know what it is, and where it starts. + if (fde->cie->has_z_lsda) { + // Check whether the LSDA's pointer encoding is usable now: only once + // we've parsed the FDE's starting address do we call reader_-> + // SetFunctionBase, so that the DW_EH_PE_funcrel encoding becomes + // usable. + if (!reader_->UsableEncoding(fde->cie->lsda_encoding)) { + reporter_->UnusablePointerEncoding(fde->cie->offset, + fde->cie->lsda_encoding); + return false; + } + + fde->lsda_address = + reader_->ReadEncodedPointer(cursor, fde->cie->lsda_encoding, &size); + if (size > data_size) + return ReportIncomplete(fde); + // Ideally, we would also complain here if there were unconsumed + // augmentation data. + } + + cursor += data_size; + } + + // The FDE's instructions start after those. + fde->instructions = cursor; + + return true; +} + +bool CallFrameInfo::Start() { + const char *buffer_end = buffer_ + buffer_length_; + const char *cursor; + bool all_ok = true; + const char *entry_end; + bool ok; + + // Traverse all the entries in buffer_, skipping CIEs and offering + // FDEs to the handler. + for (cursor = buffer_; cursor < buffer_end; + cursor = entry_end, all_ok = all_ok && ok) { + FDE fde; + + // Make it easy to skip this entry with 'continue': assume that + // things are not okay until we've checked all the data, and + // prepare the address of the next entry. + ok = false; + + // Read the entry's prologue. + if (!ReadEntryPrologue(cursor, &fde)) { + if (!fde.end) { + // If we couldn't even figure out this entry's extent, then we + // must stop processing entries altogether. + all_ok = false; + break; + } + entry_end = fde.end; + continue; + } + + // The next iteration picks up after this entry. + entry_end = fde.end; + + // Did we see an .eh_frame terminating mark? + if (fde.kind == kTerminator) { + // If there appears to be more data left in the section after the + // terminating mark, warn the user. But this is just a warning; + // we leave all_ok true. + if (fde.end < buffer_end) reporter_->EarlyEHTerminator(fde.offset); + break; + } + + // In this loop, we skip CIEs. We only parse them fully when we + // parse an FDE that refers to them. This limits our memory + // consumption (beyond the buffer itself) to that needed to + // process the largest single entry. + if (fde.kind != kFDE) { + ok = true; + continue; + } + + // Validate the CIE pointer. + if (fde.id > buffer_length_) { + reporter_->CIEPointerOutOfRange(fde.offset, fde.id); + continue; + } + + CIE cie; + + // Parse this FDE's CIE header. + if (!ReadEntryPrologue(buffer_ + fde.id, &cie)) + continue; + // This had better be an actual CIE. + if (cie.kind != kCIE) { + reporter_->BadCIEId(fde.offset, fde.id); + continue; + } + if (!ReadCIEFields(&cie)) + continue; + + // We now have the values that govern both the CIE and the FDE. + cie.cie = &cie; + fde.cie = &cie; + + // Parse the FDE's header. + if (!ReadFDEFields(&fde)) + continue; + + // Call Entry to ask the consumer if they're interested. + if (!handler_->Entry(fde.offset, fde.address, fde.size, + cie.version, cie.augmentation, + cie.return_address_register)) { + // The handler isn't interested in this entry. That's not an error. + ok = true; + continue; + } + + if (cie.has_z_augmentation) { + // Report the personality routine address, if we have one. + if (cie.has_z_personality) { + if (!handler_ + ->PersonalityRoutine(cie.personality_address, + IsIndirectEncoding(cie.personality_encoding))) + continue; + } + + // Report the language-specific data area address, if we have one. + if (cie.has_z_lsda) { + if (!handler_ + ->LanguageSpecificDataArea(fde.lsda_address, + IsIndirectEncoding(cie.lsda_encoding))) + continue; + } + + // If this is a signal-handling frame, report that. + if (cie.has_z_signal_frame) { + if (!handler_->SignalHandler()) + continue; + } + } + + // Interpret the CIE's instructions, and then the FDE's instructions. + State state(reader_, handler_, reporter_, fde.address); + ok = state.InterpretCIE(cie) && state.InterpretFDE(fde); + + // Tell the ByteReader that the function start address from the + // FDE header is no longer valid. + reader_->ClearFunctionBase(); + + // Report the end of the entry. + handler_->End(); + } + + return all_ok; +} + +const char *CallFrameInfo::KindName(EntryKind kind) { + if (kind == CallFrameInfo::kUnknown) + return "entry"; + else if (kind == CallFrameInfo::kCIE) + return "common information entry"; + else if (kind == CallFrameInfo::kFDE) + return "frame description entry"; + else { + assert (kind == CallFrameInfo::kTerminator); + return ".eh_frame sequence terminator"; + } +} + +bool CallFrameInfo::ReportIncomplete(Entry *entry) { + reporter_->Incomplete(entry->offset, entry->kind); + return false; +} + +void CallFrameInfo::Reporter::Incomplete(uint64 offset, + CallFrameInfo::EntryKind kind) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str()); +} + +void CallFrameInfo::Reporter::EarlyEHTerminator(uint64 offset) { + fprintf(stderr, + "%s: CFI at offset 0x%llx in '%s': saw end-of-data marker" + " before end of section contents\n", + filename_.c_str(), offset, section_.c_str()); +} + +void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset, + uint64 cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE pointer is out of range: 0x%llx\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE pointer does not point to a CIE: 0x%llx\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unrecognized version: %d\n", + filename_.c_str(), offset, section_.c_str(), version); +} + +void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset, + const std::string &aug) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unrecognized augmentation: '%s'\n", + filename_.c_str(), offset, section_.c_str(), aug.c_str()); +} + +void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64 offset, + uint8 encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " 'z' augmentation specifies invalid pointer encoding: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset, + uint8 encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " 'z' augmentation specifies a pointer encoding for which" + " we have no base address: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " the DW_CFA_restore instruction at offset 0x%llx" + " cannot be used in a common information entry\n", + filename_.c_str(), offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::BadInstruction(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the instruction at offset 0x%llx is unrecognized\n", + filename_.c_str(), CallFrameInfo::KindName(kind), + offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::NoCFARule(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the instruction at offset 0x%llx assumes that a CFA rule has" + " been set, but none has been set\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%llx" + " should pop a saved state from the stack, but the stack is empty\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%llx" + " would clear the CFA rule in effect\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +} // namespace dwarf2reader diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.h b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.h new file mode 100644 index 0000000..cd61fb5 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader.h @@ -0,0 +1,1050 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// CFI reader author: Jim Blandy + +// This file contains definitions related to the DWARF2/3 reader and +// it's handler interfaces. +// The DWARF2/3 specification can be found at +// http://dwarf.freestandards.org and should be considered required +// reading if you wish to modify the implementation. +// Only a cursory attempt is made to explain terminology that is +// used here, as it is much better explained in the standard documents +#ifndef COMMON_DWARF_DWARF2READER_H__ +#define COMMON_DWARF_DWARF2READER_H__ + +#include +#include +#include +#include +#include + +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/types.h" + +namespace dwarf2reader { +struct LineStateMachine; +class Dwarf2Handler; +class LineInfoHandler; + +// This maps from a string naming a section to a pair containing a +// the data for the section, and the size of the section. +typedef std::map > SectionMap; +typedef std::list > + AttributeList; +typedef AttributeList::iterator AttributeIterator; +typedef AttributeList::const_iterator ConstAttributeIterator; + +struct LineInfoHeader { + uint64 total_length; + uint16 version; + uint64 prologue_length; + uint8 min_insn_length; // insn stands for instructin + bool default_is_stmt; // stmt stands for statement + int8 line_base; + uint8 line_range; + uint8 opcode_base; + // Use a pointer so that signalsafe_addr2line is able to use this structure + // without heap allocation problem. + std::vector *std_opcode_lengths; +}; + +class LineInfo { + public: + + // Initializes a .debug_line reader. Buffer and buffer length point + // to the beginning and length of the line information to read. + // Reader is a ByteReader class that has the endianness set + // properly. + LineInfo(const char* buffer_, uint64 buffer_length, + ByteReader* reader, LineInfoHandler* handler); + + virtual ~LineInfo() { + if (header_.std_opcode_lengths) { + delete header_.std_opcode_lengths; + } + } + + // Start processing line info, and calling callbacks in the handler. + // Consumes the line number information for a single compilation unit. + // Returns the number of bytes processed. + uint64 Start(); + + // Process a single line info opcode at START using the state + // machine at LSM. Return true if we should define a line using the + // current state of the line state machine. Place the length of the + // opcode in LEN. + // If LSM_PASSES_PC is non-NULL, this function also checks if the lsm + // passes the address of PC. In other words, LSM_PASSES_PC will be + // set to true, if the following condition is met. + // + // lsm's old address < PC <= lsm's new address + static bool ProcessOneOpcode(ByteReader* reader, + LineInfoHandler* handler, + const struct LineInfoHeader &header, + const char* start, + struct LineStateMachine* lsm, + size_t* len, + uintptr pc, + bool *lsm_passes_pc); + + private: + // Reads the DWARF2/3 header for this line info. + void ReadHeader(); + + // Reads the DWARF2/3 line information + void ReadLines(); + + // The associated handler to call processing functions in + LineInfoHandler* handler_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // A DWARF2/3 line info header. This is not the same size as + // in the actual file, as the one in the file may have a 32 bit or + // 64 bit lengths + + struct LineInfoHeader header_; + + // buffer is the buffer for our line info, starting at exactly where + // the line info to read is. after_header is the place right after + // the end of the line information header. + const char* buffer_; + uint64 buffer_length_; + const char* after_header_; +}; + +// This class is the main interface between the line info reader and +// the client. The virtual functions inside this get called for +// interesting events that happen during line info reading. The +// default implementation does nothing + +class LineInfoHandler { + public: + LineInfoHandler() { } + + virtual ~LineInfoHandler() { } + + // Called when we define a directory. NAME is the directory name, + // DIR_NUM is the directory number + virtual void DefineDir(const std::string& name, uint32 dir_num) { } + + // Called when we define a filename. NAME is the filename, FILE_NUM + // is the file number which is -1 if the file index is the next + // index after the last numbered index (this happens when files are + // dynamically defined by the line program), DIR_NUM is the + // directory index for the directory name of this file, MOD_TIME is + // the modification time of the file, and LENGTH is the length of + // the file + virtual void DefineFile(const std::string& name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length) { } + + // Called when the line info reader has a new line, address pair + // ready for us. ADDRESS is the address of the code, LENGTH is the + // length of its machine code in bytes, FILE_NUM is the file number + // containing the code, LINE_NUM is the line number in that file for + // the code, and COLUMN_NUM is the column number the code starts at, + // if we know it (0 otherwise). + virtual void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num) { } +}; + +// The base of DWARF2/3 debug info is a DIE (Debugging Information +// Entry. +// DWARF groups DIE's into a tree and calls the root of this tree a +// "compilation unit". Most of the time, there is one compilation +// unit in the .debug_info section for each file that had debug info +// generated. +// Each DIE consists of + +// 1. a tag specifying a thing that is being described (ie +// DW_TAG_subprogram for functions, DW_TAG_variable for variables, etc +// 2. attributes (such as DW_AT_location for location in memory, +// DW_AT_name for name), and data for each attribute. +// 3. A flag saying whether the DIE has children or not + +// In order to gain some amount of compression, the format of +// each DIE (tag name, attributes and data forms for the attributes) +// are stored in a separate table called the "abbreviation table". +// This is done because a large number of DIEs have the exact same tag +// and list of attributes, but different data for those attributes. +// As a result, the .debug_info section is just a stream of data, and +// requires reading of the .debug_abbrev section to say what the data +// means. + +// As a warning to the user, it should be noted that the reason for +// using absolute offsets from the beginning of .debug_info is that +// DWARF2/3 supports referencing DIE's from other DIE's by their offset +// from either the current compilation unit start, *or* the beginning +// of the .debug_info section. This means it is possible to reference +// a DIE in one compilation unit from a DIE in another compilation +// unit. This style of reference is usually used to eliminate +// duplicated information that occurs across compilation +// units, such as base types, etc. GCC 3.4+ support this with +// -feliminate-dwarf2-dups. Other toolchains will sometimes do +// duplicate elimination in the linker. + +class CompilationUnit { + public: + + // Initialize a compilation unit. This requires a map of sections, + // the offset of this compilation unit in the .debug_info section, a + // ByteReader, and a Dwarf2Handler class to call callbacks in. + CompilationUnit(const SectionMap& sections, uint64 offset, + ByteReader* reader, Dwarf2Handler* handler); + virtual ~CompilationUnit() { + if (abbrevs_) delete abbrevs_; + } + + // Begin reading a Dwarf2 compilation unit, and calling the + // callbacks in the Dwarf2Handler + + // Return the full length of the compilation unit, including + // headers. This plus the starting offset passed to the constructor + // is the offset of the end of the compilation unit --- and the + // start of the next compilation unit, if there is one. + uint64 Start(); + + private: + + // This struct represents a single DWARF2/3 abbreviation + // The abbreviation tells how to read a DWARF2/3 DIE, and consist of a + // tag and a list of attributes, as well as the data form of each attribute. + struct Abbrev { + uint64 number; + enum DwarfTag tag; + bool has_children; + AttributeList attributes; + }; + + // A DWARF2/3 compilation unit header. This is not the same size as + // in the actual file, as the one in the file may have a 32 bit or + // 64 bit length. + struct CompilationUnitHeader { + uint64 length; + uint16 version; + uint64 abbrev_offset; + uint8 address_size; + } header_; + + // Reads the DWARF2/3 header for this compilation unit. + void ReadHeader(); + + // Reads the DWARF2/3 abbreviations for this compilation unit + void ReadAbbrevs(); + + // Processes a single DIE for this compilation unit and return a new + // pointer just past the end of it + const char* ProcessDIE(uint64 dieoffset, + const char* start, + const Abbrev& abbrev); + + // Processes a single attribute and return a new pointer just past the + // end of it + const char* ProcessAttribute(uint64 dieoffset, + const char* start, + enum DwarfAttribute attr, + enum DwarfForm form); + + // Processes all DIEs for this compilation unit + void ProcessDIEs(); + + // Skips the die with attributes specified in ABBREV starting at + // START, and return the new place to position the stream to. + const char* SkipDIE(const char* start, + const Abbrev& abbrev); + + // Skips the attribute starting at START, with FORM, and return the + // new place to position the stream to. + const char* SkipAttribute(const char* start, + enum DwarfForm form); + + // Offset from section start is the offset of this compilation unit + // from the beginning of the .debug_info section. + uint64 offset_from_section_start_; + + // buffer is the buffer for our CU, starting at .debug_info + offset + // passed in from constructor. + // after_header points to right after the compilation unit header. + const char* buffer_; + uint64 buffer_length_; + const char* after_header_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // The map of sections in our file to buffers containing their data + const SectionMap& sections_; + + // The associated handler to call processing functions in + Dwarf2Handler* handler_; + + // Set of DWARF2/3 abbreviations for this compilation unit. Indexed + // by abbreviation number, which means that abbrevs_[0] is not + // valid. + std::vector* abbrevs_; + + // String section buffer and length, if we have a string section. + // This is here to avoid doing a section lookup for strings in + // ProcessAttribute, which is in the hot path for DWARF2 reading. + const char* string_buffer_; + uint64 string_buffer_length_; +}; + +// This class is the main interface between the reader and the +// client. The virtual functions inside this get called for +// interesting events that happen during DWARF2 reading. +// The default implementation skips everything. + +class Dwarf2Handler { + public: + Dwarf2Handler() { } + + virtual ~Dwarf2Handler() { } + + // Start to process a compilation unit at OFFSET from the beginning of the + // .debug_info section. Return false if you would like to skip this + // compilation unit. + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { return false; } + + // Start to process a DIE at OFFSET from the beginning of the .debug_info + // section. Return false if you would like to skip this DIE. + virtual bool StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { return false; } + + // Called when we have an attribute with unsigned data to give to our + // handler. The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + + // Called when we have an attribute with signed data to give to our handler. + // The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { } + + // Called when we have an attribute whose value is a reference to + // another DIE. The attribute belongs to the DIE at OFFSET from the + // beginning of the .debug_info section. Its name is ATTR, its form + // is FORM, and the offset of the DIE being referred to from the + // beginning of the .debug_info section is DATA. + virtual void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + + // Called when we have an attribute with a buffer of data to give to our + // handler. The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, DATA points to + // the buffer's contents, and its length in bytes is LENGTH. The buffer is + // owned by the caller, not the callee, and may not persist for very long. + // If you want the data to be available later, it needs to be copied. + virtual void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len) { } + + // Called when we have an attribute with string data to give to our handler. + // The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data) { } + + // Called when we have an attribute whose value is the 64-bit signature + // of a type unit in the .debug_types section. OFFSET is the offset of + // the DIE whose attribute we're reporting. ATTR and FORM are the + // attribute's name and form. SIGNATURE is the type unit's signature. + virtual void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { } + + // Called when finished processing the DIE at OFFSET. + // Because DWARF2/3 specifies a tree of DIEs, you may get starts + // before ends of the previous DIE, as we process children before + // ending the parent. + virtual void EndDIE(uint64 offset) { } + +}; + +// This class is a reader for DWARF's Call Frame Information. CFI +// describes how to unwind stack frames --- even for functions that do +// not follow fixed conventions for saving registers, whose frame size +// varies as they execute, etc. +// +// CFI describes, at each machine instruction, how to compute the +// stack frame's base address, how to find the return address, and +// where to find the saved values of the caller's registers (if the +// callee has stashed them somewhere to free up the registers for its +// own use). +// +// For example, suppose we have a function whose machine code looks +// like this (imagine an assembly language that looks like C, for a +// machine with 32-bit registers, and a stack that grows towards lower +// addresses): +// +// func: ; entry point; return address at sp +// func+0: sp = sp - 16 ; allocate space for stack frame +// func+1: sp[12] = r0 ; save r0 at sp+12 +// ... ; other code, not frame-related +// func+10: sp -= 4; *sp = x ; push some x on the stack +// ... ; other code, not frame-related +// func+20: r0 = sp[16] ; restore saved r0 +// func+21: sp += 20 ; pop whole stack frame +// func+22: pc = *sp; sp += 4 ; pop return address and jump to it +// +// DWARF CFI is (a very compressed representation of) a table with a +// row for each machine instruction address and a column for each +// register showing how to restore it, if possible. +// +// A special column named "CFA", for "Canonical Frame Address", tells how +// to compute the base address of the frame; registers' entries may +// refer to the CFA in describing where the registers are saved. +// +// Another special column, named "RA", represents the return address. +// +// For example, here is a complete (uncompressed) table describing the +// function above: +// +// insn cfa r0 r1 ... ra +// ======================================= +// func+0: sp cfa[0] +// func+1: sp+16 cfa[0] +// func+2: sp+16 cfa[-4] cfa[0] +// func+11: sp+20 cfa[-4] cfa[0] +// func+21: sp+20 cfa[0] +// func+22: sp cfa[0] +// +// Some things to note here: +// +// - Each row describes the state of affairs *before* executing the +// instruction at the given address. Thus, the row for func+0 +// describes the state before we allocate the stack frame. In the +// next row, the formula for computing the CFA has changed, +// reflecting that allocation. +// +// - The other entries are written in terms of the CFA; this allows +// them to remain unchanged as the stack pointer gets bumped around. +// For example, the rule for recovering the return address (the "ra" +// column) remains unchanged throughout the function, even as the +// stack pointer takes on three different offsets from the return +// address. +// +// - Although we haven't shown it, most calling conventions designate +// "callee-saves" and "caller-saves" registers. The callee must +// preserve the values of callee-saves registers; if it uses them, +// it must save their original values somewhere, and restore them +// before it returns. In contrast, the callee is free to trash +// caller-saves registers; if the callee uses these, it will +// probably not bother to save them anywhere, and the CFI will +// probably mark their values as "unrecoverable". +// +// (However, since the caller cannot assume the callee was going to +// save them, caller-saves registers are probably dead in the caller +// anyway, so compilers usually don't generate CFA for caller-saves +// registers.) +// +// - Exactly where the CFA points is a matter of convention that +// depends on the architecture and ABI in use. In the example, the +// CFA is the value the stack pointer had upon entry to the +// function, pointing at the saved return address. But on the x86, +// the call frame information generated by GCC follows the +// convention that the CFA is the address *after* the saved return +// address. +// +// But by definition, the CFA remains constant throughout the +// lifetime of the frame. This makes it a useful value for other +// columns to refer to. It is also gives debuggers a useful handle +// for identifying a frame. +// +// If you look at the table above, you'll notice that a given entry is +// often the same as the one immediately above it: most instructions +// change only one or two aspects of the stack frame, if they affect +// it at all. The DWARF format takes advantage of this fact, and +// reduces the size of the data by mentioning only the addresses and +// columns at which changes take place. So for the above, DWARF CFI +// data would only actually mention the following: +// +// insn cfa r0 r1 ... ra +// ======================================= +// func+0: sp cfa[0] +// func+1: sp+16 +// func+2: cfa[-4] +// func+11: sp+20 +// func+21: r0 +// func+22: sp +// +// In fact, this is the way the parser reports CFI to the consumer: as +// a series of statements of the form, "At address X, column Y changed +// to Z," and related conventions for describing the initial state. +// +// Naturally, it would be impractical to have to scan the entire +// program's CFI, noting changes as we go, just to recover the +// unwinding rules in effect at one particular instruction. To avoid +// this, CFI data is grouped into "entries", each of which covers a +// specified range of addresses and begins with a complete statement +// of the rules for all recoverable registers at that starting +// address. Each entry typically covers a single function. +// +// Thus, to compute the contents of a given row of the table --- that +// is, rules for recovering the CFA, RA, and registers at a given +// instruction --- the consumer should find the entry that covers that +// instruction's address, start with the initial state supplied at the +// beginning of the entry, and work forward until it has processed all +// the changes up to and including those for the present instruction. +// +// There are seven kinds of rules that can appear in an entry of the +// table: +// +// - "undefined": The given register is not preserved by the callee; +// its value cannot be recovered. +// +// - "same value": This register has the same value it did in the callee. +// +// - offset(N): The register is saved at offset N from the CFA. +// +// - val_offset(N): The value the register had in the caller is the +// CFA plus offset N. (This is usually only useful for describing +// the stack pointer.) +// +// - register(R): The register's value was saved in another register R. +// +// - expression(E): Evaluating the DWARF expression E using the +// current frame's registers' values yields the address at which the +// register was saved. +// +// - val_expression(E): Evaluating the DWARF expression E using the +// current frame's registers' values yields the value the register +// had in the caller. + +class CallFrameInfo { + public: + // The different kinds of entries one finds in CFI. Used internally, + // and for error reporting. + enum EntryKind { kUnknown, kCIE, kFDE, kTerminator }; + + // The handler class to which the parser hands the parsed call frame + // information. Defined below. + class Handler; + + // A reporter class, which CallFrameInfo uses to report errors + // encountered while parsing call frame information. Defined below. + class Reporter; + + // Create a DWARF CFI parser. BUFFER points to the contents of the + // .debug_frame section to parse; BUFFER_LENGTH is its length in bytes. + // REPORTER is an error reporter the parser should use to report + // problems. READER is a ByteReader instance that has the endianness and + // address size set properly. Report the data we find to HANDLER. + // + // This class can also parse Linux C++ exception handling data, as found + // in '.eh_frame' sections. This data is a variant of DWARF CFI that is + // placed in loadable segments so that it is present in the program's + // address space, and is interpreted by the C++ runtime to search the + // call stack for a handler interested in the exception being thrown, + // actually pop the frames, and find cleanup code to run. + // + // There are two differences between the call frame information described + // in the DWARF standard and the exception handling data Linux places in + // the .eh_frame section: + // + // - Exception handling data uses uses a different format for call frame + // information entry headers. The distinguished CIE id, the way FDEs + // refer to their CIEs, and the way the end of the series of entries is + // determined are all slightly different. + // + // If the constructor's EH_FRAME argument is true, then the + // CallFrameInfo parses the entry headers as Linux C++ exception + // handling data. If EH_FRAME is false or omitted, the CallFrameInfo + // parses standard DWARF call frame information. + // + // - Linux C++ exception handling data uses CIE augmentation strings + // beginning with 'z' to specify the presence of additional data after + // the CIE and FDE headers and special encodings used for addresses in + // frame description entries. + // + // CallFrameInfo can handle 'z' augmentations in either DWARF CFI or + // exception handling data if you have supplied READER with the base + // addresses needed to interpret the pointer encodings that 'z' + // augmentations can specify. See the ByteReader interface for details + // about the base addresses. See the CallFrameInfo::Handler interface + // for details about the additional information one might find in + // 'z'-augmented data. + // + // Thus: + // + // - If you are parsing standard DWARF CFI, as found in a .debug_frame + // section, you should pass false for the EH_FRAME argument, or omit + // it, and you need not worry about providing READER with the + // additional base addresses. + // + // - If you want to parse Linux C++ exception handling data from a + // .eh_frame section, you should pass EH_FRAME as true, and call + // READER's Set*Base member functions before calling our Start method. + // + // - If you want to parse DWARF CFI that uses the 'z' augmentations + // (although I don't think any toolchain ever emits such data), you + // could pass false for EH_FRAME, but call READER's Set*Base members. + // + // The extensions the Linux C++ ABI makes to DWARF for exception + // handling are described here, rather poorly: + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html + // + // The mechanics of C++ exception handling, personality routines, + // and language-specific data areas are described here, rather nicely: + // http://www.codesourcery.com/public/cxx-abi/abi-eh.html + CallFrameInfo(const char *buffer, size_t buffer_length, + ByteReader *reader, Handler *handler, Reporter *reporter, + bool eh_frame = false) + : buffer_(buffer), buffer_length_(buffer_length), + reader_(reader), handler_(handler), reporter_(reporter), + eh_frame_(eh_frame) { } + + ~CallFrameInfo() { } + + // Parse the entries in BUFFER, reporting what we find to HANDLER. + // Return true if we reach the end of the section successfully, or + // false if we encounter an error. + bool Start(); + + // Return the textual name of KIND. For error reporting. + static const char *KindName(EntryKind kind); + + private: + + struct CIE; + + // A CFI entry, either an FDE or a CIE. + struct Entry { + // The starting offset of the entry in the section, for error + // reporting. + size_t offset; + + // The start of this entry in the buffer. + const char *start; + + // Which kind of entry this is. + // + // We want to be able to use this for error reporting even while we're + // in the midst of parsing. Error reporting code may assume that kind, + // offset, and start fields are valid, although kind may be kUnknown. + EntryKind kind; + + // The end of this entry's common prologue (initial length and id), and + // the start of this entry's kind-specific fields. + const char *fields; + + // The start of this entry's instructions. + const char *instructions; + + // The address past the entry's last byte in the buffer. (Note that + // since offset points to the entry's initial length field, and the + // length field is the number of bytes after that field, this is not + // simply buffer_ + offset + length.) + const char *end; + + // For both DWARF CFI and .eh_frame sections, this is the CIE id in a + // CIE, and the offset of the associated CIE in an FDE. + uint64 id; + + // The CIE that applies to this entry, if we've parsed it. If this is a + // CIE, then this field points to this structure. + CIE *cie; + }; + + // A common information entry (CIE). + struct CIE: public Entry { + uint8 version; // CFI data version number + std::string augmentation; // vendor format extension markers + uint64 code_alignment_factor; // scale for code address adjustments + int data_alignment_factor; // scale for stack pointer adjustments + unsigned return_address_register; // which register holds the return addr + + // True if this CIE includes Linux C++ ABI 'z' augmentation data. + bool has_z_augmentation; + + // Parsed 'z' augmentation data. These are meaningful only if + // has_z_augmentation is true. + bool has_z_lsda; // The 'z' augmentation included 'L'. + bool has_z_personality; // The 'z' augmentation included 'P'. + bool has_z_signal_frame; // The 'z' augmentation included 'S'. + + // If has_z_lsda is true, this is the encoding to be used for language- + // specific data area pointers in FDEs. + DwarfPointerEncoding lsda_encoding; + + // If has_z_personality is true, this is the encoding used for the + // personality routine pointer in the augmentation data. + DwarfPointerEncoding personality_encoding; + + // If has_z_personality is true, this is the address of the personality + // routine --- or, if personality_encoding & DW_EH_PE_indirect, the + // address where the personality routine's address is stored. + uint64 personality_address; + + // This is the encoding used for addresses in the FDE header and + // in DW_CFA_set_loc instructions. This is always valid, whether + // or not we saw a 'z' augmentation string; its default value is + // DW_EH_PE_absptr, which is what normal DWARF CFI uses. + DwarfPointerEncoding pointer_encoding; + }; + + // A frame description entry (FDE). + struct FDE: public Entry { + uint64 address; // start address of described code + uint64 size; // size of described code, in bytes + + // If cie->has_z_lsda is true, then this is the language-specific data + // area's address --- or its address's address, if cie->lsda_encoding + // has the DW_EH_PE_indirect bit set. + uint64 lsda_address; + }; + + // Internal use. + class Rule; + class UndefinedRule; + class SameValueRule; + class OffsetRule; + class ValOffsetRule; + class RegisterRule; + class ExpressionRule; + class ValExpressionRule; + class RuleMap; + class State; + + // Parse the initial length and id of a CFI entry, either a CIE, an FDE, + // or a .eh_frame end-of-data mark. CURSOR points to the beginning of the + // data to parse. On success, populate ENTRY as appropriate, and return + // true. On failure, report the problem, and return false. Even if we + // return false, set ENTRY->end to the first byte after the entry if we + // were able to figure that out, or NULL if we weren't. + bool ReadEntryPrologue(const char *cursor, Entry *entry); + + // Parse the fields of a CIE after the entry prologue, including any 'z' + // augmentation data. Assume that the 'Entry' fields of CIE are + // populated; use CIE->fields and CIE->end as the start and limit for + // parsing. On success, populate the rest of *CIE, and return true; on + // failure, report the problem and return false. + bool ReadCIEFields(CIE *cie); + + // Parse the fields of an FDE after the entry prologue, including any 'z' + // augmentation data. Assume that the 'Entry' fields of *FDE are + // initialized; use FDE->fields and FDE->end as the start and limit for + // parsing. Assume that FDE->cie is fully initialized. On success, + // populate the rest of *FDE, and return true; on failure, report the + // problem and return false. + bool ReadFDEFields(FDE *fde); + + // Report that ENTRY is incomplete, and return false. This is just a + // trivial wrapper for invoking reporter_->Incomplete; it provides a + // little brevity. + bool ReportIncomplete(Entry *entry); + + // Return true if ENCODING has the DW_EH_PE_indirect bit set. + static bool IsIndirectEncoding(DwarfPointerEncoding encoding) { + return encoding & DW_EH_PE_indirect; + } + + // The contents of the DWARF .debug_info section we're parsing. + const char *buffer_; + size_t buffer_length_; + + // For reading multi-byte values with the appropriate endianness. + ByteReader *reader_; + + // The handler to which we should report the data we find. + Handler *handler_; + + // For reporting problems in the info we're parsing. + Reporter *reporter_; + + // True if we are processing .eh_frame-format data. + bool eh_frame_; +}; + +// The handler class for CallFrameInfo. The a CFI parser calls the +// member functions of a handler object to report the data it finds. +class CallFrameInfo::Handler { + public: + // The pseudo-register number for the canonical frame address. + enum { kCFARegister = -1 }; + + Handler() { } + virtual ~Handler() { } + + // The parser has found CFI for the machine code at ADDRESS, + // extending for LENGTH bytes. OFFSET is the offset of the frame + // description entry in the section, for use in error messages. + // VERSION is the version number of the CFI format. AUGMENTATION is + // a string describing any producer-specific extensions present in + // the data. RETURN_ADDRESS is the number of the register that holds + // the address to which the function should return. + // + // Entry should return true to process this CFI, or false to skip to + // the next entry. + // + // The parser invokes Entry for each Frame Description Entry (FDE) + // it finds. The parser doesn't report Common Information Entries + // to the handler explicitly; instead, if the handler elects to + // process a given FDE, the parser reiterates the appropriate CIE's + // contents at the beginning of the FDE's rules. + virtual bool Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const std::string &augmentation, + unsigned return_address) = 0; + + // When the Entry function returns true, the parser calls these + // handler functions repeatedly to describe the rules for recovering + // registers at each instruction in the given range of machine code. + // Immediately after a call to Entry, the handler should assume that + // the rule for each callee-saves register is "unchanged" --- that + // is, that the register still has the value it had in the caller. + // + // If a *Rule function returns true, we continue processing this entry's + // instructions. If a *Rule function returns false, we stop evaluating + // instructions, and skip to the next entry. Either way, we call End + // before going on to the next entry. + // + // In all of these functions, if the REG parameter is kCFARegister, then + // the rule describes how to find the canonical frame address. + // kCFARegister may be passed as a BASE_REGISTER argument, meaning that + // the canonical frame address should be used as the base address for the + // computation. All other REG values will be positive. + + // At ADDRESS, register REG's value is not recoverable. + virtual bool UndefinedRule(uint64 address, int reg) = 0; + + // At ADDRESS, register REG's value is the same as that it had in + // the caller. + virtual bool SameValueRule(uint64 address, int reg) = 0; + + // At ADDRESS, register REG has been saved at offset OFFSET from + // BASE_REGISTER. + virtual bool OffsetRule(uint64 address, int reg, + int base_register, long offset) = 0; + + // At ADDRESS, the caller's value of register REG is the current + // value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an + // address at which the register's value is saved.) + virtual bool ValOffsetRule(uint64 address, int reg, + int base_register, long offset) = 0; + + // At ADDRESS, register REG has been saved in BASE_REGISTER. This differs + // from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that + // BASE_REGISTER is the "home" for REG's saved value: if you want to + // assign to a variable whose home is REG in the calling frame, you + // should put the value in BASE_REGISTER. + virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0; + + // At ADDRESS, the DWARF expression EXPRESSION yields the address at + // which REG was saved. + virtual bool ExpressionRule(uint64 address, int reg, + const std::string &expression) = 0; + + // At ADDRESS, the DWARF expression EXPRESSION yields the caller's + // value for REG. (This rule doesn't provide an address at which the + // register's value is saved.) + virtual bool ValExpressionRule(uint64 address, int reg, + const std::string &expression) = 0; + + // Indicate that the rules for the address range reported by the + // last call to Entry are complete. End should return true if + // everything is okay, or false if an error has occurred and parsing + // should stop. + virtual bool End() = 0; + + // Handler functions for Linux C++ exception handling data. These are + // only called if the data includes 'z' augmentation strings. + + // The Linux C++ ABI uses an extension of the DWARF CFI format to + // walk the stack to propagate exceptions from the throw to the + // appropriate catch, and do the appropriate cleanups along the way. + // CFI entries used for exception handling have two additional data + // associated with them: + // + // - The "language-specific data area" describes which exception + // types the function has 'catch' clauses for, and indicates how + // to go about re-entering the function at the appropriate catch + // clause. If the exception is not caught, it describes the + // destructors that must run before the frame is popped. + // + // - The "personality routine" is responsible for interpreting the + // language-specific data area's contents, and deciding whether + // the exception should continue to propagate down the stack, + // perhaps after doing some cleanup for this frame, or whether the + // exception will be caught here. + // + // In principle, the language-specific data area is opaque to + // everybody but the personality routine. In practice, these values + // may be useful or interesting to readers with extra context, and + // we have to at least skip them anyway, so we might as well report + // them to the handler. + + // This entry's exception handling personality routine's address is + // ADDRESS. If INDIRECT is true, then ADDRESS is the address at + // which the routine's address is stored. The default definition for + // this handler function simply returns true, allowing parsing of + // the entry to continue. + virtual bool PersonalityRoutine(uint64 address, bool indirect) { + return true; + } + + // This entry's language-specific data area (LSDA) is located at + // ADDRESS. If INDIRECT is true, then ADDRESS is the address at + // which the area's address is stored. The default definition for + // this handler function simply returns true, allowing parsing of + // the entry to continue. + virtual bool LanguageSpecificDataArea(uint64 address, bool indirect) { + return true; + } + + // This entry describes a signal trampoline --- this frame is the + // caller of a signal handler. The default definition for this + // handler function simply returns true, allowing parsing of the + // entry to continue. + // + // The best description of the rationale for and meaning of signal + // trampoline CFI entries seems to be in the GCC bug database: + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208 + virtual bool SignalHandler() { return true; } +}; + +// The CallFrameInfo class makes calls on an instance of this class to +// report errors or warn about problems in the data it is parsing. The +// default definitions of these methods print a message to stderr, but +// you can make a derived class that overrides them. +class CallFrameInfo::Reporter { + public: + // Create an error reporter which attributes troubles to the section + // named SECTION in FILENAME. + // + // Normally SECTION would be .debug_frame, but the Mac puts CFI data + // in a Mach-O section named __debug_frame. If we support + // Linux-style exception handling data, we could be reading an + // .eh_frame section. + Reporter(const std::string &filename, + const std::string §ion = ".debug_frame") + : filename_(filename), section_(section) { } + virtual ~Reporter() { } + + // The CFI entry at OFFSET ends too early to be well-formed. KIND + // indicates what kind of entry it is; KIND can be kUnknown if we + // haven't parsed enough of the entry to tell yet. + virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind); + + // The .eh_frame data has a four-byte zero at OFFSET where the next + // entry's length would be; this is a terminator. However, the buffer + // length as given to the CallFrameInfo constructor says there should be + // more data. + virtual void EarlyEHTerminator(uint64 offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the + // section is not that large. + virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry + // there is not a CIE. + virtual void BadCIEId(uint64 offset, uint64 cie_offset); + + // The FDE at OFFSET refers to a CIE with version number VERSION, + // which we don't recognize. We cannot parse DWARF CFI if it uses + // a version number we don't recognize. + virtual void UnrecognizedVersion(uint64 offset, int version); + + // The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION, + // which we don't recognize. We cannot parse DWARF CFI if it uses + // augmentations we don't recognize. + virtual void UnrecognizedAugmentation(uint64 offset, + const std::string &augmentation); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, is not + // a valid encoding. + virtual void InvalidPointerEncoding(uint64 offset, uint8 encoding); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, depends + // on a base address which has not been supplied. + virtual void UnusablePointerEncoding(uint64 offset, uint8 encoding); + + // The CIE at OFFSET contains a DW_CFA_restore instruction at + // INSN_OFFSET, which may not appear in a CIE. + virtual void RestoreInCIE(uint64 offset, uint64 insn_offset); + + // The entry at OFFSET, of kind KIND, has an unrecognized + // instruction at INSN_OFFSET. + virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The instruction at INSN_OFFSET in the entry at OFFSET, of kind + // KIND, establishes a rule that cites the CFA, but we have not + // established a CFA rule yet. + virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The instruction at INSN_OFFSET in the entry at OFFSET, of kind + // KIND, is a DW_CFA_restore_state instruction, but the stack of + // saved states is empty. + virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The DW_CFA_remember_state instruction at INSN_OFFSET in the entry + // at OFFSET, of kind KIND, would restore a state that has no CFA + // rule, whereas the current state does have a CFA rule. This is + // bogus input, which the CallFrameInfo::Handler interface doesn't + // (and shouldn't) have any way to report. + virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + protected: + // The name of the file whose CFI we're reading. + std::string filename_; + + // The name of the CFI section in that file. + std::string section_; +}; + +} // namespace dwarf2reader + +#endif // UTIL_DEBUGINFO_DWARF2READER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_cfi_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_cfi_unittest.cc new file mode 100644 index 0000000..271d1b6 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_cfi_unittest.cc @@ -0,0 +1,2452 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo + +#include + +#include +#include + +// The '.eh_frame' format, used by the Linux C++ ABI for exception +// handling, is poorly specified. To help test our support for .eh_frame, +// if you #define WRITE_ELF while compiling this file, and add the +// 'include' directory from the binutils, gcc, or gdb source tree to the +// #include path, then each test that calls the +// PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will write +// an ELF file containing a .debug_frame or .eh_frame section; you can then +// use tools like readelf to examine the test data, and check the tools' +// interpretation against the test's intentions. Each ELF file is named +// "cfitest-TEST", where TEST identifies the particular test. +#ifdef WRITE_ELF +#include +#include +#include +extern "C" { +// To compile with WRITE_ELF, you should add the 'include' directory +// of the binutils, gcc, or gdb source tree to your #include path; +// that directory contains this header. +#include "elf/common.h" +} +#endif + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/cfi_assembler.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::CFISection; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; + +using dwarf2reader::DwarfPointerEncoding; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::ByteReader; +using dwarf2reader::CallFrameInfo; + +using std::string; +using std::vector; +using testing::InSequence; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::_; + +#ifdef WRITE_ELF +void WriteELFFrameSection(const char *filename, const char *section_name, + const CFISection §ion); +#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) \ + WriteELFFrameSection("cfitest-" name, ".debug_frame", section); +#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) \ + WriteELFFrameSection("cfitest-" name, ".eh_frame", section); +#else +#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) +#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) +#endif + +class MockCallFrameInfoHandler: public CallFrameInfo::Handler { + public: + MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address)); + MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg)); + MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg)); + MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register, + long offset)); + MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register, + long offset)); + MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register)); + MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg, + const string &expression)); + MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg, + const string &expression)); + MOCK_METHOD0(End, bool()); + MOCK_METHOD2(PersonalityRoutine, bool(uint64 address, bool indirect)); + MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64 address, bool indirect)); + MOCK_METHOD0(SignalHandler, bool()); +}; + +class MockCallFrameErrorReporter: public CallFrameInfo::Reporter { + public: + MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { } + MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind)); + MOCK_METHOD1(EarlyEHTerminator, void(uint64)); + MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64)); + MOCK_METHOD2(BadCIEId, void(uint64, uint64)); + MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version)); + MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &)); + MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8)); + MOCK_METHOD2(UnusablePointerEncoding, void(uint64, uint8)); + MOCK_METHOD2(RestoreInCIE, void(uint64, uint64)); + MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64)); + MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64)); + MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64)); +}; + +struct CFIFixture { + + enum { kCFARegister = CallFrameInfo::Handler::kCFARegister }; + + CFIFixture() { + // Default expectations for the data handler. + // + // - Leave Entry and End without expectations, as it's probably a + // good idea to set those explicitly in each test. + // + // - Expect the *Rule functions to not be called, + // so that each test can simply list the calls they expect. + // + // I gather I could use StrictMock for this, but the manual seems + // to suggest using that only as a last resort, and this isn't so + // bad. + EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0); + EXPECT_CALL(handler, SameValueRule(_, _)).Times(0); + EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0); + EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0); + EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0); + EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0); + EXPECT_CALL(handler, PersonalityRoutine(_, _)).Times(0); + EXPECT_CALL(handler, LanguageSpecificDataArea(_, _)).Times(0); + EXPECT_CALL(handler, SignalHandler()).Times(0); + + // Default expectations for the error/warning reporer. + EXPECT_CALL(reporter, Incomplete(_, _)).Times(0); + EXPECT_CALL(reporter, EarlyEHTerminator(_)).Times(0); + EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0); + EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0); + EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0); + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0); + EXPECT_CALL(reporter, InvalidPointerEncoding(_, _)).Times(0); + EXPECT_CALL(reporter, UnusablePointerEncoding(_, _)).Times(0); + EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0); + EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0); + EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0); + EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0); + } + + MockCallFrameInfoHandler handler; + MockCallFrameErrorReporter reporter; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, EmptyRegion) { + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + static const char data[1] = { 42 }; + + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +TEST_F(CFI, IncompleteLength32) { + CFISection section(kBigEndian, 8); + section + // Not even long enough for an initial length. + .D16(0xa0f) + // Padding to keep valgrind happy. We subtract these off when we + // construct the parser. + .D16(0); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size() - 2, + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, IncompleteLength64) { + CFISection section(kLittleEndian, 4); + section + // An incomplete 64-bit DWARF initial length. + .D32(0xffffffff).D32(0x71fbaec2) + // Padding to keep valgrind happy. We subtract these off when we + // construct the parser. + .D32(0); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size() - 4, + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, IncompleteId32) { + CFISection section(kBigEndian, 8); + section + .D32(3) // Initial length, not long enough for id + .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id + .CIEHeader(8727, 3983, 8889, 3, "") + .FinishEntry(); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, BadId32) { + CFISection section(kBigEndian, 8); + section + .D32(0x100) // Initial length + .D32(0xe802fade) // bogus ID + .Append(0x100 - 4, 0x42); // make the length true + section + .CIEHeader(1672, 9872, 8529, 3, "") + .FinishEntry(); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// A lone CIE shouldn't cause any handler calls. +TEST_F(CFI, SingleCIE) { + CFISection section(kLittleEndian, 4); + section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, ""); + section.Append(10, dwarf2reader::DW_CFA_nop); + section.FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("SingleCIE", section); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// One FDE, one CIE. +TEST_F(CFI, OneFDE) { + CFISection section(kBigEndian, 4); + Label cie; + section + .Mark(&cie) + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "") + .FinishEntry() + .FDEHeader(cie, 0x7714740d, 0x3d5a10cd) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("OneFDE", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// Two FDEs share a CIE. +TEST_F(CFI, TwoFDEsOneCIE) { + CFISection section(kBigEndian, 4); + Label cie; + section + // First FDE. readelf complains about this one because it makes + // a forward reference to its CIE. + .FDEHeader(cie, 0xa42744df, 0xa3b42121) + .FinishEntry() + // CIE. + .Mark(&cie) + .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "") + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0x6057d391, 0x700f608d) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsOneCIE", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// Two FDEs, two CIEs. +TEST_F(CFI, TwoFDEsTwoCIEs) { + CFISection section(kLittleEndian, 8); + Label cie1, cie2; + section + // First CIE. + .Mark(&cie1) + .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "") + .FinishEntry() + // First FDE which cites second CIE. readelf complains about + // this one because it makes a forward reference to its CIE. + .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL) + .FinishEntry() + // Second FDE, which cites first CIE. + .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL) + .FinishEntry() + // Second CIE. + .Mark(&cie2) + .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "") + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsTwoCIEs", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2, + "", 0x61d2c581)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3, + "", 0xbf45e65a)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// An FDE whose CIE specifies a version we don't recognize. +TEST_F(CFI, BadVersion) { + CFISection section(kBigEndian, 4); + Label cie1, cie2; + section + .Mark(&cie1) + .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "") + .FinishEntry() + // We should skip this entry, as its CIE specifies a version we + // don't recognize. + .FDEHeader(cie1, 0x08852292, 0x2204004a) + .FinishEntry() + // Despite the above, we should visit this entry. + .Mark(&cie2) + .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "") + .FinishEntry() + .FDEHeader(cie2, 0x2094735a, 0x6e875501) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("BadVersion", section); + + EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52)) + .WillOnce(Return()); + + { + InSequence s; + // We should see no mention of the first FDE, but we should get + // a call to Entry for the second. + EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "", + 0x96cb3264)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// An FDE whose CIE specifies an augmentation we don't recognize. +TEST_F(CFI, BadAugmentation) { + CFISection section(kBigEndian, 4); + Label cie1, cie2; + section + .Mark(&cie1) + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!") + .FinishEntry() + // We should skip this entry, as its CIE specifies an + // augmentation we don't recognize. + .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd) + .FinishEntry() + // Despite the above, we should visit this entry. + .Mark(&cie2) + .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "") + .FinishEntry() + .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("BadAugmentation", section); + + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!")) + .WillOnce(Return()); + + { + InSequence s; + // We should see no mention of the first FDE, but we should get + // a call to Entry for the second. + EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "", + 0xf2f519b2)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// The return address column field is a byte in CFI version 1 +// (DWARF2), but a ULEB128 value in version 3 (DWARF3). +TEST_F(CFI, CIEVersion1ReturnColumn) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, using the version 1 format: return column is a ubyte. + .Mark(&cie) + // Use a value for the return column that is parsed differently + // as a ubyte and as a ULEB128. + .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "") + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0xb8d347b5, 0x825e55dc) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion1ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// The return address column field is a byte in CFI version 1 +// (DWARF2), but a ULEB128 value in version 3 (DWARF3). +TEST_F(CFI, CIEVersion3ReturnColumn) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, using the version 3 format: return column is a ULEB128. + .Mark(&cie) + // Use a value for the return column that is parsed differently + // as a ubyte and as a ULEB128. + .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "") + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +struct CFIInsnFixture: public CFIFixture { + CFIInsnFixture() : CFIFixture() { + data_factor = 0xb6f; + return_register = 0x9be1ed9f; + version = 3; + cfa_base_register = 0x383a3aa; + cfa_offset = 0xf748; + } + + // Prepare SECTION to receive FDE instructions. + // + // - Append a stock CIE header that establishes the fixture's + // code_factor, data_factor, return_register, version, and + // augmentation values. + // - Have the CIE set up a CFA rule using cfa_base_register and + // cfa_offset. + // - Append a stock FDE header, referring to the above CIE, for the + // fde_size bytes at fde_start. Choose fde_start and fde_size + // appropriately for the section's address size. + // - Set appropriate expectations on handler in sequence s for the + // frame description entry and the CIE's CFA rule. + // + // On return, SECTION is ready to have FDE instructions appended to + // it, and its FinishEntry member called. + void StockCIEAndFDE(CFISection *section) { + // Choose appropriate constants for our address size. + if (section->AddressSize() == 4) { + fde_start = 0xc628ecfbU; + fde_size = 0x5dee04a2; + code_factor = 0x60b; + } else { + assert(section->AddressSize() == 8); + fde_start = 0x0005c57ce7806bd3ULL; + fde_size = 0x2699521b5e333100ULL; + code_factor = 0x01008e32855274a8ULL; + } + + // Create the CIE. + (*section) + .Mark(&cie_label) + .CIEHeader(code_factor, data_factor, return_register, version, + "") + .D8(dwarf2reader::DW_CFA_def_cfa) + .ULEB128(cfa_base_register) + .ULEB128(cfa_offset) + .FinishEntry(); + + // Create the FDE. + section->FDEHeader(cie_label, fde_start, fde_size); + + // Expect an Entry call for the FDE and a ValOffsetRule call for the + // CIE's CFA rule. + EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "", + return_register)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister, + cfa_base_register, cfa_offset)) + .InSequence(s) + .WillOnce(Return(true)); + } + + // Run the contents of SECTION through a CallFrameInfo parser, + // expecting parser.Start to return SUCCEEDS + void ParseSection(CFISection *section, bool succeeds = true) { + string contents; + EXPECT_TRUE(section->GetContents(&contents)); + dwarf2reader::Endianness endianness; + if (section->endianness() == kBigEndian) + endianness = ENDIANNESS_BIG; + else { + assert(section->endianness() == kLittleEndian); + endianness = ENDIANNESS_LITTLE; + } + ByteReader byte_reader(endianness); + byte_reader.SetAddressSize(section->AddressSize()); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + if (succeeds) + EXPECT_TRUE(parser.Start()); + else + EXPECT_FALSE(parser.Start()); + } + + Label cie_label; + Sequence s; + uint64 code_factor; + int data_factor; + unsigned return_register; + unsigned version; + unsigned cfa_base_register; + int cfa_offset; + uint64 fde_start, fde_size; +}; + +class CFIInsn: public CFIInsnFixture, public Test { }; + +TEST_F(CFIInsn, DW_CFA_set_loc) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_set_loc).D32(0xb1ee3e7a) + // Use DW_CFA_def_cfa to force a handler call that we can use to + // check the effect of the DW_CFA_set_loc. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_set_loc", section); + + EXPECT_CALL(handler, + ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc | 0x2a) + // Use DW_CFA_def_cfa to force a handler call that we can use to + // check the effect of the DW_CFA_advance_loc. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc", section); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start + 0x2a * code_factor, + kCFARegister, 0x5bbb3715, 0x0186c7bf)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc1) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc1).D8(0xd8) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc1", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0xd8 * code_factor), + kCFARegister, 0x69d5696a, 0x1eb7fc93)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc2) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc2).D16(0x3adb) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc2", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x3adb * code_factor), + kCFARegister, 0x3a368bed, 0x3194ee37)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc4) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc4).D32(0x15813c88) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc4", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x15813c88ULL * code_factor), + kCFARegister, 0x135270c5, 0x24bad7cb)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_MIPS_advance_loc8) { + code_factor = 0x2d; + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc8", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor), + kCFARegister, 0xe17ed602, 0x3d162e7f)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_def_cfa", section); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_sf) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea) + .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7, + 0x9ea * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da, + -0x40a2 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_register) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// DW_CFA_def_cfa_register should have no effect when applied to a +// non-base/offset rule. +TEST_F(CFIInsn, DW_CFA_def_cfa_registerBadRule) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("needle in a haystack") + .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49) + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, kCFARegister, + "needle in a haystack")) + .WillRepeatedly(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + 0x1e8e3b9b)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_offset_sf) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(0x970) + .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + 0x970 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + -0x2cd * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// DW_CFA_def_cfa_offset should have no effect when applied to a +// non-base/offset rule. +TEST_F(CFIInsn, DW_CFA_def_cfa_offsetBadRule) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("six ways to Sunday") + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, kCFARegister, "six ways to Sunday")) + .WillRepeatedly(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_expression) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("eating crow") + .FinishEntry(); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister, + "eating crow")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_undefined) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x300ce45d) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_same_value) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3865a760) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x9f6) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset_extended) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x402b, kCFARegister, 0xb48 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset_extended_sf) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset_extended_sf) + .ULEB128(0x997c23ee).LEB128(0x2d00) + .D8(dwarf2reader::DW_CFA_offset_extended_sf) + .ULEB128(0x9519eb82).LEB128(-0xa77) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x997c23ee, + kCFARegister, 0x2d00 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x9519eb82, + kCFARegister, -0xa77 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x623562fe, + kCFARegister, 0x673 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_offset_sf) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab) + .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x6f4f, + kCFARegister, 0xaab * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x2483, + kCFARegister, -0x8a2 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_register) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_expression) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xa1619fb2) + .Block("plus ça change, plus c'est la même chose") + .FinishEntry(); + + EXPECT_CALL(handler, + ExpressionRule(fde_start, 0xa1619fb2, + "plus ça change, plus c'est la même chose")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_expression) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xc5e4a9e3) + .Block("he who has the gold makes the rules") + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, 0xc5e4a9e3, + "he who has the gold makes the rules")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restore) { + CFISection section(kLittleEndian, 8); + code_factor = 0x01bd188a9b1fa083ULL; + data_factor = -0x1ac8; + return_register = 0x8c35b049; + version = 2; + fde_start = 0x2d70fe998298bbb1ULL; + fde_size = 0x46ccc2e63cf0b108ULL; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, + "") + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8) + // Provide an offset(N) rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0xb348) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide a new offset(N) rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x13) + .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0x9a50) + // At a third address, restore the original rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x01) + .D8(dwarf2reader::DW_CFA_restore | 0x3c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x6ca1d50e, 0x372e38e8)) + .WillOnce(Return(true)); + // CIE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x3c, kCFARegister, 0xb348 * data_factor)) + .WillOnce(Return(true)); + // FDE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x13 * code_factor, 0x3c, + kCFARegister, 0x9a50 * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c, + kCFARegister, 0xb348 * data_factor)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restoreNoRule) { + CFISection section(kBigEndian, 4); + code_factor = 0x005f78143c1c3b82ULL; + data_factor = 0x25d0; + return_register = 0xe8; + version = 1; + fde_start = 0x4062e30f; + fde_size = 0x5302a389; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, "") + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide an offset(N) rule for register 0x2c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x7) + .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x1f47) + // At a third address, restore the (missing) CIE rule for register 0x2c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0xb) + .D8(dwarf2reader::DW_CFA_restore | 0x2c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x470aa334, 0x099ef127)) + .WillOnce(Return(true)); + // FDE's rule for register 0x2c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x7 * code_factor, 0x2c, + kCFARegister, 0x1f47 * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's (missing) rule for register 0x2c. + EXPECT_CALL(handler, + SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restore_extended) { + CFISection section(kBigEndian, 4); + code_factor = 0x126e; + data_factor = -0xd8b; + return_register = 0x77711787; + version = 3; + fde_start = 0x01f55a45; + fde_size = 0x452adb80; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, + "", true /* dwarf64 */ ) + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5) + // Provide an offset(N) rule for register 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0xc979) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x3) + .D8(dwarf2reader::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b) + // At a third address, restore the original rule for register 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x04) + .D8(dwarf2reader::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5)) + .WillOnce(Return(true)); + // CIE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister, + 0xc979 * data_factor)) + .WillOnce(Return(true)); + // FDE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c, + kCFARegister, 0x3b7b * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c, + kCFARegister, 0xc979 * data_factor)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_remember_and_restore_state) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + + // We create a state, save it, modify it, and then restore. We + // refer to the state that is overridden the restore as the + // "outgoing" state, and the restored state the "incoming" state. + // + // Register outgoing incoming expect + // 1 offset(N) no rule new "same value" rule + // 2 register(R) offset(N) report changed rule + // 3 offset(N) offset(M) report changed offset + // 4 offset(N) offset(N) no report + // 5 offset(N) no rule new "same value" rule + section + // Create the "incoming" state, which we will save and later restore. + .D8(dwarf2reader::DW_CFA_offset | 2).ULEB128(0x9806) + .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0x995d) + .D8(dwarf2reader::DW_CFA_offset | 4).ULEB128(0x7055) + .D8(dwarf2reader::DW_CFA_remember_state) + // Advance to a new instruction; an implementation could legitimately + // ignore all but the final rule for a given register at a given address. + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + // Create the "outgoing" state, which we will discard. + .D8(dwarf2reader::DW_CFA_offset | 1).ULEB128(0xea1a) + .D8(dwarf2reader::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767) + .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0xdd29) + .D8(dwarf2reader::DW_CFA_offset | 5).ULEB128(0xf1ce) + // At a third address, restore the incoming state. + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + uint64 addr = fde_start; + + // Expect the incoming rules to be reported. + EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + + addr += code_factor; + + // After the save, we establish the outgoing rule set. + EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor)) + .InSequence(s).WillOnce(Return(true)); + + addr += code_factor; + + // Finally, after the restore, expect to see the differences from + // the outgoing to the incoming rules reported. + EXPECT_CALL(handler, SameValueRule(addr, 1)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(addr, 5)) + .InSequence(s).WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// Check that restoring a rule set reports changes to the CFA rule. +TEST_F(CFIInsn, DW_CFA_remember_and_restore_stateCFA) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + + section + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x90481102) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister, + cfa_base_register, 0x90481102)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister, + cfa_base_register, cfa_offset)) + .InSequence(s).WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_nop) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_nop) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b) + .D8(dwarf2reader::DW_CFA_nop) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_window_save) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_window_save) + .FinishEntry(); + + // Don't include all the rules in any particular sequence. + + // The caller's %o0-%o7 have become the callee's %i0-%i7. This is + // the GCC register numbering. + for (int i = 8; i < 16; i++) + EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16)) + .WillOnce(Return(true)); + // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of + // its frame. + for (int i = 16; i < 32; i++) + EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4)) + .WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_args_size) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_args_size).ULEB128(0xeddfa520) + // Verify that we see this, meaning we parsed the above properly. + .D8(dwarf2reader::DW_CFA_offset | 0x23).ULEB128(0x269) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_negative_offset_extended) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_negative_offset_extended) + .ULEB128(0x430cc87a).ULEB128(0x613) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x430cc87a, + kCFARegister, -0x613 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// Three FDEs: skip the second +TEST_F(CFIInsn, SkipFDE) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, used by all FDEs. + .Mark(&cie) + .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "") + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad) + .FinishEntry() + // First FDE. + .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf) + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18) + .FinishEntry() + // Third FDE. + .FDEHeader(cie, 0xf681cfc8, 0x7e4594e) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4) + .FinishEntry(); + + { + InSequence s; + + // Process the first FDE. + EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister, + 0x42ed390b, 0x98f43aad)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + + // Skip the second FDE. + EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849)) + .WillOnce(Return(false)); + + // Process the third FDE. + EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister, + 0x42ed390b, 0x98f43aad)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +// Quit processing in the middle of an entry's instructions. +TEST_F(CFIInsn, QuitMidentry) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431) + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat") + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431)) + .InSequence(s).WillOnce(Return(false)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion, false); +} + +class CFIRestore: public CFIInsnFixture, public Test { }; + +TEST_F(CFIRestore, RestoreUndefinedRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x0bac878e) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreUndefinedRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x7dedff5f) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x7dedff5f) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreSameValueRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0xadbc9b3a) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreSameValueRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3d90dcb5) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x3d90dcb5) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x14).ULEB128(0xb6f) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x14, + kCFARegister, 0xb6f * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xeb7) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x21) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleChangedOffset) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0x134) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xf4f) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, + kCFARegister, 0x134 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21, + kCFARegister, 0xf4f * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, + kCFARegister, 0x134 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6, + kCFARegister, 0xe4c * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xf17c36d6) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleChangedValOffset) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b, + kCFARegister, 0x562 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b, + kCFARegister, 0xe88 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b, + kCFARegister, 0x562 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xe39acce5) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5, + 0x095f1559)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleChangedRegister) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1, + 0xbabb4742)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1, + 0x16607d6a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, + "elf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleChangedExpression) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("orc") + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739, + "orc")) + .InSequence(s).WillOnce(Return(true)); + // Expectations are not wishes. + EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739, + "smurf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x666ae152) + .Block("hideous") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xb5ca5c46) + .Block("revolting") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChanged", section); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, + "revolting")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleChangedValExpression) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("repulsive") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("nauseous") + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChangedValExpression", + section); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739, + "nauseous")) + .InSequence(s).WillOnce(Return(true)); + // Expectations are not wishes. + EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739, + "repulsive")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +struct EHFrameFixture: public CFIInsnFixture { + EHFrameFixture() + : CFIInsnFixture(), section(kBigEndian, 4, true) { + encoded_pointer_bases.cfi = 0x7f496cb2; + encoded_pointer_bases.text = 0x540f67b6; + encoded_pointer_bases.data = 0xe3eab768; + section.SetEncodedPointerBases(encoded_pointer_bases); + } + CFISection section; + CFISection::EncodedPointerBases encoded_pointer_bases; + + // Parse CFIInsnFixture::ParseSection, but parse the section as + // .eh_frame data, supplying stock base addresses. + void ParseEHFrameSection(CFISection *section, bool succeeds = true) { + EXPECT_TRUE(section->ContainsEHFrame()); + string contents; + EXPECT_TRUE(section->GetContents(&contents)); + dwarf2reader::Endianness endianness; + if (section->endianness() == kBigEndian) + endianness = ENDIANNESS_BIG; + else { + assert(section->endianness() == kLittleEndian); + endianness = ENDIANNESS_LITTLE; + } + ByteReader byte_reader(endianness); + byte_reader.SetAddressSize(section->AddressSize()); + byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi, contents.data()); + byte_reader.SetTextBase(encoded_pointer_bases.text); + byte_reader.SetDataBase(encoded_pointer_bases.data); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter, true); + if (succeeds) + EXPECT_TRUE(parser.Start()); + else + EXPECT_FALSE(parser.Start()); + } + +}; + +class EHFrame: public EHFrameFixture, public Test { }; + +// A simple CIE, an FDE, and a terminator. +TEST_F(EHFrame, Terminator) { + Label cie; + section + .Mark(&cie) + .CIEHeader(9968, 2466, 67, 1, "") + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372) + .FinishEntry() + .FDEHeader(cie, 0x848037a1, 0x7b30475e) + .D8(dwarf2reader::DW_CFA_set_loc).D32(0x17713850) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(5721) + .FinishEntry() + .D32(0) // Terminate the sequence. + // This FDE should be ignored. + .FDEHeader(cie, 0xf19629fe, 0x439fb09b) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.Terminator", section); + + EXPECT_CALL(handler, Entry(_, 0x848037a1, 0x7b30475e, 1, "", 67)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0x848037a1, kCFARegister, 3772, 1372)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0x17713850, 5721)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(reporter, EarlyEHTerminator(_)) + .InSequence(s).WillOnce(Return()); + + ParseEHFrameSection(§ion); +} + +// The parser should recognize the Linux Standards Base 'z' augmentations. +TEST_F(EHFrame, SimpleFDE) { + DwarfPointerEncoding lsda_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect + | dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_sdata2); + DwarfPointerEncoding fde_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_udata2); + + section.SetPointerEncoding(fde_encoding); + section.SetEncodedPointerBases(encoded_pointer_bases); + Label cie; + section + .Mark(&cie) + .CIEHeader(4873, 7012, 100, 1, "zSLPR") + .ULEB128(7) // Augmentation data length + .D8(lsda_encoding) // LSDA pointer format + .D8(dwarf2reader::DW_EH_PE_pcrel) // personality pointer format + .EncodedPointer(0x97baa00, dwarf2reader::DW_EH_PE_pcrel) // and value + .D8(fde_encoding) // FDE pointer format + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(6706).ULEB128(31) + .FinishEntry() + .FDEHeader(cie, 0x540f6b56, 0xf686) + .ULEB128(2) // Augmentation data length + .EncodedPointer(0xe3eab475, lsda_encoding) // LSDA pointer, signed + .D8(dwarf2reader::DW_CFA_set_loc) + .EncodedPointer(0x540fa4ce, fde_encoding) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x675e) + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.SimpleFDE", section); + + EXPECT_CALL(handler, Entry(_, 0x540f6b56, 0xf686, 1, "zSLPR", 100)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, PersonalityRoutine(0x97baa00, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, LanguageSpecificDataArea(0xe3eab475, true)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SignalHandler()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0x540f6b56, kCFARegister, 6706, 31)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0x540fa4ce, 0x675e)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// Check that we can handle an empty 'z' augmentation. +TEST_F(EHFrame, EmptyZ) { + Label cie; + section + .Mark(&cie) + .CIEHeader(5955, 5805, 228, 1, "z") + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247) + .FinishEntry() + .FDEHeader(cie, 0xda007738, 0xfb55c641) + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_advance_loc1).D8(11) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(3769) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.EmptyZ", section); + + EXPECT_CALL(handler, Entry(_, 0xda007738, 0xfb55c641, 1, "z", 228)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xda007738, kCFARegister, 3629, 247)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0xda007738 + 11 * 5955, 3769)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// Check that we recognize bad 'z' augmentation characters. +TEST_F(EHFrame, BadZ) { + Label cie; + section + .Mark(&cie) + .CIEHeader(6937, 1045, 142, 1, "zQ") + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725) + .FinishEntry() + .FDEHeader(cie, 0x1293efa8, 0x236f53f2) + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_advance_loc | 12) + .D8(dwarf2reader::DW_CFA_register).ULEB128(5667).ULEB128(3462) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.BadZ", section); + + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "zQ")) + .WillOnce(Return()); + + ParseEHFrameSection(§ion, false); +} + +TEST_F(EHFrame, zL) { + Label cie; + DwarfPointerEncoding lsda_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel + | dwarf2reader::DW_EH_PE_udata2); + section + .Mark(&cie) + .CIEHeader(9285, 9959, 54, 1, "zL") + .ULEB128(1) // Augmentation data length + .D8(lsda_encoding) // encoding for LSDA pointer in FDE + + .FinishEntry() + .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) + .ULEB128(2) // Augmentation data length + .EncodedPointer(0xd40099cd, lsda_encoding) // LSDA pointer + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zL", section); + + EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zL", 54)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, LanguageSpecificDataArea(0xd40099cd, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zP) { + Label cie; + DwarfPointerEncoding personality_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_udata2); + section + .Mark(&cie) + .CIEHeader(1097, 6313, 17, 1, "zP") + .ULEB128(3) // Augmentation data length + .D8(personality_encoding) // encoding for personality routine + .EncodedPointer(0xe3eaccac, personality_encoding) // value + .FinishEntry() + .FDEHeader(cie, 0x0c8350c9, 0xbef11087) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zP", section); + + EXPECT_CALL(handler, Entry(_, 0x0c8350c9, 0xbef11087, 1, "zP", 17)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, PersonalityRoutine(0xe3eaccac, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zR) { + Label cie; + DwarfPointerEncoding pointer_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_sdata2); + section.SetPointerEncoding(pointer_encoding); + section + .Mark(&cie) + .CIEHeader(8011, 5496, 75, 1, "zR") + .ULEB128(1) // Augmentation data length + .D8(pointer_encoding) // encoding for FDE addresses + .FinishEntry() + .FDEHeader(cie, 0x540f9431, 0xbd0) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zR", section); + + EXPECT_CALL(handler, Entry(_, 0x540f9431, 0xbd0, 1, "zR", 75)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zS) { + Label cie; + section + .Mark(&cie) + .CIEHeader(9217, 7694, 57, 1, "zS") + .ULEB128(0) // Augmentation data length + .FinishEntry() + .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zS", section); + + EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zS", 57)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SignalHandler()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// These tests require manual inspection of the test output. +struct CFIReporterFixture { + CFIReporterFixture() : reporter("test file name", "test section name") { } + CallFrameInfo::Reporter reporter; +}; + +class CFIReporter: public CFIReporterFixture, public Test { }; + +TEST_F(CFIReporter, Incomplete) { + reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown); +} + +TEST_F(CFIReporter, EarlyEHTerminator) { + reporter.EarlyEHTerminator(0x0102030405060708ULL); +} + +TEST_F(CFIReporter, CIEPointerOutOfRange) { + reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, BadCIEId) { + reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, UnrecognizedVersion) { + reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43); +} + +TEST_F(CFIReporter, UnrecognizedAugmentation) { + reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles"); +} + +TEST_F(CFIReporter, InvalidPointerEncoding) { + reporter.InvalidPointerEncoding(0x0123456789abcdefULL, 0x42); +} + +TEST_F(CFIReporter, UnusablePointerEncoding) { + reporter.UnusablePointerEncoding(0x0123456789abcdefULL, 0x42); +} + +TEST_F(CFIReporter, RestoreInCIE) { + reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, BadInstruction) { + reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, NoCFARule) { + reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, EmptyStateStack) { + reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kTerminator, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, ClearingCFARule) { + reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE, + 0xfedcba9876543210ULL); +} + +#ifdef WRITE_ELF +// See comments at the top of the file mentioning WRITE_ELF for details. + +using google_breakpad::test_assembler::Section; + +struct ELFSectionHeader { + ELFSectionHeader(unsigned int set_type) + : type(set_type), flags(0), address(0), link(0), info(0), + alignment(1), entry_size(0) { } + Label name; + unsigned int type; + u_int64_t flags; + u_int64_t address; + Label file_offset; + Label file_size; + unsigned int link; + unsigned int info; + u_int64_t alignment; + u_int64_t entry_size; +}; + +void AppendSectionHeader(CFISection *table, const ELFSectionHeader &header) { + (*table) + .D32(header.name) // name, index in string tbl + .D32(header.type) // type + .Address(header.flags) // flags + .Address(header.address) // address in memory + .Address(header.file_offset) // offset in ELF file + .Address(header.file_size) // length in bytes + .D32(header.link) // link to related section + .D32(header.info) // miscellaneous + .Address(header.alignment) // alignment + .Address(header.entry_size); // entry size +} + +void WriteELFFrameSection(const char *filename, const char *cfi_name, + const CFISection &cfi) { + int elf_class = cfi.AddressSize() == 4 ? ELFCLASS32 : ELFCLASS64; + int elf_data = (cfi.endianness() == kBigEndian + ? ELFDATA2MSB : ELFDATA2LSB); + CFISection elf(cfi.endianness(), cfi.AddressSize()); + Label elf_header_size, section_table_offset; + elf + .Append("\x7f" "ELF") + .D8(elf_class) // 32-bit or 64-bit ELF + .D8(elf_data) // endianness + .D8(1) // ELF version + .D8(ELFOSABI_LINUX) // Operating System/ABI indication + .D8(0) // ABI version + .Append(7, 0xda) // padding + .D16(ET_EXEC) // file type: executable file + .D16(EM_386) // architecture: Intel IA-32 + .D32(EV_CURRENT); // ELF version + elf + .Address(0x0123456789abcdefULL) // program entry point + .Address(0) // program header offset + .Address(section_table_offset) // section header offset + .D32(0) // processor-specific flags + .D16(elf_header_size) // ELF header size in bytes */ + .D16(elf_class == ELFCLASS32 ? 32 : 56) // program header entry size + .D16(0) // program header table entry count + .D16(elf_class == ELFCLASS32 ? 40 : 64) // section header entry size + .D16(3) // section count + .D16(1) // section name string table + .Mark(&elf_header_size); + + // The null section. Every ELF file has one, as the first entry in + // the section header table. + ELFSectionHeader null_header(SHT_NULL); + null_header.file_offset = 0; + null_header.file_size = 0; + + // The CFI section. The whole reason for writing out this ELF file + // is to put this in it so that we can run other dumping programs on + // it to check its contents. + ELFSectionHeader cfi_header(SHT_PROGBITS); + cfi_header.file_size = cfi.Size(); + + // The section holding the names of the sections. This is the + // section whose index appears in the e_shstrndx member of the ELF + // header. + ELFSectionHeader section_names_header(SHT_STRTAB); + CFISection section_names(cfi.endianness(), cfi.AddressSize()); + section_names + .Mark(&null_header.name) + .AppendCString("") + .Mark(§ion_names_header.name) + .AppendCString(".shstrtab") + .Mark(&cfi_header.name) + .AppendCString(cfi_name) + .Mark(§ion_names_header.file_size); + + // Create the section table. The ELF header's e_shoff member refers + // to this, and the e_shnum member gives the number of entries it + // contains. + CFISection section_table(cfi.endianness(), cfi.AddressSize()); + AppendSectionHeader(§ion_table, null_header); + AppendSectionHeader(§ion_table, section_names_header); + AppendSectionHeader(§ion_table, cfi_header); + + // Append the section table and the section contents to the ELF file. + elf + .Mark(§ion_table_offset) + .Append(section_table) + .Mark(§ion_names_header.file_offset) + .Append(section_names) + .Mark(&cfi_header.file_offset) + .Append(cfi); + + string contents; + if (!elf.GetContents(&contents)) { + fprintf(stderr, "failed to get ELF file contents\n"); + exit(1); + } + + FILE *out = fopen(filename, "w"); + if (!out) { + fprintf(stderr, "error opening ELF file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + if (fwrite(contents.data(), 1, contents.size(), out) != contents.size()) { + fprintf(stderr, "error writing ELF data to '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + if (fclose(out) == EOF) { + fprintf(stderr, "error closing ELF file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } +} +#endif diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_die_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_die_unittest.cc new file mode 100644 index 0000000..e76fcae --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_die_unittest.cc @@ -0,0 +1,486 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit + +#include + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader_test_common.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; + +using dwarf2reader::AttributeList; +using dwarf2reader::ByteReader; +using dwarf2reader::CompilationUnit; +using dwarf2reader::Dwarf2Handler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfHasChild; +using dwarf2reader::DwarfTag; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::SectionMap; + +using std::string; +using std::vector; +using testing::InSequence; +using testing::Pointee; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::TestWithParam; +using testing::_; + +class MockDwarf2Handler: public Dwarf2Handler { + public: + MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version)); + MOCK_METHOD3(StartDIE, bool(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs)); + MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data)); + MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len)); + MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data)); + MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 signature)); + MOCK_METHOD1(EndDIE, void(uint64 offset)); +}; + +struct DIEFixture { + + DIEFixture() { + // Fix the initial offset of the .debug_info and .debug_abbrev sections. + info.start() = 0; + abbrevs.start() = 0; + + // Default expectations for the data handler. + EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, StartDIE(_, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); + EXPECT_CALL(handler, EndDIE(_)).Times(0); + } + + // Return a reference to a section map whose .debug_info section refers + // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This + // function returns a reference to the same SectionMap each time; new + // calls wipe out maps established by earlier calls. + const SectionMap &MakeSectionMap() { + // Copy the sections' contents into strings that will live as long as + // the map itself. + assert(info.GetContents(&info_contents)); + assert(abbrevs.GetContents(&abbrevs_contents)); + section_map.clear(); + section_map[".debug_info"].first = info_contents.data(); + section_map[".debug_info"].second = info_contents.size(); + section_map[".debug_abbrev"].first = abbrevs_contents.data(); + section_map[".debug_abbrev"].second = abbrevs_contents.size(); + return section_map; + } + + TestCompilationUnit info; + TestAbbrevTable abbrevs; + MockDwarf2Handler handler; + string abbrevs_contents, info_contents; + SectionMap section_map; +}; + +struct DwarfHeaderParams { + DwarfHeaderParams(Endianness endianness, size_t format_size, + int version, size_t address_size) + : endianness(endianness), format_size(format_size), + version(version), address_size(address_size) { } + Endianness endianness; + size_t format_size; // 4-byte or 8-byte DWARF offsets + int version; + size_t address_size; +}; + +class DwarfHeader: public DIEFixture, + public TestWithParam { }; + +TEST_P(DwarfHeader, Header) { + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_children_yes) + .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) + .EndAbbrev() + .EndTable(); + + info.set_format_size(GetParam().format_size); + info.set_endianness(GetParam().endianness); + + info.Header(GetParam().version, abbrev_table, GetParam().address_size) + .ULEB128(1) // DW_TAG_compile_unit, with children + .AppendCString("sam") // DW_AT_name, DW_FORM_string + .D8(0); // end of children + info.Finish(); + + { + InSequence s; + EXPECT_CALL(handler, + StartCompilationUnit(0, GetParam().address_size, + GetParam().format_size, _, + GetParam().version)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit, _)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string, + "sam")) + .WillOnce(Return()); + EXPECT_CALL(handler, EndDIE(_)) + .WillOnce(Return()); + } + + ByteReader byte_reader(GetParam().endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); + EXPECT_EQ(parser.Start(), info_contents.size()); +} + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfHeader, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); + +struct DwarfFormsFixture: public DIEFixture { + // Start a compilation unit, as directed by |params|, containing one + // childless DIE of the given tag, with one attribute of the given name + // and form. The 'info' fixture member is left just after the abbrev + // code, waiting for the attribute value to be appended. + void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, + DwarfTag tag, DwarfAttribute name, + DwarfForm form) { + // Create the abbreviation table. + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) + .Attribute(name, form) + .EndAbbrev() + .EndTable(); + + // Create the compilation unit, up to the attribute value. + info.set_format_size(params.format_size); + info.set_endianness(params.endianness); + info.Header(params.version, abbrev_table, params.address_size) + .ULEB128(1); // abbrev code + } + + // Set up handler to expect a compilation unit matching |params|, + // containing one childless DIE of the given tag, in the sequence s. Stop + // just before the expectations. + void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, + DwarfTag tag, uint64 offset=0) { + EXPECT_CALL(handler, + StartCompilationUnit(offset, params.address_size, + params.format_size, _, + params.version)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, tag, _)) + .InSequence(s) + .WillOnce(Return(true)); + } + + void ExpectEndCompilationUnit() { + EXPECT_CALL(handler, EndDIE(_)) + .InSequence(s) + .WillOnce(Return()); + } + + void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { + ByteReader byte_reader(params.endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler); + EXPECT_EQ(offset + parser.Start(), info_contents.size()); + } + + // The sequence to which the fixture's methods append expectations. + Sequence s; +}; + +struct DwarfForms: public DwarfFormsFixture, + public TestWithParam { }; + +TEST_P(DwarfForms, addr) { + StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr); + u_int64_t value; + if (GetParam().address_size == 4) { + value = 0xc8e9ffcc; + info.D32(value); + } else { + value = 0xe942517fc2768564ULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2_empty) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + info.D16(0); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + _, 0)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + unsigned char data[258]; + memset(data, '*', sizeof(data)); + info.D16(sizeof(data)) + .Append(data, sizeof(data)); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + Pointee('*'), 258)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, flag_present) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, + (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present); + // DW_FORM_flag_present occupies no space in the DIE. + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2); + EXPECT_CALL(handler, + ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present, + 1)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, sec_offset) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, + (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset); + u_int64_t value; + if (GetParam().format_size == 4) { + value = 0xacc9c388; + info.D32(value); + } else { + value = 0xcffe5696ffe3ed0aULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, exprloc) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, + (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc); + info.ULEB128(29) + .Append(29, 173); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc, + Pointee(173), 29)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, ref_sig8) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +// A value passed to ProcessAttributeSignature is just an absolute number, +// not an offset within the compilation unit as most of the other +// DW_FORM_ref forms are. Check that the reader doesn't try to apply any +// offset to the signature, by reading it from a compilation unit that does +// not start at the beginning of the section. +TEST_P(DwarfForms, ref_sig8_not_first) { + info.Append(98, '*'); + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam(), 98); +} + +// Tests for the other attribute forms could go here. + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfForms, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_test_common.h b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_test_common.h new file mode 100644 index 0000000..e46931a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/dwarf2reader_test_common.h @@ -0,0 +1,149 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf2reader_test_common.h: Define TestCompilationUnit and +// TestAbbrevTable, classes for creating properly (and improperly) +// formatted DWARF compilation unit data for unit tests. + +#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ +#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ + +#include "common/test_assembler.h" +#include "common/dwarf/dwarf2enums.h" + +// A subclass of test_assembler::Section, specialized for constructing +// DWARF compilation units. +class TestCompilationUnit: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef google_breakpad::test_assembler::Label Label; + + // Set the section's DWARF format size (the 32-bit DWARF format or the + // 64-bit DWARF format, for lengths and section offsets --- not the + // address size) to format_size. + void set_format_size(size_t format_size) { + assert(format_size == 4 || format_size == 8); + format_size_ = format_size; + } + + // Append a DWARF section offset value, of the appropriate size for this + // compilation unit. + template + void SectionOffset(T offset) { + if (format_size_ == 4) + D32(offset); + else + D64(offset); + } + + // Append a DWARF compilation unit header to the section, with the given + // DWARF version, abbrev table offset, and address size. + TestCompilationUnit &Header(int version, const Label &abbrev_offset, + size_t address_size) { + if (format_size_ == 4) { + D32(length_); + } else { + D32(0xffffffff); + D64(length_); + } + post_length_offset_ = Size(); + D16(version); + SectionOffset(abbrev_offset); + D8(address_size); + return *this; + } + + // Mark the end of this header's DIEs. + TestCompilationUnit &Finish() { + length_ = Size() - post_length_offset_; + return *this; + } + + private: + // The DWARF format size for this compilation unit. + size_t format_size_; + + // The offset of the point in the compilation unit header immediately + // after the initial length field. + u_int64_t post_length_offset_; + + // The length of the compilation unit, not including the initial length field. + Label length_; +}; + +// A subclass of test_assembler::Section specialized for constructing DWARF +// abbreviation tables. +class TestAbbrevTable: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef dwarf2reader::DwarfHasChild DwarfHasChild; + typedef google_breakpad::test_assembler::Label Label; + + // Start a new abbreviation table entry for abbreviation code |code|, + // encoding a DIE whose tag is |tag|, and which has children if and only + // if |has_children| is true. + TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) { + assert(code != 0); + ULEB128(code); + ULEB128(static_cast(tag)); + D8(static_cast(has_children)); + return *this; + }; + + // Add an attribute to the current abbreviation code whose name is |name| + // and whose form is |form|. + TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) { + ULEB128(static_cast(name)); + ULEB128(static_cast(form)); + return *this; + } + + // Finish the current abbreviation code. + TestAbbrevTable &EndAbbrev() { + ULEB128(0); + ULEB128(0); + return *this; + } + + // Finish the current abbreviation table. + TestAbbrevTable &EndTable() { + ULEB128(0); + return *this; + } +}; + +#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.cc b/src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.cc new file mode 100644 index 0000000..4a08045 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.cc @@ -0,0 +1,229 @@ +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a client for the dwarf2reader to extract function and line +// information from the debug info. + +#include +#include +#include + +#include +#include +#include +#include + +#include "common/dwarf/functioninfo.h" + +#include "common/dwarf/bytereader.h" + + +namespace dwarf2reader { + +CULineInfoHandler::CULineInfoHandler(std::vector* files, + std::vector* dirs, + LineMap* linemap):linemap_(linemap), + files_(files), + dirs_(dirs) { + // The dirs and files are 1 indexed, so just make sure we put + // nothing in the 0 vector. + assert(dirs->size() == 0); + assert(files->size() == 0); + dirs->push_back(""); + SourceFileInfo s; + s.name = ""; + s.lowpc = ULLONG_MAX; + files->push_back(s); +} + +void CULineInfoHandler::DefineDir(const std::string& name, uint32 dir_num) { + // These should never come out of order, actually + assert(dir_num == dirs_->size()); + dirs_->push_back(name); +} + +void CULineInfoHandler::DefineFile(const std::string& name, + int32 file_num, uint32 dir_num, + uint64 mod_time, uint64 length) { + assert(dir_num >= 0); + assert(dir_num < dirs_->size()); + + // These should never come out of order, actually. + if (file_num == (int32)files_->size() || file_num == -1) { + std::string dir = dirs_->at(dir_num); + + SourceFileInfo s; + s.lowpc = ULLONG_MAX; + + if (dir == "") { + s.name = name; + } else { + s.name = dir + "/" + name; + } + + files_->push_back(s); + } else { + fprintf(stderr, "error in DefineFile"); + } +} + +void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, + uint32 line_num, uint32 column_num) { + if (file_num < files_->size()) { + linemap_->insert( + std::make_pair(address, + std::make_pair(files_->at(file_num).name.c_str(), + line_num))); + + if(address < files_->at(file_num).lowpc) { + files_->at(file_num).lowpc = address; + } + } else { + fprintf(stderr,"error in AddLine"); + } +} + +bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, + uint8 address_size, + uint8 offset_size, + uint64 cu_length, + uint8 dwarf_version) { + current_compilation_unit_offset_ = offset; + return true; +} + + +// For function info, we only care about subprograms and inlined +// subroutines. For line info, the DW_AT_stmt_list lives in the +// compile unit tag. + +bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { + switch (tag) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: { + current_function_info_ = new FunctionInfo; + current_function_info_->lowpc = current_function_info_->highpc = 0; + current_function_info_->name = ""; + current_function_info_->line = 0; + current_function_info_->file = ""; + offset_to_funcinfo_->insert(std::make_pair(offset, + current_function_info_)); + }; + // FALLTHROUGH + case DW_TAG_compile_unit: + return true; + default: + return false; + } + return false; +} + +// Only care about the name attribute for functions + +void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string &data) { + if (current_function_info_) { + if (attr == DW_AT_name) + current_function_info_->name = data; + else if(attr == DW_AT_MIPS_linkage_name) + current_function_info_->mangled_name = data; + } +} + +void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (attr == DW_AT_stmt_list) { + SectionMap::const_iterator iter = sections_.find("__debug_line"); + assert(iter != sections_.end()); + + // this should be a scoped_ptr but we dont' use boost :-( + std::auto_ptr lireader(new LineInfo(iter->second.first + data, + iter->second.second - data, + reader_, linehandler_)); + lireader->Start(); + } else if (current_function_info_) { + switch (attr) { + case DW_AT_low_pc: + current_function_info_->lowpc = data; + break; + case DW_AT_high_pc: + current_function_info_->highpc = data; + break; + case DW_AT_decl_line: + current_function_info_->line = data; + break; + case DW_AT_decl_file: + current_function_info_->file = files_->at(data).name; + break; + default: + break; + } + } +} + +void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (current_function_info_) { + switch (attr) { + case DW_AT_specification: { + // Some functions have a "specification" attribute + // which means they were defined elsewhere. The name + // attribute is not repeated, and must be taken from + // the specification DIE. Here we'll assume that + // any DIE referenced in this manner will already have + // been seen, but that's not really required by the spec. + FunctionMap::iterator iter = offset_to_funcinfo_->find(data); + if (iter != offset_to_funcinfo_->end()) { + current_function_info_->name = iter->second->name; + current_function_info_->mangled_name = iter->second->mangled_name; + } else { + // If you hit this, this code probably needs to be rewritten. + fprintf(stderr, "Error: DW_AT_specification was seen before the referenced DIE! (Looking for DIE at offset %08llx, in DIE at offset %08llx)\n", data, offset); + } + break; + } + default: + break; + } + } +} + +void CUFunctionInfoHandler::EndDIE(uint64 offset) { + if (current_function_info_ && current_function_info_->lowpc) + address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, + current_function_info_)); +} + +} // namespace dwarf2reader diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.h b/src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.h new file mode 100644 index 0000000..85a31ff --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/functioninfo.h @@ -0,0 +1,188 @@ +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// This file contains the definitions for a DWARF2/3 information +// collector that uses the DWARF2/3 reader interface to build a mapping +// of addresses to files, lines, and functions. + +#ifndef COMMON_DWARF_FUNCTIONINFO_H__ +#define COMMON_DWARF_FUNCTIONINFO_H__ + +#include +#include +#include +#include + +#include "common/dwarf/dwarf2reader.h" + + +namespace dwarf2reader { + +struct FunctionInfo { + // Name of the function + std::string name; + // Mangled name of the function + std::string mangled_name; + // File containing this function + std::string file; + // Line number for start of function. + uint32 line; + // Beginning address for this function + uint64 lowpc; + // End address for this function. + uint64 highpc; +}; + +struct SourceFileInfo { + // Name of the source file name + std::string name; + // Low address of source file name + uint64 lowpc; +}; + +typedef std::map FunctionMap; +typedef std::map > LineMap; + +// This class is a basic line info handler that fills in the dirs, +// file, and linemap passed into it with the data produced from the +// LineInfoHandler. +class CULineInfoHandler: public LineInfoHandler { + public: + + // + CULineInfoHandler(std::vector* files, + std::vector* dirs, + LineMap* linemap); + virtual ~CULineInfoHandler() { } + + // Called when we define a directory. We just place NAME into dirs_ + // at position DIR_NUM. + virtual void DefineDir(const std::string& name, uint32 dir_num); + + // Called when we define a filename. We just place + // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM. + virtual void DefineFile(const std::string& name, int32 file_num, + uint32 dir_num, uint64 mod_time, uint64 length); + + + // Called when the line info reader has a new line, address pair + // ready for us. ADDRESS is the address of the code, LENGTH is the + // length of its machine code in bytes, FILE_NUM is the file number + // containing the code, LINE_NUM is the line number in that file for + // the code, and COLUMN_NUM is the column number the code starts at, + // if we know it (0 otherwise). + virtual void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num); + + private: + LineMap* linemap_; + std::vector* files_; + std::vector* dirs_; +}; + +class CUFunctionInfoHandler: public Dwarf2Handler { + public: + CUFunctionInfoHandler(std::vector* files, + std::vector* dirs, + LineMap* linemap, + FunctionMap* offset_to_funcinfo, + FunctionMap* address_to_funcinfo, + CULineInfoHandler* linehandler, + const SectionMap& sections, + ByteReader* reader) + : files_(files), dirs_(dirs), linemap_(linemap), + offset_to_funcinfo_(offset_to_funcinfo), + address_to_funcinfo_(address_to_funcinfo), + linehandler_(linehandler), sections_(sections), + reader_(reader), current_function_info_(NULL) { } + + virtual ~CUFunctionInfoHandler() { } + + // Start to process a compilation unit at OFFSET from the beginning of the + // .debug_info section. We want to see all compilation units, so we + // always return true. + + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + + // Start to process a DIE at OFFSET from the beginning of the + // .debug_info section. We only care about function related DIE's. + virtual bool StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs); + + // Called when we have an attribute with unsigned data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + virtual void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Called when we have an attribute with a DIE reference to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the offset of the referenced DIE from the start of the + // .debug_info section is in DATA. + virtual void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Called when we have an attribute with string data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + virtual void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data); + + // Called when finished processing the DIE at OFFSET. + // Because DWARF2/3 specifies a tree of DIEs, you may get starts + // before ends of the previous DIE, as we process children before + // ending the parent. + virtual void EndDIE(uint64 offset); + + private: + std::vector* files_; + std::vector* dirs_; + LineMap* linemap_; + FunctionMap* offset_to_funcinfo_; + FunctionMap* address_to_funcinfo_; + CULineInfoHandler* linehandler_; + const SectionMap& sections_; + ByteReader* reader_; + FunctionInfo* current_function_info_; + uint64 current_compilation_unit_offset_; +}; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_FUNCTIONINFO_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/line_state_machine.h b/src/lib/crashdump/gbreakpad/common/dwarf/line_state_machine.h new file mode 100644 index 0000000..0ff72ab --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/line_state_machine.h @@ -0,0 +1,61 @@ +// Copyright 2008 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_DWARF_LINE_STATE_MACHINE_H__ +#define COMMON_DWARF_LINE_STATE_MACHINE_H__ + +namespace dwarf2reader { + +// This is the format of a DWARF2/3 line state machine that we process +// opcodes using. There is no need for anything outside the lineinfo +// processor to know how this works. +struct LineStateMachine { + void Reset(bool default_is_stmt) { + file_num = 1; + address = 0; + line_num = 1; + column_num = 0; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + } + + uint32 file_num; + uint64 address; + uint32 line_num; + uint32 column_num; + bool is_stmt; // stmt means statement. + bool basic_block; + bool end_sequence; +}; + +} // namespace dwarf2reader + + +#endif // COMMON_DWARF_LINE_STATE_MACHINE_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf/types.h b/src/lib/crashdump/gbreakpad/common/dwarf/types.h new file mode 100644 index 0000000..61ca457 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf/types.h @@ -0,0 +1,55 @@ +// Copyright 2008 Google, Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// This file contains some typedefs for basic types + + +#ifndef _COMMON_DWARF_TYPES_H__ +#define _COMMON_DWARF_TYPES_H__ + +#include + +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +#ifdef __PTRDIFF_TYPE__ +typedef __PTRDIFF_TYPE__ intptr; +typedef unsigned __PTRDIFF_TYPE__ uintptr; +#else +#error "Can't find pointer-sized integral types." +#endif + +#endif // _COMMON_DWARF_TYPES_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.cc b/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.cc new file mode 100644 index 0000000..9aeb8ed --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.cc @@ -0,0 +1,247 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// Implementation of google_breakpad::DwarfCFIToModule. +// See dwarf_cfi_to_module.h for details. + +#include + +#include "common/dwarf_cfi_to_module.h" + +namespace google_breakpad { + +using std::ostringstream; + +vector DwarfCFIToModule::RegisterNames::MakeVector( + const char * const *strings, + size_t size) { + vector names(strings, strings + size); + return names; +} + +vector DwarfCFIToModule::RegisterNames::I386() { + static const char *const names[] = { + "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", + "$eip", "$eflags", "$unused1", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$unused2", "$unused3", + "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$fcw", "$fsw", "$mxcsr", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", + "$tr", "$ldtr" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +vector DwarfCFIToModule::RegisterNames::X86_64() { + static const char *const names[] = { + "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", + "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", + "$rip", + "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$rflags", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", + "$fs.base", "$gs.base", "$unused3", "$unused4", + "$tr", "$ldtr", + "$mxcsr", "$fcw", "$fsw" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +vector DwarfCFIToModule::RegisterNames::ARM() { + static const char *const names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "fps", "cpsr" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + +bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address) { + assert(!entry_); + + // If dwarf2reader::CallFrameInfo can handle this version and + // augmentation, then we should be okay with that, so there's no + // need to check them here. + + // Get ready to collect entries. + entry_ = new Module::StackFrameEntry; + entry_->address = address; + entry_->size = length; + entry_offset_ = offset; + return_address_ = return_address; + + // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI + // may not establish any rule for .ra if the return address column + // is an ordinary register, and that register holds the return + // address on entry to the function. So establish an initial .ra + // rule citing the return address register. + if (return_address_ < register_names_.size()) + entry_->initial_rules[ra_name_] = register_names_[return_address_]; + + return true; +} + +string DwarfCFIToModule::RegisterName(int i) { + assert(entry_); + if (i < 0) { + assert(i == kCFARegister); + return cfa_name_; + } + unsigned reg = i; + if (reg == return_address_) + return ra_name_; + + if (reg < register_names_.size()) + return register_names_[reg]; + + reporter_->UnnamedRegister(entry_offset_, reg); + char buf[30]; + sprintf(buf, "unnamed_register%u", reg); + return buf; +} + +void DwarfCFIToModule::Record(Module::Address address, int reg, + const string &rule) { + assert(entry_); + + // Place the name in our global set of strings, and then use the string + // from the set. Even though the assignment looks like a copy, all the + // major std::string implementations use reference counting internally, + // so the effect is to have all our data structures share copies of rules + // whenever possible. Since register names are drawn from a + // vector, register names are already shared. + string shared_rule = *common_strings_.insert(rule).first; + + // Is this one of this entry's initial rules? + if (address == entry_->address) + entry_->initial_rules[RegisterName(reg)] = shared_rule; + // File it under the appropriate address. + else + entry_->rule_changes[address][RegisterName(reg)] = shared_rule; +} + +bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { + reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { + ostringstream s; + s << RegisterName(reg); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, + int base_register, long offset) { + ostringstream s; + s << RegisterName(base_register) << " " << offset << " + ^"; + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, + int base_register, long offset) { + ostringstream s; + s << RegisterName(base_register) << " " << offset << " +"; + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, + int base_register) { + ostringstream s; + s << RegisterName(base_register); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, + const string &expression) { + reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, + const string &expression) { + reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::End() { + module_->AddStackFrameEntry(entry_); + entry_ = NULL; + return true; +} + +void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx refers to register %d," + " whose name we don't know\n", + file_.c_str(), section_.c_str(), offset, reg); +} + +void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset, + const string ®) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx sets the rule for " + "register '%s' to 'undefined', but the Breakpad symbol file format" + " cannot express this\n", + file_.c_str(), section_.c_str(), offset, reg.c_str()); +} + +void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset, + const string ®) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx uses a DWARF expression to" + " describe how to recover register '%s', " + " but this translator cannot yet translate DWARF expressions to" + " Breakpad postfix expressions\n", + file_.c_str(), section_.c_str(), offset, reg.c_str()); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.h b/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.h new file mode 100644 index 0000000..d29a796 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module.h @@ -0,0 +1,196 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which +// accepts parsed DWARF call frame info and adds it to a +// google_breakpad::Module object, which can write that information to +// a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H +#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H + +#include +#include + +#include +#include +#include + +#include "common/module.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +using dwarf2reader::CallFrameInfo; +using google_breakpad::Module; +using std::set; +using std::string; +using std::vector; + +// A class that accepts parsed call frame information from the DWARF +// CFI parser and populates a google_breakpad::Module object with the +// contents. +class DwarfCFIToModule: public CallFrameInfo::Handler { + public: + + // DwarfCFIToModule uses an instance of this class to report errors + // detected while converting DWARF CFI to Breakpad STACK CFI records. + class Reporter { + public: + // Create a reporter that writes messages to the standard error + // stream. FILE is the name of the file we're processing, and + // SECTION is the name of the section within that file that we're + // looking at (.debug_frame, .eh_frame, etc.). + Reporter(const string &file, const string §ion) + : file_(file), section_(section) { } + virtual ~Reporter() { } + + // The DWARF CFI entry at OFFSET cites register REG, but REG is not + // covered by the vector of register names passed to the + // DwarfCFIToModule constructor, nor does it match the return + // address column number for this entry. + virtual void UnnamedRegister(size_t offset, int reg); + + // The DWARF CFI entry at OFFSET says that REG is undefined, but the + // Breakpad symbol file format cannot express this. + virtual void UndefinedNotSupported(size_t offset, const string ®); + + // The DWARF CFI entry at OFFSET says that REG uses a DWARF + // expression to find its value, but DwarfCFIToModule is not + // capable of translating DWARF expressions to Breakpad postfix + // expressions. + virtual void ExpressionsNotSupported(size_t offset, const string ®); + + protected: + string file_, section_; + }; + + // Register name tables. If TABLE is a vector returned by one of these + // functions, then TABLE[R] is the name of the register numbered R in + // DWARF call frame information. + class RegisterNames { + public: + // Intel's "x86" or IA-32. + static vector I386(); + + // AMD x86_64, AMD64, Intel EM64T, or Intel 64 + static vector X86_64(); + + // ARM. + static vector ARM(); + + private: + // Given STRINGS, an array of C strings with SIZE elements, return an + // equivalent vector. + static vector MakeVector(const char * const *strings, size_t size); + }; + + // Create a handler for the dwarf2reader::CallFrameInfo parser that + // records the stack unwinding information it receives in MODULE. + // + // Use REGISTER_NAMES[I] as the name of register number I; *this + // keeps a reference to the vector, so the vector should remain + // alive for as long as the DwarfCFIToModule does. + // + // Use REPORTER for reporting problems encountered in the conversion + // process. + DwarfCFIToModule(Module *module, const vector ®ister_names, + Reporter *reporter) + : module_(module), register_names_(register_names), reporter_(reporter), + entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") { + } + virtual ~DwarfCFIToModule() { delete entry_; } + + virtual bool Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address); + virtual bool UndefinedRule(uint64 address, int reg); + virtual bool SameValueRule(uint64 address, int reg); + virtual bool OffsetRule(uint64 address, int reg, + int base_register, long offset); + virtual bool ValOffsetRule(uint64 address, int reg, + int base_register, long offset); + virtual bool RegisterRule(uint64 address, int reg, int base_register); + virtual bool ExpressionRule(uint64 address, int reg, + const string &expression); + virtual bool ValExpressionRule(uint64 address, int reg, + const string &expression); + virtual bool End(); + + private: + // Return the name to use for register REG. + string RegisterName(int i); + + // Record RULE for register REG at ADDRESS. + void Record(Module::Address address, int reg, const string &rule); + + // The module to which we should add entries. + Module *module_; + + // Map from register numbers to register names. + const vector ®ister_names_; + + // The reporter to use to report problems. + Reporter *reporter_; + + // The current entry we're constructing. + Module::StackFrameEntry *entry_; + + // The section offset of the current frame description entry, for + // use in error messages. + size_t entry_offset_; + + // The return address column for that entry. + unsigned return_address_; + + // The names of the return address and canonical frame address. Putting + // these here instead of using string literals allows us to share their + // texts in reference-counted std::string implementations (all the + // popular ones). Many, many rules cite these strings. + string cfa_name_, ra_name_; + + // A set of strings used by this CFI. Before storing a string in one of + // our data structures, insert it into this set, and then use the string + // from the set. + // + // Because std::string uses reference counting internally, simply using + // strings from this set, even if passed by value, assigned, or held + // directly in structures and containers (map, for example), + // causes those strings to share a single instance of each distinct piece + // of text. + set common_strings_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module_unittest.cc new file mode 100644 index 0000000..3d12949 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_cfi_to_module_unittest.cc @@ -0,0 +1,294 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf_cfi_to_module.h" + +using std::string; +using std::vector; + +using google_breakpad::Module; +using google_breakpad::DwarfCFIToModule; +using testing::ContainerEq; +using testing::Test; +using testing::_; + +struct MockCFIReporter: public DwarfCFIToModule::Reporter { + MockCFIReporter(const string &file, const string §ion) + : Reporter(file, section) { } + MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg)); + MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®)); + MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®)); +}; + +struct DwarfCFIToModuleFixture { + DwarfCFIToModuleFixture() + : module("module name", "module os", "module arch", "module id"), + reporter("reporter file", "reporter section"), + handler(&module, register_names, &reporter) { + register_names.push_back("reg0"); + register_names.push_back("reg1"); + register_names.push_back("reg2"); + register_names.push_back("reg3"); + register_names.push_back("reg4"); + register_names.push_back("reg5"); + register_names.push_back("reg6"); + register_names.push_back("reg7"); + register_names.push_back("sp"); + register_names.push_back("pc"); + + EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0); + EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0); + EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0); + } + + Module module; + vector register_names; + MockCFIReporter reporter; + DwarfCFIToModule handler; + vector entries; +}; + +class Entry: public DwarfCFIToModuleFixture, public Test { }; + +TEST_F(Entry, Accept) { + ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL, + 0xb440ce248169c8d6ULL, 3, "", 0xea93c106)); + ASSERT_TRUE(handler.End()); + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address); + EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Entry, AcceptOldVersion) { + ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL, + 0xc771f44958d40bbcULL, 1, "", 0x093c945e)); + ASSERT_TRUE(handler.End()); + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address); + EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +struct RuleFixture: public DwarfCFIToModuleFixture { + RuleFixture() : DwarfCFIToModuleFixture() { + entry_address = 0x89327ebf86b47492ULL; + entry_size = 0x2f8cd573072fe02aULL; + return_reg = 0x7886a346; + } + void StartEntry() { + ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size, + 3, "", return_reg)); + } + void CheckEntry() { + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(entry_address, entries[0]->address); + EXPECT_EQ(entry_size, entries[0]->size); + } + uint64 entry_address, entry_size; + unsigned return_reg; +}; + +class Rule: public RuleFixture, public Test { }; + +TEST_F(Rule, UndefinedRule) { + EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7")); + StartEntry(); + ASSERT_TRUE(handler.UndefinedRule(entry_address, 7)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, SameValueRule) { + StartEntry(); + ASSERT_TRUE(handler.SameValueRule(entry_address, 6)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial["reg6"] = "reg6"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, OffsetRule) { + StartEntry(); + ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg, + DwarfCFIToModule::kCFARegister, + 16927065)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, OffsetRuleNegative) { + StartEntry(); + ASSERT_TRUE(handler.OffsetRule(entry_address + 1, + DwarfCFIToModule::kCFARegister, 4, -34530721)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, ValOffsetRule) { + // Use an unnamed register number, to exercise that branch of RegisterName. + EXPECT_CALL(reporter, UnnamedRegister(_, 10)); + StartEntry(); + ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7, + DwarfCFIToModule::kCFARegister, + 10, 61812979)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 0x5ab7][".cfa"] = + "unnamed_register10 61812979 +"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, RegisterRule) { + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg3"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, ExpressionRule) { + EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2")); + StartEntry(); + ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2, + "it takes two to tango")); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, ValExpressionRule) { + EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0")); + StartEntry(); + ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0, + "bit off more than he could chew")); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRule) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg2"; + expected_initial["reg0"] = "reg1"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRuleOverride) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg1"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRuleLater) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg2"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".ra"] = "reg1"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST(RegisterNames, I386) { + vector names = DwarfCFIToModule::RegisterNames::I386(); + + EXPECT_EQ("$eax", names[0]); + EXPECT_EQ("$ecx", names[1]); + EXPECT_EQ("$esp", names[4]); + EXPECT_EQ("$eip", names[8]); +} + +TEST(RegisterNames, ARM) { + vector names = DwarfCFIToModule::RegisterNames::ARM(); + + EXPECT_EQ("r0", names[0]); + EXPECT_EQ("r10", names[10]); + EXPECT_EQ("sp", names[13]); + EXPECT_EQ("lr", names[14]); + EXPECT_EQ("pc", names[15]); +} + +TEST(RegisterNames, X86_64) { + vector names = DwarfCFIToModule::RegisterNames::X86_64(); + + EXPECT_EQ("$rax", names[0]); + EXPECT_EQ("$rdx", names[1]); + EXPECT_EQ("$rbp", names[6]); + EXPECT_EQ("$rsp", names[7]); + EXPECT_EQ("$rip", names[16]); +} diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.cc b/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.cc new file mode 100644 index 0000000..ded5f83 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.cc @@ -0,0 +1,936 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h. + +// For PRI* macros, before anything else might #include it. +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif /* __STDC_FORMAT_MACROS */ + +#include "common/dwarf_cu_to_module.h" + +#include +#include +#include + +#include +#include +#include + +#include "common/dwarf_line_to_module.h" + +namespace google_breakpad { + +using std::map; +using std::pair; +using std::set; +using std::vector; + +// Data provided by a DWARF specification DIE. +// +// In DWARF, the DIE for a definition may contain a DW_AT_specification +// attribute giving the offset of the corresponding declaration DIE, and +// the definition DIE may omit information given in the declaration. For +// example, it's common for a function's address range to appear only in +// its definition DIE, but its name to appear only in its declaration +// DIE. +// +// The dumper needs to be able to follow DW_AT_specification links to +// bring all this information together in a FUNC record. Conveniently, +// DIEs that are the target of such links have a DW_AT_declaration flag +// set, so we can identify them when we first see them, and record their +// contents for later reference. +// +// A Specification holds information gathered from a declaration DIE that +// we may need if we find a DW_AT_specification link pointing to it. +struct DwarfCUToModule::Specification { + // The name of the enclosing scope, or the empty string if there is none. + string enclosing_name; + + // The name for the specification DIE itself, without any enclosing + // name components. + string unqualified_name; +}; + +// An abstract origin -- base definition of an inline function. +struct AbstractOrigin { + AbstractOrigin() : name() {} + AbstractOrigin(const string& name) : name(name) {} + + string name; +}; + +typedef map AbstractOriginByOffset; + +// Data global to the DWARF-bearing file that is private to the +// DWARF-to-Module process. +struct DwarfCUToModule::FilePrivate { + // A set of strings used in this CU. Before storing a string in one of + // our data structures, insert it into this set, and then use the string + // from the set. + // + // Because std::string uses reference counting internally, simply using + // strings from this set, even if passed by value, assigned, or held + // directly in structures and containers (map, for example), + // causes those strings to share a single instance of each distinct piece + // of text. + set common_strings; + + // A map from offsets of DIEs within the .debug_info section to + // Specifications describing those DIEs. Specification references can + // cross compilation unit boundaries. + SpecificationByOffset specifications; + + AbstractOriginByOffset origins; +}; + +DwarfCUToModule::FileContext::FileContext(const string &filename_arg, + Module *module_arg) + : filename(filename_arg), module(module_arg) { + file_private = new FilePrivate(); +} + +DwarfCUToModule::FileContext::~FileContext() { + delete file_private; +} + +// Information global to the particular compilation unit we're +// parsing. This is for data shared across the CU's entire DIE tree, +// and parameters from the code invoking the CU parser. +struct DwarfCUToModule::CUContext { + CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg) + : file_context(file_context_arg), + reporter(reporter_arg), + language(Language::CPlusPlus) { } + ~CUContext() { + for (vector::iterator it = functions.begin(); + it != functions.end(); it++) + delete *it; + }; + + // The DWARF-bearing file into which this CU was incorporated. + FileContext *file_context; + + // For printing error messages. + WarningReporter *reporter; + + // The source language of this compilation unit. + const Language *language; + + // The functions defined in this compilation unit. We accumulate + // them here during parsing. Then, in DwarfCUToModule::Finish, we + // assign them lines and add them to file_context->module. + // + // Destroying this destroys all the functions this vector points to. + vector functions; +}; + +// Information about the context of a particular DIE. This is for +// information that changes as we descend the tree towards the leaves: +// the containing classes/namespaces, etc. +struct DwarfCUToModule::DIEContext { + // The fully-qualified name of the context. For example, for a + // tree like: + // + // DW_TAG_namespace Foo + // DW_TAG_class Bar + // DW_TAG_subprogram Baz + // + // in a C++ compilation unit, the DIEContext's name for the + // DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's + // name for the DW_TAG_namespace DIE would be "". + string name; +}; + +// An abstract base class for all the dumper's DIE handlers. +class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler { + public: + // Create a handler for the DIE at OFFSET whose compilation unit is + // described by CU_CONTEXT, and whose immediate context is described + // by PARENT_CONTEXT. + GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : cu_context_(cu_context), + parent_context_(parent_context), + offset_(offset), + declaration_(false), + specification_(NULL) { } + + // Derived classes' ProcessAttributeUnsigned can defer to this to + // handle DW_AT_declaration, or simply not override it. + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Derived classes' ProcessAttributeReference can defer to this to + // handle DW_AT_specification, or simply not override it. + void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Derived classes' ProcessAttributeReference can defer to this to + // handle DW_AT_specification, or simply not override it. + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + + protected: + // Compute and return the fully-qualified name of the DIE. If this + // DIE is a declaration DIE, to be cited by other DIEs' + // DW_AT_specification attributes, record its enclosing name and + // unqualified name in the specification table. + // + // Use this from EndAttributes member functions, not ProcessAttribute* + // functions; only the former can be sure that all the DIE's attributes + // have been seen. + string ComputeQualifiedName(); + + CUContext *cu_context_; + DIEContext *parent_context_; + uint64 offset_; + + // If this DIE has a DW_AT_declaration attribute, this is its value. + // It is false on DIEs with no DW_AT_declaration attribute. + bool declaration_; + + // If this DIE has a DW_AT_specification attribute, this is the + // Specification structure for the DIE the attribute refers to. + // Otherwise, this is NULL. + Specification *specification_; + + // The value of the DW_AT_name attribute, or the empty string if the + // DIE has no such attribute. + string name_attribute_; +}; + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break; + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_specification: { + // Find the Specification to which this attribute refers, and + // set specification_ appropriately. We could do more processing + // here, but it's better to leave the real work to our + // EndAttribute member function, at which point we know we have + // seen all the DIE's attributes. + FileContext *file_context = cu_context_->file_context; + SpecificationByOffset *specifications + = &file_context->file_private->specifications; + SpecificationByOffset::iterator spec = specifications->find(data); + if (spec != specifications->end()) { + specification_ = &spec->second; + } else { + // Technically, there's no reason a DW_AT_specification + // couldn't be a forward reference, but supporting that would + // be a lot of work (changing to a two-pass structure), and I + // don't think any producers we care about ever emit such + // things. + cu_context_->reporter->UnknownSpecification(offset_, data); + } + break; + } + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + switch (attr) { + case dwarf2reader::DW_AT_name: { + // Place the name in our global set of strings, and then use the + // string from the set. Even though the assignment looks like a copy, + // all the major std::string implementations use reference counting + // internally, so the effect is to have all our data structures share + // copies of strings whenever possible. + pair::iterator, bool> result = + cu_context_->file_context->file_private->common_strings.insert(data); + name_attribute_ = *result.first; + break; + } + default: break; + } +} + +string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { + // Find our unqualified name. If the DIE has its own DW_AT_name + // attribute, then use that; otherwise, check our specification. + const string *unqualified_name; + if (name_attribute_.empty() && specification_) + unqualified_name = &specification_->unqualified_name; + else + unqualified_name = &name_attribute_; + + // Find the name of our enclosing context. If we have a + // specification, it's the specification's enclosing context that + // counts; otherwise, use this DIE's context. + const string *enclosing_name; + if (specification_) + enclosing_name = &specification_->enclosing_name; + else + enclosing_name = &parent_context_->name; + + // If this DIE was marked as a declaration, record its names in the + // specification table. + if (declaration_) { + FileContext *file_context = cu_context_->file_context; + Specification spec; + spec.enclosing_name = *enclosing_name; + spec.unqualified_name = *unqualified_name; + file_context->file_private->specifications[offset_] = spec; + } + + // Combine the enclosing name and unqualified name to produce our + // own fully-qualified name. + return cu_context_->language->MakeQualifiedName(*enclosing_name, + *unqualified_name); +} + +// A handler class for DW_TAG_subprogram DIEs. +class DwarfCUToModule::FuncHandler: public GenericDIEHandler { + public: + FuncHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : GenericDIEHandler(cu_context, parent_context, offset), + low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { } + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + bool EndAttributes(); + void Finish(); + + private: + // The fully-qualified name, as derived from name_attribute_, + // specification_, parent_context_. Computed in EndAttributes. + string name_; + uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc + const AbstractOrigin* abstract_origin_; + bool inline_; +}; + +void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + // If this attribute is present at all --- even if its value is + // DW_INL_not_inlined --- then GCC may cite it as someone else's + // DW_AT_abstract_origin attribute. + case dwarf2reader::DW_AT_inline: inline_ = true; break; + + case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break; + case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break; + default: + GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + // If this attribute is present at all --- even if its value is + // DW_INL_not_inlined --- then GCC may cite it as someone else's + // DW_AT_abstract_origin attribute. + case dwarf2reader::DW_AT_inline: inline_ = true; break; + + default: + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch(attr) { + case dwarf2reader::DW_AT_abstract_origin: { + const AbstractOriginByOffset& origins = + cu_context_->file_context->file_private->origins; + AbstractOriginByOffset::const_iterator origin = origins.find(data); + if (origin != origins.end()) { + abstract_origin_ = &(origin->second); + } else { + cu_context_->reporter->UnknownAbstractOrigin(offset_, data); + } + break; + } + default: + GenericDIEHandler::ProcessAttributeReference(attr, form, data); + break; + } +} + +bool DwarfCUToModule::FuncHandler::EndAttributes() { + // Compute our name, and record a specification, if appropriate. + name_ = ComputeQualifiedName(); + if (name_.empty() && abstract_origin_) { + name_ = abstract_origin_->name; + } + return true; +} + +void DwarfCUToModule::FuncHandler::Finish() { + // Did we collect the information we need? Not all DWARF function + // entries have low and high addresses (for example, inlined + // functions that were never used), but all the ones we're + // interested in cover a non-empty range of bytes. + if (low_pc_ < high_pc_) { + // Create a Module::Function based on the data we've gathered, and + // add it to the functions_ list. + Module::Function *func = new Module::Function; + // Malformed DWARF may omit the name, but all Module::Functions must + // have names. + if (!name_.empty()) { + func->name = name_; + } else { + cu_context_->reporter->UnnamedFunction(offset_); + func->name = ""; + } + func->address = low_pc_; + func->size = high_pc_ - low_pc_; + func->parameter_size = 0; + if (func->address) { + // If the function address is zero this is a sign that this function + // description is just empty debug data and should just be discarded. + cu_context_->functions.push_back(func); + } + } else if (inline_) { + AbstractOrigin origin(name_); + cu_context_->file_context->file_private->origins[offset_] = origin; + } +} + +// A handler for DIEs that contain functions and contribute a +// component to their names: namespaces, classes, etc. +class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler { + public: + NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : GenericDIEHandler(cu_context, parent_context, offset) { } + bool EndAttributes(); + DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs); + + private: + DIEContext child_context_; // A context for our children. +}; + +bool DwarfCUToModule::NamedScopeHandler::EndAttributes() { + child_context_.name = ComputeQualifiedName(); + return true; +} + +dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler( + uint64 offset, + enum DwarfTag tag, + const AttributeList &attrs) { + switch (tag) { + case dwarf2reader::DW_TAG_subprogram: + return new FuncHandler(cu_context_, &child_context_, offset); + case dwarf2reader::DW_TAG_namespace: + case dwarf2reader::DW_TAG_class_type: + case dwarf2reader::DW_TAG_structure_type: + case dwarf2reader::DW_TAG_union_type: + return new NamedScopeHandler(cu_context_, &child_context_, offset); + default: + return NULL; + } +} + +void DwarfCUToModule::WarningReporter::CUHeading() { + if (printed_cu_header_) + return; + fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n", + filename_.c_str(), cu_name_.c_str(), cu_offset_); + printed_cu_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset, + uint64 target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification" + " attribute referring to the die at offset 0x%llx, which either" + " was not marked as a declaration, or comes later in the file\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset, + uint64 target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin" + " attribute referring to the die at offset 0x%llx, which either" + " was not marked as an inline, or comes later in the file\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::MissingSection(const string &name) { + CUHeading(); + fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n", + filename_.c_str(), name.c_str()); +} + +void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: line number data offset beyond end" + " of '.debug_line' section\n", + filename_.c_str()); +} + +void DwarfCUToModule::WarningReporter::UncoveredHeading() { + if (printed_unpaired_header_) + return; + CUHeading(); + fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n", + filename_.c_str()); + printed_unpaired_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UncoveredFunction( + const Module::Function &function) { + if (!uncovered_warnings_enabled_) + return; + UncoveredHeading(); + fprintf(stderr, " function%s: %s\n", + function.size == 0 ? " (zero-length)" : "", + function.name.c_str()); +} + +void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) { + if (!uncovered_warnings_enabled_) + return; + UncoveredHeading(); + fprintf(stderr, " line%s: %s:%d at 0x%" PRIx64 "\n", + (line.size == 0 ? " (zero-length)" : ""), + line.file->name.c_str(), line.number, line.address); +} + +void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: function at offset 0x%llx has no name\n", + filename_.c_str(), offset); +} + +DwarfCUToModule::DwarfCUToModule(FileContext *file_context, + LineToModuleFunctor *line_reader, + WarningReporter *reporter) + : line_reader_(line_reader), has_source_line_info_(false) { + cu_context_ = new CUContext(file_context, reporter); + child_context_ = new DIEContext(); +} + +DwarfCUToModule::~DwarfCUToModule() { + delete cu_context_; + delete child_context_; +} + +void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + case dwarf2reader::DW_AT_language: // source language of this CU + SetLanguage(static_cast(data)); + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_stmt_list: // Line number information. + has_source_line_info_ = true; + source_line_offset_ = data; + break; + case dwarf2reader::DW_AT_language: // source language of this CU + SetLanguage(static_cast(data)); + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + if (attr == dwarf2reader::DW_AT_name) + cu_context_->reporter->SetCUName(data); +} + +bool DwarfCUToModule::EndAttributes() { + return true; +} + +dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler( + uint64 offset, + enum DwarfTag tag, + const AttributeList &attrs) { + switch (tag) { + case dwarf2reader::DW_TAG_subprogram: + return new FuncHandler(cu_context_, child_context_, offset); + case dwarf2reader::DW_TAG_namespace: + case dwarf2reader::DW_TAG_class_type: + case dwarf2reader::DW_TAG_structure_type: + case dwarf2reader::DW_TAG_union_type: + return new NamedScopeHandler(cu_context_, child_context_, offset); + default: + return NULL; + } +} + +void DwarfCUToModule::SetLanguage(DwarfLanguage language) { + switch (language) { + case dwarf2reader::DW_LANG_Java: + cu_context_->language = Language::Java; + break; + + // DWARF has no generic language code for assembly language; this is + // what the GNU toolchain uses. + case dwarf2reader::DW_LANG_Mips_Assembler: + cu_context_->language = Language::Assembler; + break; + + // C++ covers so many cases that it probably has some way to cope + // with whatever the other languages throw at us. So make it the + // default. + // + // Objective C and Objective C++ seem to create entries for + // methods whose DW_AT_name values are already fully-qualified: + // "-[Classname method:]". These appear at the top level. + // + // DWARF data for C should never include namespaces or functions + // nested in struct types, but if it ever does, then C++'s + // notation is probably not a bad choice for that. + default: + case dwarf2reader::DW_LANG_ObjC: + case dwarf2reader::DW_LANG_ObjC_plus_plus: + case dwarf2reader::DW_LANG_C: + case dwarf2reader::DW_LANG_C89: + case dwarf2reader::DW_LANG_C99: + case dwarf2reader::DW_LANG_C_plus_plus: + cu_context_->language = Language::CPlusPlus; + break; + } +} + +void DwarfCUToModule::ReadSourceLines(uint64 offset) { + const dwarf2reader::SectionMap §ion_map + = cu_context_->file_context->section_map; + dwarf2reader::SectionMap::const_iterator map_entry + = section_map.find(".debug_line"); + // Mac OS X puts DWARF data in sections whose names begin with "__" + // instead of ".". + if (map_entry == section_map.end()) + map_entry = section_map.find("__debug_line"); + if (map_entry == section_map.end()) { + cu_context_->reporter->MissingSection(".debug_line"); + return; + } + const char *section_start = map_entry->second.first; + uint64 section_length = map_entry->second.second; + if (offset >= section_length) { + cu_context_->reporter->BadLineInfoOffset(offset); + return; + } + (*line_reader_)(section_start + offset, section_length - offset, + cu_context_->file_context->module, &lines_); +} + +namespace { +// Return true if ADDRESS falls within the range of ITEM. +template +inline bool within(const T &item, Module::Address address) { + // Because Module::Address is unsigned, and unsigned arithmetic + // wraps around, this will be false if ADDRESS falls before the + // start of ITEM, or if it falls after ITEM's end. + return address - item.address < item.size; +} +} + +void DwarfCUToModule::AssignLinesToFunctions() { + vector *functions = &cu_context_->functions; + WarningReporter *reporter = cu_context_->reporter; + + // This would be simpler if we assumed that source line entries + // don't cross function boundaries. However, there's no real reason + // to assume that (say) a series of function definitions on the same + // line wouldn't get coalesced into one line number entry. The + // DWARF spec certainly makes no such promises. + // + // So treat the functions and lines as peers, and take the trouble + // to compute their ranges' intersections precisely. In any case, + // the hair here is a constant factor for performance; the + // complexity from here on out is linear. + + // Put both our functions and lines in order by address. + sort(functions->begin(), functions->end(), + Module::Function::CompareByAddress); + sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress); + + // The last line that we used any piece of. We use this only for + // generating warnings. + const Module::Line *last_line_used = NULL; + + // The last function and line we warned about --- so we can avoid + // doing so more than once. + const Module::Function *last_function_cited = NULL; + const Module::Line *last_line_cited = NULL; + + // Make a single pass through both vectors from lower to higher + // addresses, populating each Function's lines vector with lines + // from our lines_ vector that fall within the function's address + // range. + vector::iterator func_it = functions->begin(); + vector::const_iterator line_it = lines_.begin(); + + Module::Address current; + + // Pointers to the referents of func_it and line_it, or NULL if the + // iterator is at the end of the sequence. + Module::Function *func; + const Module::Line *line; + + // Start current at the beginning of the first line or function, + // whichever is earlier. + if (func_it != functions->end() && line_it != lines_.end()) { + func = *func_it; + line = &*line_it; + current = std::min(func->address, line->address); + } else if (line_it != lines_.end()) { + func = NULL; + line = &*line_it; + current = line->address; + } else if (func_it != functions->end()) { + func = *func_it; + line = NULL; + current = (*func_it)->address; + } else { + return; + } + + while (func || line) { + // This loop has two invariants that hold at the top. + // + // First, at least one of the iterators is not at the end of its + // sequence, and those that are not refer to the earliest + // function or line that contains or starts after CURRENT. + // + // Note that every byte is in one of four states: it is covered + // or not covered by a function, and, independently, it is + // covered or not covered by a line. + // + // The second invariant is that CURRENT refers to a byte whose + // state is different from its predecessor, or it refers to the + // first byte in the address space. In other words, CURRENT is + // always the address of a transition. + // + // Note that, although each iteration advances CURRENT from one + // transition address to the next in each iteration, it might + // not advance the iterators. Suppose we have a function that + // starts with a line, has a gap, and then a second line, and + // suppose that we enter an iteration with CURRENT at the end of + // the first line. The next transition address is the start of + // the second line, after the gap, so the iteration should + // advance CURRENT to that point. At the head of that iteration, + // the invariants require that the line iterator be pointing at + // the second line. But this is also true at the head of the + // next. And clearly, the iteration must not change the function + // iterator. So neither iterator moves. + + // Assert the first invariant (see above). + assert(!func || current < func->address || within(*func, current)); + assert(!line || current < line->address || within(*line, current)); + + // The next transition after CURRENT. + Module::Address next_transition; + + // Figure out which state we're in, add lines or warn, and compute + // the next transition address. + if (func && current >= func->address) { + if (line && current >= line->address) { + // Covered by both a line and a function. + Module::Address func_left = func->size - (current - func->address); + Module::Address line_left = line->size - (current - line->address); + // This may overflow, but things work out. + next_transition = current + std::min(func_left, line_left); + Module::Line l = *line; + l.address = current; + l.size = next_transition - current; + func->lines.push_back(l); + last_line_used = line; + } else { + // Covered by a function, but no line. + if (func != last_function_cited) { + reporter->UncoveredFunction(*func); + last_function_cited = func; + } + if (line && within(*func, line->address)) + next_transition = line->address; + else + // If this overflows, we'll catch it below. + next_transition = func->address + func->size; + } + } else { + if (line && current >= line->address) { + // Covered by a line, but no function. + // + // If GCC emits padding after one function to align the start + // of the next, then it will attribute the padding + // instructions to the last source line of function (to reduce + // the size of the line number info), but omit it from the + // DW_AT_{low,high}_pc range given in .debug_info (since it + // costs nothing to be precise there). If we did use at least + // some of the line we're about to skip, and it ends at the + // start of the next function, then assume this is what + // happened, and don't warn. + if (line != last_line_cited + && !(func + && line == last_line_used + && func->address - line->address == line->size)) { + reporter->UncoveredLine(*line); + last_line_cited = line; + } + if (func && within(*line, func->address)) + next_transition = func->address; + else + // If this overflows, we'll catch it below. + next_transition = line->address + line->size; + } else { + // Covered by neither a function nor a line. By the invariant, + // both func and line begin after CURRENT. The next transition + // is the start of the next function or next line, whichever + // is earliest. + assert (func || line); + if (func && line) + next_transition = std::min(func->address, line->address); + else if (func) + next_transition = func->address; + else + next_transition = line->address; + } + } + + // If a function or line abuts the end of the address space, then + // next_transition may end up being zero, in which case we've completed + // our pass. Handle that here, instead of trying to deal with it in + // each place we compute next_transition. + if (!next_transition) + break; + + // Advance iterators as needed. If lines overlap or functions overlap, + // then we could go around more than once. We don't worry too much + // about what result we produce in that case, just as long as we don't + // hang or crash. + while (func_it != functions->end() + && next_transition >= (*func_it)->address + && !within(**func_it, next_transition)) + func_it++; + func = (func_it != functions->end()) ? *func_it : NULL; + while (line_it != lines_.end() + && next_transition >= line_it->address + && !within(*line_it, next_transition)) + line_it++; + line = (line_it != lines_.end()) ? &*line_it : NULL; + + // We must make progress. + assert(next_transition > current); + current = next_transition; + } +} + +void DwarfCUToModule::Finish() { + // Assembly language files have no function data, and that gives us + // no place to store our line numbers (even though the GNU toolchain + // will happily produce source line info for assembly language + // files). To avoid spurious warnings about lines we can't assign + // to functions, skip CUs in languages that lack functions. + if (!cu_context_->language->HasFunctions()) + return; + + // Read source line info, if we have any. + if (has_source_line_info_) + ReadSourceLines(source_line_offset_); + + vector *functions = &cu_context_->functions; + + // Dole out lines to the appropriate functions. + AssignLinesToFunctions(); + + // Add our functions, which now have source lines assigned to them, + // to module_. + cu_context_->file_context->module->AddFunctions(functions->begin(), + functions->end()); + + // Ownership of the function objects has shifted from cu_context to + // the Module. + functions->clear(); +} + +bool DwarfCUToModule::StartCompilationUnit(uint64 offset, + uint8 address_size, + uint8 offset_size, + uint64 cu_length, + uint8 dwarf_version) { + return dwarf_version >= 2; +} + +bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { + // We don't deal with partial compilation units (the only other tag + // likely to be used for root DIE). + return tag == dwarf2reader::DW_TAG_compile_unit; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.h b/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.h new file mode 100644 index 0000000..9ab86a1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module.h @@ -0,0 +1,276 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// Add DWARF debugging information to a Breakpad symbol file. This +// file defines the DwarfCUToModule class, which accepts parsed DWARF +// data and populates a google_breakpad::Module with the results; the +// Module can then write its contents as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__ +#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__ + +#include + +#include "common/language.h" +#include "common/module.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +using dwarf2reader::AttributeList; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfLanguage; +using dwarf2reader::DwarfTag; + +// Populate a google_breakpad::Module with DWARF debugging information. +// +// An instance of this class can be provided as a handler to a +// dwarf2reader::DIEDispatcher, which can in turn be a handler for a +// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results +// of parsing to populate a google_breakpad::Module with source file, +// function, and source line information. +class DwarfCUToModule: public dwarf2reader::RootDIEHandler { + struct FilePrivate; + public: + + // Information global to the DWARF-bearing file we are processing, + // for use by DwarfCUToModule. Each DwarfCUToModule instance deals + // with a single compilation unit within the file, but information + // global to the whole file is held here. The client is responsible + // for filling it in appropriately (except for the 'file_private' + // field, which the constructor and destructor take care of), and + // then providing it to the DwarfCUToModule instance for each + // compilation unit we process in that file. + struct FileContext { + FileContext(const string &filename_arg, Module *module_arg); + ~FileContext(); + + // The name of this file, for use in error messages. + string filename; + + // A map of this file's sections, used for finding other DWARF + // sections that the .debug_info section may refer to. + dwarf2reader::SectionMap section_map; + + // The Module to which we're contributing definitions. + Module *module; + + // Inter-compilation unit data used internally by the handlers. + FilePrivate *file_private; + }; + + // An abstract base class for functors that handle DWARF line data + // for DwarfCUToModule. DwarfCUToModule could certainly just use + // dwarf2reader::LineInfo itself directly, but decoupling things + // this way makes unit testing a little easier. + class LineToModuleFunctor { + public: + LineToModuleFunctor() { } + virtual ~LineToModuleFunctor() { } + + // Populate MODULE and LINES with source file names and code/line + // mappings, given a pointer to some DWARF line number data + // PROGRAM, and an overestimate of its size. Add no zero-length + // lines to LINES. + virtual void operator()(const char *program, uint64 length, + Module *module, vector *lines) = 0; + }; + + // The interface DwarfCUToModule uses to report warnings. The member + // function definitions for this class write messages to stderr, but + // you can override them if you'd like to detect or report these + // conditions yourself. + class WarningReporter { + public: + // Warn about problems in the DWARF file FILENAME, in the + // compilation unit at OFFSET. + WarningReporter(const string &filename, uint64 cu_offset) + : filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false), + printed_unpaired_header_(false), + uncovered_warnings_enabled_(false) { } + virtual ~WarningReporter() { } + + // Set the name of the compilation unit we're processing to NAME. + virtual void SetCUName(const string &name) { cu_name_ = name; } + + // Accessor and setter for uncovered_warnings_enabled_. + // UncoveredFunction and UncoveredLine only report a problem if that is + // true. By default, these warnings are disabled, because those + // conditions occur occasionally in healthy code. + virtual bool uncovered_warnings_enabled() const { + return uncovered_warnings_enabled_; + } + virtual void set_uncovered_warnings_enabled(bool value) { + uncovered_warnings_enabled_ = value; + } + + // A DW_AT_specification in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as a declaration, + // at TARGET. + virtual void UnknownSpecification(uint64 offset, uint64 target); + + // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as inline, at TARGET. + virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); + + // We were unable to find the DWARF section named SECTION_NAME. + virtual void MissingSection(const string §ion_name); + + // The CU's DW_AT_stmt_list offset OFFSET is bogus. + virtual void BadLineInfoOffset(uint64 offset); + + // FUNCTION includes code covered by no line number data. + virtual void UncoveredFunction(const Module::Function &function); + + // Line number NUMBER in LINE_FILE, of length LENGTH, includes code + // covered by no function. + virtual void UncoveredLine(const Module::Line &line); + + // The DW_TAG_subprogram DIE at OFFSET has no name specified directly + // in the DIE, nor via a DW_AT_specification or DW_AT_abstract_origin + // link. + virtual void UnnamedFunction(uint64 offset); + + protected: + string filename_; + uint64 cu_offset_; + string cu_name_; + bool printed_cu_header_; + bool printed_unpaired_header_; + bool uncovered_warnings_enabled_; + + private: + // Print a per-CU heading, once. + void CUHeading(); + // Print an unpaired function/line heading, once. + void UncoveredHeading(); + }; + + // Create a DWARF debugging info handler for a compilation unit + // within FILE_CONTEXT. This uses information received from the + // dwarf2reader::CompilationUnit DWARF parser to populate + // FILE_CONTEXT->module. Use LINE_READER to handle the compilation + // unit's line number data. Use REPORTER to report problems with the + // data we find. + DwarfCUToModule(FileContext *file_context, + LineToModuleFunctor *line_reader, + WarningReporter *reporter); + ~DwarfCUToModule(); + + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + bool EndAttributes(); + DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs); + + // Assign all our source Lines to the Functions that cover their + // addresses, and then add them to module_. + void Finish(); + + bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + bool StartRootDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs); + + private: + + // Used internally by the handler. Full definitions are in + // dwarf_cu_to_module.cc. + struct FilePrivate; + struct Specification; + struct CUContext; + struct DIEContext; + class GenericDIEHandler; + class FuncHandler; + class NamedScopeHandler; + + // A map from section offsets to specifications. + typedef map SpecificationByOffset; + + // Set this compilation unit's source language to LANGUAGE. + void SetLanguage(DwarfLanguage language); + + // Read source line information at OFFSET in the .debug_line + // section. Record source files in module_, but record source lines + // in lines_; we apportion them to functions in + // AssignLinesToFunctions. + void ReadSourceLines(uint64 offset); + + // Assign the lines in lines_ to the individual line lists of the + // functions in functions_. (DWARF line information maps an entire + // compilation unit at a time, and gives no indication of which + // lines belong to which functions, beyond their addresses.) + void AssignLinesToFunctions(); + + // The only reason cu_context_ and child_context_ are pointers is + // that we want to keep their definitions private to + // dwarf_cu_to_module.cc, instead of listing them all here. They are + // owned by this DwarfCUToModule: the constructor sets them, and the + // destructor deletes them. + + // The functor to use to handle line number data. + LineToModuleFunctor *line_reader_; + + // This compilation unit's context. + CUContext *cu_context_; + + // A context for our children. + DIEContext *child_context_; + + // True if this compilation unit has source line information. + bool has_source_line_info_; + + // The offset of this compilation unit's line number information in + // the .debug_line section. + uint64 source_line_offset_; + + // The line numbers we have seen thus far. We accumulate these here + // during parsing. Then, in Finish, we call AssignLinesToFunctions + // to dole them out to the appropriate functions. + vector lines_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module_unittest.cc new file mode 100644 index 0000000..03b0954 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_cu_to_module_unittest.cc @@ -0,0 +1,1728 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf_cu_to_module.h" + +using std::make_pair; +using std::string; +using std::vector; + +using dwarf2reader::AttributeList; +using dwarf2reader::DIEHandler; +using dwarf2reader::DwarfTag; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfInline; +using dwarf2reader::RootDIEHandler; +using google_breakpad::DwarfCUToModule; +using google_breakpad::Module; + +using ::testing::_; +using ::testing::AtMost; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::Test; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +// Mock classes. + +class MockLineToModuleFunctor: public DwarfCUToModule::LineToModuleFunctor { + public: + MOCK_METHOD4(mock_apply, void(const char *program, uint64 length, + Module *module, vector *lines)); + void operator()(const char *program, uint64 length, + Module *module, vector *lines) { + mock_apply(program, length, module, lines); + } +}; + +class MockWarningReporter: public DwarfCUToModule::WarningReporter { + public: + MockWarningReporter(const string &filename, uint64 cu_offset) + : DwarfCUToModule::WarningReporter(filename, cu_offset) { } + MOCK_METHOD1(SetCUName, void(const string &name)); + MOCK_METHOD2(UnknownSpecification, void(uint64 offset, uint64 target)); + MOCK_METHOD2(UnknownAbstractOrigin, void(uint64 offset, uint64 target)); + MOCK_METHOD1(MissingSection, void(const string §ion_name)); + MOCK_METHOD1(BadLineInfoOffset, void(uint64 offset)); + MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); + MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); + MOCK_METHOD1(UnnamedFunction, void(uint64 offset)); +}; + +// A fixture class including all the objects needed to handle a +// compilation unit, and their entourage. It includes member functions +// for doing common kinds of setup and tests. +class CUFixtureBase { + public: + + // If we have: + // + // vector lines; + // AppendLinesFunctor appender(lines); + // + // then doing: + // + // appender(line_program, length, module, line_vector); + // + // will append lines to the end of line_vector. We can use this with + // MockLineToModuleFunctor like this: + // + // MockLineToModuleFunctor l2m; + // EXPECT_CALL(l2m, mock_apply(_,_,_,_)) + // .WillOnce(DoAll(Invoke(appender), Return())); + // + // in which case calling l2m with some line vector will append lines. + class AppendLinesFunctor { + public: + AppendLinesFunctor(const vector *lines) : lines_(lines) { } + void operator()(const char *program, uint64 length, + Module *module, vector *lines) { + lines->insert(lines->end(), lines_->begin(), lines_->end()); + } + private: + const vector *lines_; + }; + + CUFixtureBase() + : module_("module-name", "module-os", "module-arch", "module-id"), + file_context_("dwarf-filename", &module_), + language_(dwarf2reader::DW_LANG_none), + language_signed_(false), + appender_(&lines_), + reporter_("dwarf-filename", 0xcf8f9bb6443d29b5LL), + root_handler_(&file_context_, &line_reader_, &reporter_), + functions_filled_(false) { + // By default, expect no warnings to be reported, and expect the + // compilation unit's name to be provided. The test can override + // these expectations. + EXPECT_CALL(reporter_, SetCUName("compilation-unit-name")).Times(1); + EXPECT_CALL(reporter_, UnknownSpecification(_, _)).Times(0); + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, _)).Times(0); + EXPECT_CALL(reporter_, MissingSection(_)).Times(0); + EXPECT_CALL(reporter_, BadLineInfoOffset(_)).Times(0); + EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0); + EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0); + EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(0); + + // By default, expect the line program reader not to be invoked. We + // may override this in StartCU. + EXPECT_CALL(line_reader_, mock_apply(_,_,_,_)).Times(0); + + // The handler will consult this section map to decide what to + // pass to our line reader. + file_context_.section_map[".debug_line"] = make_pair(dummy_line_program_, + dummy_line_size_); + } + + // Add a line with the given address, size, filename, and line + // number to the end of the statement list the handler will receive + // when it invokes its LineToModuleFunctor. Call this before calling + // StartCU. + void PushLine(Module::Address address, Module::Address size, + const string &filename, int line_number); + + // Use LANGUAGE for the compilation unit. More precisely, arrange + // for StartCU to pass the compilation unit's root DIE a + // DW_AT_language attribute whose value is LANGUAGE. + void SetLanguage(dwarf2reader::DwarfLanguage language) { + language_ = language; + } + + // If SIGNED true, have StartCU report DW_AT_language as a signed + // attribute; if false, have it report it as unsigned. + void SetLanguageSigned(bool is_signed) { language_signed_ = is_signed; } + + // Call the handler this.root_handler_'s StartCompilationUnit and + // StartRootDIE member functions, passing it appropriate attributes as + // determined by prior calls to PushLine and SetLanguage. Leave + // this.root_handler_ ready to hear about children: call + // this.root_handler_.EndAttributes, but not this.root_handler_.Finish. + void StartCU(); + + // Add some strange attributes/form pairs to the end of ATTRS. + void PushBackStrangeAttributes(dwarf2reader::AttributeList *attrs); + + // Have HANDLER process some strange attribute/form/value triples. + // These will match those promised by PushBackStrangeAttributes. + void ProcessStrangeAttributes(dwarf2reader::DIEHandler *handler); + + // Start a child DIE of PARENT with the given tag and name. Leave + // the handler ready to hear about children: call EndAttributes, but + // not Finish. + DIEHandler *StartNamedDIE(DIEHandler *parent, DwarfTag tag, + const string &name); + + // Start a child DIE of PARENT with the given tag and a + // DW_AT_specification attribute whose value is SPECIFICATION. Leave + // the handler ready to hear about children: call EndAttributes, but + // not Finish. If NAME is non-zero, use it as the DW_AT_name + // attribute. + DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag, + uint64 specification, const char *name = NULL); + + // Define a function as a child of PARENT with the given name, + // address, and size. Call EndAttributes and Finish; one cannot + // define children of the defined function's DIE. + void DefineFunction(DIEHandler *parent, const string &name, + Module::Address address, Module::Address size); + + // Create a declaration DIE as a child of PARENT with the given + // offset, tag and name. If NAME is the empty string, don't provide + // a DW_AT_name attribute. Call EndAttributes and Finish. + void DeclarationDIE(DIEHandler *parent, uint64 offset, + DwarfTag tag, const string &name); + + // Create a definition DIE as a child of PARENT with the given tag + // that refers to the declaration DIE at offset SPECIFICATION as its + // specification. If NAME is non-empty, pass it as the DW_AT_name + // attribute. If SIZE is non-zero, record ADDRESS and SIZE as + // low_pc/high_pc attributes. + void DefinitionDIE(DIEHandler *parent, DwarfTag tag, + uint64 specification, const string &name, + Module::Address address = 0, Module::Address size = 0); + + // Create an inline DW_TAG_subprogram DIE as a child of PARENT. If + // SPECIFICATION is non-zero, then the DIE refers to the declaration DIE at + // offset SPECIFICATION as its specification. If Name is non-empty, pass it + // as the DW_AT_name attribute. + void AbstractInstanceDIE(DIEHandler *parent, uint64 offset, + DwarfInline type, uint64 specification, + const string &name, + DwarfForm form = dwarf2reader::DW_FORM_data1); + + // Create a DW_TAG_subprogram DIE as a child of PARENT that refers to + // ORIGIN in its DW_AT_abstract_origin attribute. If NAME is the empty + // string, don't provide a DW_AT_name attribute. + void DefineInlineInstanceDIE(DIEHandler *parent, const string &name, + uint64 origin, Module::Address address, + Module::Address size); + + // The following Test* functions should be called after calling + // this.root_handler_.Finish. After that point, no further calls + // should be made on the handler. + + // Test that the number of functions defined in the module this.module_ is + // equal to EXPECTED. + void TestFunctionCount(size_t expected); + + // Test that the I'th function (ordered by address) in the module + // this.module_ has the given name, address, and size, and that its + // parameter size is zero. + void TestFunction(int i, const string &name, + Module::Address address, Module::Address size); + + // Test that the number of source lines owned by the I'th function + // in the module this.module_ is equal to EXPECTED. + void TestLineCount(int i, size_t expected); + + // Test that the J'th line (ordered by address) of the I'th function + // (again, by address) has the given address, size, filename, and + // line number. + void TestLine(int i, int j, Module::Address address, Module::Address size, + const string &filename, int number); + + // Actual objects under test. + Module module_; + DwarfCUToModule::FileContext file_context_; + + // If this is not DW_LANG_none, we'll pass it as a DW_AT_language + // attribute to the compilation unit. This defaults to DW_LANG_none. + dwarf2reader::DwarfLanguage language_; + + // If this is true, report DW_AT_language as a signed value; if false, + // report it as an unsigned value. + bool language_signed_; + + // If this is not empty, we'll give the CU a DW_AT_stmt_list + // attribute that, when passed to line_reader_, adds these lines to the + // provided lines array. + vector lines_; + + // Mock line program reader. + MockLineToModuleFunctor line_reader_; + AppendLinesFunctor appender_; + static const char dummy_line_program_[]; + static const size_t dummy_line_size_; + + MockWarningReporter reporter_; + DwarfCUToModule root_handler_; + + private: + // Fill functions_, if we haven't already. + void FillFunctions(); + + // If functions_filled_ is true, this is a table of functions we've + // extracted from module_, sorted by address. + vector functions_; + // True if we have filled the above vector with this.module_'s function list. + bool functions_filled_; +}; + +const char CUFixtureBase::dummy_line_program_[] = "lots of fun data"; +const size_t CUFixtureBase::dummy_line_size_ = + sizeof (CUFixtureBase::dummy_line_program_); + +void CUFixtureBase::PushLine(Module::Address address, Module::Address size, + const string &filename, int line_number) { + Module::Line l; + l.address = address; + l.size = size; + l.file = module_.FindFile(filename); + l.number = line_number; + lines_.push_back(l); +} + +void CUFixtureBase::StartCU() { + // If we have lines, make the line reader expect to be invoked at + // most once. (Hey, if the handler can pass its tests without + // bothering to read the line number data, that's great.) + // Have it add the lines passed to PushLine. Otherwise, leave the + // initial expectation (no calls) in force. + if (!lines_.empty()) + EXPECT_CALL(line_reader_, + mock_apply(&dummy_line_program_[0], dummy_line_size_, + &module_, _)) + .Times(AtMost(1)) + .WillOnce(DoAll(Invoke(appender_), Return())); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44, + 0x4241b4f33720dd5cULL, 3)); + { + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + if (!lines_.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4)); + if (language_ != dwarf2reader::DW_LANG_none) + attrs.push_back(make_pair(dwarf2reader::DW_AT_language, + language_signed_ + ? dwarf2reader::DW_FORM_sdata + : dwarf2reader::DW_FORM_udata)); + ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, + dwarf2reader::DW_TAG_compile_unit, + attrs)); + } + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + "compilation-unit-name"); + if (!lines_.empty()) + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4, + 0); + if (language_ != dwarf2reader::DW_LANG_none) { + if (language_signed_) + root_handler_.ProcessAttributeSigned(dwarf2reader::DW_AT_language, + dwarf2reader::DW_FORM_sdata, + language_); + else + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_language, + dwarf2reader::DW_FORM_udata, + language_); + } + ASSERT_TRUE(root_handler_.EndAttributes()); +} + +void CUFixtureBase::PushBackStrangeAttributes( + dwarf2reader::AttributeList *attrs) { + attrs->push_back(make_pair((DwarfAttribute) 0xf560dead, + (DwarfForm) 0x4106e4db)); + attrs->push_back(make_pair((DwarfAttribute) 0x85380095, + (DwarfForm) 0x0f16fe87)); + attrs->push_back(make_pair((DwarfAttribute) 0xf7f7480f, + (DwarfForm) 0x829e038a)); + attrs->push_back(make_pair((DwarfAttribute) 0xa55ffb51, + (DwarfForm) 0x2f43b041)); + attrs->push_back(make_pair((DwarfAttribute) 0x2fde304a, + (DwarfForm) 0x895ffa23)); +} + +void CUFixtureBase::ProcessStrangeAttributes( + dwarf2reader::DIEHandler *handler) { + handler->ProcessAttributeUnsigned((DwarfAttribute) 0xf560dead, + (DwarfForm) 0x4106e4db, + 0xa592571997facda1ULL); + handler->ProcessAttributeSigned((DwarfAttribute) 0x85380095, + (DwarfForm) 0x0f16fe87, + 0x12602a4e3bf1f446LL); + handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f, + (DwarfForm) 0x829e038a, + 0x50fddef44734fdecULL); + static const char buffer[10] = "frobynode"; + handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51, + (DwarfForm) 0x2f43b041, + buffer, sizeof(buffer)); + handler->ProcessAttributeString((DwarfAttribute) 0x2f43b041, + (DwarfForm) 0x895ffa23, + "strange string"); +} + +DIEHandler *CUFixtureBase::StartNamedDIE(DIEHandler *parent, + DwarfTag tag, + const string &name) { + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + PushBackStrangeAttributes(&attrs); + dwarf2reader::DIEHandler *handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag, attrs); + if (!handler) + return NULL; + handler->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + ProcessStrangeAttributes(handler); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +DIEHandler *CUFixtureBase::StartSpecifiedDIE(DIEHandler *parent, + DwarfTag tag, + uint64 specification, + const char *name) { + dwarf2reader::AttributeList attrs; + if (name) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4)); + dwarf2reader::DIEHandler *handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag, attrs); + if (!handler) + return NULL; + if (name) + handler->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + handler->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +void CUFixtureBase::DefineFunction(dwarf2reader::DIEHandler *parent, + const string &name, Module::Address address, + Module::Address size) { + dwarf2reader::AttributeList func_attrs; + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr)); + PushBackStrangeAttributes(&func_attrs); + dwarf2reader::DIEHandler *func + = parent->FindChildHandler(0xe34797c7e68590a8LL, + dwarf2reader::DW_TAG_subprogram, + func_attrs); + ASSERT_TRUE(func != NULL); + func->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::DeclarationDIE(DIEHandler *parent, uint64 offset, + DwarfTag tag, + const string &name) { + dwarf2reader::AttributeList attrs; + if (!name.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_declaration, + dwarf2reader::DW_FORM_flag)); + dwarf2reader::DIEHandler *die = parent->FindChildHandler(offset, tag, attrs); + ASSERT_TRUE(die != NULL); + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_declaration, + dwarf2reader::DW_FORM_flag, + 1); + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefinitionDIE(DIEHandler *parent, + DwarfTag tag, + uint64 specification, + const string &name, + Module::Address address, + Module::Address size) { + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4)); + if (!name.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + if (size) { + attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr)); + } + dwarf2reader::DIEHandler *die + = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag, attrs); + ASSERT_TRUE(die != NULL); + die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + if (size) { + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + } + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::AbstractInstanceDIE(DIEHandler *parent, + uint64 offset, + DwarfInline type, + uint64 specification, + const string &name, + DwarfForm form) { + dwarf2reader::AttributeList attrs; + if (specification != 0ULL) + attrs.push_back(make_pair(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_inline, form)); + if (!name.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + dwarf2reader::DIEHandler *die + = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram, attrs); + ASSERT_TRUE(die != NULL); + if (specification != 0ULL) + die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (form == dwarf2reader::DW_FORM_sdata) { + die->ProcessAttributeSigned(dwarf2reader::DW_AT_inline, form, type); + } else { + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_inline, form, type); + } + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler *parent, + const string &name, + uint64 origin, + Module::Address address, + Module::Address size) { + dwarf2reader::AttributeList func_attrs; + if (!name.empty()) + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_abstract_origin, + dwarf2reader::DW_FORM_ref4)); + PushBackStrangeAttributes(&func_attrs); + dwarf2reader::DIEHandler *func + = parent->FindChildHandler(0x11c70f94c6e87ccdLL, + dwarf2reader::DW_TAG_subprogram, + func_attrs); + ASSERT_TRUE(func != NULL); + if (!name.empty()) { + func->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + } + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + func->ProcessAttributeReference(dwarf2reader::DW_AT_abstract_origin, + dwarf2reader::DW_FORM_ref4, + origin); + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::FillFunctions() { + if (functions_filled_) + return; + module_.GetFunctions(&functions_, functions_.end()); + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + functions_filled_ = true; +} + +void CUFixtureBase::TestFunctionCount(size_t expected) { + FillFunctions(); + ASSERT_EQ(expected, functions_.size()); +} + +void CUFixtureBase::TestFunction(int i, const string &name, + Module::Address address, + Module::Address size) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + + Module::Function *function = functions_[i]; + EXPECT_EQ(name, function->name); + EXPECT_EQ(address, function->address); + EXPECT_EQ(size, function->size); + EXPECT_EQ(0U, function->parameter_size); +} + +void CUFixtureBase::TestLineCount(int i, size_t expected) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + + ASSERT_EQ(expected, functions_[i]->lines.size()); +} + +void CUFixtureBase::TestLine(int i, int j, + Module::Address address, Module::Address size, + const string &filename, int number) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + ASSERT_LT((size_t) j, functions_[i]->lines.size()); + + Module::Line *line = &functions_[i]->lines[j]; + EXPECT_EQ(address, line->address); + EXPECT_EQ(size, line->size); + EXPECT_EQ(filename, line->file->name.c_str()); + EXPECT_EQ(number, line->number); +} + +// Include caller locations for our test subroutines. +#define TRACE(call) do { SCOPED_TRACE("called from here"); call; } while (0) +#define PushLine(a,b,c,d) TRACE(PushLine((a),(b),(c),(d))) +#define SetLanguage(a) TRACE(SetLanguage(a)) +#define StartCU() TRACE(StartCU()) +#define DefineFunction(a,b,c,d) TRACE(DefineFunction((a),(b),(c),(d))) +#define DeclarationDIE(a,b,c,d) TRACE(DeclarationDIE((a),(b),(c),(d))) +#define DefinitionDIE(a,b,c,d,e,f) TRACE(DefinitionDIE((a),(b),(c),(d),(e),(f))) +#define TestFunctionCount(a) TRACE(TestFunctionCount(a)) +#define TestFunction(a,b,c,d) TRACE(TestFunction((a),(b),(c),(d))) +#define TestLineCount(a,b) TRACE(TestLineCount((a),(b))) +#define TestLine(a,b,c,d,e,f) TRACE(TestLine((a),(b),(c),(d),(e),(f))) + +class SimpleCU: public CUFixtureBase, public Test { +}; + +TEST_F(SimpleCU, OneFunc) { + PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); + + StartCU(); + DefineFunction(&root_handler_, "function1", + 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); + TestLineCount(0, 1); + TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", + 246571772); +} + +TEST_F(SimpleCU, IrrelevantRootChildren) { + StartCU(); + dwarf2reader::AttributeList no_attrs; + EXPECT_FALSE(root_handler_ + .FindChildHandler(0x7db32bff4e2dcfb1ULL, + dwarf2reader::DW_TAG_lexical_block, no_attrs)); +} + +TEST_F(SimpleCU, IrrelevantNamedScopeChildren) { + StartCU(); + dwarf2reader::AttributeList no_attrs; + DIEHandler *class_A_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); + EXPECT_TRUE(class_A_handler != NULL); + EXPECT_FALSE(class_A_handler + ->FindChildHandler(0x02e55999b865e4e9ULL, + dwarf2reader::DW_TAG_lexical_block, + no_attrs)); + delete class_A_handler; +} + +// Verify that FileContexts can safely be deleted unused. +TEST_F(SimpleCU, UnusedFileContext) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m); + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); +} + +TEST_F(SimpleCU, InlineFunction) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +TEST_F(SimpleCU, InlineFunctionSignedAttribute) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name", + dwarf2reader::DW_FORM_sdata); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// Any DIE with an DW_AT_inline attribute can be cited by +// DW_AT_abstract_origin attributes --- even if the value of the +// DW_AT_inline attribute is DW_INL_not_inlined. +TEST_F(SimpleCU, AbstractOriginNotInlined) { + PushLine(0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL, "line-file", 6111581); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x93e9cdad52826b39ULL, + dwarf2reader::DW_INL_not_inlined, 0, "abstract-instance"); + DefineInlineInstanceDIE(&root_handler_, "", 0x93e9cdad52826b39ULL, + 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "abstract-instance", + 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); +} + +TEST_F(SimpleCU, UnknownAbstractOrigin) { + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, 1ULL)).WillOnce(Return()); + EXPECT_CALL(reporter_, UnnamedFunction(0x11c70f94c6e87ccdLL)) + .WillOnce(Return()); + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 1ULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +TEST_F(SimpleCU, UnnamedFunction) { + EXPECT_CALL(reporter_, UnnamedFunction(0xe34797c7e68590a8LL)) + .WillOnce(Return()); + PushLine(0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL, "line-file", 14044850); + + StartCU(); + DefineFunction(&root_handler_, "", + 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "", + 0x72b80e41a0ac1d40ULL, 0x537174f231ee181cULL); +} + +// An address range. +struct Range { + Module::Address start, end; +}; + +// Test data for pairing functions and lines. +struct Situation { + // Two function intervals, and two line intervals. + Range functions[2], lines[2]; + + // The number of lines we expect to be assigned to each of the + // functions, and the address ranges. + int paired_count[2]; + Range paired[2][2]; + + // The number of functions that are not entirely covered by lines, + // and vice versa. + int uncovered_functions, uncovered_lines; +}; + +#define PAIRING(func1_start, func1_end, func2_start, func2_end, \ + line1_start, line1_end, line2_start, line2_end, \ + func1_num_lines, func2_num_lines, \ + func1_line1_start, func1_line1_end, \ + func1_line2_start, func1_line2_end, \ + func2_line1_start, func2_line1_end, \ + func2_line2_start, func2_line2_end, \ + uncovered_functions, uncovered_lines) \ + { { { func1_start, func1_end }, { func2_start, func2_end } }, \ + { { line1_start, line1_end }, { line2_start, line2_end } }, \ + { func1_num_lines, func2_num_lines }, \ + { { { func1_line1_start, func1_line1_end }, \ + { func1_line2_start, func1_line2_end } }, \ + { { func2_line1_start, func2_line1_end }, \ + { func2_line2_start, func2_line2_end } } }, \ + uncovered_functions, uncovered_lines }, + +Situation situations[] = { +#include "common/testdata/func-line-pairing.h" +}; + +#undef PAIRING + +class FuncLinePairing: public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_CASE_P(AllSituations, FuncLinePairing, + ValuesIn(situations)); + +TEST_P(FuncLinePairing, Pairing) { + const Situation &s = GetParam(); + PushLine(s.lines[0].start, + s.lines[0].end - s.lines[0].start, + "line-file", 67636963); + PushLine(s.lines[1].start, + s.lines[1].end - s.lines[1].start, + "line-file", 67636963); + if (s.uncovered_functions) + EXPECT_CALL(reporter_, UncoveredFunction(_)) + .Times(s.uncovered_functions) + .WillRepeatedly(Return()); + if (s.uncovered_lines) + EXPECT_CALL(reporter_, UncoveredLine(_)) + .Times(s.uncovered_lines) + .WillRepeatedly(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", + s.functions[0].start, + s.functions[0].end - s.functions[0].start); + DefineFunction(&root_handler_, "function2", + s.functions[1].start, + s.functions[1].end - s.functions[1].start); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", + s.functions[0].start, + s.functions[0].end - s.functions[0].start); + TestLineCount(0, s.paired_count[0]); + for (int i = 0; i < s.paired_count[0]; i++) + TestLine(0, i, s.paired[0][i].start, + s.paired[0][i].end - s.paired[0][i].start, + "line-file", 67636963); + TestFunction(1, "function2", + s.functions[1].start, + s.functions[1].end - s.functions[1].start); + TestLineCount(1, s.paired_count[1]); + for (int i = 0; i < s.paired_count[1]; i++) + TestLine(1, i, s.paired[1][i].start, + s.paired[1][i].end - s.paired[1][i].start, + "line-file", 67636963); +} + +TEST_F(FuncLinePairing, EmptyCU) { + + StartCU(); + root_handler_.Finish(); + + TestFunctionCount(0); +} + +TEST_F(FuncLinePairing, LinesNoFuncs) { + PushLine(40, 2, "line-file", 82485646); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + root_handler_.Finish(); + + TestFunctionCount(0); +} + +TEST_F(FuncLinePairing, FuncsNoLines) { + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 0x127da12ffcf5c51fULL, 0x1000U); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x127da12ffcf5c51fULL, 0x1000U); +} + +TEST_F(FuncLinePairing, GapThenFunction) { + PushLine(20, 2, "line-file-2", 174314698); + PushLine(10, 2, "line-file-1", 263008005); + + StartCU(); + DefineFunction(&root_handler_, "function1", 10, 2); + DefineFunction(&root_handler_, "function2", 20, 2); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 10, 2); + TestLineCount(0, 1); + TestLine(0, 0, 10, 2, "line-file-1", 263008005); + TestFunction(1, "function2", 20, 2); + TestLineCount(1, 1); + TestLine(1, 0, 20, 2, "line-file-2", 174314698); +} + +// If GCC emits padding after one function to align the start of +// the next, then it will attribute the padding instructions to +// the last source line of function (to reduce the size of the +// line number info), but omit it from the DW_AT_{low,high}_pc +// range given in .debug_info (since it costs nothing to be +// precise there). If we did use at least some of the line +// we're about to skip, then assume this is what happened, and +// don't warn. +TEST_F(FuncLinePairing, GCCAlignmentStretch) { + PushLine(10, 10, "line-file", 63351048); + PushLine(20, 10, "line-file", 61661044); + + StartCU(); + DefineFunction(&root_handler_, "function1", 10, 5); + // five-byte gap between functions, covered by line 63351048. + // This should not elicit a warning. + DefineFunction(&root_handler_, "function2", 20, 10); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 10, 5); + TestLineCount(0, 1); + TestLine(0, 0, 10, 5, "line-file", 63351048); + TestFunction(1, "function2", 20, 10); + TestLineCount(1, 1); + TestLine(1, 0, 20, 10, "line-file", 61661044); +} + +// Unfortunately, neither the DWARF parser's handler interface nor the +// DIEHandler interface is capable of expressing a function that abuts +// the end of the address space: the high_pc value looks like zero. + +TEST_F(FuncLinePairing, LineAtEndOfAddressSpace) { + PushLine(0xfffffffffffffff0ULL, 16, "line-file", 63351048); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 0xfffffffffffffff0ULL, 6); + DefineFunction(&root_handler_, "function2", 0xfffffffffffffffaULL, 5); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 0xfffffffffffffff0ULL, 6); + TestLineCount(0, 1); + TestLine(0, 0, 0xfffffffffffffff0ULL, 6, "line-file", 63351048); + TestFunction(1, "function2", 0xfffffffffffffffaULL, 5); + TestLineCount(1, 1); + TestLine(1, 0, 0xfffffffffffffffaULL, 5, "line-file", 63351048); +} + +// A function with more than one uncovered area should only be warned +// about once. +TEST_F(FuncLinePairing, WarnOnceFunc) { + PushLine(20, 1, "line-file-2", 262951329); + PushLine(11, 1, "line-file-1", 219964021); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function", 10, 11); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function", 10, 11); + TestLineCount(0, 2); + TestLine(0, 0, 11, 1, "line-file-1", 219964021); + TestLine(0, 1, 20, 1, "line-file-2", 262951329); +} + +// A line with more than one uncovered area should only be warned +// about once. +TEST_F(FuncLinePairing, WarnOnceLine) { + PushLine(10, 20, "filename1", 118581871); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 11, 1); + DefineFunction(&root_handler_, "function2", 13, 1); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 11, 1); + TestLineCount(0, 1); + TestLine(0, 0, 11, 1, "filename1", 118581871); + TestFunction(1, "function2", 13, 1); + TestLineCount(1, 1); + TestLine(1, 0, 13, 1, "filename1", 118581871); +} + +class CXXQualifiedNames: public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_CASE_P(VersusEnclosures, CXXQualifiedNames, + Values(dwarf2reader::DW_TAG_class_type, + dwarf2reader::DW_TAG_structure_type, + dwarf2reader::DW_TAG_union_type, + dwarf2reader::DW_TAG_namespace)); + +TEST_P(CXXQualifiedNames, TwoFunctions) { + DwarfTag tag = GetParam(); + + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + PushLine(20, 1, "filename2", 95115701); + + StartCU(); + DIEHandler *enclosure_handler = StartNamedDIE(&root_handler_, tag, + "Enclosure"); + EXPECT_TRUE(enclosure_handler != NULL); + DefineFunction(enclosure_handler, "func_B", 10, 1); + DefineFunction(enclosure_handler, "func_C", 20, 1); + enclosure_handler->Finish(); + delete enclosure_handler; + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "Enclosure::func_B", 10, 1); + TestFunction(1, "Enclosure::func_C", 20, 1); +} + +TEST_P(CXXQualifiedNames, FuncInEnclosureInNamespace) { + DwarfTag tag = GetParam(); + + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "line-file", 69819327); + + StartCU(); + DIEHandler *namespace_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "Namespace"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag, + "Enclosure"); + EXPECT_TRUE(enclosure_handler != NULL); + DefineFunction(enclosure_handler, "function", 10, 1); + enclosure_handler->Finish(); + delete enclosure_handler; + namespace_handler->Finish(); + delete namespace_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "Namespace::Enclosure::function", 10, 1); +} + +TEST_F(CXXQualifiedNames, FunctionInClassInStructInNamespace) { + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + + StartCU(); + DIEHandler *namespace_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "namespace_A"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler *struct_handler + = StartNamedDIE(namespace_handler, dwarf2reader::DW_TAG_structure_type, + "struct_B"); + EXPECT_TRUE(struct_handler != NULL); + DIEHandler *class_handler + = StartNamedDIE(struct_handler, dwarf2reader::DW_TAG_class_type, + "class_C"); + DefineFunction(class_handler, "function_D", 10, 1); + class_handler->Finish(); + delete class_handler; + struct_handler->Finish(); + delete struct_handler; + namespace_handler->Finish(); + delete namespace_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "namespace_A::struct_B::class_C::function_D", 10, 1); +} + +struct LanguageAndQualifiedName { + dwarf2reader::DwarfLanguage language; + const char *name; +}; + +const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = { + { dwarf2reader::DW_LANG_none, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C89, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C99, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C_plus_plus, "class_A::function_B" }, + { dwarf2reader::DW_LANG_Java, "class_A.function_B" }, + { dwarf2reader::DW_LANG_Cobol74, "class_A::function_B" }, + { dwarf2reader::DW_LANG_Mips_Assembler, NULL } +}; + +class QualifiedForLanguage: + public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage, + ValuesIn(LanguageAndQualifiedNameCases)); + +TEST_P(QualifiedForLanguage, MemberFunction) { + const LanguageAndQualifiedName ¶m = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + DefineFunction(class_handler, "function_B", 10, 1); + class_handler->Finish(); + delete class_handler; + root_handler_.Finish(); + + if (param.name) { + TestFunctionCount(1); + TestFunction(0, param.name, 10, 1); + } else { + TestFunctionCount(0); + } +} + +TEST_P(QualifiedForLanguage, MemberFunctionSignedLanguage) { + const LanguageAndQualifiedName ¶m = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + SetLanguageSigned(true); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + DefineFunction(class_handler, "function_B", 10, 1); + class_handler->Finish(); + delete class_handler; + root_handler_.Finish(); + + if (param.name) { + TestFunctionCount(1); + TestFunction(0, param.name, 10, 1); + } else { + TestFunctionCount(0); + } +} + +class Specifications: public CUFixtureBase, public Test { }; + +TEST_F(Specifications, Function) { + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "declaration-name", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MemberFunction) { + PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); + DeclarationDIE(class_handler, 0x7d83028c431406e8ULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + class_handler->Finish(); + delete class_handler; + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x7d83028c431406e8ULL, "", + 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class_A::declaration-name", + 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); +} + +// This case should gather the name from both the definition and the +// declaration's parent. +TEST_F(Specifications, FunctionDeclarationParent) { + PushLine(0x463c9ddf405be227ULL, 0x6a47774af5049680ULL, "line-file", 70254922); + + StartCU(); + { + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + ASSERT_TRUE(class_handler != NULL); + DeclarationDIE(class_handler, 0x0e0e877c8404544aULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + class_handler->Finish(); + delete class_handler; + } + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x0e0e877c8404544aULL, "definition-name", + 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class_A::definition-name", + 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); +} + +// Named scopes should also gather enclosing name components from +// their declarations. +TEST_F(Specifications, NamedScopeDeclarationParent) { + PushLine(0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, "line-file", 77392604); + + StartCU(); + { + DIEHandler *space_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "space_A"); + ASSERT_TRUE(space_handler != NULL); + DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL, + dwarf2reader::DW_TAG_class_type, "class-declaration-name"); + space_handler->Finish(); + delete space_handler; + } + + { + DIEHandler *class_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0x419bb1d12f9a73a2ULL, "class-definition-name"); + ASSERT_TRUE(class_handler != NULL); + DefineFunction(class_handler, "function", + 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); + class_handler->Finish(); + delete class_handler; + } + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "space_A::class-definition-name::function", + 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); +} + +// This test recreates bug 364. +TEST_F(Specifications, InlineFunction) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "inline-name"); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0xcd3c51b946fb1eeeLL, ""); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// Check name construction for a long chain containing each combination of: +// - struct, union, class, namespace +// - direct and definition +TEST_F(Specifications, LongChain) { + PushLine(0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL, "line-file", 21192926); + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + + StartCU(); + // The structure we're building here is: + // space_A full definition + // space_B declaration + // space_B definition + // struct_C full definition + // struct_D declaration + // struct_D definition + // union_E full definition + // union_F declaration + // union_F definition + // class_G full definition + // class_H declaration + // class_H definition + // func_I declaration + // func_I definition + // + // So: + // - space_A, struct_C, union_E, and class_G don't use specifications; + // - space_B, struct_D, union_F, and class_H do. + // - func_I uses a specification. + // + // The full name for func_I is thus: + // + // space_A::space_B::struct_C::struct_D::union_E::union_F:: + // class_G::class_H::func_I + { + DIEHandler *space_A_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "space_A"); + DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL, + dwarf2reader::DW_TAG_namespace, "space_B"); + space_A_handler->Finish(); + delete space_A_handler; + } + + { + DIEHandler *space_B_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + 0x2e111126496596e2ULL); + DIEHandler *struct_C_handler + = StartNamedDIE(space_B_handler, dwarf2reader::DW_TAG_structure_type, + "struct_C"); + DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL, + dwarf2reader::DW_TAG_structure_type, "struct_D"); + struct_C_handler->Finish(); + delete struct_C_handler; + space_B_handler->Finish(); + delete space_B_handler; + } + + { + DIEHandler *struct_D_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_structure_type, + 0x20cd423bf2a25a4cULL); + DIEHandler *union_E_handler + = StartNamedDIE(struct_D_handler, dwarf2reader::DW_TAG_union_type, + "union_E"); + DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL, + dwarf2reader::DW_TAG_union_type, "union_F"); + union_E_handler->Finish(); + delete union_E_handler; + struct_D_handler->Finish(); + delete struct_D_handler; + } + + { + DIEHandler *union_F_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_union_type, + 0xe25c84805aa58c32ULL); + DIEHandler *class_G_handler + = StartNamedDIE(union_F_handler, dwarf2reader::DW_TAG_class_type, + "class_G"); + DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL, + dwarf2reader::DW_TAG_class_type, "class_H"); + class_G_handler->Finish(); + delete class_G_handler; + union_F_handler->Finish(); + delete union_F_handler; + } + + { + DIEHandler *class_H_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0xb70d960dcc173b6eULL); + DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL, + dwarf2reader::DW_TAG_subprogram, "func_I"); + class_H_handler->Finish(); + delete class_H_handler; + } + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x27ff829e3bf69f37ULL, "", + 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "space_A::space_B::struct_C::struct_D::union_E::union_F" + "::class_G::class_H::func_I", + 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); +} + +TEST_F(Specifications, InterCU) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + MockLineToModuleFunctor lr; + EXPECT_CALL(lr, mock_apply(_,_,_,_)).Times(0); + dwarf2reader::AttributeList no_attrs; + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + // First CU. Declares class_A. + { + DwarfCUToModule root1_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + dwarf2reader::AttributeList attrs; + PushBackStrangeAttributes(&attrs); + ASSERT_TRUE(root1_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit, + attrs)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + dwarf2reader::DW_TAG_class_type, "class_A"); + root1_handler.Finish(); + } + + // Second CU. Defines class_A, declares member_func_B. + { + DwarfCUToModule root2_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root2_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit, + no_attrs)); + ASSERT_TRUE(root2_handler.EndAttributes()); + DIEHandler *class_A_handler + = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + dwarf2reader::DW_TAG_subprogram, "member_func_B"); + class_A_handler->Finish(); + delete class_A_handler; + root2_handler.Finish(); + } + + // Third CU. Defines member_func_B. + { + DwarfCUToModule root3_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root3_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit, + no_attrs)); + ASSERT_TRUE(root3_handler.EndAttributes()); + DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, + 0xb01fef8b380bd1a2ULL, "", + 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); + root3_handler.Finish(); + } + + vector functions; + m.GetFunctions(&functions, functions.end()); + EXPECT_EQ(1U, functions.size()); + EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str()); +} + +TEST_F(Specifications, BadOffset) { + PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272); + EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL)) + .WillOnce(Return()); + + StartCU(); + DeclarationDIE(&root_handler_, 0xefd7f7752c27b7e4ULL, + dwarf2reader::DW_TAG_subprogram, ""); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x2be953efa6f9a996ULL, "function", + 0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL); + root_handler_.Finish(); +} + +TEST_F(Specifications, FunctionDefinitionHasOwnName) { + PushLine(0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL, "line-file", 56792403); + + StartCU(); + DeclarationDIE(&root_handler_, 0xc34ff4786cae78bdULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xc34ff4786cae78bdULL, "definition-name", + 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "definition-name", + 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); +} + +TEST_F(Specifications, ClassDefinitionHasOwnName) { + PushLine(0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL, "line-file", 57119241); + + StartCU(); + DeclarationDIE(&root_handler_, 0xd0fe467ec2f1a58cULL, + dwarf2reader::DW_TAG_class_type, "class-declaration-name"); + + dwarf2reader::DIEHandler *class_definition + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0xd0fe467ec2f1a58cULL, "class-definition-name"); + ASSERT_TRUE(class_definition); + DeclarationDIE(class_definition, 0x6d028229c15623dbULL, + dwarf2reader::DW_TAG_subprogram, + "function-declaration-name"); + class_definition->Finish(); + delete class_definition; + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x6d028229c15623dbULL, "function-definition-name", + 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class-definition-name::function-definition-name", + 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); +} + +// DIEs that cite a specification should prefer the specification's +// parents over their own when choosing qualified names. In this test, +// we take the name from our definition but the enclosing scope name +// from our declaration. I don't see why they'd ever be different, but +// we want to verify what DwarfCUToModule is looking at. +TEST_F(Specifications, PreferSpecificationParents) { + PushLine(0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL, "line-file", 79488694); + + StartCU(); + { + dwarf2reader::DIEHandler *declaration_class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "declaration-class"); + DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, + dwarf2reader::DW_TAG_subprogram, "function-declaration"); + declaration_class_handler->Finish(); + delete declaration_class_handler; + } + { + dwarf2reader::DIEHandler *definition_class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "definition-class"); + DefinitionDIE(definition_class_handler, dwarf2reader::DW_TAG_subprogram, + 0x9ddb35517455ef7aULL, "function-definition", + 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); + definition_class_handler->Finish(); + delete definition_class_handler; + } + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "declaration-class::function-definition", + 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); +} + +class CUErrors: public CUFixtureBase, public Test { }; + +TEST_F(CUErrors, BadStmtList) { + EXPECT_CALL(reporter_, BadLineInfoOffset(dummy_line_size_ + 10)).Times(1); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xc591d5b037543d7cULL, 0x11, 0xcd, + 0x2d7d19546cf6590cULL, 3)); + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4)); + ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL, + dwarf2reader::DW_TAG_compile_unit, + attrs)); + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + "compilation-unit-name"); + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4, + dummy_line_size_ + 10); + root_handler_.EndAttributes(); + root_handler_.Finish(); +} + +TEST_F(CUErrors, NoLineSection) { + EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1); + PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290); + // Delete the entry for .debug_line added by the fixture class's constructor. + file_context_.section_map.clear(); + + StartCU(); + root_handler_.Finish(); +} + +TEST_F(CUErrors, BadDwarfVersion1) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_FALSE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 1)); +} + +TEST_F(CUErrors, GoodDwarfVersion2) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 2)); +} + +TEST_F(CUErrors, GoodDwarfVersion3) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 3)); +} + +TEST_F(CUErrors, BadCURootDIETag) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 3)); + + dwarf2reader::AttributeList no_attrs; + ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, + dwarf2reader::DW_TAG_subprogram, + no_attrs)); +} + +// Tests for DwarfCUToModule::Reporter. These just produce (or fail to +// produce) output, so their results need to be checked by hand. +struct Reporter: public Test { + Reporter() + : reporter("filename", 0x123456789abcdef0ULL) { + reporter.SetCUName("compilation-unit-name"); + + function.name = "function name"; + function.address = 0x19c45c30770c1eb0ULL; + function.size = 0x89808a5bdfa0a6a3ULL; + function.parameter_size = 0x6a329f18683dcd51ULL; + + file.name = "source file name"; + + line.address = 0x3606ac6267aebeccULL; + line.size = 0x5de482229f32556aULL; + line.file = &file; + line.number = 93400201; + } + + DwarfCUToModule::WarningReporter reporter; + Module::Function function; + Module::File file; + Module::Line line; +}; + +TEST_F(Reporter, UnknownSpecification) { + reporter.UnknownSpecification(0x123456789abcdef1ULL, 0x323456789abcdef2ULL); +} + +TEST_F(Reporter, UnknownAbstractOrigin) { + reporter.UnknownAbstractOrigin(0x123456789abcdef1ULL, 0x323456789abcdef2ULL); +} + +TEST_F(Reporter, MissingSection) { + reporter.MissingSection("section name"); +} + +TEST_F(Reporter, BadLineInfoOffset) { + reporter.BadLineInfoOffset(0x123456789abcdef1ULL); +} + +TEST_F(Reporter, UncoveredFunctionDisabled) { + reporter.UncoveredFunction(function); + EXPECT_FALSE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UncoveredFunctionEnabled) { + reporter.set_uncovered_warnings_enabled(true); + reporter.UncoveredFunction(function); + EXPECT_TRUE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UncoveredLineDisabled) { + reporter.UncoveredLine(line); + EXPECT_FALSE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UncoveredLineEnabled) { + reporter.set_uncovered_warnings_enabled(true); + reporter.UncoveredLine(line); + EXPECT_TRUE(reporter.uncovered_warnings_enabled()); +} + +TEST_F(Reporter, UnnamedFunction) { + reporter.UnnamedFunction(0x90c0baff9dedb2d9ULL); +} + +// Would be nice to also test: +// - overlapping lines, functions diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.cc b/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.cc new file mode 100644 index 0000000..d987370 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class. +// See dwarf_line_to_module.h for details. + +#include "common/dwarf_line_to_module.h" + +#include + +// Trying to support Windows paths in a reasonable way adds a lot of +// variations to test; it would be better to just put off dealing with +// it until we actually have to deal with DWARF on Windows. + +// Return true if PATH is an absolute path, false if it is relative. +static bool PathIsAbsolute(const std::string &path) { + return (path.size() >= 1 && path[0] == '/'); +} + +// If PATH is an absolute path, return PATH. If PATH is a relative path, +// treat it as relative to BASE and return the combined path. +static std::string ExpandPath(const std::string &path, + const std::string &base) { + if (PathIsAbsolute(path)) + return path; + return base + "/" + path; +} + +namespace google_breakpad { + +void DwarfLineToModule::DefineDir(const std::string &name, uint32 dir_num) { + // Directory number zero is reserved to mean the compilation + // directory. Silently ignore attempts to redefine it. + if (dir_num != 0) + directories_[dir_num] = name; +} + +void DwarfLineToModule::DefineFile(const std::string &name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length) { + if (file_num == -1) + file_num = ++highest_file_number_; + else if (file_num > highest_file_number_) + highest_file_number_ = file_num; + + std::string full_name; + if (dir_num != 0) { + DirectoryTable::const_iterator directory_it = directories_.find(dir_num); + if (directory_it != directories_.end()) { + full_name = ExpandPath(name, directory_it->second); + } else { + if (!warned_bad_directory_number_) { + fprintf(stderr, "warning: DWARF line number data refers to undefined" + " directory numbers\n"); + warned_bad_directory_number_ = true; + } + full_name = name; // just treat name as relative + } + } else { + // Directory number zero is the compilation directory; we just report + // relative paths in that case. + full_name = name; + } + + // Find a Module::File object of the given name, and add it to the + // file table. + files_[file_num] = module_->FindFile(full_name); +} + +void DwarfLineToModule::AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, + uint32 column_num) { + if (length == 0) + return; + + // Clip lines not to extend beyond the end of the address space. + if (address + length < address) + length = -address; + + // Should we omit this line? (See the comments for omitted_line_end_.) + if (address == 0 || address == omitted_line_end_) { + omitted_line_end_ = address + length; + return; + } else { + omitted_line_end_ = 0; + } + + // Find the source file being referred to. + Module::File *file = files_[file_num]; + if (!file) { + if (!warned_bad_file_number_) { + fprintf(stderr, "warning: DWARF line number data refers to " + "undefined file numbers\n"); + warned_bad_file_number_ = true; + } + return; + } + Module::Line line; + line.address = address; + // We set the size when we get the next line or the EndSequence call. + line.size = length; + line.file = file; + line.number = line_num; + lines_->push_back(line); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.h b/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.h new file mode 100644 index 0000000..49b3eb3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module.h @@ -0,0 +1,179 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// The DwarfLineToModule class accepts line number information from a +// DWARF parser and adds it to a google_breakpad::Module. The Module +// can write that data out as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H +#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H + +#include "common/module.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +// A class for producing a vector of google_breakpad::Module::Line +// instances from parsed DWARF line number data. +// +// An instance of this class can be provided as a handler to a +// dwarf2reader::LineInfo DWARF line number information parser. The +// handler accepts source location information from the parser and +// uses it to produce a vector of google_breakpad::Module::Line +// objects, referring to google_breakpad::Module::File objects added +// to a particular google_breakpad::Module. +// +// GNU toolchain omitted sections support: +// ====================================== +// +// Given the right options, the GNU toolchain will omit unreferenced +// functions from the final executable. Unfortunately, when it does so, it +// does not remove the associated portions of the DWARF line number +// program; instead, it gives the DW_LNE_set_address instructions referring +// to the now-deleted code addresses of zero. Given this input, the DWARF +// line parser will call AddLine with a series of lines starting at address +// zero. For example, here is the output from 'readelf -wl' for a program +// with four functions, the first three of which have been omitted: +// +// Line Number Statements: +// Extended opcode 2: set Address to 0x0 +// Advance Line by 14 to 15 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 +// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 +// Advance PC by 2 to 0xd +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x0 +// Advance Line by 14 to 15 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 +// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 +// Advance PC by 2 to 0xd +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x0 +// Advance Line by 19 to 20 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21 +// Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22 +// Advance PC by 2 to 0xa +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x80483a4 +// Advance Line by 23 to 24 +// Copy +// Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25 +// Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26 +// Advance PC by 6 to 0x80483bd +// Extended opcode 1: End of Sequence +// +// Instead of collecting runs of lines describing code that is not there, +// we try to recognize and drop them. Since the linker doesn't explicitly +// distinguish references to dropped sections from genuine references to +// code at address zero, we must use a heuristic. We have chosen: +// +// - If a line starts at address zero, omit it. (On the platforms +// breakpad targets, it is extremely unlikely that there will be code +// at address zero.) +// +// - If a line starts immediately after an omitted line, omit it too. +class DwarfLineToModule: public dwarf2reader::LineInfoHandler { + public: + // As the DWARF line info parser passes us line records, add source + // files to MODULE, and add all lines to the end of LINES. LINES + // need not be empty. If the parser hands us a zero-length line, we + // omit it. If the parser hands us a line that extends beyond the + // end of the address space, we clip it. It's up to our client to + // sort out which lines belong to which functions; we don't add them + // to any particular function in MODULE ourselves. + DwarfLineToModule(Module *module, vector *lines) + : module_(module), + lines_(lines), + highest_file_number_(-1), + omitted_line_end_(0), + warned_bad_file_number_(false), + warned_bad_directory_number_(false) { } + + ~DwarfLineToModule() { } + + void DefineDir(const std::string &name, uint32 dir_num); + void DefineFile(const std::string &name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length); + void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num); + + private: + + typedef std::map DirectoryTable; + typedef std::map FileTable; + + // The module we're contributing debugging info to. Owned by our + // client. + Module *module_; + + // The vector of lines we're accumulating. Owned by our client. + // + // In a Module, as in a breakpad symbol file, lines belong to + // specific functions, but DWARF simply assigns lines to addresses; + // one must infer the line/function relationship using the + // functions' beginning and ending addresses. So we can't add these + // to the appropriate function from module_ until we've read the + // function info as well. Instead, we accumulate lines here, and let + // whoever constructed this sort it all out. + vector *lines_; + + // A table mapping directory numbers to paths. + DirectoryTable directories_; + + // A table mapping file numbers to Module::File pointers. + FileTable files_; + + // The highest file number we've seen so far, or -1 if we've seen + // none. Used for dynamically defined file numbers. + int32 highest_file_number_; + + // This is the ending address of the last line we omitted, or zero if we + // didn't omit the previous line. It is zero before we have received any + // AddLine calls. + uint64 omitted_line_end_; + + // True if we've warned about: + bool warned_bad_file_number_; // bad file numbers + bool warned_bad_directory_number_; // bad directory numbers +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H diff --git a/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module_unittest.cc b/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module_unittest.cc new file mode 100644 index 0000000..1e123e9 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/dwarf_line_to_module_unittest.cc @@ -0,0 +1,343 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule. + +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf_line_to_module.h" + +using std::vector; + +using google_breakpad::DwarfLineToModule; +using google_breakpad::Module; +using google_breakpad::Module; + +TEST(SimpleModule, One) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); + h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, + 0x4c090cbf, 0x1cf9fe0d); + + vector files; + m.GetFiles(&files); + EXPECT_EQ(1U, files.size()); + EXPECT_STREQ("file1", files[0]->name.c_str()); + + EXPECT_EQ(1U, lines.size()); + EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address); + EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size); + EXPECT_TRUE(lines[0].file == files[0]); + EXPECT_EQ(0x4c090cbf, lines[0].number); +} + +TEST(SimpleModule, Many) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 0x838299ab); + h.DefineDir("directory2", 0xf85de023); + h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0); + h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0); + h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0); + h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0); + h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a, + 0x15b0f0a9U, 0x3ff5abd6U); + h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4, + 0x4d259ce9U, 0x41c5ee32U); + h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56, + 0x1ee9fa4fU, 0xbf70e46aU); + h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c, + 0x77fc280eU, 0x2c4a728cU); + h.DefineFile("file3", -1, 0, 0, 0); + h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5, + 0x75047044U, 0xb6a0016cU); + + vector files; + m.GetFiles(&files); + ASSERT_EQ(5U, files.size()); + EXPECT_STREQ("directory1/file1", files[0]->name.c_str()); + EXPECT_STREQ("directory1/file2", files[1]->name.c_str()); + EXPECT_STREQ("directory2/file1", files[2]->name.c_str()); + EXPECT_STREQ("directory2/file2", files[3]->name.c_str()); + EXPECT_STREQ("file3", files[4]->name.c_str()); + + ASSERT_EQ(5U, lines.size()); + + EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address); + EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size); + EXPECT_TRUE(lines[0].file == files[0]); + EXPECT_EQ(0x15b0f0a9, lines[0].number); + + EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address); + EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size); + EXPECT_TRUE(lines[1].file == files[2]); + EXPECT_EQ(0x4d259ce9, lines[1].number); + + EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address); + EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size); + EXPECT_TRUE(lines[2].file == files[1]); + EXPECT_EQ(0x1ee9fa4f, lines[2].number); + + EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address); + EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size); + EXPECT_TRUE(lines[3].file == files[3]); + EXPECT_EQ(0x77fc280e, lines[3].number); + + EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address); + EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size); + EXPECT_TRUE(lines[4].file == files[4]); + EXPECT_EQ(0x75047044, lines[4].number); +} + +TEST(Filenames, Absolute) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("/absolute", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector files; + m.GetFiles(&files); + ASSERT_EQ(1U, files.size()); + EXPECT_STREQ("/absolute", files[0]->name.c_str()); + ASSERT_EQ(1U, lines.size()); + EXPECT_TRUE(lines[0].file == files[0]); +} + +TEST(Filenames, Relative) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("relative", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector files; + m.GetFiles(&files); + ASSERT_EQ(1U, files.size()); + EXPECT_STREQ("directory1/relative", files[0]->name.c_str()); + ASSERT_EQ(1U, lines.size()); + EXPECT_TRUE(lines[0].file == files[0]); +} + +TEST(Filenames, StrangeFile) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("directory1/", lines[0].file->name.c_str()); +} + +TEST(Filenames, StrangeDirectory) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("", 1); + h.DefineFile("file1", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/file1", lines[0].file->name.c_str()); +} + +TEST(Filenames, StrangeDirectoryAndFile) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("", 1); + h.DefineFile("", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/", lines[0].file->name.c_str()); +} + +// We should silently ignore attempts to define directory number zero, +// since that is always the compilation directory. +TEST(ModuleErrors, DirectoryZero) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory0", 0); // should be ignored + h.DefineFile("relative", 1, 0, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("relative", lines[0].file->name.c_str()); +} + +// We should refuse to add lines with bogus file numbers. We should +// produce only one warning, however. +TEST(ModuleErrors, BadFileNumber) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("relative", 1, 0, 0, 0); + h.AddLine(1, 1, 2, 0, 0); // bad file number + h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning) + + EXPECT_EQ(0U, lines.size()); +} + +// We should treat files with bogus directory numbers as relative to +// the compilation unit. +TEST(ModuleErrors, BadDirectoryNumber) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number + h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning) + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str()); +} + +// We promise not to report empty lines. +TEST(ModuleErrors, EmptyLine) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(1, 0, 1, 0, 0); + + ASSERT_EQ(0U, lines.size()); +} + +// We are supposed to clip lines that extend beyond the end of the +// address space. +TEST(ModuleErrors, BigLine) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(1U, lines[0].size); +} + +// The 'Omitted' tests verify that we correctly omit line information +// for code in sections that the linker has dropped. See "GNU +// toolchain omitted sections support" at the top of the +// DwarfLineToModule class. + +TEST(Omitted, DroppedThenGood) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0, 10, 1, 83816211, 0); // should be omitted + h.AddLine(20, 10, 1, 13059195, 0); // should be recorded + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(13059195, lines[0].number); +} + +TEST(Omitted, GoodThenDropped) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded + h.AddLine(0, 10, 1, 44793413, 0); // should be omitted + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(41454594, lines[0].number); +} + +TEST(Omitted, Mix1) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded + h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded + h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted + h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted + h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted + h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded + h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded + + ASSERT_EQ(4U, lines.size()); + EXPECT_EQ(58932642, lines[0].number); + EXPECT_EQ(39847385, lines[1].number); + EXPECT_EQ(91806582, lines[2].number); + EXPECT_EQ(56169221, lines[3].number); +} + +TEST(Omitted, Mix2) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted + h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted + h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted + h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded + h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded + h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted + h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted + h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted + + ASSERT_EQ(2U, lines.size()); + EXPECT_EQ(67355743, lines[0].number); + EXPECT_EQ(23365776, lines[1].number); +} diff --git a/src/lib/crashdump/gbreakpad/common/language.cc b/src/lib/crashdump/gbreakpad/common/language.cc new file mode 100644 index 0000000..c2fd81f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/language.cc @@ -0,0 +1,83 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// language.cc: Subclasses and singletons for google_breakpad::Language. +// See language.h for details. + +#include "common/language.h" + +namespace google_breakpad { + +// C++ language-specific operations. +class CPPLanguage: public Language { + public: + CPPLanguage() {} + string MakeQualifiedName(const string &parent_name, + const string &name) const { + if (parent_name.empty()) + return name; + else + return parent_name + "::" + name; + } +}; + +CPPLanguage CPPLanguageSingleton; + +// Java language-specific operations. +class JavaLanguage: public Language { + public: + string MakeQualifiedName(const string &parent_name, + const string &name) const { + if (parent_name.empty()) + return name; + else + return parent_name + "." + name; + } +}; + +JavaLanguage JavaLanguageSingleton; + +// Assembler language-specific operations. +class AssemblerLanguage: public Language { + bool HasFunctions() const { return false; } + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return name; + } +}; + +AssemblerLanguage AssemblerLanguageSingleton; + +const Language * const Language::CPlusPlus = &CPPLanguageSingleton; +const Language * const Language::Java = &JavaLanguageSingleton; +const Language * const Language::Assembler = &AssemblerLanguageSingleton; + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/language.h b/src/lib/crashdump/gbreakpad/common/language.h new file mode 100644 index 0000000..03bdf7f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/language.h @@ -0,0 +1,84 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// language.h: Define google_breakpad::Language. Instances of +// subclasses of this class provide language-appropriate operations +// for the Breakpad symbol dumper. + +#ifndef COMMON_LINUX_LANGUAGE_H__ +#define COMMON_LINUX_LANGUAGE_H__ + +#include + +namespace google_breakpad { + +using std::string; + +// An abstract base class for language-specific operations. We choose +// an instance of a subclass of this when we find the CU's language. +// This class's definitions are appropriate for CUs with no specified +// language. +class Language { + public: + // Return true if this language has functions to which we can assign + // line numbers. (Debugging info for assembly language, for example, + // can have source location information, but does not have functions + // recorded using DW_TAG_subprogram DIEs.) + virtual bool HasFunctions() const { return true; } + + // Construct a fully-qualified, language-appropriate form of NAME, + // given that PARENT_NAME is the name of the construct enclosing + // NAME. If PARENT_NAME is the empty string, then NAME is a + // top-level name. + // + // This API sort of assumes that a fully-qualified name is always + // some simple textual composition of the unqualified name and its + // parent's name, and that we don't need to know anything else about + // the parent or the child (say, their DIEs' tags) to do the job. + // This is true for the languages we support at the moment, and + // keeps things concrete. Perhaps a more refined operation would + // take into account the parent and child DIE types, allow languages + // to use their own data type for complex parent names, etc. But if + // C++ doesn't need all that, who would? + virtual string MakeQualifiedName (const string &parent_name, + const string &name) const = 0; + + // Instances for specific languages. + static const Language * const CPlusPlus, + * const Java, + * const Assembler; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_LANGUAGE_H__ diff --git a/src/lib/crashdump/gbreakpad/common/linux/dump_symbols.cc b/src/lib/crashdump/gbreakpad/common/linux/dump_symbols.cc new file mode 100644 index 0000000..f02503a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/dump_symbols.cc @@ -0,0 +1,826 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Restructured in 2009 by: Jim Blandy + +// dump_symbols.cc: implement google_breakpad::WriteSymbolFile: +// Find all the debugging info in a file and dump it as a Breakpad symbol file. + +#include "common/linux/dump_symbols.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/dwarf_cfi_to_module.h" +#include "common/dwarf_cu_to_module.h" +#include "common/dwarf_line_to_module.h" +#include "common/linux/elf_symbols_to_module.h" +#include "common/linux/file_id.h" +#include "common/module.h" +#include "common/stabs_reader.h" +#include "common/stabs_to_module.h" + +// This namespace contains helper functions. +namespace { + +using google_breakpad::DwarfCFIToModule; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::Module; +using google_breakpad::StabsToModule; + +// +// FDWrapper +// +// Wrapper class to make sure opened file is closed. +// +class FDWrapper { + public: + explicit FDWrapper(int fd) : + fd_(fd) {} + ~FDWrapper() { + if (fd_ != -1) + close(fd_); + } + int get() { + return fd_; + } + int release() { + int fd = fd_; + fd_ = -1; + return fd; + } + private: + int fd_; +}; + +// +// MmapWrapper +// +// Wrapper class to make sure mapped regions are unmapped. +// +class MmapWrapper { + public: + MmapWrapper() : is_set_(false) {} + ~MmapWrapper() { + assert(is_set_); + if (base_ != NULL) { + assert(size_ > 0); + munmap(base_, size_); + } + } + void set(void *mapped_address, size_t mapped_size) { + is_set_ = true; + base_ = mapped_address; + size_ = mapped_size; + } + void release() { + assert(is_set_); + base_ = NULL; + size_ = 0; + } + + private: + bool is_set_; + void *base_; + size_t size_; +}; + + +// Fix offset into virtual address by adding the mapped base into offsets. +// Make life easier when want to find something by offset. +static void FixAddress(void *obj_base) { + ElfW(Addr) base = reinterpret_cast(obj_base); + ElfW(Ehdr) *elf_header = static_cast(obj_base); + elf_header->e_phoff += base; + elf_header->e_shoff += base; + ElfW(Shdr) *sections = reinterpret_cast(elf_header->e_shoff); + for (int i = 0; i < elf_header->e_shnum; ++i) + sections[i].sh_offset += base; +} + +// Find the preferred loading address of the binary. +static ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers, + int nheader) { + for (int i = 0; i < nheader; ++i) { + const ElfW(Phdr) &header = program_headers[i]; + // For executable, it is the PT_LOAD segment with offset to zero. + if (header.p_type == PT_LOAD && + header.p_offset == 0) + return header.p_vaddr; + } + // For other types of ELF, return 0. + return 0; +} + +static bool IsValidElf(const ElfW(Ehdr) *elf_header) { + return memcmp(elf_header, ELFMAG, SELFMAG) == 0; +} + +static const ElfW(Shdr) *FindSectionByName(const char *name, + const ElfW(Shdr) *sections, + const ElfW(Shdr) *section_names, + int nsection) { + assert(name != NULL); + assert(sections != NULL); + assert(nsection > 0); + + int name_len = strlen(name); + if (name_len == 0) + return NULL; + + // Find the end of the section name section, to make sure that + // comparisons don't run off the end of the section. + const char *names_end = + reinterpret_cast(section_names->sh_offset + section_names->sh_size); + + for (int i = 0; i < nsection; ++i) { + const char *section_name = + reinterpret_cast(section_names->sh_offset + sections[i].sh_name); + if (names_end - section_name >= name_len + 1 && + strcmp(name, section_name) == 0) { + if (sections[i].sh_type == SHT_NOBITS) { + fprintf(stderr, + "Section %s found, but ignored because type=SHT_NOBITS.\n", + name); + return NULL; + } + return sections + i; + } + } + return NULL; +} + +static bool LoadStabs(const ElfW(Ehdr) *elf_header, + const ElfW(Shdr) *stab_section, + const ElfW(Shdr) *stabstr_section, + const bool big_endian, + Module *module) { + // A callback object to handle data from the STABS reader. + StabsToModule handler(module); + // Find the addresses of the STABS data, and create a STABS reader object. + // On Linux, STABS entries always have 32-bit values, regardless of the + // address size of the architecture whose code they're describing, and + // the strings are always "unitized". + uint8_t *stabs = reinterpret_cast(stab_section->sh_offset); + uint8_t *stabstr = reinterpret_cast(stabstr_section->sh_offset); + google_breakpad::StabsReader reader(stabs, stab_section->sh_size, + stabstr, stabstr_section->sh_size, + big_endian, 4, true, &handler); + // Read the STABS data, and do post-processing. + if (!reader.Process()) + return false; + handler.Finalize(); + return true; +} + +// A line-to-module loader that accepts line number info parsed by +// dwarf2reader::LineInfo and populates a Module and a line vector +// with the results. +class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor { + public: + // Create a line-to-module converter using BYTE_READER. + explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader) + : byte_reader_(byte_reader) { } + void operator()(const char *program, uint64 length, + Module *module, std::vector *lines) { + DwarfLineToModule handler(module, lines); + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); + parser.Start(); + } + private: + dwarf2reader::ByteReader *byte_reader_; +}; + +static bool LoadDwarf(const std::string &dwarf_filename, + const ElfW(Ehdr) *elf_header, + const bool big_endian, + Module *module) { + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + dwarf2reader::ByteReader byte_reader(endianness); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(dwarf_filename, module); + + // Build a map of the ELF file's sections. + const ElfW(Shdr) *sections + = reinterpret_cast(elf_header->e_shoff); + int num_sections = elf_header->e_shnum; + const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; + for (int i = 0; i < num_sections; i++) { + const ElfW(Shdr) *section = §ions[i]; + std::string name = reinterpret_cast(section_names->sh_offset + + section->sh_name); + const char *contents = reinterpret_cast(section->sh_offset); + uint64 length = section->sh_size; + file_context.section_map[name] = std::make_pair(contents, length); + } + + // Parse all the compilation units in the .debug_info section. + DumperLineToModule line_to_module(&byte_reader); + std::pair debug_info_section + = file_context.section_map[".debug_info"]; + // We should never have been called if the file doesn't have a + // .debug_info section. + assert(debug_info_section.first); + uint64 debug_info_length = debug_info_section.second; + for (uint64 offset = 0; offset < debug_info_length;) { + // Make a handler for the root DIE that populates MODULE with the + // data we find. + DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); + DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); + // Make a Dwarf2Handler that drives our DIEHandler. + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + dwarf2reader::CompilationUnit reader(file_context.section_map, + offset, + &byte_reader, + &die_dispatcher); + // Process the entire compilation unit; get the offset of the next. + offset += reader.Start(); + } + return true; +} + +// Fill REGISTER_NAMES with the register names appropriate to the +// machine architecture given in HEADER, indexed by the register +// numbers used in DWARF call frame information. Return true on +// success, or false if we don't recognize HEADER's machine +// architecture. +static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, + std::vector *register_names) { + switch (elf_header->e_machine) { + case EM_386: + *register_names = DwarfCFIToModule::RegisterNames::I386(); + return true; + case EM_ARM: + *register_names = DwarfCFIToModule::RegisterNames::ARM(); + return true; + case EM_X86_64: + *register_names = DwarfCFIToModule::RegisterNames::X86_64(); + return true; + default: + return false; + } +} + +static bool LoadDwarfCFI(const std::string &dwarf_filename, + const ElfW(Ehdr) *elf_header, + const char *section_name, + const ElfW(Shdr) *section, + const bool eh_frame, + const ElfW(Shdr) *got_section, + const ElfW(Shdr) *text_section, + const bool big_endian, + Module *module) { + // Find the appropriate set of register names for this file's + // architecture. + std::vector register_names; + if (!DwarfCFIRegisterNames(elf_header, ®ister_names)) { + fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" + " cannot convert DWARF call frame information\n", + dwarf_filename.c_str(), elf_header->e_machine); + return false; + } + + const dwarf2reader::Endianness endianness = big_endian ? + dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; + + // Find the call frame information and its size. + const char *cfi = reinterpret_cast(section->sh_offset); + size_t cfi_size = section->sh_size; + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(endianness); + // Since we're using the ElfW macro, we're not actually capable of + // processing both ELF32 and ELF64 files with the same program; that + // would take a bit more work. But this will work out well enough. + if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) + byte_reader.SetAddressSize(4); + else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) + byte_reader.SetAddressSize(8); + else { + fprintf(stderr, "%s: bad file class in ELF header: %d\n", + dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]); + return false; + } + // Provide the base addresses for .eh_frame encoded pointers, if + // possible. + byte_reader.SetCFIDataBase(section->sh_addr, cfi); + if (got_section) + byte_reader.SetDataBase(got_section->sh_addr); + if (text_section) + byte_reader.SetTextBase(text_section->sh_addr); + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, + section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + parser.Start(); + return true; +} + +bool LoadELF(const std::string &obj_file, MmapWrapper* map_wrapper, + ElfW(Ehdr) **elf_header) { + int obj_fd = open(obj_file.c_str(), O_RDONLY); + if (obj_fd < 0) { + fprintf(stderr, "Failed to open ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + FDWrapper obj_fd_wrapper(obj_fd); + struct stat st; + if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { + fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + void *obj_base = mmap(NULL, st.st_size, + PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); + if (obj_base == MAP_FAILED) { + fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); + return false; + } + map_wrapper->set(obj_base, st.st_size); + *elf_header = reinterpret_cast(obj_base); + if (!IsValidElf(*elf_header)) { + fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); + return false; + } + return true; +} + +// Get the endianness of ELF_HEADER. If it's invalid, return false. +bool ElfEndianness(const ElfW(Ehdr) *elf_header, bool *big_endian) { + if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) { + *big_endian = false; + return true; + } + if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) { + *big_endian = true; + return true; + } + + fprintf(stderr, "bad data encoding in ELF header: %d\n", + elf_header->e_ident[EI_DATA]); + return false; +} + +// Read the .gnu_debuglink and get the debug file name. If anything goes +// wrong, return an empty string. +static std::string ReadDebugLink(const ElfW(Shdr) *debuglink_section, + const std::string &obj_file, + const std::string &debug_dir) { + char *debuglink = reinterpret_cast(debuglink_section->sh_offset); + size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32. + debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes. + + // Sanity check. + if (debuglink_len != debuglink_section->sh_size) { + fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " + "%zx %zx\n", debuglink_len, debuglink_section->sh_size); + return ""; + } + + std::string debuglink_path = debug_dir + "/" + debuglink; + int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); + if (debuglink_fd < 0) { + fprintf(stderr, "Failed to open debug ELF file '%s' for '%s': %s\n", + debuglink_path.c_str(), obj_file.c_str(), strerror(errno)); + return ""; + } + FDWrapper debuglink_fd_wrapper(debuglink_fd); + // TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink + // section. + + return debuglink_path; +} + +// +// LoadSymbolsInfo +// +// Holds the state between the two calls to LoadSymbols() in case we have to +// follow the .gnu_debuglink section and load debug information from a +// different file. +// +class LoadSymbolsInfo { + public: + explicit LoadSymbolsInfo(const std::string &dbg_dir) : + debug_dir_(dbg_dir), + has_loading_addr_(false) {} + + // Keeps track of which sections have been loaded so we don't accidentally + // load it twice from two different files. + void LoadedSection(const std::string §ion) { + if (loaded_sections_.count(section) == 0) { + loaded_sections_.insert(section); + } else { + fprintf(stderr, "Section %s has already been loaded.\n", + section.c_str()); + } + } + + // We expect the ELF file and linked debug file to have the same preferred + // loading address. + void set_loading_addr(ElfW(Addr) addr, const std::string &filename) { + if (!has_loading_addr_) { + loading_addr_ = addr; + loaded_file_ = filename; + return; + } + + if (addr != loading_addr_) { + fprintf(stderr, + "ELF file '%s' and debug ELF file '%s' " + "have different load addresses.\n", + loaded_file_.c_str(), filename.c_str()); + assert(false); + } + } + + // Setters and getters + const std::string &debug_dir() const { + return debug_dir_; + } + + std::string debuglink_file() const { + return debuglink_file_; + } + void set_debuglink_file(std::string file) { + debuglink_file_ = file; + } + + private: + const std::string &debug_dir_; // Directory with the debug ELF file. + + std::string debuglink_file_; // Full path to the debug ELF file. + + bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid. + + ElfW(Addr) loading_addr_; // Saves the preferred loading address from the + // first call to LoadSymbols(). + + std::string loaded_file_; // Name of the file loaded from the first call to + // LoadSymbols(). + + std::set loaded_sections_; // Tracks the Loaded ELF sections + // between calls to LoadSymbols(). +}; + +static bool LoadSymbols(const std::string &obj_file, + const bool big_endian, + ElfW(Ehdr) *elf_header, + const bool read_gnu_debug_link, + LoadSymbolsInfo *info, + Module *module) { + // Translate all offsets in section headers into address. + FixAddress(elf_header); + ElfW(Addr) loading_addr = GetLoadingAddress( + reinterpret_cast(elf_header->e_phoff), + elf_header->e_phnum); + module->SetLoadAddress(loading_addr); + info->set_loading_addr(loading_addr, obj_file); + + const ElfW(Shdr) *sections = + reinterpret_cast(elf_header->e_shoff); + const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; + bool found_debug_info_section = false; + bool found_usable_info = false; + + // Look for STABS debugging information, and load it if present. + const ElfW(Shdr) *stab_section + = FindSectionByName(".stab", sections, section_names, + elf_header->e_shnum); + if (stab_section) { + const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections; + if (stabstr_section) { + found_debug_info_section = true; + found_usable_info = true; + info->LoadedSection(".stab"); + if (!LoadStabs(elf_header, stab_section, stabstr_section, big_endian, + module)) { + fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS" + " debugging information\n", obj_file.c_str()); + } + } + } + + // Look for DWARF debugging information, and load it if present. + const ElfW(Shdr) *dwarf_section + = FindSectionByName(".debug_info", sections, section_names, + elf_header->e_shnum); + if (dwarf_section) { + found_debug_info_section = true; + found_usable_info = true; + info->LoadedSection(".debug_info"); + if (!LoadDwarf(obj_file, elf_header, big_endian, module)) + fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " + "DWARF debugging information\n", obj_file.c_str()); + } + + // Dwarf Call Frame Information (CFI) is actually independent from + // the other DWARF debugging information, and can be used alone. + const ElfW(Shdr) *dwarf_cfi_section = + FindSectionByName(".debug_frame", sections, section_names, + elf_header->e_shnum); + if (dwarf_cfi_section) { + // Ignore the return value of this function; even without call frame + // information, the other debugging information could be perfectly + // useful. + info->LoadedSection(".debug_frame"); + bool result = + LoadDwarfCFI(obj_file, elf_header, ".debug_frame", + dwarf_cfi_section, false, 0, 0, big_endian, module); + found_usable_info = found_usable_info || result; + } + + // Linux C++ exception handling information can also provide + // unwinding data. + const ElfW(Shdr) *eh_frame_section = + FindSectionByName(".eh_frame", sections, section_names, + elf_header->e_shnum); + if (eh_frame_section) { + // Pointers in .eh_frame data may be relative to the base addresses of + // certain sections. Provide those sections if present. + const ElfW(Shdr) *got_section = + FindSectionByName(".got", sections, section_names, elf_header->e_shnum); + const ElfW(Shdr) *text_section = + FindSectionByName(".text", sections, section_names, + elf_header->e_shnum); + info->LoadedSection(".eh_frame"); + // As above, ignore the return value of this function. + bool result = + LoadDwarfCFI(obj_file, elf_header, ".eh_frame", eh_frame_section, true, + got_section, text_section, big_endian, module); + found_usable_info = found_usable_info || result; + } + + if (!found_debug_info_section) { + fprintf(stderr, "%s: file contains no debugging information" + " (no \".stab\" or \".debug_info\" sections)\n", + obj_file.c_str()); + + // Failed, but maybe we can find a .gnu_debuglink section? + if (read_gnu_debug_link) { + const ElfW(Shdr) *gnu_debuglink_section + = FindSectionByName(".gnu_debuglink", sections, section_names, + elf_header->e_shnum); + if (gnu_debuglink_section) { + if (!info->debug_dir().empty()) { + std::string debuglink_file = + ReadDebugLink(gnu_debuglink_section, obj_file, info->debug_dir()); + info->set_debuglink_file(debuglink_file); + } else { + fprintf(stderr, ".gnu_debuglink section found in '%s', " + "but no debug path specified.\n", obj_file.c_str()); + } + } else { + fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", + obj_file.c_str()); + } + } else { + // The caller doesn't want to consult .gnu_debuglink. + // See if there are export symbols available. + const ElfW(Shdr) *dynsym_section = + FindSectionByName(".dynsym", sections, section_names, + elf_header->e_shnum); + const ElfW(Shdr) *dynstr_section = + FindSectionByName(".dynstr", sections, section_names, + elf_header->e_shnum); + if (dynsym_section && dynstr_section) { + info->LoadedSection(".dynsym"); + fprintf(stderr, "Have .dynsym + .dynstr\n"); + + uint8_t* dynsyms = + reinterpret_cast(dynsym_section->sh_offset); + uint8_t* dynstrs = + reinterpret_cast(dynstr_section->sh_offset); + bool result = + ELFSymbolsToModule(dynsyms, + dynsym_section->sh_size, + dynstrs, + dynstr_section->sh_size, + big_endian, + // This could change to something more useful + // when support for dumping cross-architecture + // symbols is finished. + sizeof(ElfW(Addr)), + module); + found_usable_info = found_usable_info || result; + } + + // Return true if some usable information was found, since + // the caller doesn't want to use .gnu_debuglink. + return found_usable_info; + } + + // No debug info was found, let the user try again with .gnu_debuglink + // if present. + return false; + } + + return true; +} + +// Return the breakpad symbol file identifier for the architecture of +// ELF_HEADER. +const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) { + ElfW(Half) arch = elf_header->e_machine; + switch (arch) { + case EM_386: return "x86"; + case EM_ARM: return "arm"; + case EM_MIPS: return "mips"; + case EM_PPC64: return "ppc64"; + case EM_PPC: return "ppc"; + case EM_S390: return "s390"; + case EM_SPARC: return "sparc"; + case EM_SPARCV9: return "sparcv9"; + case EM_X86_64: return "x86_64"; + default: return NULL; + } +} + +// Format the Elf file identifier in IDENTIFIER as a UUID with the +// dashes removed. +std::string FormatIdentifier(unsigned char identifier[16]) { + char identifier_str[40]; + google_breakpad::FileID::ConvertIdentifierToString( + identifier, + identifier_str, + sizeof(identifier_str)); + std::string id_no_dash; + for (int i = 0; identifier_str[i] != '\0'; ++i) + if (identifier_str[i] != '-') + id_no_dash += identifier_str[i]; + // Add an extra "0" by the end. PDB files on Windows have an 'age' + // number appended to the end of the file identifier; this isn't + // really used or necessary on other platforms, but let's preserve + // the pattern. + id_no_dash += '0'; + return id_no_dash; +} + +// Return the non-directory portion of FILENAME: the portion after the +// last slash, or the whole filename if there are no slashes. +std::string BaseFileName(const std::string &filename) { + // Lots of copies! basename's behavior is less than ideal. + char *c_filename = strdup(filename.c_str()); + std::string base = basename(c_filename); + free(c_filename); + return base; +} + +} // namespace + +namespace google_breakpad { + +// Not explicitly exported, but not static so it can be used in unit tests. +// Ideally obj_file would be const, but internally this code does write +// to some ELF header fields to make its work simpler. +bool WriteSymbolFileInternal(uint8_t* obj_file, + const std::string &obj_filename, + const std::string &debug_dir, + bool cfi, + std::ostream &sym_stream) { + ElfW(Ehdr) *elf_header = reinterpret_cast(obj_file); + + if (!IsValidElf(elf_header)) { + fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); + return false; + } + + unsigned char identifier[16]; + if (!google_breakpad::FileID::ElfFileIdentifierFromMappedFile(elf_header, + identifier)) { + fprintf(stderr, "%s: unable to generate file identifier\n", + obj_filename.c_str()); + return false; + } + + const char *architecture = ElfArchitecture(elf_header); + if (!architecture) { + fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", + obj_filename.c_str(), elf_header->e_machine); + return false; + } + + // Figure out what endianness this file is. + bool big_endian; + if (!ElfEndianness(elf_header, &big_endian)) + return false; + + std::string name = BaseFileName(obj_filename); + std::string os = "Linux"; + std::string id = FormatIdentifier(identifier); + + LoadSymbolsInfo info(debug_dir); + Module module(name, os, architecture, id); + if (!LoadSymbols(obj_filename, big_endian, elf_header, !debug_dir.empty(), + &info, &module)) { + const std::string debuglink_file = info.debuglink_file(); + if (debuglink_file.empty()) + return false; + + // Load debuglink ELF file. + fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); + MmapWrapper debug_map_wrapper; + ElfW(Ehdr) *debug_elf_header = NULL; + if (!LoadELF(debuglink_file, &debug_map_wrapper, &debug_elf_header)) + return false; + // Sanity checks to make sure everything matches up. + const char *debug_architecture = ElfArchitecture(debug_elf_header); + if (!debug_architecture) { + fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", + debuglink_file.c_str(), debug_elf_header->e_machine); + return false; + } + if (strcmp(architecture, debug_architecture)) { + fprintf(stderr, "%s with ELF machine architecture %s does not match " + "%s with ELF architecture %s\n", + debuglink_file.c_str(), debug_architecture, + obj_filename.c_str(), architecture); + return false; + } + + bool debug_big_endian; + if (!ElfEndianness(debug_elf_header, &debug_big_endian)) + return false; + if (debug_big_endian != big_endian) { + fprintf(stderr, "%s and %s does not match in endianness\n", + obj_filename.c_str(), debuglink_file.c_str()); + return false; + } + + if (!LoadSymbols(debuglink_file, debug_big_endian, debug_elf_header, + false, &info, &module)) { + return false; + } + } + if (!module.Write(sym_stream, cfi)) + return false; + + return true; +} + +bool WriteSymbolFile(const std::string &obj_file, + const std::string &debug_dir, + bool cfi, + std::ostream &sym_stream) { + MmapWrapper map_wrapper; + ElfW(Ehdr) *elf_header = NULL; + if (!LoadELF(obj_file, &map_wrapper, &elf_header)) + return false; + + return WriteSymbolFileInternal(reinterpret_cast(elf_header), + obj_file, debug_dir, cfi, sym_stream); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/dump_symbols.h b/src/lib/crashdump/gbreakpad/common/linux/dump_symbols.h new file mode 100644 index 0000000..9bf54d3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/dump_symbols.h @@ -0,0 +1,56 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// dump_symbols.h: Read debugging information from an ELF file, and write +// it out as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__ +#define COMMON_LINUX_DUMP_SYMBOLS_H__ + +#include +#include + +namespace google_breakpad { + +// Find all the debugging information in OBJ_FILE, an ELF executable +// or shared library, and write it to SYM_STREAM in the Breakpad symbol +// file format. +// If OBJ_FILE has been stripped but contains a .gnu_debuglink section, +// then look for the debug file in DEBUG_DIR. +// If CFI is set to false, then omit the CFI section. +bool WriteSymbolFile(const std::string &obj_file, + const std::string &debug_dir, + bool cfi, + std::ostream &sym_stream); + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DUMP_SYMBOLS_H__ diff --git a/src/lib/crashdump/gbreakpad/common/linux/dump_symbols_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/dump_symbols_unittest.cc new file mode 100644 index 0000000..c6d4d2d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/dump_symbols_unittest.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Ted Mielczarek + +// dump_symbols_unittest.cc: +// Unittests for google_breakpad::DumpSymbols + +#include +#include +#include + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/synth_elf.h" + +namespace google_breakpad { +bool WriteSymbolFileInternal(uint8_t* obj_file, + const std::string &obj_filename, + const std::string &debug_dir, + bool cfi, + std::ostream &sym_stream); +} + +using google_breakpad::synth_elf::ELF; +using google_breakpad::synth_elf::StringTable; +using google_breakpad::synth_elf::SymbolTable; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; +using google_breakpad::WriteSymbolFileInternal; +using std::string; +using std::stringstream; +using std::vector; +using ::testing::Test; + +class DumpSymbols : public Test { + public: + void GetElfContents(ELF& elf) { + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_LT(0, contents.size()); + + elfdata_v.clear(); + elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); + elfdata = &elfdata_v[0]; + } + + vector elfdata_v; + uint8_t* elfdata; +}; + +TEST_F(DumpSymbols, Invalid) { + Elf32_Ehdr header; + memset(&header, 0, sizeof(header)); + stringstream s; + EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast(&header), + "foo", + "", + true, + s)); +} + +// TODO(ted): Fix the dump_symbols code to deal with cross-word-size +// ELF files. +#if __ELF_NATIVE_CLASS == 32 +TEST_F(DumpSymbols, SimplePublic32) { + ELF elf(EM_386, ELFCLASS32, kLittleEndian); + // Zero out text section for simplicity. + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + + // Add a public symbol. + StringTable table(kLittleEndian); + SymbolTable syms(kLittleEndian, 4, table); + syms.AddSymbol("superfunc", (uint32_t)0x1000, (uint32_t)0x10, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF + 1); + int index = elf.AddSection(".dynstr", table, SHT_STRTAB); + elf.AddSection(".dynsym", syms, + SHT_DYNSYM, // type + SHF_ALLOC, // flags + 0, // addr + index, // link + sizeof(Elf32_Sym)); // entsize + + elf.Finish(); + GetElfContents(elf); + + stringstream s; + ASSERT_TRUE(WriteSymbolFileInternal(elfdata, + "foo", + "", + true, + s)); + EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n" + "PUBLIC 1000 0 superfunc\n", + s.str()); +} +#endif + +#if __ELF_NATIVE_CLASS == 64 +TEST_F(DumpSymbols, SimplePublic64) { + ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian); + // Zero out text section for simplicity. + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + + // Add a public symbol. + StringTable table(kLittleEndian); + SymbolTable syms(kLittleEndian, 8, table); + syms.AddSymbol("superfunc", (uint64_t)0x1000, (uint64_t)0x10, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF + 1); + int index = elf.AddSection(".dynstr", table, SHT_STRTAB); + elf.AddSection(".dynsym", syms, + SHT_DYNSYM, // type + SHF_ALLOC, // flags + 0, // addr + index, // link + sizeof(Elf64_Sym)); // entsize + + elf.Finish(); + GetElfContents(elf); + + stringstream s; + ASSERT_TRUE(WriteSymbolFileInternal(elfdata, + "foo", + "", + true, + s)); + EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n" + "PUBLIC 1000 0 superfunc\n", + s.str()); +} +#endif diff --git a/src/lib/crashdump/gbreakpad/common/linux/eintr_wrapper.h b/src/lib/crashdump/gbreakpad/common/linux/eintr_wrapper.h new file mode 100644 index 0000000..225311e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/eintr_wrapper.h @@ -0,0 +1,47 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_LINUX_EINTR_WRAPPER_H_ +#define COMMON_LINUX_EINTR_WRAPPER_H_ + +#include + +// This provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// + +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) + +#endif // ifndef COMMON_LINUX_EINTR_WRAPPER_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.cc b/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.cc new file mode 100644 index 0000000..0e7db7b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.cc @@ -0,0 +1,179 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// elf_core_dump.cc: Implement google_breakpad::ElfCoreDump. +// See elf_core_dump.h for details. + +#include "common/linux/elf_core_dump.h" + +#include +#include + +namespace google_breakpad { + +// Implementation of ElfCoreDump::Note. + +ElfCoreDump::Note::Note() {} + +ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {} + +bool ElfCoreDump::Note::IsValid() const { + return GetHeader() != NULL; +} + +const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const { + return content_.GetData(0); +} + +ElfCoreDump::Word ElfCoreDump::Note::GetType() const { + const Nhdr* header = GetHeader(); + // 0 is not being used as a NOTE type. + return header ? header->n_type : 0; +} + +MemoryRange ElfCoreDump::Note::GetName() const { + const Nhdr* header = GetHeader(); + if (header) { + return content_.Subrange(sizeof(Nhdr), header->n_namesz); + } + return MemoryRange(); +} + +MemoryRange ElfCoreDump::Note::GetDescription() const { + const Nhdr* header = GetHeader(); + if (header) { + return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz), + header->n_descsz); + } + return MemoryRange(); +} + +ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const { + MemoryRange next_content; + const Nhdr* header = GetHeader(); + if (header) { + size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz); + next_offset = AlignedSize(next_offset + header->n_descsz); + next_content = + content_.Subrange(next_offset, content_.length() - next_offset); + } + return Note(next_content); +} + +// static +size_t ElfCoreDump::Note::AlignedSize(size_t size) { + size_t mask = sizeof(Word) - 1; + return (size + mask) & ~mask; +} + + +// Implementation of ElfCoreDump. + +ElfCoreDump::ElfCoreDump() {} + +ElfCoreDump::ElfCoreDump(const MemoryRange& content) + : content_(content) { +} + +void ElfCoreDump::SetContent(const MemoryRange& content) { + content_ = content; +} + +bool ElfCoreDump::IsValid() const { + const Ehdr* header = GetHeader(); + return (header && + header->e_ident[0] == ELFMAG0 && + header->e_ident[1] == ELFMAG1 && + header->e_ident[2] == ELFMAG2 && + header->e_ident[3] == ELFMAG3 && + header->e_ident[4] == kClass && + header->e_version == EV_CURRENT && + header->e_type == ET_CORE); +} + +const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const { + return content_.GetData(0); +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const { + const Ehdr* header = GetHeader(); + if (header) { + return reinterpret_cast(content_.GetArrayElement( + header->e_phoff, header->e_phentsize, index)); + } + return NULL; +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType( + Word type) const { + for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { + const Phdr* program = GetProgramHeader(i); + if (program->p_type == type) { + return program; + } + } + return NULL; +} + +unsigned ElfCoreDump::GetProgramHeaderCount() const { + const Ehdr* header = GetHeader(); + return header ? header->e_phnum : 0; +} + +bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) { + for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { + const Phdr* program = GetProgramHeader(i); + if (program->p_type != PT_LOAD) + continue; + + size_t offset_in_segment = virtual_address - program->p_vaddr; + if (virtual_address >= program->p_vaddr && + offset_in_segment < program->p_filesz) { + const void* data = + content_.GetData(program->p_offset + offset_in_segment, length); + if (data) { + memcpy(buffer, data, length); + return true; + } + } + } + return false; +} + +ElfCoreDump::Note ElfCoreDump::GetFirstNote() const { + MemoryRange note_content; + const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE); + if (program_header) { + note_content = content_.Subrange(program_header->p_offset, + program_header->p_filesz); + } + return Note(note_content); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.h b/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.h new file mode 100644 index 0000000..dd846f0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump.h @@ -0,0 +1,153 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// elf_core_dump.h: Define the google_breakpad::ElfCoreDump class, which +// encapsulates an ELF core dump file mapped into memory. + +#ifndef COMMON_LINUX_ELF_CORE_DUMP_H_ +#define COMMON_LINUX_ELF_CORE_DUMP_H_ + +#include +#if !defined(__ANDROID__) +#include +#endif +#include + +#include "common/memory_range.h" +#if defined(__ANDROID__) +#include "common/linux/android_link.h" +#endif + +namespace google_breakpad { + +// A class encapsulating an ELF core dump file mapped into memory, which +// provides methods for accessing program headers and the note section. +class ElfCoreDump { + public: + // ELF types based on the value of __WORDSIZE. + typedef ElfW(Ehdr) Ehdr; + typedef ElfW(Nhdr) Nhdr; + typedef ElfW(Phdr) Phdr; + typedef ElfW(Word) Word; + typedef ElfW(Addr) Addr; +#if __WORDSIZE == 32 + static const int kClass = ELFCLASS32; +#elif __WORDSIZE == 64 + static const int kClass = ELFCLASS64; +#else +#error "Unsupported __WORDSIZE for ElfCoreDump." +#endif + + // A class encapsulating the note content in a core dump, which provides + // methods for accessing the name and description of a note. + class Note { + public: + Note(); + + // Constructor that takes the note content from |content|. + explicit Note(const MemoryRange& content); + + // Returns true if this note is valid, i,e. a note header is found in + // |content_|, or false otherwise. + bool IsValid() const; + + // Returns the note header, or NULL if no note header is found in + // |content_|. + const Nhdr* GetHeader() const; + + // Returns the note type, or 0 if no note header is found in |content_|. + Word GetType() const; + + // Returns a memory range covering the note name, or an empty range + // if no valid note name is found in |content_|. + MemoryRange GetName() const; + + // Returns a memory range covering the note description, or an empty + // range if no valid note description is found in |content_|. + MemoryRange GetDescription() const; + + // Returns the note following this note, or an empty note if no valid + // note is found after this note. + Note GetNextNote() const; + + private: + // Returns the size in bytes round up to the word alignment, specified + // for the note section, of a given size in bytes. + static size_t AlignedSize(size_t size); + + // Note content. + MemoryRange content_; + }; + + ElfCoreDump(); + + // Constructor that takes the core dump content from |content|. + explicit ElfCoreDump(const MemoryRange& content); + + // Sets the core dump content to |content|. + void SetContent(const MemoryRange& content); + + // Returns true if a valid ELF header in the core dump, or false otherwise. + bool IsValid() const; + + // Returns the ELF header in the core dump, or NULL if no ELF header + // is found in |content_|. + const Ehdr* GetHeader() const; + + // Returns the |index|-th program header in the core dump, or NULL if no + // ELF header is found in |content_| or |index| is out of bounds. + const Phdr* GetProgramHeader(unsigned index) const; + + // Returns the first program header of |type| in the core dump, or NULL if + // no ELF header is found in |content_| or no program header of |type| is + // found. + const Phdr* GetFirstProgramHeaderOfType(Word type) const; + + // Returns the number of program headers in the core dump, or 0 if no + // ELF header is found in |content_|. + unsigned GetProgramHeaderCount() const; + + // Copies |length| bytes of data starting at |virtual_address| in the core + // dump to |buffer|. |buffer| should be a valid pointer to a buffer of at + // least |length| bytes. Returns true if the data to be copied is found in + // the core dump, or false otherwise. + bool CopyData(void* buffer, Addr virtual_address, size_t length); + + // Returns the first note found in the note section of the core dump, or + // an empty note if no note is found. + Note GetFirstNote() const; + + private: + // Core dump content. + MemoryRange content_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_ELF_CORE_DUMP_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump_unittest.cc new file mode 100644 index 0000000..11920f2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/elf_core_dump_unittest.cc @@ -0,0 +1,245 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump. + +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/elf_core_dump.h" +#include "common/linux/memory_mapped_file.h" +#include "common/tests/file_utils.h" +#include "common/linux/tests/crash_generator.h" + +using google_breakpad::AutoTempDir; +using google_breakpad::CrashGenerator; +using google_breakpad::ElfCoreDump; +using google_breakpad::MemoryMappedFile; +using google_breakpad::MemoryRange; +using google_breakpad::WriteFile; +using std::set; +using std::string; + +TEST(ElfCoreDumpTest, DefaultConstructor) { + ElfCoreDump core; + EXPECT_FALSE(core.IsValid()); + EXPECT_EQ(NULL, core.GetHeader()); + EXPECT_EQ(0, core.GetProgramHeaderCount()); + EXPECT_EQ(NULL, core.GetProgramHeader(0)); + EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); + EXPECT_FALSE(core.GetFirstNote().IsValid()); +} + +TEST(ElfCoreDumpTest, TestElfHeader) { + ElfCoreDump::Ehdr header; + memset(&header, 0, sizeof(header)); + + AutoTempDir temp_dir; + string core_path = temp_dir.path() + "/core"; + const char* core_file = core_path.c_str(); + MemoryMappedFile mapped_core_file; + ElfCoreDump core; + + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1)); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + EXPECT_EQ(NULL, core.GetHeader()); + EXPECT_EQ(0, core.GetProgramHeaderCount()); + EXPECT_EQ(NULL, core.GetProgramHeader(0)); + EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); + EXPECT_FALSE(core.GetFirstNote().IsValid()); + + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[0] = ELFMAG0; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[1] = ELFMAG1; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[2] = ELFMAG2; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[3] = ELFMAG3; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_ident[4] = ElfCoreDump::kClass; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_version = EV_CURRENT; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_FALSE(core.IsValid()); + + header.e_type = ET_CORE; + ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); + ASSERT_TRUE(mapped_core_file.Map(core_file)); + core.SetContent(mapped_core_file.content()); + EXPECT_TRUE(core.IsValid()); +} + +TEST(ElfCoreDumpTest, ValidCoreFile) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " + "due to non-default core pattern"); + return; + } + + const unsigned kNumOfThreads = 3; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGABRT; + // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in + // CrashGenerator is identified and fixed. + if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, NULL)) { + fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " + "due to no core dump generated"); + return; + } + pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread); + set expected_thread_ids; + for (unsigned i = 0; i < kNumOfThreads; ++i) { + expected_thread_ids.insert(crash_generator.GetThreadId(i)); + } + + MemoryMappedFile mapped_core_file; + ASSERT_TRUE(mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str())); + + ElfCoreDump core; + core.SetContent(mapped_core_file.content()); + EXPECT_TRUE(core.IsValid()); + + // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are + // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): + // Thread Name Type + // ------------------------------------------------------------------- + // 1st thread CORE NT_PRSTATUS + // process-wide CORE NT_PRPSINFO + // process-wide CORE NT_AUXV + // 1st thread CORE NT_FPREGSET + // 1st thread LINUX NT_PRXFPREG + // 1st thread LINUX NT_386_TLS + // + // 2nd thread CORE NT_PRSTATUS + // 2nd thread CORE NT_FPREGSET + // 2nd thread LINUX NT_PRXFPREG + // 2nd thread LINUX NT_386_TLS + // + // 3rd thread CORE NT_PRSTATUS + // 3rd thread CORE NT_FPREGSET + // 3rd thread LINUX NT_PRXFPREG + // 3rd thread LINUX NT_386_TLS + + size_t num_nt_prpsinfo = 0; + size_t num_nt_prstatus = 0; + size_t num_nt_fpregset = 0; + size_t num_nt_prxfpreg = 0; + set actual_thread_ids; + ElfCoreDump::Note note = core.GetFirstNote(); + while (note.IsValid()) { + MemoryRange name = note.GetName(); + MemoryRange description = note.GetDescription(); + EXPECT_FALSE(name.IsEmpty()); + EXPECT_FALSE(description.IsEmpty()); + + switch (note.GetType()) { + case NT_PRPSINFO: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(elf_prpsinfo), description.length()); + ++num_nt_prpsinfo; + break; + } + case NT_PRSTATUS: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(elf_prstatus), description.length()); + const elf_prstatus* status = description.GetData(0); + actual_thread_ids.insert(status->pr_pid); + if (num_nt_prstatus == 0) { + EXPECT_EQ(expected_crash_thread_id, status->pr_pid); + EXPECT_EQ(kCrashSignal, status->pr_info.si_signo); + } + ++num_nt_prstatus; + break; + } +#if defined(__i386) || defined(__x86_64) + case NT_FPREGSET: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(user_fpregs_struct), description.length()); + ++num_nt_fpregset; + break; + } +#endif +#if defined(__i386) + case NT_PRXFPREG: { + EXPECT_TRUE(description.data() != NULL); + EXPECT_EQ(sizeof(user_fpxregs_struct), description.length()); + ++num_nt_prxfpreg; + break; + } +#endif + default: + break; + } + note = note.GetNextNote(); + } + + EXPECT_TRUE(expected_thread_ids == actual_thread_ids); + EXPECT_EQ(1, num_nt_prpsinfo); + EXPECT_EQ(kNumOfThreads, num_nt_prstatus); +#if defined(__i386) || defined(__x86_64) + EXPECT_EQ(kNumOfThreads, num_nt_fpregset); +#endif +#if defined(__i386) + EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg); +#endif +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.cc b/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.cc new file mode 100644 index 0000000..82d53dd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.cc @@ -0,0 +1,168 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Ted Mielczarek + +#include "common/linux/elf_symbols_to_module.h" + +#include +#include + +#include "common/byte_cursor.h" +#include "common/module.h" + +namespace google_breakpad { + +class ELFSymbolIterator { +public: + // The contents of an ELF symbol, adjusted for the host's endianness, + // word size, and so on. Corresponds to the data in Elf32_Sym / Elf64_Sym. + struct Symbol { + // True if this iterator has reached the end of the symbol array. When + // this is set, the other members of this structure are not valid. + bool at_end; + + // The number of this symbol within the list. + size_t index; + + // The current symbol's name offset. This is the offset within the + // string table. + size_t name_offset; + + // The current symbol's value, size, info and shndx fields. + uint64_t value; + uint64_t size; + unsigned char info; + uint16_t shndx; + }; + + // Create an ELFSymbolIterator walking the symbols in BUFFER. Treat the + // symbols as big-endian if BIG_ENDIAN is true, as little-endian + // otherwise. Assume each symbol has a 'value' field whose size is + // VALUE_SIZE. + // + ELFSymbolIterator(const ByteBuffer *buffer, bool big_endian, + size_t value_size) + : value_size_(value_size), cursor_(buffer, big_endian) { + // Actually, weird sizes could be handled just fine, but they're + // probably mistakes --- expressed in bits, say. + assert(value_size == 4 || value_size == 8); + symbol_.index = 0; + Fetch(); + } + + // Move to the next symbol. This function's behavior is undefined if + // at_end() is true when it is called. + ELFSymbolIterator &operator++() { Fetch(); symbol_.index++; return *this; } + + // Dereferencing this iterator produces a reference to an Symbol structure + // that holds the current symbol's values. The symbol is owned by this + // SymbolIterator, and will be invalidated at the next call to operator++. + const Symbol &operator*() const { return symbol_; } + const Symbol *operator->() const { return &symbol_; } + +private: + // Read the symbol at cursor_, and set symbol_ appropriately. + void Fetch() { + // Elf32_Sym and Elf64_Sym have different layouts. + unsigned char other; + if (value_size_ == 4) { + // Elf32_Sym + cursor_ + .Read(4, false, &symbol_.name_offset) + .Read(4, false, &symbol_.value) + .Read(4, false, &symbol_.size) + .Read(1, false, &symbol_.info) + .Read(1, false, &other) + .Read(2, false, &symbol_.shndx); + } else { + // Elf64_Sym + cursor_ + .Read(4, false, &symbol_.name_offset) + .Read(1, false, &symbol_.info) + .Read(1, false, &other) + .Read(2, false, &symbol_.shndx) + .Read(8, false, &symbol_.value) + .Read(8, false, &symbol_.size); + } + symbol_.at_end = !cursor_; + } + + // The size of symbols' value field, in bytes. + size_t value_size_; + + // A byte cursor traversing buffer_. + ByteCursor cursor_; + + // Values for the symbol this iterator refers to. + Symbol symbol_; +}; + +const char *SymbolString(ptrdiff_t offset, ByteBuffer& strings) { + if (offset < 0 || (size_t) offset >= strings.Size()) { + // Return the null string. + offset = 0; + } + return reinterpret_cast(strings.start + offset); +} + +bool ELFSymbolsToModule(const uint8_t *symtab_section, + size_t symtab_size, + const uint8_t *string_section, + size_t string_size, + const bool big_endian, + size_t value_size, + Module *module) { + ByteBuffer symbols(symtab_section, symtab_size); + // Ensure that the string section is null-terminated. + if (string_section[string_size - 1] != '\0') { + const void* null_terminator = memrchr(string_section, '\0', string_size); + string_size = reinterpret_cast(null_terminator) + - string_section; + } + ByteBuffer strings(string_section, string_size); + + // The iterator walking the symbol table. + ELFSymbolIterator iterator(&symbols, big_endian, value_size); + + while(!iterator->at_end) { + if (ELF32_ST_TYPE(iterator->info) == STT_FUNC && + iterator->shndx != SHN_UNDEF) { + Module::Extern *ext = new Module::Extern; + ext->name = SymbolString(iterator->name_offset, strings); + ext->address = iterator->value; + module->AddExtern(ext); + } + ++iterator; + } + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.h b/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.h new file mode 100644 index 0000000..2e7c097 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module.h @@ -0,0 +1,58 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Ted Mielczarek + +// elf_symbols_to_module.h: Exposes ELFSymbolsToModule, a function +// for reading ELF symbol tables and inserting exported symbol names +// into a google_breakpad::Module as Extern definitions. + +#ifndef BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ +#define BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ + +#include +#include + +namespace google_breakpad { + +class Module; + +bool ELFSymbolsToModule(const uint8_t *symtab_section, + size_t symtab_size, + const uint8_t *string_section, + size_t string_size, + const bool big_endian, + size_t value_size, + Module *module); + +} // namespace google_breakpad + + +#endif // BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module_unittest.cc new file mode 100644 index 0000000..a3b874f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/elf_symbols_to_module_unittest.cc @@ -0,0 +1,370 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Ted Mielczarek + +// elf_symbols_to_module_unittest.cc: +// Unittests for google_breakpad::ELFSymbolsToModule + +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/elf_symbols_to_module.h" +#include "common/linux/synth_elf.h" +#include "common/module.h" +#include "common/test_assembler.h" + +using google_breakpad::Module; +using google_breakpad::synth_elf::StringTable; +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using ::testing::Test; +using ::testing::TestWithParam; +using std::string; +using std::vector; + +class ELFSymbolsToModuleTestFixture { +public: + ELFSymbolsToModuleTestFixture(Endianness endianness, + size_t value_size) : module("a", "b", "c", "d"), + section(endianness), + table(endianness), + value_size(value_size) {} + + bool ProcessSection() { + string section_contents, table_contents; + section.GetContents(§ion_contents); + table.GetContents(&table_contents); + + bool ret = ELFSymbolsToModule(reinterpret_cast(section_contents.data()), + section_contents.size(), + reinterpret_cast(table_contents.data()), + table_contents.size(), + section.endianness() == kBigEndian, + value_size, + &module); + module.GetExterns(&externs, externs.end()); + return ret; + } + + Module module; + Section section; + StringTable table; + string section_contents; + // 4 or 8 (bytes) + size_t value_size; + + vector externs; +}; + +class ELFSymbolsToModuleTest32 : public ELFSymbolsToModuleTestFixture, + public TestWithParam { +public: + ELFSymbolsToModuleTest32() : ELFSymbolsToModuleTestFixture(GetParam(), 4) {} + + void AddElf32Sym(const string& name, uint32_t value, + uint32_t size, unsigned info, uint16_t shndx) { + section + .D32(table.Add(name)) + .D32(value) + .D32(size) + .D8(info) + .D8(0) // other + .D16(shndx); + } +}; + +TEST_P(ELFSymbolsToModuleTest32, NoFuncs) { + ProcessSection(); + + ASSERT_EQ((size_t)0, externs.size()); +} + +TEST_P(ELFSymbolsToModuleTest32, OneFunc) { + const string kFuncName = "superfunc"; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest32, NameOutOfBounds) { + const string kFuncName = ""; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + table.Add("Foo"); + table.Add("Bar"); + // Can't use AddElf32Sym because it puts in a valid string offset. + section + .D32((uint32_t)table.Here().Value() + 1) + .D32(kFuncAddr) + .D32(kFuncSize) + .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) + .D8(0) // other + .D16(SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest32, NonTerminatedStringTable) { + const string kFuncName = ""; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + table.Add("Foo"); + table.Add("Bar"); + // Add a non-null-terminated string to the end of the string table + Label l; + table + .Mark(&l) + .Append("Unterminated"); + // Can't use AddElf32Sym because it puts in a valid string offset. + section + .D32((uint32_t)l.Value()) + .D32(kFuncAddr) + .D32(kFuncSize) + .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) + .D8(0) // other + .D16(SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest32, MultipleFuncs) { + const string kFuncName1 = "superfunc"; + const uint32_t kFuncAddr1 = 0x10001000; + const uint32_t kFuncSize1 = 0x10; + const string kFuncName2 = "awesomefunc"; + const uint32_t kFuncAddr2 = 0x20002000; + const uint32_t kFuncSize2 = 0x2f; + const string kFuncName3 = "megafunc"; + const uint32_t kFuncAddr3 = 0x30003000; + const uint32_t kFuncSize3 = 0x3c; + + AddElf32Sym(kFuncName1, kFuncAddr1, kFuncSize1, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + AddElf32Sym(kFuncName2, kFuncAddr2, kFuncSize2, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 2); + AddElf32Sym(kFuncName3, kFuncAddr3, kFuncSize3, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 3); + + ProcessSection(); + + ASSERT_EQ((size_t)3, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName1, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); + Module::Extern *extern2 = externs[1]; + EXPECT_EQ(kFuncName2, extern2->name); + EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); + Module::Extern *extern3 = externs[2]; + EXPECT_EQ(kFuncName3, extern3->name); + EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); +} + +TEST_P(ELFSymbolsToModuleTest32, SkipStuff) { + const string kFuncName = "superfunc"; + const uint32_t kFuncAddr = 0x1000; + const uint32_t kFuncSize = 0x10; + + // Should skip functions in SHN_UNDEF + AddElf32Sym("skipme", 0xFFFF, 0x10, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF); + AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + // Should skip non-STT_FUNC entries. + AddElf32Sym("skipmetoo", 0xAAAA, 0x10, + ELF32_ST_INFO(STB_GLOBAL, STT_FILE), + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +// Run all the 32-bit tests with both endianness +INSTANTIATE_TEST_CASE_P(Endian, + ELFSymbolsToModuleTest32, + ::testing::Values(kLittleEndian, kBigEndian)); + +// Similar tests, but with 64-bit values. Ostensibly this could be +// shoehorned into the parameterization by using ::testing::Combine, +// but that would make it difficult to get the types right since these +// actual test cases aren't parameterized. This could also be written +// as a type-parameterized test, but combining that with a value-parameterized +// test seemed really ugly, and also makes it harder to test 64-bit +// values. +class ELFSymbolsToModuleTest64 : public ELFSymbolsToModuleTestFixture, + public TestWithParam { +public: + ELFSymbolsToModuleTest64() : ELFSymbolsToModuleTestFixture(GetParam(), 8) {} + + void AddElf64Sym(const string& name, uint64_t value, + uint64_t size, unsigned info, uint16_t shndx) { + section + .D32(table.Add(name)) + .D8(info) + .D8(0) // other + .D16(shndx) + .D64(value) + .D64(size); + } +}; + +TEST_P(ELFSymbolsToModuleTest64, NoFuncs) { + ProcessSection(); + + ASSERT_EQ((size_t)0, externs.size()); +} + +TEST_P(ELFSymbolsToModuleTest64, OneFunc) { + const string kFuncName = "superfunc"; + const uint64_t kFuncAddr = 0x1000200030004000ULL; + const uint64_t kFuncSize = 0x1000; + + AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +TEST_P(ELFSymbolsToModuleTest64, MultipleFuncs) { + const string kFuncName1 = "superfunc"; + const uint64_t kFuncAddr1 = 0x1000100010001000ULL; + const uint64_t kFuncSize1 = 0x1000; + const string kFuncName2 = "awesomefunc"; + const uint64_t kFuncAddr2 = 0x2000200020002000ULL; + const uint64_t kFuncSize2 = 0x2f00; + const string kFuncName3 = "megafunc"; + const uint64_t kFuncAddr3 = 0x3000300030003000ULL; + const uint64_t kFuncSize3 = 0x3c00; + + AddElf64Sym(kFuncName1, kFuncAddr1, kFuncSize1, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + AddElf64Sym(kFuncName2, kFuncAddr2, kFuncSize2, + ELF64_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 2); + AddElf64Sym(kFuncName3, kFuncAddr3, kFuncSize3, + ELF64_ST_INFO(STB_LOCAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 3); + + ProcessSection(); + + ASSERT_EQ((size_t)3, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName1, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); + Module::Extern *extern2 = externs[1]; + EXPECT_EQ(kFuncName2, extern2->name); + EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); + Module::Extern *extern3 = externs[2]; + EXPECT_EQ(kFuncName3, extern3->name); + EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); +} + +TEST_P(ELFSymbolsToModuleTest64, SkipStuff) { + const string kFuncName = "superfunc"; + const uint64_t kFuncAddr = 0x1000100010001000ULL; + const uint64_t kFuncSize = 0x1000; + + // Should skip functions in SHN_UNDEF + AddElf64Sym("skipme", 0xFFFF, 0x10, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF); + AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, + ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), + // Doesn't really matter, just can't be SHN_UNDEF. + SHN_UNDEF + 1); + // Should skip non-STT_FUNC entries. + AddElf64Sym("skipmetoo", 0xAAAA, 0x10, + ELF64_ST_INFO(STB_GLOBAL, STT_FILE), + SHN_UNDEF + 1); + + ProcessSection(); + + ASSERT_EQ((size_t)1, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_EQ(kFuncName, extern1->name); + EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); +} + +// Run all the 64-bit tests with both endianness +INSTANTIATE_TEST_CASE_P(Endian, + ELFSymbolsToModuleTest64, + ::testing::Values(kLittleEndian, kBigEndian)); diff --git a/src/lib/crashdump/gbreakpad/common/linux/file_id.cc b/src/lib/crashdump/gbreakpad/common/linux/file_id.cc new file mode 100644 index 0000000..ff2f29e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/file_id.cc @@ -0,0 +1,281 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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_id.cc: Return a unique identifier for a file +// +// See file_id.h for documentation +// + +#include "common/linux/file_id.h" + +#include +#include +#if defined(__ANDROID__) +#include +#include "client/linux/android_link.h" +#else +#include +#include +#endif +#include + +#include + +#include "common/linux/linux_libc_support.h" +#include "common/linux/memory_mapped_file.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + +FileID::FileID(const char* path) { + strncpy(path_, path, sizeof(path_)); +} + +struct ElfClass32 { + typedef Elf32_Ehdr Ehdr; + typedef Elf32_Nhdr Nhdr; + typedef Elf32_Shdr Shdr; + static const int kClass = ELFCLASS32; +}; + +struct ElfClass64 { + typedef Elf64_Ehdr Ehdr; + typedef Elf64_Nhdr Nhdr; + typedef Elf64_Shdr Shdr; + static const int kClass = ELFCLASS64; +}; + +// These six functions are also used inside the crashed process, so be safe +// and use the syscall/libc wrappers instead of direct syscalls or libc. +template +static void FindElfClassSection(const char *elf_base, + const char *section_name, + uint32_t section_type, + const void **section_start, + int *section_size) { + typedef typename ElfClass::Ehdr Ehdr; + typedef typename ElfClass::Shdr Shdr; + + assert(elf_base); + assert(section_start); + assert(section_size); + + assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); + + int name_len = my_strlen(section_name); + + const Ehdr* elf_header = reinterpret_cast(elf_base); + assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); + + const Shdr* sections = + reinterpret_cast(elf_base + elf_header->e_shoff); + const Shdr* string_section = sections + elf_header->e_shstrndx; + + const Shdr* section = NULL; + for (int i = 0; i < elf_header->e_shnum; ++i) { + if (sections[i].sh_type == section_type) { + const char* current_section_name = (char*)(elf_base + + string_section->sh_offset + + sections[i].sh_name); + if (!my_strncmp(current_section_name, section_name, name_len)) { + section = §ions[i]; + break; + } + } + } + if (section != NULL && section->sh_size > 0) { + *section_start = elf_base + section->sh_offset; + *section_size = section->sh_size; + } +} + +// Attempt to find a section named |section_name| of type |section_type| +// in the ELF binary data at |elf_mapped_base|. On success, returns true +// and sets |*section_start| to point to the start of the section data, +// and |*section_size| to the size of the section's data. If |elfclass| +// is not NULL, set |*elfclass| to the ELF file class. +static bool FindElfSection(const void *elf_mapped_base, + const char *section_name, + uint32_t section_type, + const void **section_start, + int *section_size, + int *elfclass) { + assert(elf_mapped_base); + assert(section_start); + assert(section_size); + + *section_start = NULL; + *section_size = 0; + + const char* elf_base = + static_cast(elf_mapped_base); + const ElfW(Ehdr)* elf_header = + reinterpret_cast(elf_base); + if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0) + return false; + + if (elfclass) { + *elfclass = elf_header->e_ident[EI_CLASS]; + } + + if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) { + FindElfClassSection(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; + } else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) { + FindElfClassSection(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; + } + + return false; +} + +template +static bool ElfClassBuildIDNoteIdentifier(const void *section, + uint8_t identifier[kMDGUIDSize]) { + typedef typename ElfClass::Nhdr Nhdr; + + const Nhdr* note_header = reinterpret_cast(section); + if (note_header->n_type != NT_GNU_BUILD_ID || + note_header->n_descsz == 0) { + return false; + } + + const char* build_id = reinterpret_cast(section) + + sizeof(Nhdr) + note_header->n_namesz; + // Copy as many bits of the build ID as will fit + // into the GUID space. + my_memset(identifier, 0, kMDGUIDSize); + memcpy(identifier, build_id, + std::min(kMDGUIDSize, (size_t)note_header->n_descsz)); + + return true; +} + +// Attempt to locate a .note.gnu.build-id section in an ELF binary +// and copy as many bytes of it as will fit into |identifier|. +static bool FindElfBuildIDNote(const void *elf_mapped_base, + uint8_t identifier[kMDGUIDSize]) { + void* note_section; + int note_size, elfclass; + if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, + (const void**)¬e_section, ¬e_size, &elfclass) || + note_size == 0) { + return false; + } + + if (elfclass == ELFCLASS32) { + return ElfClassBuildIDNoteIdentifier(note_section, identifier); + } else if (elfclass == ELFCLASS64) { + return ElfClassBuildIDNoteIdentifier(note_section, identifier); + } + + return false; +} + +// Attempt to locate the .text section of an ELF binary and generate +// a simple hash by XORing the first page worth of bytes into |identifier|. +static bool HashElfTextSection(const void *elf_mapped_base, + uint8_t identifier[kMDGUIDSize]) { + void* text_section; + int text_size; + if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, + (const void**)&text_section, &text_size, NULL) || + text_size == 0) { + return false; + } + + my_memset(identifier, 0, kMDGUIDSize); + const uint8_t* ptr = reinterpret_cast(text_section); + const uint8_t* ptr_end = ptr + std::min(text_size, 4096); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) + identifier[i] ^= ptr[i]; + ptr += kMDGUIDSize; + } + return true; +} + +// static +bool FileID::ElfFileIdentifierFromMappedFile(const void* base, + uint8_t identifier[kMDGUIDSize]) { + // Look for a build id note first. + if (FindElfBuildIDNote(base, identifier)) + return true; + + // Fall back on hashing the first page of the text section. + return HashElfTextSection(base, identifier); +} + +bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { + MemoryMappedFile mapped_file(path_); + if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? + return false; + + return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); +} + +// static +void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], + char* buffer, int buffer_length) { + uint8_t identifier_swapped[kMDGUIDSize]; + + // Endian-ness swap to match dump processor expectation. + memcpy(identifier_swapped, identifier, kMDGUIDSize); + uint32_t* data1 = reinterpret_cast(identifier_swapped); + *data1 = htonl(*data1); + uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); + *data2 = htons(*data2); + uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); + *data3 = htons(*data3); + + int buffer_idx = 0; + for (unsigned int idx = 0; + (buffer_idx < buffer_length) && (idx < kMDGUIDSize); + ++idx) { + int hi = (identifier_swapped[idx] >> 4) & 0x0F; + int lo = (identifier_swapped[idx]) & 0x0F; + + if (idx == 4 || idx == 6 || idx == 8 || idx == 10) + buffer[buffer_idx++] = '-'; + + buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; + buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; + } + + // NULL terminate + buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/file_id.h b/src/lib/crashdump/gbreakpad/common/linux/file_id.h new file mode 100644 index 0000000..70a6b3f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/file_id.h @@ -0,0 +1,77 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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_id.h: Return a unique identifier for a file +// + +#ifndef COMMON_LINUX_FILE_ID_H__ +#define COMMON_LINUX_FILE_ID_H__ + +#include + +#include "common/linux/guid_creator.h" + +namespace google_breakpad { + +static const size_t kMDGUIDSize = sizeof(MDGUID); + +class FileID { + public: + explicit FileID(const char* path); + ~FileID() {} + + // Load the identifier for the elf file path specified in the constructor into + // |identifier|. Return false if the identifier could not be created for the + // file. + // The current implementation will look for a .note.gnu.build-id + // section and use that as the file id, otherwise it falls back to + // XORing the first 4096 bytes of the .text section to generate an identifier. + bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]); + + // Load the identifier for the elf file mapped into memory at |base| into + // |identifier|. Return false if the identifier could not be created for the + // file. + static bool ElfFileIdentifierFromMappedFile(const void* base, + uint8_t identifier[kMDGUIDSize]); + + // Convert the |identifier| data to a NULL terminated string. The string will + // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). + // The |buffer| should be at least 37 bytes long to receive all of the data + // and termination. Shorter buffers will contain truncated data. + static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], + char* buffer, int buffer_length); + + private: + // Storage for the path specified + char path_[PATH_MAX]; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_FILE_ID_H__ diff --git a/src/lib/crashdump/gbreakpad/common/linux/file_id_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/file_id_unittest.cc new file mode 100644 index 0000000..94bc80e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/file_id_unittest.cc @@ -0,0 +1,284 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Unit tests for FileID + +#include +#include + +#include "common/linux/file_id.h" +#include "common/linux/safe_readlink.h" +#include "common/linux/synth_elf.h" +#include "common/test_assembler.h" +#include "common/tests/auto_tempdir.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; +using google_breakpad::SafeReadLink; +using google_breakpad::synth_elf::BuildIDNote; +using google_breakpad::synth_elf::ELF; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Section; + +namespace { + +// Simply calling Section::Append(size, byte) produces a uninteresting pattern +// that tends to get hashed to 0000...0000. This populates the section with +// data to produce better hashes. +void PopulateSection(Section* section, int size, int prime_number) { + for (int i = 0; i < size; i++) + section->Append(1, (i % prime_number) % 256); +} + +} // namespace + +TEST(FileIDStripTest, StripSelf) { + // Calculate the File ID of this binary using + // FileID::ElfFileIdentifier, then make a copy of this binary, + // strip it, and ensure that the result is the same. + char exe_name[PATH_MAX]; + ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); + + // copy our binary to a temp file, and strip it + AutoTempDir temp_dir; + std::string templ = temp_dir.path() + "/file-id-unittest"; + char cmdline[4096]; + sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str()); + ASSERT_EQ(system(cmdline), 0); + sprintf(cmdline, "strip \"%s\"", templ.c_str()); + ASSERT_EQ(system(cmdline), 0); + + uint8_t identifier1[sizeof(MDGUID)]; + uint8_t identifier2[sizeof(MDGUID)]; + FileID fileid1(exe_name); + EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); + FileID fileid2(templ.c_str()); + EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); + char identifier_string1[37]; + char identifier_string2[37]; + FileID::ConvertIdentifierToString(identifier1, identifier_string1, + 37); + FileID::ConvertIdentifierToString(identifier2, identifier_string2, + 37); + EXPECT_STREQ(identifier_string1, identifier_string2); +} + +class FileIDTest : public testing::Test { +public: + void GetElfContents(ELF& elf) { + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_LT(0, contents.size()); + + elfdata_v.clear(); + elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); + elfdata = &elfdata_v[0]; + } + + vector elfdata_v; + uint8_t* elfdata; +}; + +TEST_F(FileIDTest, ElfClass) { + uint8_t identifier[sizeof(MDGUID)]; + const char expected_identifier_string[] = + "80808080-8080-0000-0000-008080808080"; + char identifier_string[sizeof(expected_identifier_string)]; + const size_t kTextSectionSize = 128; + + ELF elf32(EM_386, ELFCLASS32, kLittleEndian); + Section text32(kLittleEndian); + for (size_t i = 0; i < kTextSectionSize; ++i) { + text32.D8(i * 3); + } + elf32.AddSection(".text", text32, SHT_PROGBITS); + elf32.Finish(); + GetElfContents(elf32); + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier)); + + FileID::ConvertIdentifierToString(identifier, identifier_string, + sizeof(identifier_string)); + EXPECT_STREQ(expected_identifier_string, identifier_string); + + memset(identifier, 0, sizeof(identifier)); + memset(identifier_string, 0, sizeof(identifier_string)); + + ELF elf64(EM_X86_64, ELFCLASS64, kLittleEndian); + Section text64(kLittleEndian); + for (size_t i = 0; i < kTextSectionSize; ++i) { + text64.D8(i * 3); + } + elf64.AddSection(".text", text64, SHT_PROGBITS); + elf64.Finish(); + GetElfContents(elf64); + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier)); + + FileID::ConvertIdentifierToString(identifier, identifier_string, + sizeof(identifier_string)); + EXPECT_STREQ(expected_identifier_string, identifier_string); +} + +TEST_F(FileIDTest, BuildID) { + const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; + char expected_identifier_string[] = + "00000000-0000-0000-0000-000000000000"; + FileID::ConvertIdentifierToString(kExpectedIdentifier, + expected_identifier_string, + sizeof(expected_identifier_string)); + + uint8_t identifier[sizeof(MDGUID)]; + char identifier_string[sizeof(expected_identifier_string)]; + + ELF elf32(EM_386, ELFCLASS32, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf32.AddSection(".text", text, SHT_PROGBITS); + BuildIDNote::AppendSection(elf32, + kExpectedIdentifier, + sizeof(kExpectedIdentifier)); + elf32.Finish(); + GetElfContents(elf32); + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier)); + + FileID::ConvertIdentifierToString(identifier, identifier_string, + sizeof(identifier_string)); + EXPECT_STREQ(expected_identifier_string, identifier_string); + + memset(identifier, 0, sizeof(identifier)); + memset(identifier_string, 0, sizeof(identifier_string)); + + ELF elf64(EM_X86_64, ELFCLASS64, kLittleEndian); + // Re-use empty text section from previous test + elf64.AddSection(".text", text, SHT_PROGBITS); + BuildIDNote::AppendSection(elf64, + kExpectedIdentifier, + sizeof(kExpectedIdentifier)); + elf64.Finish(); + GetElfContents(elf64); + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier)); + + FileID::ConvertIdentifierToString(identifier, identifier_string, + sizeof(identifier_string)); + EXPECT_STREQ(expected_identifier_string, identifier_string); +} + +// Test to make sure two files with different text sections produce +// different hashes when not using a build id. +TEST_F(FileIDTest, UniqueHashes32) { + char identifier_string_1[] = + "00000000-0000-0000-0000-000000000000"; + char identifier_string_2[] = + "00000000-0000-0000-0000-000000000000"; + uint8_t identifier_1[sizeof(MDGUID)]; + uint8_t identifier_2[sizeof(MDGUID)]; + + { + ELF elf1(EM_386, ELFCLASS32, kLittleEndian); + Section foo_1(kLittleEndian); + PopulateSection(&foo_1, 32, 5); + elf1.AddSection(".foo", foo_1, SHT_PROGBITS); + Section text_1(kLittleEndian); + PopulateSection(&text_1, 4096, 17); + elf1.AddSection(".text", text_1, SHT_PROGBITS); + elf1.Finish(); + GetElfContents(elf1); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1)); + FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, + sizeof(identifier_string_1)); + + { + ELF elf2(EM_386, ELFCLASS32, kLittleEndian); + Section text_2(kLittleEndian); + Section foo_2(kLittleEndian); + PopulateSection(&foo_2, 32, 5); + elf2.AddSection(".foo", foo_2, SHT_PROGBITS); + PopulateSection(&text_2, 4096, 31); + elf2.AddSection(".text", text_2, SHT_PROGBITS); + elf2.Finish(); + GetElfContents(elf2); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2)); + FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, + sizeof(identifier_string_2)); + + EXPECT_STRNE(identifier_string_1, identifier_string_2); +} + +// Same as UniqueHashes32, for x86-64. +TEST_F(FileIDTest, UniqueHashes64) { + char identifier_string_1[] = + "00000000-0000-0000-0000-000000000000"; + char identifier_string_2[] = + "00000000-0000-0000-0000-000000000000"; + uint8_t identifier_1[sizeof(MDGUID)]; + uint8_t identifier_2[sizeof(MDGUID)]; + + { + ELF elf1(EM_X86_64, ELFCLASS64, kLittleEndian); + Section foo_1(kLittleEndian); + PopulateSection(&foo_1, 32, 5); + elf1.AddSection(".foo", foo_1, SHT_PROGBITS); + Section text_1(kLittleEndian); + PopulateSection(&text_1, 4096, 17); + elf1.AddSection(".text", text_1, SHT_PROGBITS); + elf1.Finish(); + GetElfContents(elf1); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_1)); + FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, + sizeof(identifier_string_1)); + + { + ELF elf2(EM_X86_64, ELFCLASS64, kLittleEndian); + Section text_2(kLittleEndian); + Section foo_2(kLittleEndian); + PopulateSection(&foo_2, 32, 5); + elf2.AddSection(".foo", foo_2, SHT_PROGBITS); + PopulateSection(&text_2, 4096, 31); + elf2.AddSection(".text", text_2, SHT_PROGBITS); + elf2.Finish(); + GetElfContents(elf2); + } + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier_2)); + FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, + sizeof(identifier_string_2)); + + EXPECT_STRNE(identifier_string_1, identifier_string_2); +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.cc b/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.cc new file mode 100644 index 0000000..b739a6f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.cc @@ -0,0 +1,199 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/linux/google_crashdump_uploader.h" +#include "common/linux/libcurl_wrapper.h" + +#include +#include +#include + +#include + +using std::string; + +namespace google_breakpad { + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword) { + LibcurlWrapper* http_layer = new LibcurlWrapper(); + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer) { + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +void GoogleCrashdumpUploader::Init(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer) { + product_ = product; + version_ = version; + guid_ = guid; + ptime_ = ptime; + ctime_ = ctime; + email_ = email; + comments_ = comments; + http_layer_ = http_layer; + + crash_server_ = crash_server; + proxy_host_ = proxy_host; + proxy_userpassword_ = proxy_userpassword; + minidump_pathname_ = minidump_pathname; + std::cout << "Uploader initializing"; + std::cout << "\tProduct: " << product_; + std::cout << "\tVersion: " << version_; + std::cout << "\tGUID: " << guid_; + if (!ptime_.empty()) { + std::cout << "\tProcess uptime: " << ptime_; + } + if (!ctime_.empty()) { + std::cout << "\tCumulative Process uptime: " << ctime_; + } + if (!email_.empty()) { + std::cout << "\tEmail: " << email_; + } + if (!comments_.empty()) { + std::cout << "\tComments: " << comments_; + } +} + +bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() { + std::string error_text; + if (product_.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (version_.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (guid_.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (minidump_pathname_.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + std::cout << error_text; + return false; + } + return true; + +} + +bool GoogleCrashdumpUploader::Upload() { + bool ok = http_layer_->Init(); + if (!ok) { + std::cout << "http layer init failed"; + return ok; + } + + if (!CheckRequiredParametersArePresent()) { + return false; + } + + struct stat st; + int err = stat(minidump_pathname_.c_str(), &st); + if (err) { + std::cout << minidump_pathname_ << " could not be found"; + return false; + } + + parameters_["prod"] = product_; + parameters_["ver"] = version_; + parameters_["guid"] = guid_; + parameters_["ptime"] = ptime_; + parameters_["ctime"] = ctime_; + parameters_["email"] = email_; + parameters_["comments_"] = comments_; + if (!http_layer_->AddFile(minidump_pathname_, + "upload_file_minidump")) { + return false; + } + std::cout << "Sending request to " << crash_server_; + return http_layer_->SendRequest(crash_server_, + parameters_, + NULL); +} +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.h b/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.h new file mode 100644 index 0000000..5cea17d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader.h @@ -0,0 +1,98 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 + +namespace google_breakpad { + +class LibcurlWrapper; + +class GoogleCrashdumpUploader { + public: + GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword); + + GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer); + + void Init(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer); + bool Upload(); + + private: + bool CheckRequiredParametersArePresent(); + + LibcurlWrapper* http_layer_; + std::string product_; + std::string version_; + std::string guid_; + std::string ptime_; + std::string ctime_; + std::string email_; + std::string comments_; + std::string minidump_pathname_; + + std::string crash_server_; + std::string proxy_host_; + std::string proxy_userpassword_; + + std::map parameters_; +}; +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader_test.cc b/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader_test.cc new file mode 100644 index 0000000..c65355c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/google_crashdump_uploader_test.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Unit test for crash dump uploader. + +#include "common/linux/google_crashdump_uploader.h" +#include "common/linux/libcurl_wrapper.h" +#include "breakpad_googletest_includes.h" + +namespace google_breakpad { + +using ::testing::Return; +using ::testing::_; + +class MockLibcurlWrapper : public LibcurlWrapper { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD2(SetProxy, bool(const std::string& proxy_host, + const std::string& proxy_userpwd)); + MOCK_METHOD2(AddFile, bool(const std::string& upload_file_path, + const std::string& basename)); + MOCK_METHOD3(SendRequest, + bool(const std::string& url, + const std::map& parameters, + std::string* server_response)); +}; + +class GoogleCrashdumpUploaderTest : public ::testing::Test { +}; + +TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload()); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { + // Create a temp file + char tempfn[80] = "/tmp/googletest-upload-XXXXXX"; + int fd = mkstemp(tempfn); + ASSERT_NE(fd, -1); + close(fd); + + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); + EXPECT_CALL(m, + SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + tempfn, + "http://foo.com", + "", + "", + &m); + ASSERT_TRUE(uploader->Upload()); +} + + +TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest(_,_,_)).Times(0); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload()); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { + // Test with empty product name. + GoogleCrashdumpUploader uploader("", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + ""); + ASSERT_FALSE(uploader.Upload()); + + // Test with empty product version. + GoogleCrashdumpUploader uploader1("product", + "", + "AAA-BBB", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + + ASSERT_FALSE(uploader1.Upload()); + + // Test with empty client GUID. + GoogleCrashdumpUploader uploader2("product", + "1.0", + "", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + ASSERT_FALSE(uploader2.Upload()); +} +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/guid_creator.cc b/src/lib/crashdump/gbreakpad/common/linux/guid_creator.cc new file mode 100644 index 0000000..426f93a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/guid_creator.cc @@ -0,0 +1,104 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/linux/guid_creator.h" + +#include +#include +#include +#include +#include +#include + +// +// GUIDGenerator +// +// This class is used to generate random GUID. +// Currently use random number to generate a GUID since Linux has +// no native GUID generator. This should be OK since we don't expect +// crash to happen very offen. +// +class GUIDGenerator { + public: + static u_int32_t BytesToUInt32(const u_int8_t bytes[]) { + return ((u_int32_t) bytes[0] + | ((u_int32_t) bytes[1] << 8) + | ((u_int32_t) bytes[2] << 16) + | ((u_int32_t) bytes[3] << 24)); + } + + static void UInt32ToBytes(u_int8_t bytes[], u_int32_t n) { + bytes[0] = n & 0xff; + bytes[1] = (n >> 8) & 0xff; + bytes[2] = (n >> 16) & 0xff; + bytes[3] = (n >> 24) & 0xff; + } + + static bool CreateGUID(GUID *guid) { + InitOnce(); + guid->data1 = random(); + guid->data2 = (u_int16_t)(random()); + guid->data3 = (u_int16_t)(random()); + UInt32ToBytes(&guid->data4[0], random()); + UInt32ToBytes(&guid->data4[4], random()); + return true; + } + + private: + static void InitOnce() { + pthread_once(&once_control, &InitOnceImpl); + } + + static void InitOnceImpl() { + srandom(time(NULL)); + } + + static pthread_once_t once_control; +}; + +pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT; + +bool CreateGUID(GUID *guid) { + return GUIDGenerator::CreateGUID(guid); +} + +// Parse guid to string. +bool GUIDToString(const GUID *guid, char *buf, int buf_len) { + // Should allow more space the the max length of GUID. + assert(buf_len > kGUIDStringLength); + int num = snprintf(buf, buf_len, kGUIDFormatString, + guid->data1, guid->data2, guid->data3, + GUIDGenerator::BytesToUInt32(&(guid->data4[0])), + GUIDGenerator::BytesToUInt32(&(guid->data4[4]))); + if (num != kGUIDStringLength) + return false; + + buf[num] = '\0'; + return true; +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/guid_creator.h b/src/lib/crashdump/gbreakpad/common/linux/guid_creator.h new file mode 100644 index 0000000..c86d856 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/guid_creator.h @@ -0,0 +1,48 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_LINUX_GUID_CREATOR_H__ +#define COMMON_LINUX_GUID_CREATOR_H__ + +#include "google_breakpad/common/minidump_format.h" + +typedef MDGUID GUID; + +// Format string for parsing GUID. +#define kGUIDFormatString "%08x-%04x-%04x-%08x-%08x" +// Length of GUID string. Don't count the ending '\0'. +#define kGUIDStringLength 36 + +// Create a guid. +bool CreateGUID(GUID *guid); + +// Get the string from guid. +bool GUIDToString(const GUID *guid, char *buf, int buf_len); + +#endif diff --git a/src/lib/crashdump/gbreakpad/common/linux/http_upload.cc b/src/lib/crashdump/gbreakpad/common/linux/http_upload.cc new file mode 100644 index 0000000..f69237c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/http_upload.cc @@ -0,0 +1,206 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/linux/http_upload.h" + +#include +#include +#include "third_party/curl/curl.h" + +namespace { + +// Callback to get the response data from server. +static size_t WriteCallback(void *ptr, size_t size, + size_t nmemb, void *userp) { + if (!userp) + return 0; + + std::string *response = reinterpret_cast(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast(ptr), real_size); + return real_size; +} + +} // namespace + +namespace google_breakpad { + +static const char kUserAgent[] = "Breakpad/1.0 (Linux)"; + +// static +bool HTTPUpload::SendRequest(const string &url, + const map ¶meters, + const string &upload_file, + const string &file_part_name, + const string &proxy, + const string &proxy_user_pwd, + const string &ca_certificate_file, + string *response_body, + long *response_code, + string *error_description) { + if (response_code != NULL) + *response_code = 0; + + if (!CheckParameters(parameters)) + return false; + + void *curl_lib = dlopen("libcurl.so", RTLD_NOW); + if (!curl_lib) { + if (error_description != NULL) + *error_description = dlerror(); + curl_lib = dlopen("libcurl.so.4", RTLD_NOW); + } + if (!curl_lib) { + // Debian gives libcurl a different name when it is built against GnuTLS + // instead of OpenSSL. + curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW); + } + if (!curl_lib) { + curl_lib = dlopen("libcurl.so.3", RTLD_NOW); + } + if (!curl_lib) { + return false; + } + + CURL* (*curl_easy_init)(void); + *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init"); + CURL *curl = (*curl_easy_init)(); + if (error_description != NULL) + *error_description = "No Error"; + + if (!curl) { + dlclose(curl_lib); + return false; + } + + CURLcode err_code = CURLE_OK; + CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...); + *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt"); + (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str()); + (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent); + // Set proxy information if necessary. + if (!proxy.empty()) + (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str()); + if (!proxy_user_pwd.empty()) + (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str()); + + if (!ca_certificate_file.empty()) + (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str()); + + struct curl_httppost *formpost = NULL; + struct curl_httppost *lastptr = NULL; + // Add form data. + CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...); + *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd"); + map::const_iterator iter = parameters.begin(); + for (; iter != parameters.end(); ++iter) + (*curl_formadd)(&formpost, &lastptr, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_COPYCONTENTS, iter->second.c_str(), + CURLFORM_END); + + // Add form file. + (*curl_formadd)(&formpost, &lastptr, + CURLFORM_COPYNAME, file_part_name.c_str(), + CURLFORM_FILE, upload_file.c_str(), + CURLFORM_END); + + (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost); + + // Disable 100-continue header. + struct curl_slist *headerlist = NULL; + char buf[] = "Expect:"; + struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *); + *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append"); + headerlist = (*curl_slist_append)(headerlist, buf); + (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist); + + if (response_body != NULL) { + (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA, + reinterpret_cast(response_body)); + } + + // Fail if 400+ is returned from the web server. + (*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1); + + CURLcode (*curl_easy_perform)(CURL *); + *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform"); + err_code = (*curl_easy_perform)(curl); + if (response_code != NULL) { + CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...); + *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo"); + (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code); + } + const char* (*curl_easy_strerror)(CURLcode); + *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror"); +#ifndef NDEBUG + if (err_code != CURLE_OK) + fprintf(stderr, "Failed to send http request to %s, error: %s\n", + url.c_str(), + (*curl_easy_strerror)(err_code)); +#endif + if (error_description != NULL) + *error_description = (*curl_easy_strerror)(err_code); + + void (*curl_easy_cleanup)(CURL *); + *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup"); + (*curl_easy_cleanup)(curl); + if (formpost != NULL) { + void (*curl_formfree)(struct curl_httppost *); + *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree"); + (*curl_formfree)(formpost); + } + if (headerlist != NULL) { + void (*curl_slist_free_all)(struct curl_slist *); + *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all"); + (*curl_slist_free_all)(headerlist); + } + dlclose(curl_lib); + return err_code == CURLE_OK; +} + +// static +bool HTTPUpload::CheckParameters(const map ¶meters) { + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + const string &str = pos->first; + if (str.size() == 0) + return false; // disallow empty parameter names + for (unsigned int i = 0; i < str.size(); ++i) { + int c = str[i]; + if (c < 32 || c == '"' || c > 127) { + return false; + } + } + } + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/http_upload.h b/src/lib/crashdump/gbreakpad/common/linux/http_upload.h new file mode 100644 index 0000000..22b6296 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/http_upload.h @@ -0,0 +1,87 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST +// request using libcurl. It currently supports requests that contain +// a set of string parameters (key/value pairs), and a file to upload. + +#ifndef COMMON_LINUX_HTTP_UPLOAD_H__ +#define COMMON_LINUX_HTTP_UPLOAD_H__ + +#include +#include + +namespace google_breakpad { + +using std::string; +using std::map; + +class HTTPUpload { + public: + // Sends the given set of parameters, along with the contents of + // upload_file, as a multipart POST request to the given URL. + // file_part_name contains the name of the file part of the request + // (i.e. it corresponds to the name= attribute on an . + // Parameter names must contain only printable ASCII characters, + // and may not contain a quote (") character. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + // If the send fails, a description of the error will be + // returned in error_description. + static bool SendRequest(const string &url, + const map ¶meters, + const string &upload_file, + const string &file_part_name, + const string &proxy, + const string &proxy_user_pwd, + const string &ca_certificate_file, + string *response_body, + long *response_code, + string *error_description); + + private: + // Checks that the given list of parameters has only printable + // ASCII characters in the parameter name, and does not contain + // any quote (") characters. Returns true if so. + static bool CheckParameters(const map ¶meters); + + // No instances of this class should be created. + // Disallow all constructors, destructors, and operator=. + HTTPUpload(); + explicit HTTPUpload(const HTTPUpload &); + void operator=(const HTTPUpload &); + ~HTTPUpload(); +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_HTTP_UPLOAD_H__ diff --git a/src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.cc b/src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.cc new file mode 100644 index 0000000..8b61aa0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.cc @@ -0,0 +1,223 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/linux/libcurl_wrapper.h" + +using std::string; + +namespace google_breakpad { +LibcurlWrapper::LibcurlWrapper() + : init_ok_(false), + formpost_(NULL), + lastptr_(NULL), + headerlist_(NULL) { + curl_lib_ = dlopen("libcurl.so", RTLD_NOW); + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW); + } + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW); + } + if (!curl_lib_) { + std::cout << "Could not find libcurl via dlopen"; + return; + } + std::cout << "LibcurlWrapper init succeeded"; + init_ok_ = true; + return; +} + +bool LibcurlWrapper::SetProxy(const std::string& proxy_host, + const std::string& proxy_userpwd) { + if (!init_ok_) { + return false; + } + // Set proxy information if necessary. + if (!proxy_host.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str()); + } else { + std::cout << "SetProxy called with empty proxy host."; + return false; + } + if (!proxy_userpwd.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str()); + } else { + std::cout << "SetProxy called with empty proxy username/password."; + return false; + } + std::cout << "Set proxy host to " << proxy_host; + return true; +} + +bool LibcurlWrapper::AddFile(const std::string& upload_file_path, + const std::string& basename) { + if (!init_ok_) { + return false; + } + std::cout << "Adding " << upload_file_path << " to form upload."; + // Add form file. + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, basename.c_str(), + CURLFORM_FILE, upload_file_path.c_str(), + CURLFORM_END); + + return true; +} + +// Callback to get the response data from server. +static size_t WriteCallback(void *ptr, size_t size, + size_t nmemb, void *userp) { + if (!userp) + return 0; + + std::string *response = reinterpret_cast(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast(ptr), real_size); + return real_size; +} + +bool LibcurlWrapper::SendRequest(const std::string& url, + const std::map& parameters, + std::string* server_response) { + (*easy_setopt_)(curl_, CURLOPT_URL, url.c_str()); + std::map::const_iterator iter = parameters.begin(); + for (; iter != parameters.end(); ++iter) + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_COPYCONTENTS, iter->second.c_str(), + CURLFORM_END); + + (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_); + if (server_response != NULL) { + (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); + (*easy_setopt_)(curl_, CURLOPT_WRITEDATA, + reinterpret_cast(server_response)); + } + + CURLcode err_code = CURLE_OK; + err_code = (*easy_perform_)(curl_); + easy_strerror_ = reinterpret_cast + (dlsym(curl_lib_, "curl_easy_strerror")); + +#ifndef NDEBUG + if (err_code != CURLE_OK) + fprintf(stderr, "Failed to send http request to %s, error: %s\n", + url.c_str(), + (*easy_strerror_)(err_code)); +#endif + if (headerlist_ != NULL) { + (*slist_free_all_)(headerlist_); + } + + (*easy_cleanup_)(curl_); + if (formpost_ != NULL) { + (*formfree_)(formpost_); + } + + return err_code == CURLE_OK; +} + +bool LibcurlWrapper::Init() { + if (!init_ok_) { + std::cout << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages"; + return false; + } + + if (!SetFunctionPointers()) { + std::cout << "Could not find function pointers"; + init_ok_ = false; + return false; + } + + curl_ = (*easy_init_)(); + + last_curl_error_ = "No Error"; + + if (!curl_) { + dlclose(curl_lib_); + std::cout << "Curl initialization failed"; + return false; + } + + // Disable 100-continue header. + char buf[] = "Expect:"; + + headerlist_ = (*slist_append_)(headerlist_, buf); + (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_); + return true; +} + +#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \ + var = reinterpret_cast(dlsym(curl_lib_, function_name)); \ + if (!var) { \ + std::cout << "Could not find libcurl function " << function_name; \ + init_ok_ = false; \ + return false; \ + } + +bool LibcurlWrapper::SetFunctionPointers() { + + SET_AND_CHECK_FUNCTION_POINTER(easy_init_, + "curl_easy_init", + CURL*(*)()); + + SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_, + "curl_easy_setopt", + CURLcode(*)(CURL*, CURLoption, ...)); + + SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd", + CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...)); + + SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append", + curl_slist*(*)(curl_slist*, const char*)); + + SET_AND_CHECK_FUNCTION_POINTER(easy_perform_, + "curl_easy_perform", + CURLcode(*)(CURL*)); + + SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_, + "curl_easy_cleanup", + void(*)(CURL*)); + + SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, + "curl_slist_free_all", + void(*)(curl_slist*)); + + SET_AND_CHECK_FUNCTION_POINTER(formfree_, + "curl_formfree", + void(*)(curl_httppost*)); + return true; +} + +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.h b/src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.h new file mode 100644 index 0000000..3e53e61 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/libcurl_wrapper.h @@ -0,0 +1,83 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// A wrapper for libcurl to do HTTP Uploads, to support easy mocking +// and unit testing of the HTTPUpload class. + +#include +#include + +#include "third_party/curl/curl.h" + +namespace google_breakpad { +class LibcurlWrapper { + public: + LibcurlWrapper(); + virtual bool Init(); + virtual bool SetProxy(const std::string& proxy_host, + const std::string& proxy_userpwd); + virtual bool AddFile(const std::string& upload_file_path, + const std::string& basename); + virtual bool SendRequest(const std::string& url, + const std::map& parameters, + std::string* server_response); + private: + // This function initializes class state corresponding to function + // pointers into the CURL library. + bool SetFunctionPointers(); + + bool init_ok_; // Whether init succeeded + void* curl_lib_; // Pointer to result of dlopen() on + // curl library + std::string last_curl_error_; // The text of the last error when + // dealing + // with CURL. + + CURL *curl_; // Pointer for handle for CURL calls. + + CURL* (*easy_init_)(void); + + // Stateful pointers for calling into curl_formadd() + struct curl_httppost *formpost_; + struct curl_httppost *lastptr_; + struct curl_slist *headerlist_; + + // Function pointers into CURL library + CURLcode (*easy_setopt_)(CURL *, CURLoption, ...); + CURLFORMcode (*formadd_)(struct curl_httppost **, + struct curl_httppost **, ...); + struct curl_slist* (*slist_append_)(struct curl_slist *, const char *); + void (*slist_free_all_)(struct curl_slist *); + CURLcode (*easy_perform_)(CURL *); + const char* (*easy_strerror_)(CURLcode); + void (*easy_cleanup_)(CURL *); + void (*formfree_)(struct curl_httppost *); + +}; +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/linux_libc_support.h b/src/lib/crashdump/gbreakpad/common/linux/linux_libc_support.h new file mode 100644 index 0000000..2ef2dc1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/linux_libc_support.h @@ -0,0 +1,178 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This header provides replacements for libc functions that we need. We if +// call the libc functions directly we risk crashing in the dynamic linker as +// it tries to resolve uncached PLT entries. + +#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ +#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ + +#include +#include +#include + +extern "C" { + +static inline size_t +my_strlen(const char* s) { + size_t len = 0; + while (*s++) len++; + return len; +} + +static inline int +my_strcmp(const char* a, const char* b) { + for (;;) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } +} + +static inline int +my_strncmp(const char* a, const char* b, size_t len) { + for (size_t i = 0; i < len; ++i) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } + + return 0; +} + +// Parse a non-negative integer. +// result: (output) the resulting non-negative integer +// s: a NUL terminated string +// Return true iff successful. +static inline bool +my_strtoui(int* result, const char* s) { + if (*s == 0) + return false; + int r = 0; + for (;; s++) { + if (*s == 0) + break; + const int old_r = r; + r *= 10; + if (*s < '0' || *s > '9') + return false; + r += *s - '0'; + if (r < old_r) + return false; + } + + *result = r; + return true; +} + +// Return the length of the given, non-negative integer when expressed in base +// 10. +static inline unsigned +my_int_len(intmax_t i) { + if (!i) + return 1; + + int len = 0; + while (i) { + len++; + i /= 10; + } + + return len; +} + +// Convert a non-negative integer to a string +// output: (output) the resulting string is written here. This buffer must be +// large enough to hold the resulting string. Call |my_int_len| to get the +// required length. +// i: the non-negative integer to serialise. +// i_len: the length of the integer in base 10 (see |my_int_len|). +static inline void +my_itos(char* output, intmax_t i, unsigned i_len) { + for (unsigned index = i_len; index; --index, i /= 10) + output[index - 1] = '0' + (i % 10); +} + +static inline const char* +my_strchr(const char* haystack, char needle) { + while (*haystack && *haystack != needle) + haystack++; + if (*haystack == needle) + return haystack; + return (const char*) 0; +} + +// Read a hex value +// result: (output) the resulting value +// s: a string +// Returns a pointer to the first invalid charactor. +static inline const char* +my_read_hex_ptr(uintptr_t* result, const char* s) { + uintptr_t r = 0; + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { + r <<= 4; + r += *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + r <<= 4; + r += (*s - 'a') + 10; + } else if (*s >= 'A' && *s <= 'F') { + r <<= 4; + r += (*s - 'A') + 10; + } else { + break; + } + } + + *result = r; + return s; +} + +static inline void +my_memset(void* ip, char c, size_t len) { + char* p = (char *) ip; + while (len--) + *p++ = c; +} + +} // extern "C" + +#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/linux_libc_support_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/linux_libc_support_unittest.cc new file mode 100644 index 0000000..a7c5a26 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/linux_libc_support_unittest.cc @@ -0,0 +1,157 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/linux/linux_libc_support.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +typedef testing::Test LinuxLibcSupportTest; +} + +TEST(LinuxLibcSupportTest, strlen) { + static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL }; + for (unsigned i = 0; ; ++i) { + if (!test_data[i]) + break; + ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i])); + } +} + +TEST(LinuxLibcSupportTest, strcmp) { + static const char* test_data[] = { + "", "", + "a", "", + "", "a", + "a", "b", + "a", "a", + "ab", "aa", + "abc", "ab", + "abc", "abc", + NULL, + }; + + for (unsigned i = 0; ; ++i) { + if (!test_data[i*2]) + break; + int libc_result = strcmp(test_data[i*2], test_data[i*2 + 1]); + if (libc_result > 1) + libc_result = 1; + else if (libc_result < -1) + libc_result = -1; + ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), libc_result); + } +} + +TEST(LinuxLibcSupportTest, strtoui) { + int result; + + ASSERT_FALSE(my_strtoui(&result, "")); + ASSERT_FALSE(my_strtoui(&result, "-1")); + ASSERT_FALSE(my_strtoui(&result, "-")); + ASSERT_FALSE(my_strtoui(&result, "a")); + ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398")); + + ASSERT_TRUE(my_strtoui(&result, "0")); + ASSERT_EQ(result, 0); + ASSERT_TRUE(my_strtoui(&result, "1")); + ASSERT_EQ(result, 1); + ASSERT_TRUE(my_strtoui(&result, "12")); + ASSERT_EQ(result, 12); + ASSERT_TRUE(my_strtoui(&result, "123")); + ASSERT_EQ(result, 123); + ASSERT_TRUE(my_strtoui(&result, "0123")); + ASSERT_EQ(result, 123); +} + +TEST(LinuxLibcSupportTest, int_len) { + ASSERT_EQ(my_int_len(0), 1); + ASSERT_EQ(my_int_len(2), 1); + ASSERT_EQ(my_int_len(5), 1); + ASSERT_EQ(my_int_len(9), 1); + ASSERT_EQ(my_int_len(10), 2); + ASSERT_EQ(my_int_len(99), 2); + ASSERT_EQ(my_int_len(100), 3); + ASSERT_EQ(my_int_len(101), 3); + ASSERT_EQ(my_int_len(1000), 4); +} + +TEST(LinuxLibcSupportTest, itos) { + char buf[10]; + + my_itos(buf, 0, 1); + ASSERT_EQ(0, memcmp(buf, "0", 1)); + + my_itos(buf, 1, 1); + ASSERT_EQ(0, memcmp(buf, "1", 1)); + + my_itos(buf, 10, 2); + ASSERT_EQ(0, memcmp(buf, "10", 2)); + + my_itos(buf, 63, 2); + ASSERT_EQ(0, memcmp(buf, "63", 2)); + + my_itos(buf, 101, 3); + ASSERT_EQ(0, memcmp(buf, "101", 2)); +} + +TEST(LinuxLibcSupportTest, strchr) { + ASSERT_EQ(NULL, my_strchr("abc", 'd')); + ASSERT_EQ(NULL, my_strchr("", 'd')); + ASSERT_EQ(NULL, my_strchr("efghi", 'd')); + + ASSERT_TRUE(my_strchr("a", 'a')); + ASSERT_TRUE(my_strchr("abc", 'a')); + ASSERT_TRUE(my_strchr("bcda", 'a')); + ASSERT_TRUE(my_strchr("sdfasdf", 'a')); +} + +TEST(LinuxLibcSupportTest, read_hex_ptr) { + uintptr_t result; + const char* last; + + last = my_read_hex_ptr(&result, ""); + ASSERT_EQ(result, 0); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0"); + ASSERT_EQ(result, 0); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123"); + ASSERT_EQ(result, 0x123); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a"); + ASSERT_EQ(result, 0x123a); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a-"); + ASSERT_EQ(result, 0x123a); + ASSERT_EQ(*last, '-'); +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.cc b/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.cc new file mode 100644 index 0000000..32fc3b1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.cc @@ -0,0 +1,108 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// memory_mapped_file.cc: Implement google_breakpad::MemoryMappedFile. +// See memory_mapped_file.h for details. + +#include "common/linux/memory_mapped_file.h" + +#include +#include +#if defined(__ANDROID__) +#include +#endif +#include + +#include "common/memory_range.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +MemoryMappedFile::MemoryMappedFile() {} + +MemoryMappedFile::MemoryMappedFile(const char* path) { + Map(path); +} + +MemoryMappedFile::~MemoryMappedFile() { + Unmap(); +} + +bool MemoryMappedFile::Map(const char* path) { + Unmap(); + + int fd = sys_open(path, O_RDONLY, 0); + if (fd == -1) { + return false; + } + +#if defined(__ANDROID__) + struct stat st; + if (fstat(fd, &st) != 0) { +#elif defined(__x86_64__) + struct kernel_stat st; + if (sys_fstat(fd, &st) == -1 || st.st_size < 0) { +#else + struct kernel_stat64 st; + if (sys_fstat64(fd, &st) == -1 || st.st_size < 0) { +#endif + sys_close(fd); + return false; + } + + // If the file size is zero, simply use an empty MemoryRange and return + // true. Don't bother to call mmap() even though mmap() can handle an + // empty file on some platforms. + if (st.st_size == 0) { + sys_close(fd); + return true; + } + +#if defined(__x86_64__) + void* data = sys_mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +#else + void* data = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +#endif + sys_close(fd); + if (data == MAP_FAILED) { + return false; + } + + content_.Set(data, st.st_size); + return true; +} + +void MemoryMappedFile::Unmap() { + if (content_.data()) { + sys_munmap(const_cast(content_.data()), content_.length()); + content_.Set(NULL, 0); + } +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.h b/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.h new file mode 100644 index 0000000..6abd5b0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file.h @@ -0,0 +1,86 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// memory_mapped_file.h: Define the google_breakpad::MemoryMappedFile +// class, which maps a file into memory for read-only access. + +#ifndef COMMON_LINUX_MEMORY_MAPPED_FILE_H_ +#define COMMON_LINUX_MEMORY_MAPPED_FILE_H_ + +#include "common/basictypes.h" +#include "common/memory_range.h" + +namespace google_breakpad { + +// A utility class for mapping a file into memory for read-only access of +// the file content. Its implementation avoids calling into libc functions +// by directly making system calls for open, close, mmap, and munmap. +class MemoryMappedFile { + public: + MemoryMappedFile(); + + // Constructor that calls Map() to map a file at |path| into memory. + // If Map() fails, the object behaves as if it is default constructed. + explicit MemoryMappedFile(const char* path); + + ~MemoryMappedFile(); + + // Maps a file at |path| into memory, which can then be accessed via + // content() as a MemoryRange object or via data(), and returns true on + // success. Mapping an empty file will succeed but with data() and size() + // returning NULL and 0, respectively. An existing mapping is unmapped + // before a new mapping is created. + bool Map(const char* path); + + // Unmaps the memory for the mapped file. It's a no-op if no file is + // mapped. + void Unmap(); + + // Returns a MemoryRange object that covers the memory for the mapped + // file. The MemoryRange object is empty if no file is mapped. + const MemoryRange& content() const { return content_; } + + // Returns a pointer to the beginning of the memory for the mapped file. + // or NULL if no file is mapped or the mapped file is empty. + const void* data() const { return content_.data(); } + + // Returns the size in bytes of the mapped file, or zero if no file + // is mapped. + size_t size() const { return content_.length(); } + + private: + // Mapped file content as a MemoryRange object. + MemoryRange content_; + + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MEMORY_MAPPED_FILE_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file_unittest.cc new file mode 100644 index 0000000..cf89bca --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/memory_mapped_file_unittest.cc @@ -0,0 +1,175 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// memory_mapped_file_unittest.cc: +// Unit tests for google_breakpad::MemoryMappedFile. + +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/memory_mapped_file.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" + +using google_breakpad::AutoTempDir; +using google_breakpad::MemoryMappedFile; +using google_breakpad::WriteFile; +using std::string; + +namespace { + +class MemoryMappedFileTest : public testing::Test { + protected: + void ExpectNoMappedData(const MemoryMappedFile& mapped_file) { + EXPECT_TRUE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() == NULL); + EXPECT_EQ(0, mapped_file.size()); + } +}; + +} // namespace + +TEST_F(MemoryMappedFileTest, DefaultConstructor) { + MemoryMappedFile mapped_file; + ExpectNoMappedData(mapped_file); +} + +TEST_F(MemoryMappedFileTest, UnmapWithoutMap) { + MemoryMappedFile mapped_file; + mapped_file.Unmap(); +} + +TEST_F(MemoryMappedFileTest, MapNonexistentFile) { + { + MemoryMappedFile mapped_file("nonexistent-file"); + ExpectNoMappedData(mapped_file); + } + { + MemoryMappedFile mapped_file; + EXPECT_FALSE(mapped_file.Map("nonexistent-file")); + ExpectNoMappedData(mapped_file); + } +} + +TEST_F(MemoryMappedFileTest, MapEmptyFile) { + AutoTempDir temp_dir; + string test_file = temp_dir.path() + "/empty_file"; + ASSERT_TRUE(WriteFile(test_file.c_str(), NULL, 0)); + + { + MemoryMappedFile mapped_file(test_file.c_str()); + ExpectNoMappedData(mapped_file); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file.c_str())); + ExpectNoMappedData(mapped_file); + } +} + +TEST_F(MemoryMappedFileTest, MapNonEmptyFile) { + char data[256]; + size_t data_size = sizeof(data); + for (size_t i = 0; i < data_size; ++i) { + data[i] = i; + } + + AutoTempDir temp_dir; + string test_file = temp_dir.path() + "/test_file"; + ASSERT_TRUE(WriteFile(test_file.c_str(), data, data_size)); + + { + MemoryMappedFile mapped_file(test_file.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size)); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file.c_str())); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size)); + } +} + +TEST_F(MemoryMappedFileTest, RemapAfterMap) { + char data1[256]; + size_t data1_size = sizeof(data1); + for (size_t i = 0; i < data1_size; ++i) { + data1[i] = i; + } + + char data2[50]; + size_t data2_size = sizeof(data2); + for (size_t i = 0; i < data2_size; ++i) { + data2[i] = 255 - i; + } + + AutoTempDir temp_dir; + string test_file1 = temp_dir.path() + "/test_file1"; + string test_file2 = temp_dir.path() + "/test_file2"; + ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size)); + ASSERT_TRUE(WriteFile(test_file2.c_str(), data2, data2_size)); + + { + MemoryMappedFile mapped_file(test_file1.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size)); + + mapped_file.Map(test_file2.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data2_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size)); + } + { + MemoryMappedFile mapped_file; + EXPECT_TRUE(mapped_file.Map(test_file1.c_str())); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data1_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size)); + + mapped_file.Map(test_file2.c_str()); + EXPECT_FALSE(mapped_file.content().IsEmpty()); + EXPECT_TRUE(mapped_file.data() != NULL); + EXPECT_EQ(data2_size, mapped_file.size()); + EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size)); + } +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/safe_readlink.cc b/src/lib/crashdump/gbreakpad/common/linux/safe_readlink.cc new file mode 100644 index 0000000..870c28a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/safe_readlink.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// safe_readlink.cc: Implement google_breakpad::SafeReadLink. +// See safe_readlink.h for details. + +#include + +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +bool SafeReadLink(const char* path, char* buffer, size_t buffer_size) { + // sys_readlink() does not add a NULL byte to |buffer|. In order to return + // a NULL-terminated string in |buffer|, |buffer_size| should be at least + // one byte longer than the expected path length. Also, sys_readlink() + // returns the actual path length on success, which does not count the + // NULL byte, so |result_size| should be less than |buffer_size|. + ssize_t result_size = sys_readlink(path, buffer, buffer_size); + if (result_size >= 0 && static_cast(result_size) < buffer_size) { + buffer[result_size] = '\0'; + return true; + } + return false; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/safe_readlink.h b/src/lib/crashdump/gbreakpad/common/linux/safe_readlink.h new file mode 100644 index 0000000..4ae131b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/safe_readlink.h @@ -0,0 +1,65 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// safe_readlink.h: Define the google_breakpad::SafeReadLink function, +// which wraps sys_readlink and gurantees the result is NULL-terminated. + +#ifndef COMMON_LINUX_SAFE_READLINK_H_ +#define COMMON_LINUX_SAFE_READLINK_H_ + +#include + +namespace google_breakpad { + +// This function wraps sys_readlink() and performs the same functionalty, +// but guarantees |buffer| is NULL-terminated if sys_readlink() returns +// no error. It takes the same arguments as sys_readlink(), but unlike +// sys_readlink(), it returns true on success. +// +// |buffer_size| specifies the size of |buffer| in bytes. As this function +// always NULL-terminates |buffer| on success, |buffer_size| should be +// at least one byte longer than the expected path length (e.g. PATH_MAX, +// which is typically defined as the maximum length of a path name +// including the NULL byte). +// +// The implementation of this function calls sys_readlink() instead of +// readlink(), it can thus be used in the context where calling to libc +// functions is discouraged. +bool SafeReadLink(const char* path, char* buffer, size_t buffer_size); + +// Same as the three-argument version of SafeReadLink() but deduces the +// size of |buffer| if it is a char array of known size. +template +bool SafeReadLink(const char* path, char (&buffer)[N]) { + return SafeReadLink(path, buffer, sizeof(buffer)); +} + +} // namespace google_breakpad + +#endif // COMMON_LINUX_SAFE_READLINK_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/safe_readlink_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/safe_readlink_unittest.cc new file mode 100644 index 0000000..191fb9f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/safe_readlink_unittest.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// safe_readlink_unittest.cc: Unit tests for google_breakpad::SafeReadLink. + +#include "breakpad_googletest_includes.h" +#include "common/linux/safe_readlink.h" + +using google_breakpad::SafeReadLink; + +TEST(SafeReadLinkTest, ZeroBufferSize) { + char buffer[1]; + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 0)); +} + +TEST(SafeReadLinkTest, BufferSizeTooSmall) { + char buffer[1]; + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 1)); +} + +TEST(SafeReadLinkTest, BoundaryBufferSize) { + char buffer[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer, sizeof(buffer))); + size_t path_length = strlen(buffer); + EXPECT_LT(0, path_length); + EXPECT_GT(sizeof(buffer), path_length); + + // Buffer size equals to the expected path length plus 1 for the NULL byte. + char buffer2[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2, path_length + 1)); + EXPECT_EQ(path_length, strlen(buffer2)); + EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX)); + + // Buffer size equals to the expected path length. + EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, path_length)); +} + +TEST(SafeReadLinkTest, NonexistentPath) { + char buffer[PATH_MAX]; + EXPECT_FALSE(SafeReadLink("nonexistent_path", buffer, sizeof(buffer))); +} + +TEST(SafeReadLinkTest, NonSymbolicLinkPath) { + char actual_path[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", actual_path, sizeof(actual_path))); + + char buffer[PATH_MAX]; + EXPECT_FALSE(SafeReadLink(actual_path, buffer, sizeof(buffer))); +} + +TEST(SafeReadLinkTest, DeduceBufferSizeFromCharArray) { + char buffer[PATH_MAX]; + char* buffer_pointer = buffer; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer_pointer, sizeof(buffer))); + size_t path_length = strlen(buffer); + + // Use the template version of SafeReadLink to deduce the buffer size + // from the char array. + char buffer2[PATH_MAX]; + EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2)); + EXPECT_EQ(path_length, strlen(buffer2)); + EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX)); +} diff --git a/src/lib/crashdump/gbreakpad/common/linux/synth_elf.cc b/src/lib/crashdump/gbreakpad/common/linux/synth_elf.cc new file mode 100644 index 0000000..3e10b69 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/synth_elf.cc @@ -0,0 +1,204 @@ +#include "common/linux/synth_elf.h" + +#include +#include +#include +#include + +namespace google_breakpad { +namespace synth_elf { + +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + +ELF::ELF(uint16_t machine, + uint8_t file_class, + Endianness endianness) + : Section(endianness), + addr_size_(file_class == ELFCLASS64 ? 8 : 4), + program_count_(0), + section_count_(0), + section_header_table_(endianness), + section_header_strings_(endianness) { + // Could add support for more machine types here if needed. + assert(machine == EM_386 || + machine == EM_X86_64 || + machine == EM_ARM); + assert(file_class == ELFCLASS32 || file_class == ELFCLASS64); + + start() = 0; + // Add ELF header + // e_ident + // EI_MAG0...EI_MAG3 + D8(ELFMAG0); + D8(ELFMAG1); + D8(ELFMAG2); + D8(ELFMAG3); + // EI_CLASS + D8(file_class); + // EI_DATA + D8(endianness == kLittleEndian ? ELFDATA2LSB : ELFDATA2MSB); + // EI_VERSION + D8(EV_CURRENT); + // EI_OSABI + D8(ELFOSABI_SYSV); + // EI_ABIVERSION + D8(0); + // EI_PAD + Append(7, 0); + assert(Size() == EI_NIDENT); + + // e_type + D16(ET_EXEC); //TODO: allow passing ET_DYN? + // e_machine + D16(machine); + // e_version + D32(EV_CURRENT); + // e_entry + Append(endianness, addr_size_, 0); + // e_phoff + Append(endianness, addr_size_, program_header_label_); + // e_shoff + Append(endianness, addr_size_, section_header_label_); + // e_flags + D32(0); + // e_ehsize + D16(addr_size_ == 8 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); + // e_phentsize + D16(addr_size_ == 8 ? sizeof(Elf64_Phdr) : sizeof(Elf32_Phdr)); + // e_phnum + D16(program_count_label_); + // e_shentsize + D16(addr_size_ == 8 ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr)); + // e_shnum + D16(section_count_label_); + // e_shstrndx + D16(section_header_string_index_); + + // Add an empty section for SHN_UNDEF. + Section shn_undef; + AddSection("", shn_undef, SHT_NULL); +} + +int ELF::AddSection(const string& name, const Section& section, + uint32_t type, uint32_t flags, uint64_t addr, + uint32_t link, uint64_t entsize, uint64_t offset) { + Label offset_label; + Label string_label(section_header_strings_.Add(name)); + size_t size = section.Size(); + + int index = section_count_; + ++section_count_; + + section_header_table_ + // sh_name + .D32(string_label) + // sh_type + .D32(type) + // sh_flags + .Append(endianness(), addr_size_, flags) + // sh_addr + .Append(endianness(), addr_size_, addr) + // sh_offset + .Append(endianness(), addr_size_, offset_label) + // sh_size + .Append(endianness(), addr_size_, size) + // sh_link + .D32(link) + // sh_info + .D32(0) + // sh_addralign + .Append(endianness(), addr_size_, 0) + // sh_entsize + .Append(endianness(), addr_size_, entsize); + + // NULL and NOBITS sections have no content, so they + // don't need to be written to the file. + if (type == SHT_NULL) { + offset_label = 0; + } else if (type == SHT_NOBITS) { + offset_label = offset; + } else { + Mark(&offset_label); + Append(section); + Align(4); + } + return index; +} + +void ELF::Finish() { + // Add the section header string table at the end. + section_header_string_index_ = section_count_; + //printf(".shstrtab size: %ld\n", section_header_strings_.Size()); + AddSection(".shstrtab", section_header_strings_, SHT_STRTAB); + //printf("section_count_: %ld, sections_.size(): %ld\n", + // section_count_, sections_.size()); + section_count_label_ = section_count_; + program_count_label_ = program_count_; + // TODO: allow adding entries to program header table + program_header_label_ = 0; + + // Section header table starts here. + Mark(§ion_header_label_); + Append(section_header_table_); +} + +SymbolTable::SymbolTable(Endianness endianness, + size_t addr_size, + StringTable& table) : Section(endianness), + addr_size_(addr_size), + table_(table) { + assert(addr_size_ == 4 || addr_size_ == 8); +} + +void SymbolTable::AddSymbol(const string& name, uint32_t value, + uint32_t size, unsigned info, uint16_t shndx) { + assert(addr_size_ == 4); + D32(table_.Add(name)); + D32(value); + D32(size); + D8(info); + D8(0); // other + D16(shndx); +} + +void SymbolTable::AddSymbol(const string& name, uint64_t value, + uint64_t size, unsigned info, uint16_t shndx) { + assert(addr_size_ == 8); + D32(table_.Add(name)); + D8(info); + D8(0); // other + D16(shndx); + D64(value); + D64(size); +} + +BuildIDNote::BuildIDNote(const uint8_t* id_bytes, + size_t id_size, + Endianness endianness) : Section(endianness) { + const char kNoteName[] = "GNU"; + // Elf32_Nhdr and Elf64_Nhdr are exactly the same. + Elf32_Nhdr note_header; + memset(¬e_header, 0, sizeof(note_header)); + note_header.n_namesz = sizeof(kNoteName); + note_header.n_descsz = id_size; + note_header.n_type = NT_GNU_BUILD_ID; + + Append(reinterpret_cast(¬e_header), + sizeof(note_header)); + AppendCString(kNoteName); + Append(id_bytes, id_size); +} + +// static +void BuildIDNote::AppendSection(ELF& elf, + const uint8_t* id_bytes, + size_t id_size) { + const char kBuildIDSectionName[] = ".note.gnu.build-id"; + BuildIDNote note(id_bytes, id_size, elf.endianness()); + elf.AddSection(kBuildIDSectionName, note, SHT_NOTE); +} + +} // namespace synth_elf +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/synth_elf.h b/src/lib/crashdump/gbreakpad/common/linux/synth_elf.h new file mode 100644 index 0000000..90bd834 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/synth_elf.h @@ -0,0 +1,164 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Ted Mielczarek + +// synth_elf.h: Interface to synth_elf::ELF: fake ELF generator. + +#ifndef COMMON_LINUX_SYNTH_ELF_H_ +#define COMMON_LINUX_SYNTH_ELF_H_ + +#include "common/test_assembler.h" + +#include +#include +#include +#include + +namespace google_breakpad { +namespace synth_elf { + +using std::list; +using std::map; +using std::pair; +using std::string; +using test_assembler::Endianness; +using test_assembler::kLittleEndian; +using test_assembler::kUnsetEndian; +using test_assembler::Label; +using test_assembler::Section; + +// String tables are common in ELF headers, so subclass Section +// to make them easy to generate. +class StringTable : public Section { +public: + StringTable(Endianness endianness = kUnsetEndian) + : Section(endianness) { + start() = 0; + empty_string = Add(""); + } + + // Add the string s to the string table, and return + // a label containing the offset into the string table + // at which it was added. + Label Add(const string& s) { + if (strings_.find(s) != strings_.end()) + return strings_[s]; + + Label string_label(Here()); + AppendCString(s); + strings_[s] = string_label; + return string_label; + } + + // All StringTables contain an empty string as their first + // entry. + Label empty_string; + + // Avoid inserting duplicate strings. + map strings_; +}; + +// A Section representing an entire ELF file. +class ELF : public Section { + public: + ELF(uint16_t machine, // EM_386, etc + uint8_t file_class, // ELFCLASS{32,64} + Endianness endianness = kLittleEndian); + + // Add the Section section to the section header table and append it + // to the file. Returns the index of the section in the section + // header table. + int AddSection(const string& name, const Section& section, + uint32_t type, uint32_t flags = 0, uint64_t addr = 0, + uint32_t link = 0, uint64_t entsize = 0, uint64_t offset = 0); + + // Write out all data. GetContents may be used after this. + void Finish(); + + private: + // Size of an address, in bytes. + const size_t addr_size_; + + // Offset to the program header table. + Label program_header_label_; + // Number of entries in the program header table. + int program_count_; + Label program_count_label_; + + // Offset to the section header table. + Label section_header_label_; + // Number of entries in the section header table. + int section_count_; + Label section_count_label_; + // The section header table itself. + Section section_header_table_; + + // Index of the section header string table in the section + // header table. + Label section_header_string_index_; + // Section containing the names of section header table entries. + StringTable section_header_strings_; +}; + +// A class to build .symtab or .dynsym sections. +class SymbolTable : public Section { + public: + // table is the StringTable that contains symbol names. The caller + // must ensure that it remains alive for the life of the + // SymbolTable. + SymbolTable(Endianness endianness, size_t addr_size, StringTable& table); + + // Add an Elf32_Sym. + void AddSymbol(const string& name, uint32_t value, + uint32_t size, unsigned info, uint16_t shndx); + // Add an Elf64_Sym. + void AddSymbol(const string& name, uint64_t value, + uint64_t size, unsigned info, uint16_t shndx); + + private: + size_t addr_size_; + StringTable& table_; +}; + +// A class to build GNU Build ID note sections +class BuildIDNote : public Section { +public: + BuildIDNote(const uint8_t* id_bytes, size_t id_size, Endianness endianness); + + // Append a new Build ID note section to |elf|. + static void AppendSection(ELF& elf, const uint8_t* id_bytes, size_t id_size); +}; + +} // namespace synth_elf +} // namespace google_breakpad + +#endif // COMMON_LINUX_SYNTH_ELF_H_ diff --git a/src/lib/crashdump/gbreakpad/common/linux/synth_elf_unittest.cc b/src/lib/crashdump/gbreakpad/common/linux/synth_elf_unittest.cc new file mode 100644 index 0000000..8cc7ad2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/synth_elf_unittest.cc @@ -0,0 +1,265 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Ted Mielczarek + +// synth_elf_unittest.cc: +// Unittests for google_breakpad::synth_elf::ELF + +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/synth_elf.h" + +using google_breakpad::synth_elf::ELF; +using google_breakpad::synth_elf::StringTable; +using google_breakpad::synth_elf::SymbolTable; +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using std::string; +using ::testing::Test; + +class StringTableTest : public Test { +public: + StringTableTest() : table(kLittleEndian) {} + + StringTable table; +}; + +TEST_F(StringTableTest, Empty) { + EXPECT_EQ(1, table.Size()); + string contents; + ASSERT_TRUE(table.GetContents(&contents)); + const char* kExpectedContents = "\0"; + EXPECT_EQ(0, memcmp(kExpectedContents, + contents.c_str(), + contents.size())); + ASSERT_TRUE(table.empty_string.IsKnownConstant()); + EXPECT_EQ(0, table.empty_string.Value()); +} + +TEST_F(StringTableTest, Basic) { + const string s1("table fills with strings"); + const string s2("offsets preserved as labels"); + const string s3("verified with tests"); + const char* kExpectedContents = + "\0table fills with strings\0" + "offsets preserved as labels\0" + "verified with tests\0"; + Label l1(table.Add(s1)); + Label l2(table.Add(s2)); + Label l3(table.Add(s3)); + string contents; + ASSERT_TRUE(table.GetContents(&contents)); + EXPECT_EQ(0, memcmp(kExpectedContents, + contents.c_str(), + contents.size())); + // empty_string is at zero, other strings start at 1. + ASSERT_TRUE(l1.IsKnownConstant()); + EXPECT_EQ(1, l1.Value()); + // Each string has an extra byte for a trailing null. + EXPECT_EQ(1 + s1.length() + 1, l2.Value()); + EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value()); +} + +TEST_F(StringTableTest, Duplicates) { + const string s1("string 1"); + const string s2("string 2"); + const string s3(""); + const char* kExpectedContents = "\0string 1\0string 2\0"; + Label l1(table.Add(s1)); + Label l2(table.Add(s2)); + // Adding strings twice should return the same Label. + Label l3(table.Add(s3)); + Label l4(table.Add(s2)); + string contents; + ASSERT_TRUE(table.GetContents(&contents)); + EXPECT_EQ(0, memcmp(kExpectedContents, + contents.c_str(), + contents.size())); + EXPECT_EQ(0, table.empty_string.Value()); + EXPECT_EQ(table.empty_string.Value(), l3.Value()); + EXPECT_EQ(l2.Value(), l4.Value()); +} + +class SymbolTableTest : public Test {}; + +TEST_F(SymbolTableTest, Simple32) { + StringTable table(kLittleEndian); + SymbolTable syms(kLittleEndian, 4, table); + + const string kFuncName1 = "superfunc"; + const uint32_t kFuncAddr1 = 0x10001000; + const uint32_t kFuncSize1 = 0x10; + const string kFuncName2 = "awesomefunc"; + const uint32_t kFuncAddr2 = 0x20002000; + const uint32_t kFuncSize2 = 0x2f; + const string kFuncName3 = "megafunc"; + const uint32_t kFuncAddr3 = 0x30003000; + const uint32_t kFuncSize3 = 0x3c; + + syms.AddSymbol(kFuncName1, kFuncAddr1, kFuncSize1, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), + SHN_UNDEF + 1); + syms.AddSymbol(kFuncName2, kFuncAddr2, kFuncSize2, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + SHN_UNDEF + 2); + syms.AddSymbol(kFuncName3, kFuncAddr3, kFuncSize3, + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), + SHN_UNDEF + 3); + + const char kExpectedStringTable[] = "\0superfunc\0awesomefunc\0megafunc"; + const size_t kExpectedStringTableSize = sizeof(kExpectedStringTable); + EXPECT_EQ(kExpectedStringTableSize, table.Size()); + string table_contents; + table.GetContents(&table_contents); + EXPECT_EQ(0, memcmp(kExpectedStringTable, + table_contents.c_str(), + table_contents.size())); + + const uint8_t kExpectedSymbolContents[] = { + // Symbol 1 + 0x01, 0x00, 0x00, 0x00, // name + 0x00, 0x10, 0x00, 0x10, // value + 0x10, 0x00, 0x00, 0x00, // size + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // info + 0x00, // other + 0x01, 0x00, // shndx + // Symbol 2 + 0x0B, 0x00, 0x00, 0x00, // name + 0x00, 0x20, 0x00, 0x20, // value + 0x2f, 0x00, 0x00, 0x00, // size + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info + 0x00, // other + 0x02, 0x00, // shndx + // Symbol 3 + 0x17, 0x00, 0x00, 0x00, // name + 0x00, 0x30, 0x00, 0x30, // value + 0x3c, 0x00, 0x00, 0x00, // size + ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info + 0x00, // other + 0x03, 0x00, // shndx + }; + const size_t kExpectedSymbolSize = sizeof(kExpectedSymbolContents); + EXPECT_EQ(kExpectedSymbolSize, syms.Size()); + + string symbol_contents; + syms.GetContents(&symbol_contents); + EXPECT_EQ(0, memcmp(kExpectedSymbolContents, + symbol_contents.c_str(), + symbol_contents.size())); +} + +class BasicElf : public Test {}; + +// Doesn't seem worthwhile writing the tests to be endian-independent +// when they're unlikely to ever be run on big-endian systems. +#if defined(__i386__) || defined(__x86_64__) + +TEST_F(BasicElf, EmptyLE32) { + const size_t kStringTableSize = sizeof("\0.shstrtab"); + const size_t kStringTableAlign = 4 - kStringTableSize % 4; + const size_t kExpectedSize = sizeof(Elf32_Ehdr) + + // Two sections, SHT_NULL + the section header string table. + 2 * sizeof(Elf32_Shdr) + + kStringTableSize + kStringTableAlign; + + ELF elf(EM_386, ELFCLASS32, kLittleEndian); + elf.Finish(); + EXPECT_EQ(kExpectedSize, elf.Size()); + + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_EQ(kExpectedSize, contents.size()); + const Elf32_Ehdr* header = + reinterpret_cast(contents.data()); + const uint8_t kIdent[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); + EXPECT_EQ(ET_EXEC, header->e_type); + EXPECT_EQ(EM_386, header->e_machine); + EXPECT_EQ(EV_CURRENT, header->e_version); + EXPECT_EQ(0, header->e_entry); + EXPECT_EQ(0, header->e_phoff); + EXPECT_EQ(sizeof(Elf32_Ehdr) + kStringTableSize + kStringTableAlign, + header->e_shoff); + EXPECT_EQ(0, header->e_flags); + EXPECT_EQ(sizeof(Elf32_Ehdr), header->e_ehsize); + EXPECT_EQ(sizeof(Elf32_Phdr), header->e_phentsize); + EXPECT_EQ(0, header->e_phnum); + EXPECT_EQ(sizeof(Elf32_Shdr), header->e_shentsize); + EXPECT_EQ(2, header->e_shnum); + EXPECT_EQ(1, header->e_shstrndx); +} + +TEST_F(BasicElf, EmptyLE64) { + const size_t kStringTableSize = sizeof("\0.shstrtab"); + const size_t kStringTableAlign = 4 - kStringTableSize % 4; + const size_t kExpectedSize = sizeof(Elf64_Ehdr) + + // Two sections, SHT_NULL + the section header string table. + 2 * sizeof(Elf64_Shdr) + + kStringTableSize + kStringTableAlign; + + ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian); + elf.Finish(); + EXPECT_EQ(kExpectedSize, elf.Size()); + + string contents; + ASSERT_TRUE(elf.GetContents(&contents)); + ASSERT_EQ(kExpectedSize, contents.size()); + const Elf64_Ehdr* header = + reinterpret_cast(contents.data()); + const uint8_t kIdent[] = { + ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, + ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent))); + EXPECT_EQ(ET_EXEC, header->e_type); + EXPECT_EQ(EM_X86_64, header->e_machine); + EXPECT_EQ(EV_CURRENT, header->e_version); + EXPECT_EQ(0, header->e_entry); + EXPECT_EQ(0, header->e_phoff); + EXPECT_EQ(sizeof(Elf64_Ehdr) + kStringTableSize + kStringTableAlign, + header->e_shoff); + EXPECT_EQ(0, header->e_flags); + EXPECT_EQ(sizeof(Elf64_Ehdr), header->e_ehsize); + EXPECT_EQ(sizeof(Elf64_Phdr), header->e_phentsize); + EXPECT_EQ(0, header->e_phnum); + EXPECT_EQ(sizeof(Elf64_Shdr), header->e_shentsize); + EXPECT_EQ(2, header->e_shnum); + EXPECT_EQ(1, header->e_shstrndx); +} + +#endif // defined(__i386__) || defined(__x86_64__) diff --git a/src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.cc b/src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.cc new file mode 100644 index 0000000..59aced2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.cc @@ -0,0 +1,276 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// crash_generator.cc: Implement google_breakpad::CrashGenerator. +// See crash_generator.h for details. + +#include "common/linux/tests/crash_generator.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common/linux/eintr_wrapper.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" + +namespace { + +struct ThreadData { + pthread_t thread; + pthread_barrier_t* barrier; + pid_t* thread_id_ptr; +}; + +const char* const kProcFilesToCopy[] = { + "auxv", "cmdline", "environ", "maps", "status" +}; +const size_t kNumProcFilesToCopy = + sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]); + +// Core file size limit set to 1 MB, which is big enough for test purposes. +const rlim_t kCoreSizeLimit = 1024 * 1024; + +void *thread_function(void *data) { + ThreadData* thread_data = reinterpret_cast(data); + volatile pid_t thread_id = syscall(__NR_gettid); + *(thread_data->thread_id_ptr) = thread_id; + int result = pthread_barrier_wait(thread_data->barrier); + if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) { + exit(1); + } + while (true) { + pthread_yield(); + } +} + +} // namespace + +namespace google_breakpad { + +CrashGenerator::CrashGenerator() + : shared_memory_(NULL), + shared_memory_size_(0) { +} + +CrashGenerator::~CrashGenerator() { + UnmapSharedMemory(); +} + +bool CrashGenerator::HasDefaultCorePattern() const { + char buffer[8]; + ssize_t buffer_size = sizeof(buffer); + return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) && + buffer_size == 5 && memcmp(buffer, "core", 4) == 0; +} + +std::string CrashGenerator::GetCoreFilePath() const { + return temp_dir_.path() + "/core"; +} + +std::string CrashGenerator::GetDirectoryOfProcFilesCopy() const { + return temp_dir_.path() + "/proc"; +} + +pid_t CrashGenerator::GetThreadId(unsigned index) const { + return reinterpret_cast(shared_memory_)[index]; +} + +pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) { + return reinterpret_cast(shared_memory_) + index; +} + +bool CrashGenerator::MapSharedMemory(size_t memory_size) { + if (!UnmapSharedMemory()) + return false; + + void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (mapped_memory == MAP_FAILED) { + perror("CrashGenerator: Failed to map shared memory"); + return false; + } + + memset(mapped_memory, 0, memory_size); + shared_memory_ = mapped_memory; + shared_memory_size_ = memory_size; + return true; +} + +bool CrashGenerator::UnmapSharedMemory() { + if (!shared_memory_) + return true; + + if (munmap(shared_memory_, shared_memory_size_) == 0) { + shared_memory_ = NULL; + shared_memory_size_ = 0; + return true; + } + + perror("CrashGenerator: Failed to unmap shared memory"); + return false; +} + +bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const { + struct rlimit limits = { limit, limit }; + if (setrlimit(RLIMIT_CORE, &limits) == -1) { + perror("CrashGenerator: Failed to set core file size limit"); + return false; + } + return true; +} + +bool CrashGenerator::CreateChildCrash( + unsigned num_threads, unsigned crash_thread, int crash_signal, + pid_t* child_pid) { + if (num_threads == 0 || crash_thread >= num_threads) + return false; + + if (!MapSharedMemory(num_threads * sizeof(pid_t))) + return false; + + pid_t pid = fork(); + if (pid == 0) { + if (chdir(temp_dir_.path().c_str()) == -1) { + perror("CrashGenerator: Failed to change directory"); + exit(1); + } + if (SetCoreFileSizeLimit(kCoreSizeLimit)) { + CreateThreadsInChildProcess(num_threads); + std::string proc_dir = GetDirectoryOfProcFilesCopy(); + if (mkdir(proc_dir.c_str(), 0755) == -1) { + perror("CrashGenerator: Failed to create proc directory"); + exit(1); + } + if (!CopyProcFiles(getpid(), proc_dir.c_str())) { + fprintf(stderr, "CrashGenerator: Failed to copy proc files\n"); + exit(1); + } + if (kill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) { + perror("CrashGenerator: Failed to kill thread by signal"); + } + } + exit(1); + } else if (pid == -1) { + perror("CrashGenerator: Failed to create child process"); + return false; + } + + int status; + if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) { + perror("CrashGenerator: Failed to wait for child process"); + return false; + } + if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) { + fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n" + " exit status=0x%x signaled=%s sig=%d expected=%d\n", + status, WIFSIGNALED(status) ? "true" : "false", + WTERMSIG(status), crash_signal); + return false; + } + + if (child_pid) + *child_pid = pid; + return true; +} + +bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const { + char from_path[PATH_MAX], to_path[PATH_MAX]; + for (size_t i = 0; i < kNumProcFilesToCopy; ++i) { + int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s", + pid, kProcFilesToCopy[i]); + if (num_chars < 0 || num_chars >= PATH_MAX) + return false; + + num_chars = snprintf(to_path, PATH_MAX, "%s/%s", + path, kProcFilesToCopy[i]); + if (num_chars < 0 || num_chars >= PATH_MAX) + return false; + + if (!CopyFile(from_path, to_path)) + return false; + } + return true; +} + +void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) { + *GetThreadIdPointer(0) = getpid(); + + if (num_threads <= 1) + return; + + // This method does not clean up any pthread resource, as the process + // is expected to be killed anyway. + ThreadData* thread_data = new ThreadData[num_threads]; + + // Create detached threads so that we do not worry about pthread_join() + // later being called or not. + pthread_attr_t thread_attributes; + if (pthread_attr_init(&thread_attributes) != 0 || + pthread_attr_setdetachstate(&thread_attributes, + PTHREAD_CREATE_DETACHED) != 0) { + fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n"); + exit(1); + } + + pthread_barrier_t thread_barrier; + if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) { + fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n"); + exit(1); + } + + for (unsigned i = 1; i < num_threads; ++i) { + thread_data[i].barrier = &thread_barrier; + thread_data[i].thread_id_ptr = GetThreadIdPointer(i); + if (pthread_create(&thread_data[i].thread, &thread_attributes, + thread_function, &thread_data[i]) != 0) { + fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i); + exit(1); + } + } + + int result = pthread_barrier_wait(&thread_barrier); + if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) { + fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n"); + exit(1); + } + + pthread_barrier_destroy(&thread_barrier); + pthread_attr_destroy(&thread_attributes); + delete[] thread_data; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.h b/src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.h new file mode 100644 index 0000000..d05ce73 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/linux/tests/crash_generator.h @@ -0,0 +1,116 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// crash_generator.h: Define the google_breakpad::CrashGenerator class, +// which is used to generate a crash (and a core dump file) for testing. + +#ifndef COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ +#define COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ + +#include + +#include + +#include "common/tests/auto_tempdir.h" + +namespace google_breakpad { + +// A utility class for generating a crash (and a core dump file) for +// testing. It creates a child process with the specified number of +// threads, which is then termainated by the specified signal. A core +// dump file is expected to be created upon the termination of the child +// process, which can then be used for testing code that processes core +// dump files. +class CrashGenerator { + public: + CrashGenerator(); + + ~CrashGenerator(); + + // Returns true if a core dump file named 'core' will be generated in + // the current directory for a test that produces a crash by checking + // if /proc/sys/kernel/core_pattern has the default value 'core'. + bool HasDefaultCorePattern() const; + + // Returns the expected path of the core dump file. + std::string GetCoreFilePath() const; + + // Returns the directory of a copy of proc files of the child process. + std::string GetDirectoryOfProcFilesCopy() const; + + // Creates a crash (and a core dump file) by creating a child process with + // |num_threads| threads, and the terminating the child process by sending + // a signal with number |crash_signal| to the |crash_thread|-th thread. + // Returns true on success. + bool CreateChildCrash(unsigned num_threads, unsigned crash_thread, + int crash_signal, pid_t* child_pid); + + // Returns the thread ID of the |index|-th thread in the child process. + // This method does not validate |index|. + pid_t GetThreadId(unsigned index) const; + + private: + // Copies the following proc files of the process with |pid| to the directory + // at |path|: auxv, cmdline, environ, maps, status + // The directory must have been created. Returns true on success. + bool CopyProcFiles(pid_t pid, const char* path) const; + + // Creates |num_threads| threads in the child process. + void CreateThreadsInChildProcess(unsigned num_threads); + + // Sets the maximum size of core dump file (both the soft and hard limit) + // to |limit| bytes. Returns true on success. + bool SetCoreFileSizeLimit(rlim_t limit) const; + + // Creates a shared memory of |memory_size| bytes for communicating thread + // IDs between the parent and child process. Returns true on success. + bool MapSharedMemory(size_t memory_size); + + // Releases any shared memory created by MapSharedMemory(). Returns true on + // success. + bool UnmapSharedMemory(); + + // Returns the pointer to the thread ID of the |index|-th thread in the child + // process. This method does not validate |index|. + pid_t* GetThreadIdPointer(unsigned index); + + // Temporary directory in which a core file is generated. + AutoTempDir temp_dir_; + + // Shared memory for communicating thread IDs between the parent and + // child process. + void* shared_memory_; + + // Number of bytes mapped for |shared_memory_|. + size_t shared_memory_size_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_TESTS_CRASH_GENERATOR_H_ diff --git a/src/lib/crashdump/gbreakpad/common/mac/Breakpad.xcconfig b/src/lib/crashdump/gbreakpad/common/mac/Breakpad.xcconfig new file mode 100644 index 0000000..672be2c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/Breakpad.xcconfig @@ -0,0 +1,56 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +ARCHS = $(ARCHS_STANDARD_32_64_BIT) +SDKROOT = macosx10.5 + +GCC_VERSION = 4.2 +GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0 + +GCC_C_LANGUAGE_STANDARD = c99 + +GCC_WARN_CHECK_SWITCH_STATEMENTS = YES +// TODO(nealsid): Get the code so we can turn on the 64_TO_32 warning. +GCC_WARN_64_TO_32_BIT_CONVERSION = NO +GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES +GCC_WARN_ABOUT_RETURN_TYPE = YES +GCC_WARN_MISSING_PARENTHESES = YES +GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES +GCC_WARN_ABOUT_MISSING_NEWLINE = YES +GCC_WARN_SIGN_COMPARE = YES +GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES +GCC_WARN_UNDECLARED_SELECTOR = YES +GCC_WARN_UNKNOWN_PRAGMAS = YES +GCC_WARN_UNUSED_VARIABLE = YES +GCC_TREAT_WARNINGS_AS_ERRORS = YES + +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym +DEBUG_INFORMATION_FORMAT[sdk=macosx10.4][arch=*] = stabs + +ALWAYS_SEARCH_USER_PATHS = NO diff --git a/src/lib/crashdump/gbreakpad/common/mac/BreakpadDebug.xcconfig b/src/lib/crashdump/gbreakpad/common/mac/BreakpadDebug.xcconfig new file mode 100644 index 0000000..94cdd8c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/BreakpadDebug.xcconfig @@ -0,0 +1,32 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "Breakpad.xcconfig" + +GCC_OPTIMIZATION_LEVEL = 0 diff --git a/src/lib/crashdump/gbreakpad/common/mac/BreakpadRelease.xcconfig b/src/lib/crashdump/gbreakpad/common/mac/BreakpadRelease.xcconfig new file mode 100644 index 0000000..af209a4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/BreakpadRelease.xcconfig @@ -0,0 +1,33 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "Breakpad.xcconfig" + +GCC_OPTIMIZATION_LEVEL = s +GCC_WARN_UNINITIALIZED_AUTOS = YES diff --git a/src/lib/crashdump/gbreakpad/common/mac/GTMDefines.h b/src/lib/crashdump/gbreakpad/common/mac/GTMDefines.h new file mode 100644 index 0000000..b88193c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/GTMDefines.h @@ -0,0 +1,241 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// ============================================================================ + +#include +#include + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if defined (__GNUC__) && (__GNUC__ == 4) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #else + #define GTM_EXTERN extern + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_OBJECT(element, collection) \ + for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_KEY(element, collection) \ + for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #endif +#endif + +// ============================================================================ + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #endif // TARGET_IPHONE_SIMULATOR +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 +#endif + +// Provide a symbol to include/exclude extra code for GC support. (This mainly +// just controls the inclusion of finalize methods). +#ifndef GTM_SUPPORT_GC + #if GTM_IPHONE_SDK + // iPhone never needs GC + #define GTM_SUPPORT_GC 0 + #else + // We can't find a symbol to tell if GC is supported/required, so best we + // do on Mac targets is include it if we're on 10.5 or later. + #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + #define GTM_SUPPORT_GC 0 + #else + #define GTM_SUPPORT_GC 1 + #endif + #endif +#endif + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 diff --git a/src/lib/crashdump/gbreakpad/common/mac/GTMGarbageCollection.h b/src/lib/crashdump/gbreakpad/common/mac/GTMGarbageCollection.h new file mode 100644 index 0000000..93d4efa --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/GTMGarbageCollection.h @@ -0,0 +1,72 @@ +// +// GTMGarbageCollection.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +#import "GTMDefines.h" + +// This allows us to easily move our code from GC to non GC. +// They are no-ops unless we are require Leopard or above. +// See +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/index.html +// and +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1 +// for details. + +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) && !GTM_IPHONE_SDK +// General use would be to call this through GTMCFAutorelease +// but there may be a reason the you want to make something collectable +// but not autoreleased, especially in pure GC code where you don't +// want to bother with the nop autorelease. Done as a define instead of an +// inline so that tools like Clang's scan-build don't report code as leaking. +#define GTMNSMakeCollectable(cf) ((id)NSMakeCollectable(cf)) + +// GTMNSMakeUncollectable is for global maps, etc. that we don't +// want released ever. You should still retain these in non-gc code. +GTM_INLINE void GTMNSMakeUncollectable(id object) { + [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object]; +} + +// Hopefully no code really needs this, but GTMIsGarbageCollectionEnabled is +// a common way to check at runtime if GC is on. +// There are some places where GC doesn't work w/ things w/in Apple's +// frameworks, so this is here so GTM unittests and detect it, and not run +// individual tests to work around bugs in Apple's frameworks. +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return ([NSGarbageCollector defaultCollector] != nil); +} + +#else + +#define GTMNSMakeCollectable(cf) ((id)(cf)) + +GTM_INLINE void GTMNSMakeUncollectable(id object) { +} + +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return NO; +} + +#endif + +// GTMCFAutorelease makes a CF object collectable in GC mode, or adds it +// to the autorelease pool in non-GC mode. Either way it is taken care +// of. Done as a define instead of an inline so that tools like Clang's +// scan-build don't report code as leaking. +#define GTMCFAutorelease(cf) ([GTMNSMakeCollectable(cf) autorelease]) + diff --git a/src/lib/crashdump/gbreakpad/common/mac/GTMLogger.h b/src/lib/crashdump/gbreakpad/common/mac/GTMLogger.h new file mode 100644 index 0000000..7d52f01 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/GTMLogger.h @@ -0,0 +1,458 @@ +// +// GTMLogger.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Key Abstractions +// ---------------- +// +// This file declares multiple classes and protocols that are used by the +// GTMLogger logging system. The 4 main abstractions used in this file are the +// following: +// +// * logger (GTMLogger) - The main logging class that users interact with. It +// has methods for logging at different levels and uses a log writer, a log +// formatter, and a log filter to get the job done. +// +// * log writer (GTMLogWriter) - Writes a given string to some log file, where +// a "log file" can be a physical file on disk, a POST over HTTP to some URL, +// or even some in-memory structure (e.g., a ring buffer). +// +// * log formatter (GTMLogFormatter) - Given a format string and arguments as +// a va_list, returns a single formatted NSString. A "formatted string" could +// be a string with the date prepended, a string with values in a CSV format, +// or even a string of XML. +// +// * log filter (GTMLogFilter) - Given a formatted log message as an NSString +// and the level at which the message is to be logged, this class will decide +// whether the given message should be logged or not. This is a flexible way +// to filter out messages logged at a certain level, messages that contain +// certain text, or filter nothing out at all. This gives the caller the +// flexibility to dynamically enable debug logging in Release builds. +// +// This file also declares some classes to handle the common log writer, log +// formatter, and log filter cases. Callers can also create their own writers, +// formatters, and filters and they can even build them on top of the ones +// declared here. Keep in mind that your custom writer/formatter/filter may be +// called from multiple threads, so it must be thread-safe. + +#import +#import "GTMDefines.h" + +// Predeclaration of used protocols that are declared later in this file. +@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b))) +#else +#define CHECK_FORMAT_NSSTRING(a, b) +#endif + +// GTMLogger +// +// GTMLogger is the primary user-facing class for an object-oriented logging +// system. It is built on the concept of log formatters (GTMLogFormatter), log +// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is +// sent to a GTMLogger to log a message, the message is formatted using the log +// formatter, then the log filter is consulted to see if the message should be +// logged, and if so, the message is sent to the log writer to be written out. +// +// GTMLogger is intended to be a flexible and thread-safe logging solution. Its +// flexibility comes from the fact that GTMLogger instances can be customized +// with user defined formatters, filters, and writers. And these writers, +// filters, and formatters can be combined, stacked, and customized in arbitrary +// ways to suit the needs at hand. For example, multiple writers can be used at +// the same time, and a GTMLogger instance can even be used as another +// GTMLogger's writer. This allows for arbitrarily deep logging trees. +// +// A standard GTMLogger uses a writer that sends messages to standard out, a +// formatter that smacks a timestamp and a few other bits of interesting +// information on the message, and a filter that filters out debug messages from +// release builds. Using the standard log settings, a log message will look like +// the following: +// +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo= +// +// The output contains the date and time of the log message, the name of the +// process followed by its process ID/thread ID, the log level at which the +// message was logged (in the previous example the level was 1: +// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in +// this case, the log message was @"foo=%@", foo). +// +// Multiple instances of GTMLogger can be created, each configured their own +// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide +// access to a shared (i.e., globally accessible) GTMLogger instance. This makes +// it convenient for all code in a process to use the same GTMLogger instance. +// The shared GTMLogger instance can also be configured in an arbitrary, and +// these configuration changes will affect all code that logs through the shared +// instance. + +// +// Log Levels +// ---------- +// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger +// doesn't take any special action based on the log level; it simply forwards +// this information on to formatters, filters, and writers, each of which may +// optionally take action based on the level. Since log level filtering is +// performed at runtime, log messages are typically not filtered out at compile +// time. The exception to this rule is that calls to the GTMLoggerDebug() macro +// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible +// with behavior that many developers are currently used to. Note that this +// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but +// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out. +// +// Standard loggers are created with the GTMLogLevelFilter log filter, which +// filters out certain log messages based on log level, and some other settings. +// +// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on +// GTMLogger itself, there are also C macros that make usage of the shared +// GTMLogger instance very convenient. These macros are: +// +// GTMLoggerDebug(...) +// GTMLoggerInfo(...) +// GTMLoggerError(...) +// +// Again, a notable feature of these macros is that GTMLogDebug() calls *will be +// compiled out of non-DEBUG builds*. +// +// Standard Loggers +// ---------------- +// GTMLogger has the concept of "standard loggers". A standard logger is simply +// a logger that is pre-configured with some standard/common writer, formatter, +// and filter combination. Standard loggers are created using the creation +// methods beginning with "standard". The alternative to a standard logger is a +// regular logger, which will send messages to stdout, with no special +// formatting, and no filtering. +// +// How do I use GTMLogger? +// ---------------------- +// The typical way you will want to use GTMLogger is to simply use the +// GTMLogger*() macros for logging from code. That way we can easily make +// changes to the GTMLogger class and simply update the macros accordingly. Only +// your application startup code (perhaps, somewhere in main()) should use the +// GTMLogger class directly in order to configure the shared logger, which all +// of the code using the macros will be using. Again, this is just the typical +// situation. +// +// To be complete, there are cases where you may want to use GTMLogger directly, +// or even create separate GTMLogger instances for some reason. That's fine, +// too. +// +// Examples +// -------- +// The following show some common GTMLogger use cases. +// +// 1. You want to log something as simply as possible. Also, this call will only +// appear in debug builds. In non-DEBUG builds it will be completely removed. +// +// GTMLoggerDebug(@"foo = %@", foo); +// +// 2. The previous example is similar to the following. The major difference is +// that the previous call (example 1) will be compiled out of Release builds +// but this statement will not be compiled out. +// +// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo]; +// +// 3. Send all logging output from the shared logger to a file. We do this by +// creating an NSFileHandle for writing associated with a file, and setting +// that file handle as the logger's writer. +// +// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" +// create:YES]; +// [[GTMLogger sharedLogger] setWriter:f]; +// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log +// +// 4. Create a new GTMLogger that will log to a file. This example differs from +// the previous one because here we create a new GTMLogger that is different +// from the shared logger. +// +// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"]; +// [logger logInfo:@"hi temp log file"]; +// +// 5. Create a logger that writes to stdout and does NOT do any formatting to +// the log message. This might be useful, for example, when writing a help +// screen for a command-line tool to standard output. +// +// GTMLogger *logger = [GTMLogger logger]; +// [logger logInfo:@"%@ version 0.1 usage", progName]; +// +// 6. Send log output to stdout AND to a log file. The trick here is that +// NSArrays function as composite log writers, which means when an array is +// set as the log writer, it forwards all logging messages to all of its +// contained GTMLogWriters. +// +// // Create array of GTMLogWriters +// NSArray *writers = [NSArray arrayWithObjects: +// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES], +// [NSFileHandle fileHandleWithStandardOutput], nil]; +// +// GTMLogger *logger = [GTMLogger standardLogger]; +// [logger setWriter:writers]; +// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log +// +// For futher details on log writers, formatters, and filters, see the +// documentation below. +// +// NOTE: GTMLogger is application level logging. By default it does nothing +// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose +// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro +// definitions in its prefix header (see GTMDefines.h for how one would do +// that). +// +@interface GTMLogger : NSObject { + @private + id writer_; + id formatter_; + id filter_; +} + +// +// Accessors for the shared logger instance +// + +// Returns a shared/global standard GTMLogger instance. Callers should typically +// use this method to get a GTMLogger instance, unless they explicitly want +// their own instance to configure for their own needs. This is the only method +// that returns a shared instance; all the rest return new GTMLogger instances. ++ (id)sharedLogger; + +// Sets the shared logger instance to |logger|. Future calls to +sharedLogger +// will return |logger| instead. ++ (void)setSharedLogger:(GTMLogger *)logger; + +// +// Creation methods +// + +// Returns a new autoreleased GTMLogger instance that will log to stdout, using +// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter. ++ (id)standardLogger; + +// Same as +standardLogger, but logs to stderr. ++ (id)standardLoggerWithStderr; + +// Returns a new standard GTMLogger instance with a log writer that will +// write to the file at |path|, and will use the GTMLogStandardFormatter and +// GTMLogLevelFilter classes. If |path| does not exist, it will be created. ++ (id)standardLoggerWithPath:(NSString *)path; + +// Returns an autoreleased GTMLogger instance that will use the specified +// |writer|, |formatter|, and |filter|. ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// Returns an autoreleased GTMLogger instance that logs to stdout, with the +// basic formatter, and no filter. The returned logger differs from the logger +// returned by +standardLogger because this one does not do any filtering and +// does not do any special log formatting; this is the difference between a +// "regular" logger and a "standard" logger. ++ (id)logger; + +// Designated initializer. This method returns a GTMLogger initialized with the +// specified |writer|, |formatter|, and |filter|. See the setter methods below +// for what values will be used if nil is passed for a parameter. +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// +// Logging methods +// + +// Logs a message at the debug level (kGTMLoggerLevelDebug). +- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the info level (kGTMLoggerLevelInfo). +- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the error level (kGTMLoggerLevelError). +- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the assert level (kGTMLoggerLevelAssert). +- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); + + +// +// Accessors +// + +// Accessor methods for the log writer. If the log writer is set to nil, +// [NSFileHandle fileHandleWithStandardOutput] is used. +- (id)writer; +- (void)setWriter:(id)writer; + +// Accessor methods for the log formatter. If the log formatter is set to nil, +// GTMLogBasicFormatter is used. This formatter will format log messages in a +// plain printf style. +- (id)formatter; +- (void)setFormatter:(id)formatter; + +// Accessor methods for the log filter. If the log filter is set to nil, +// GTMLogNoFilter is used, which allows all log messages through. +- (id)filter; +- (void)setFilter:(id)filter; + +@end // GTMLogger + + +// Helper functions that are used by the convenience GTMLogger*() macros that +// enable the logging of function names. +@interface GTMLogger (GTMLoggerMacroHelpers) +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +@end // GTMLoggerMacroHelpers + + +// Convenience macros that log to the shared GTMLogger instance. These macros +// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() +// calls will be compiled out of non-Debug builds. +#define GTMLoggerDebug(...) \ + [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__] +#define GTMLoggerInfo(...) \ + [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__] +#define GTMLoggerError(...) \ + [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__] +#define GTMLoggerAssert(...) \ + [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__] + +// If we're not in a debug build, remove the GTMLoggerDebug statements. This +// makes calls to GTMLoggerDebug "compile out" of Release builds +#ifndef DEBUG +#undef GTMLoggerDebug +#define GTMLoggerDebug(...) do {} while(0) +#endif + +// Log levels. +typedef enum { + kGTMLoggerLevelUnknown, + kGTMLoggerLevelDebug, + kGTMLoggerLevelInfo, + kGTMLoggerLevelError, + kGTMLoggerLevelAssert, +} GTMLoggerLevel; + + +// +// Log Writers +// + +// Protocol to be implemented by a GTMLogWriter instance. +@protocol GTMLogWriter +// Writes the given log message to where the log writer is configured to write. +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogWriter + + +// Simple category on NSFileHandle that makes NSFileHandles valid log writers. +// This is convenient because something like, say, +fileHandleWithStandardError +// now becomes a valid log writer. Log messages are written to the file handle +// with a newline appended. +@interface NSFileHandle (GTMFileHandleLogWriter) +// Opens the file at |path| in append mode, and creates the file with |mode| +// if it didn't previously exist. ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode; +@end // NSFileHandle + + +// This category makes NSArray a GTMLogWriter that can be composed of other +// GTMLogWriters. This is the classic Composite GoF design pattern. When the +// GTMLogWriter -logMessage:level: message is sent to the array, the array +// forwards the message to all of its elements that implement the GTMLogWriter +// protocol. +// +// This is useful in situations where you would like to send log output to +// multiple log writers at the same time. Simply create an NSArray of the log +// writers you wish to use, then set the array as the "writer" for your +// GTMLogger instance. +@interface NSArray (GTMArrayCompositeLogWriter) +@end // GTMArrayCompositeLogWriter + + +// This category adapts the GTMLogger interface so that it can be used as a log +// writer; it's an "adapter" in the GoF Adapter pattern sense. +// +// This is useful when you want to configure a logger to log to a specific +// writer with a specific formatter and/or filter. But you want to also compose +// that with a different log writer that may have its own formatter and/or +// filter. +@interface GTMLogger (GTMLoggerLogWriter) +@end // GTMLoggerLogWriter + + +// +// Log Formatters +// + +// Protocol to be implemented by a GTMLogFormatter instance. +@protocol GTMLogFormatter +// Returns a formatted string using the format specified in |fmt| and the va +// args specified in |args|. +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level; +@end // GTMLogFormatter + + +// A basic log formatter that formats a string the same way that NSLog (or +// printf) would. It does not do anything fancy, nor does it add any data of its +// own. +@interface GTMLogBasicFormatter : NSObject +@end // GTMLogBasicFormatter + + +// A log formatter that formats the log string like the basic formatter, but +// also prepends a timestamp and some basic process info to the message, as +// shown in the following sample output. +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here +@interface GTMLogStandardFormatter : GTMLogBasicFormatter { + @private + NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS + NSString *pname_; + pid_t pid_; +} +@end // GTMLogStandardFormatter + + +// +// Log Filters +// + +// Protocol to be imlemented by a GTMLogFilter instance. +@protocol GTMLogFilter +// Returns YES if |msg| at |level| should be filtered out; NO otherwise. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogFilter + + +// A log filter that filters messages at the kGTMLoggerLevelDebug level out of +// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered +// out of non-debug builds unless GTMVerboseLogging is set in the environment or +// the processes's defaults. Messages at the kGTMLoggerLevelError level are +// never filtered. +@interface GTMLogLevelFilter : NSObject +@end // GTMLogLevelFilter + + +// A simple log filter that does NOT filter anything out; +// -filterAllowsMessage:level will always return YES. This can be a convenient +// way to enable debug-level logging in release builds (if you so desire). +@interface GTMLogNoFilter : NSObject +@end // GTMLogNoFilter diff --git a/src/lib/crashdump/gbreakpad/common/mac/GTMLogger.m b/src/lib/crashdump/gbreakpad/common/mac/GTMLogger.m new file mode 100644 index 0000000..de941d2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/GTMLogger.m @@ -0,0 +1,445 @@ +// +// GTMLogger.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMLogger.h" +#import "GTMGarbageCollection.h" +#import +#import +#import +#import + + +// Define a trivial assertion macro to avoid dependencies +#ifdef DEBUG + #define GTMLOGGER_ASSERT(expr) assert(expr) +#else + #define GTMLOGGER_ASSERT(expr) +#endif + + +@interface GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level; + +@end + + +// Reference to the shared GTMLogger instance. This is not a singleton, it's +// just an easy reference to one shared instance. +static GTMLogger *gSharedLogger = nil; + + +@implementation GTMLogger + +// Returns a pointer to the shared logger instance. If none exists, a standard +// logger is created and returned. ++ (id)sharedLogger { + @synchronized(self) { + if (gSharedLogger == nil) { + gSharedLogger = [[self standardLogger] retain]; + } + GTMLOGGER_ASSERT(gSharedLogger != nil); + } + return [[gSharedLogger retain] autorelease]; +} + ++ (void)setSharedLogger:(GTMLogger *)logger { + @synchronized(self) { + [gSharedLogger autorelease]; + gSharedLogger = [logger retain]; + } +} + ++ (id)standardLogger { + id writer = [NSFileHandle fileHandleWithStandardOutput]; + id fr = [[[GTMLogStandardFormatter alloc] init] autorelease]; + id filter = [[[GTMLogLevelFilter alloc] init] autorelease]; + return [self loggerWithWriter:writer formatter:fr filter:filter]; +} + ++ (id)standardLoggerWithStderr { + id me = [self standardLogger]; + [me setWriter:[NSFileHandle fileHandleWithStandardError]]; + return me; +} + ++ (id)standardLoggerWithPath:(NSString *)path { + NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; + if (fh == nil) return nil; + id me = [self standardLogger]; + [me setWriter:fh]; + return me; +} + ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + return [[[self alloc] initWithWriter:writer + formatter:formatter + filter:filter] autorelease]; +} + ++ (id)logger { + return [[[self alloc] init] autorelease]; +} + +- (id)init { + return [self initWithWriter:nil formatter:nil filter:nil]; +} + +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + if ((self = [super init])) { + [self setWriter:writer]; + [self setFormatter:formatter]; + [self setFilter:filter]; + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + GTMLOGGER_ASSERT(writer_ != nil); + } + return self; +} + +- (void)dealloc { + GTMLOGGER_ASSERT(writer_ != nil); + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + [writer_ release]; + [formatter_ release]; + [filter_ release]; + [super dealloc]; +} + +- (id)writer { + GTMLOGGER_ASSERT(writer_ != nil); + return [[writer_ retain] autorelease]; +} + +- (void)setWriter:(id)writer { + @synchronized(self) { + [writer_ autorelease]; + if (writer == nil) + writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; + else + writer_ = [writer retain]; + } + GTMLOGGER_ASSERT(writer_ != nil); +} + +- (id)formatter { + GTMLOGGER_ASSERT(formatter_ != nil); + return [[formatter_ retain] autorelease]; +} + +- (void)setFormatter:(id)formatter { + @synchronized(self) { + [formatter_ autorelease]; + if (formatter == nil) + formatter_ = [[GTMLogBasicFormatter alloc] init]; + else + formatter_ = [formatter retain]; + } + GTMLOGGER_ASSERT(formatter_ != nil); +} + +- (id)filter { + GTMLOGGER_ASSERT(filter_ != nil); + return [[filter_ retain] autorelease]; +} + +- (void)setFilter:(id)filter { + @synchronized(self) { + [filter_ autorelease]; + if (filter == nil) + filter_ = [[GTMLogNoFilter alloc] init]; + else + filter_ = [filter retain]; + } + GTMLOGGER_ASSERT(filter_ != nil); +} + +- (void)logDebug:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logInfo:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logError:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logAssert:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLogger + + +@implementation GTMLogger (GTMLoggerMacroHelpers) + +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLoggerMacroHelpers + + +@implementation GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + GTMLOGGER_ASSERT(writer_ != nil); + + NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; + NSString *msg = [formatter_ stringForFunc:fname + withFormat:fmt + valist:args + level:level]; + if (msg && [filter_ filterAllowsMessage:msg level:level]) + [writer_ logMessage:msg level:level]; +} + +@end // PrivateMethods + + +@implementation NSFileHandle (GTMFileHandleLogWriter) + ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { + int fd = -1; + if (path) { + int flags = O_WRONLY | O_APPEND | O_CREAT; + fd = open([path fileSystemRepresentation], flags, mode); + } + if (fd == -1) return nil; + return [[[self alloc] initWithFileDescriptor:fd + closeOnDealloc:YES] autorelease]; +} + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + NSString *line = [NSString stringWithFormat:@"%@\n", msg]; + [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; + } +} + +@end // GTMFileHandleLogWriter + + +@implementation NSArray (GTMArrayCompositeLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + id child = nil; + GTM_FOREACH_OBJECT(child, self) { + if ([child conformsToProtocol:@protocol(GTMLogWriter)]) + [child logMessage:msg level:level]; + } + } +} + +@end // GTMArrayCompositeLogWriter + + +@implementation GTMLogger (GTMLoggerLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + switch (level) { + case kGTMLoggerLevelDebug: + [self logDebug:@"%@", msg]; + break; + case kGTMLoggerLevelInfo: + [self logInfo:@"%@", msg]; + break; + case kGTMLoggerLevelError: + [self logError:@"%@", msg]; + break; + case kGTMLoggerLevelAssert: + [self logAssert:@"%@", msg]; + break; + default: + // Ignore the message. + break; + } +} + +@end // GTMLoggerLogWriter + + +@implementation GTMLogBasicFormatter + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Performance note: since we always have to create a new NSString from the + // returned CFStringRef, we may want to do a quick check here to see if |fmt| + // contains a '%', and if not, simply return 'fmt'. + CFStringRef cfmsg = NULL; + cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, + NULL, // format options + (CFStringRef)fmt, + args); + return GTMCFAutorelease(cfmsg); +} + +@end // GTMLogBasicFormatter + + +@implementation GTMLogStandardFormatter + +- (id)init { + if ((self = [super init])) { + dateFormatter_ = [[NSDateFormatter alloc] init]; + [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; + [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + pname_ = [[[NSProcessInfo processInfo] processName] copy]; + pid_ = [[NSProcessInfo processInfo] processIdentifier]; + } + return self; +} + +- (void)dealloc { + [dateFormatter_ release]; + [pname_ release]; + [super dealloc]; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + GTMLOGGER_ASSERT(dateFormatter_ != nil); + NSString *tstamp = nil; + @synchronized (dateFormatter_) { + tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; + } + return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", + tstamp, pname_, pid_, pthread_self(), + level, (func ? func : @"(no func)"), + [super stringForFunc:func withFormat:fmt valist:args level:level]]; +} + +@end // GTMLogStandardFormatter + + +@implementation GTMLogLevelFilter + +// Check the environment and the user preferences for the GTMVerboseLogging key +// to see if verbose logging has been enabled. The environment variable will +// override the defaults setting, so check the environment first. +// COV_NF_START +static BOOL IsVerboseLoggingEnabled(void) { + static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; + static char *env = NULL; + if (env == NULL) + env = getenv([kVerboseLoggingKey UTF8String]); + + if (env && env[0]) { + return (strtol(env, NULL, 10) != 0); + } + + return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; +} +// COV_NF_END + +// In DEBUG builds, log everything. If we're not in a debug build we'll assume +// that we're in a Release build. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { +#if DEBUG + return YES; +#endif + + BOOL allow = YES; + + switch (level) { + case kGTMLoggerLevelDebug: + allow = NO; + break; + case kGTMLoggerLevelInfo: + allow = (IsVerboseLoggingEnabled() == YES); + break; + case kGTMLoggerLevelError: + allow = YES; + break; + case kGTMLoggerLevelAssert: + allow = YES; + break; + default: + allow = YES; + break; + } + + return allow; +} + +@end // GTMLogLevelFilter + + +@implementation GTMLogNoFilter + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return YES; // Allow everything through +} + +@end // GTMLogNoFilter diff --git a/src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.h b/src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.h new file mode 100644 index 0000000..42e8fed --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.h @@ -0,0 +1,61 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// HTTPMultipartUpload: A multipart/form-data HTTP uploader. +// Each parameter pair is sent as a boundary +// Each file is sent with a name field in addition to the filename and data +// The data will be sent synchronously. + +#import + +@interface HTTPMultipartUpload : NSObject { + @protected + NSURL *url_; // The destination URL (STRONG) + NSDictionary *parameters_; // The key/value pairs for sending data (STRONG) + NSMutableDictionary *files_; // Dictionary of name/file-path (STRONG) + NSString *boundary_; // The boundary string (STRONG) + NSHTTPURLResponse *response_; // The response from the send (STRONG) +} + +- (id)initWithURL:(NSURL *)url; + +- (NSURL *)URL; + +- (void)setParameters:(NSDictionary *)parameters; +- (NSDictionary *)parameters; + +- (void)addFileAtPath:(NSString *)path name:(NSString *)name; +- (void)addFileContents:(NSData *)data name:(NSString *)name; +- (NSDictionary *)files; + +// Set the data and return the response +- (NSData *)send:(NSError **)error; +- (NSHTTPURLResponse *)response; + +@end diff --git a/src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.m b/src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.m new file mode 100644 index 0000000..76f38f8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/HTTPMultipartUpload.m @@ -0,0 +1,209 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +#import "HTTPMultipartUpload.h" +#import "GTMDefines.h" + +@interface HTTPMultipartUpload(PrivateMethods) +- (NSString *)multipartBoundary; +// Each of the following methods will append the starting multipart boundary, +// but not the ending one. +- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value; +- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name; +- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name; +@end + +@implementation HTTPMultipartUpload +//============================================================================= +#pragma mark - +#pragma mark || Private || +//============================================================================= +- (NSString *)multipartBoundary { + // The boundary has 27 '-' characters followed by 16 hex digits + return [NSString stringWithFormat:@"---------------------------%08X%08X", + rand(), rand()]; +} + +//============================================================================= +- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value { + NSString *escaped = + [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *fmt = + @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n"; + NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value]; + + return [form dataUsingEncoding:NSUTF8StringEncoding]; +} + +//============================================================================= +- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name { + NSMutableData *data = [NSMutableData data]; + NSString *escaped = + [name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; " + "filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n"; + NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped]; + + [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]]; + [data appendData:contents]; + + return data; +} + +//============================================================================= +- (NSData *)formDataForFile:(NSString *)file name:(NSString *)name { + NSData *contents = [NSData dataWithContentsOfFile:file]; + + return [self formDataForFileContents:contents name:name]; +} + +//============================================================================= +#pragma mark - +#pragma mark || Public || +//============================================================================= +- (id)initWithURL:(NSURL *)url { + if ((self = [super init])) { + url_ = [url copy]; + boundary_ = [[self multipartBoundary] retain]; + files_ = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +//============================================================================= +- (void)dealloc { + [url_ release]; + [parameters_ release]; + [files_ release]; + [boundary_ release]; + [response_ release]; + + [super dealloc]; +} + +//============================================================================= +- (NSURL *)URL { + return url_; +} + +//============================================================================= +- (void)setParameters:(NSDictionary *)parameters { + if (parameters != parameters_) { + [parameters_ release]; + parameters_ = [parameters copy]; + } +} + +//============================================================================= +- (NSDictionary *)parameters { + return parameters_; +} + +//============================================================================= +- (void)addFileAtPath:(NSString *)path name:(NSString *)name { + [files_ setObject:path forKey:name]; +} + +//============================================================================= +- (void)addFileContents:(NSData *)data name:(NSString *)name { + [files_ setObject:data forKey:name]; +} + +//============================================================================= +- (NSDictionary *)files { + return files_; +} + +//============================================================================= +- (NSData *)send:(NSError **)error { + NSMutableURLRequest *req = + [[NSMutableURLRequest alloc] + initWithURL:url_ cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10.0 ]; + + NSMutableData *postBody = [NSMutableData data]; + + [req setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", + boundary_] forHTTPHeaderField:@"Content-type"]; + + // Add any parameters to the message + NSArray *parameterKeys = [parameters_ allKeys]; + NSString *key; + + NSInteger count = [parameterKeys count]; + for (NSInteger i = 0; i < count; ++i) { + key = [parameterKeys objectAtIndex:i]; + [postBody appendData:[self formDataForKey:key + value:[parameters_ objectForKey:key]]]; + } + + // Add any files to the message + NSArray *fileNames = [files_ allKeys]; + count = [fileNames count]; + for (NSInteger i = 0; i < count; ++i) { + NSString *name = [fileNames objectAtIndex:i]; + id fileOrData = [files_ objectForKey:name]; + NSData *fileData; + + // The object can be either the path to a file (NSString) or the contents + // of the file (NSData). + if ([fileOrData isKindOfClass:[NSData class]]) + fileData = [self formDataForFileContents:fileOrData name:name]; + else + fileData = [self formDataForFile:fileOrData name:name]; + + [postBody appendData:fileData]; + } + + NSString *epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_]; + [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]]; + + [req setHTTPBody:postBody]; + [req setHTTPMethod:@"POST"]; + + [response_ release]; + response_ = nil; + + NSData *data = [NSURLConnection sendSynchronousRequest:req + returningResponse:&response_ + error:error]; + + [response_ retain]; + [req release]; + + return data; +} + +//============================================================================= +- (NSHTTPURLResponse *)response { + return response_; +} + +@end diff --git a/src/lib/crashdump/gbreakpad/common/mac/MachIPC.h b/src/lib/crashdump/gbreakpad/common/mac/MachIPC.h new file mode 100644 index 0000000..52bed59 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/MachIPC.h @@ -0,0 +1,301 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// MachIPC.h +// +// Some helpful wrappers for using Mach IPC calls + +#ifndef MACH_IPC_H__ +#define MACH_IPC_H__ + +#import +#import +#import +#import + +#import + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// +// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms +// + +namespace google_breakpad { +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_COPY_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// Currently the combined size of the descriptors plus data must be +// less than 1024. But as a benefit no memory allocation is necessary. +// +// TODO: could consider adding malloc() support for very large messages +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + // The receiver of the message can retrieve the raw data this way + u_int8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + u_int32_t GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); + } + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); + } + + int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { return body.msgh_descriptor_count; } + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(void *data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + + MachMessage() { + memset(this, 0, sizeof(MachMessage)); + } + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + u_int8_t data[1]; // actual size limited by sizeof(MachMessage) + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + mach_msg_size_t CalculateSize(); + + mach_msg_header_t head; + mach_msg_body_t body; + u_int8_t padding[1024]; // descriptors and data may be embedded here +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {}; +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + MachSendMessage(int32_t message_id); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + explicit ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + explicit ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + ReceivePort(const ReceivePort&); // disable copy c-tor + + mach_port_t port_; + kern_return_t init_result_; +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + explicit MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. + explicit MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + MachPortSender(const MachPortSender&); // disable copy c-tor + + mach_port_t send_port_; + kern_return_t init_result_; +}; + +} // namespace google_breakpad + +#endif // MACH_IPC_H__ diff --git a/src/lib/crashdump/gbreakpad/common/mac/MachIPC.mm b/src/lib/crashdump/gbreakpad/common/mac/MachIPC.mm new file mode 100644 index 0000000..dc9773f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/MachIPC.mm @@ -0,0 +1,306 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// MachIPC.mm +// Wrapper for mach IPC calls + +#import +#import "MachIPC.h" +#include "common/mac/bootstrap_compat.h" + +namespace google_breakpad { +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + head.msgh_local_port = MACH_PORT_NULL; + head.msgh_reserved = 0; + head.msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(void *data, + int32_t data_length) { + // first check to make sure we have enough space + size_t size = CalculateSize(); + size_t new_size = size + data_length; + + if (new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= sizeof(MachMessage) +mach_msg_size_t MachMessage::CalculateSize() { + size_t size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + head.msgh_size = static_cast(size); + + return head.msgh_size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast(padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast(padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + size_t new_size = size + sizeof(MachMsgPortDescriptor); + + if (new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + body.msgh_descriptor_count = n; + + if (n > 0) { + head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast(padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + mach_port_t task_bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(current_task, &task_bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = breakpad::BootstrapRegister( + bootstrap_port, + const_cast(receive_port_name), + port_); +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->head.msgh_bits = 0; + out_message->head.msgh_local_port = port_; + out_message->head.msgh_remote_port = MACH_PORT_NULL; + out_message->head.msgh_reserved = 0; + out_message->head.msgh_id = 0; + + mach_msg_option_t options = MACH_RCV_MSG; + if (timeout != MACH_MSG_TIMEOUT_NONE) + options |= MACH_RCV_TIMEOUT; + kern_return_t result = mach_msg(&out_message->head, + options, + 0, + sizeof(MachMessage), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t task_bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), + &task_bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(task_bootstrap_port, + const_cast(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.head.msgh_size == 0) { + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.head.msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(&message.head, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.h b/src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.h new file mode 100644 index 0000000..814a6f7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.h @@ -0,0 +1,195 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// SimpleStringDictionary.h +// + +#ifndef SimpleStringDictionary_H__ +#define SimpleStringDictionary_H__ + +#import +#import + +namespace google_breakpad { + +//============================================================================== +// SimpleStringDictionary (and associated class KeyValueEntry) implement a very +// basic dictionary container class. It has the property of not making any +// memory allocations when getting and setting values. But it is not very +// efficient, with calls to get and set values operating in linear time. +// It has the additional limitation of having a fairly small fixed capacity of +// SimpleStringDictionary::MAX_NUM_ENTRIES entries. An assert() will fire if +// the client attempts to set more than this number of key/value pairs. +// Ordinarilly a C++ programmer would use something like the std::map template +// class, or on the Macintosh would often choose CFDictionary or NSDictionary. +// But these dictionary classes may call malloc() during get and set operations. +// Google Breakpad requires that no memory allocations be made in code running +// in its exception handling thread, so it uses SimpleStringDictionary as the +// underlying implementation for the GoogleBreakpad.framework APIs: +// GoogleBreakpadSetKeyValue(), GoogleBreakpadKeyValue(), and +// GoogleBreakpadRemoveKeyValue() +// + +//============================================================================== +// KeyValueEntry +// +// A helper class used by SimpleStringDictionary representing a single +// storage cell for a key/value pair. Each key and value string are +// limited to MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). This class +// performs no memory allocations. It has methods for setting and getting +// key and value strings. +// +class KeyValueEntry { + public: + KeyValueEntry() { + Clear(); + } + + KeyValueEntry(const char *key, const char *value) { + SetKeyValue(key, value); + } + + void SetKeyValue(const char *key, const char *value) { + if (!key) { + key = ""; + } + if (!value) { + value = ""; + } + + strlcpy(key_, key, sizeof(key_)); + strlcpy(value_, value, sizeof(value_)); + } + + void SetValue(const char *value) { + if (!value) { + value = ""; + } + strlcpy(value_, value, sizeof(value_)); + }; + + // Removes the key/value + void Clear() { + memset(key_, 0, sizeof(key_)); + memset(value_, 0, sizeof(value_)); + } + + bool IsActive() const { return key_[0] != '\0'; } + const char *GetKey() const { return key_; } + const char *GetValue() const { return value_; } + + // Don't change this without considering the fixed size + // of MachMessage (in MachIPC.h) + // (see also struct KeyValueMessageData in Inspector.h) + enum {MAX_STRING_STORAGE_SIZE = 256}; + + private: + char key_[MAX_STRING_STORAGE_SIZE]; + char value_[MAX_STRING_STORAGE_SIZE]; +}; + +//============================================================================== +// This class is not an efficient dictionary, but for the purposes of breakpad +// will be just fine. We're just dealing with ten or so distinct +// key/value pairs. The idea is to avoid any malloc() or free() calls +// in certain important methods to be called when a process is in a +// crashed state. Each key and value string are limited to +// KeyValueEntry::MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). Strings passed +// in exceeding this length will be truncated. +// +class SimpleStringDictionary { + public: + SimpleStringDictionary() {}; // entries will all be cleared + + // Returns the number of active key/value pairs. The upper limit for this + // is MAX_NUM_ENTRIES. + int GetCount() const; + + // Given |key|, returns its corresponding |value|. + // If |key| is NULL, an assert will fire or NULL will be returned. If |key| + // is not found or is an empty string, NULL is returned. + const char *GetValueForKey(const char *key) const; + + // Stores a string |value| represented by |key|. If |key| is NULL or an empty + // string, this will assert (or do nothing). If |value| is NULL then + // the |key| will be removed. An empty string is OK for |value|. + void SetKeyValue(const char *key, const char *value); + + // Given |key|, removes any associated value. It will assert (or do nothing) + // if NULL is passed in. It will do nothing if |key| is not found. + void RemoveKey(const char *key); + + // This is the maximum number of key/value pairs which may be set in the + // dictionary. An assert may fire if more values than this are set. + // Don't change this without also changing comment in GoogleBreakpad.h + enum {MAX_NUM_ENTRIES = 64}; + + private: + friend class SimpleStringDictionaryIterator; + + const KeyValueEntry *GetEntry(int i) const; + + KeyValueEntry entries_[MAX_NUM_ENTRIES]; +}; + +//============================================================================== +class SimpleStringDictionaryIterator { + public: + SimpleStringDictionaryIterator(const SimpleStringDictionary &dict) + : dict_(dict), i_(0) { + } + + // Initializes iterator to the beginning (may later call Next() ) + void Start() { + i_ = 0; + } + + // like the nextObject method of NSEnumerator (in Cocoa) + // returns NULL when there are no more entries + // + const KeyValueEntry* Next() { + for (; i_ < SimpleStringDictionary::MAX_NUM_ENTRIES; ++i_) { + const KeyValueEntry *entry = dict_.GetEntry(i_); + if (entry->IsActive()) { + i_++; // move to next entry for next time + return entry; + } + } + + return NULL; // reached end of array + } + + private: + const SimpleStringDictionary& dict_; + int i_; +}; + +} // namespace google_breakpad + +#endif // SimpleStringDictionary_H__ diff --git a/src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.mm b/src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.mm new file mode 100644 index 0000000..b97b760 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/SimpleStringDictionary.mm @@ -0,0 +1,133 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// SimpleStringDictionary.mm +// Simple string dictionary that does not allocate memory +// + +#include + +#import "SimpleStringDictionary.h" + +namespace google_breakpad { + +//============================================================================== +const KeyValueEntry *SimpleStringDictionary::GetEntry(int i) const { + return (i >= 0 && i < MAX_NUM_ENTRIES) ? &entries_[i] : NULL; +} + +//============================================================================== +int SimpleStringDictionary::GetCount() const { + int count = 0; + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + if (entries_[i].IsActive() ) { + ++count; + } + } + + return count; +} + +//============================================================================== +const char *SimpleStringDictionary::GetValueForKey(const char *key) const { + assert(key); + if (!key) + return NULL; + + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + const KeyValueEntry &entry = entries_[i]; + if (entry.IsActive() && !strcmp(entry.GetKey(), key)) { + return entry.GetValue(); + } + } + + return NULL; +} + +//============================================================================== +void SimpleStringDictionary::SetKeyValue(const char *key, + const char *value) { + if (!value) { + RemoveKey(key); + return; + } + + // key must not be NULL + assert(key); + if (!key) + return; + + // key must not be empty string + assert(key[0] != '\0'); + if (key[0] == '\0') + return; + + int free_index = -1; + + // check if key already exists + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + KeyValueEntry &entry = entries_[i]; + + if (entry.IsActive()) { + if (!strcmp(entry.GetKey(), key)) { + entry.SetValue(value); + return; + } + } else { + // Make a note of an empty slot + if (free_index == -1) { + free_index = i; + } + } + } + + // check if we've run out of space + assert(free_index != -1); + + // Put new key into an empty slot (if found) + if (free_index != -1) { + entries_[free_index].SetKeyValue(key, value); + } +} + +//============================================================================== +void SimpleStringDictionary::RemoveKey(const char *key) { + assert(key); + if (!key) + return; + + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + if (!strcmp(entries_[i].GetKey(), key)) { + entries_[i].Clear(); + return; + } + } +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.cc b/src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.cc new file mode 100644 index 0000000..d875d95 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/mac/bootstrap_compat.h" + +namespace breakpad { + +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +kern_return_t BootstrapRegister(mach_port_t bp, + name_t service_name, + mach_port_t sp) { + return bootstrap_register(bp, service_name, sp); +} +#pragma GCC diagnostic warning "-Wdeprecated-declarations" + +} // namesapce breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.h b/src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.h new file mode 100644 index 0000000..8ca7357 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/bootstrap_compat.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 COMMON_MAC_BOOTSTRAP_COMPAT_H_ +#define COMMON_MAC_BOOTSTRAP_COMPAT_H_ + +#include + +namespace breakpad { + +// Wrapper for bootstrap_register to avoid deprecation warnings. +// +// In 10.6, it's possible to call bootstrap_check_in as the one-stop-shop for +// handling what bootstrap_register is used for. In 10.5, bootstrap_check_in +// can't check in a service whose name has not yet been registered, despite +// bootstrap_register being marked as deprecated in that OS release. Breakpad +// needs to register new service names, and in 10.5, calling +// bootstrap_register is the only way to achieve that. Attempts to call +// bootstrap_check_in for a new service name on 10.5 will result in +// BOOTSTRAP_UNKNOWN_SERVICE being returned rather than registration of the +// new service name. +kern_return_t BootstrapRegister(mach_port_t bp, + name_t service_name, + mach_port_t sp); + +} // namespace breakpad + +#endif // COMMON_MAC_BOOTSTRAP_COMPAT_H_ diff --git a/src/lib/crashdump/gbreakpad/common/mac/byteswap.h b/src/lib/crashdump/gbreakpad/common/mac/byteswap.h new file mode 100644 index 0000000..a5d745b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/byteswap.h @@ -0,0 +1,48 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// byteswap.h: Overloaded functions for conveniently byteswapping values. + +#ifndef COMMON_MAC_BYTESWAP_H_ +#define COMMON_MAC_BYTESWAP_H_ + +#include + +static inline uint16_t ByteSwap(uint16_t v) { return OSSwapInt16(v); } +static inline uint32_t ByteSwap(uint32_t v) { return OSSwapInt32(v); } +static inline uint64_t ByteSwap(uint64_t v) { return OSSwapInt64(v); } +static inline int16_t ByteSwap(int16_t v) { return OSSwapInt16(v); } +static inline int32_t ByteSwap(int32_t v) { return OSSwapInt32(v); } +static inline int64_t ByteSwap(int64_t v) { return OSSwapInt64(v); } + +#endif // COMMON_MAC_BYTESWAP_H_ diff --git a/src/lib/crashdump/gbreakpad/common/mac/dump_syms.h b/src/lib/crashdump/gbreakpad/common/mac/dump_syms.h new file mode 100644 index 0000000..0e2f464 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/dump_syms.h @@ -0,0 +1,172 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Author: Jim Blandy + +// dump_syms.h: Declaration of google_breakpad::DumpSymbols, a class for +// reading debugging information from Mach-O files and writing it out as a +// Breakpad symbol file. + +#include +#include +#include +#include + +#include +#include +#include + +#include "common/byte_cursor.h" +#include "common/mac/macho_reader.h" +#include "common/module.h" + +namespace google_breakpad { + +class DumpSymbols { + public: + DumpSymbols() + : input_pathname_(), + object_filename_(), + contents_(), + selected_object_file_(), + selected_object_name_() { } + ~DumpSymbols() { + [input_pathname_ release]; + [object_filename_ release]; + [contents_ release]; + } + + // Prepare to read debugging information from |filename|. |filename| may be + // the name of a universal binary, a Mach-O file, or a dSYM bundle + // containing either of the above. On success, return true; if there is a + // problem reading |filename|, report it and return false. + // + // (This class uses NSString for filenames and related values, + // because the Mac Foundation framework seems to support + // filename-related operations more fully on NSString values.) + bool Read(NSString *filename); + + // If this dumper's file includes an object file for |cpu_type| and + // |cpu_subtype|, then select that object file for dumping, and return + // true. Otherwise, return false, and leave this dumper's selected + // architecture unchanged. + // + // By default, if this dumper's file contains only one object file, then + // the dumper will dump those symbols; and if it contains more than one + // object file, then the dumper will dump the object file whose + // architecture matches that of this dumper program. + bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); + + // If this dumper's file includes an object file for |arch_name|, then select + // that object file for dumping, and return true. Otherwise, return false, + // and leave this dumper's selected architecture unchanged. + // + // By default, if this dumper's file contains only one object file, then + // the dumper will dump those symbols; and if it contains more than one + // object file, then the dumper will dump the object file whose + // architecture matches that of this dumper program. + bool SetArchitecture(const std::string &arch_name); + + // Return a pointer to an array of 'struct fat_arch' structures, + // describing the object files contained in this dumper's file. Set + // *|count| to the number of elements in the array. The returned array is + // owned by this DumpSymbols instance. + // + // If there are no available architectures, this function + // may return NULL. + const struct fat_arch *AvailableArchitectures(size_t *count) { + *count = object_files_.size(); + if (object_files_.size() > 0) + return &object_files_[0]; + return NULL; + } + + // Read the selected object file's debugging information, and write it out to + // |stream|. Write the CFI section if |cfi| is true. Return true on success; + // if an error occurs, report it and return false. + bool WriteSymbolFile(std::ostream &stream, bool cfi); + + private: + // Used internally. + class DumperLineToModule; + class LoadCommandDumper; + + // Return an identifier string for the file this DumpSymbols is dumping. + std::string Identifier(); + + // Read debugging information from |dwarf_sections|, which was taken from + // |macho_reader|, and add it to |module|. On success, return true; + // on failure, report the problem and return false. + bool ReadDwarf(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::SectionMap &dwarf_sections) const; + + // Read DWARF CFI or .eh_frame data from |section|, belonging to + // |macho_reader|, and record it in |module|. If |eh_frame| is true, + // then the data is .eh_frame-format data; otherwise, it is standard DWARF + // .debug_frame data. On success, return true; on failure, report + // the problem and return false. + bool ReadCFI(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::Section §ion, + bool eh_frame) const; + + // The name of the file or bundle whose symbols this will dump. + // This is the path given to Read, for use in error messages. + NSString *input_pathname_; + + // The name of the file this DumpSymbols will actually read debugging + // information from. Normally, this is the same as input_pathname_, but if + // filename refers to a dSYM bundle, then this is the resource file + // within that bundle. + NSString *object_filename_; + + // The complete contents of object_filename_, mapped into memory. + NSData *contents_; + + // A vector of fat_arch structures describing the object files + // object_filename_ contains. If object_filename_ refers to a fat binary, + // this may have more than one element; if it refers to a Mach-O file, this + // has exactly one element. + vector object_files_; + + // The object file in object_files_ selected to dump, or NULL if + // SetArchitecture hasn't been called yet. + const struct fat_arch *selected_object_file_; + + // A string that identifies the selected object file, for use in error + // messages. This is usually object_filename_, but if that refers to a + // fat binary, it includes an indication of the particular architecture + // within that binary. + string selected_object_name_; +}; + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/dump_syms.mm b/src/lib/crashdump/gbreakpad/common/mac/dump_syms.mm new file mode 100644 index 0000000..9783514 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/dump_syms.mm @@ -0,0 +1,496 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Author: Jim Blandy + +// dump_syms.mm: Create a symbol file for use with minidumps + +#include "common/mac/dump_syms.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/dwarf_cfi_to_module.h" +#include "common/dwarf_cu_to_module.h" +#include "common/dwarf_line_to_module.h" +#include "common/mac/file_id.h" +#include "common/mac/macho_reader.h" +#include "common/module.h" +#include "common/stabs_reader.h" +#include "common/stabs_to_module.h" + +#ifndef CPU_TYPE_ARM +#define CPU_TYPE_ARM (static_cast(12)) +#endif // CPU_TYPE_ARM + +using dwarf2reader::ByteReader; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::FileID; +using google_breakpad::mach_o::FatReader; +using google_breakpad::mach_o::Section; +using google_breakpad::mach_o::Segment; +using google_breakpad::Module; +using google_breakpad::StabsReader; +using google_breakpad::StabsToModule; +using std::make_pair; +using std::pair; +using std::string; +using std::vector; + +namespace google_breakpad { + +bool DumpSymbols::Read(NSString *filename) { + if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) { + fprintf(stderr, "Object file does not exist: %s\n", + [filename fileSystemRepresentation]); + return false; + } + + input_pathname_ = [filename retain]; + + // Does this filename refer to a dSYM bundle? + NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_]; + + if (bundle) { + // Filenames referring to bundles usually have names of the form + // ".dSYM"; however, if the user has specified a wrapper + // suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings), + // then the name may have the form "..dSYM". In + // either case, the resource name for the file containing the DWARF + // info within the bundle is . + // + // Since there's no way to tell how much to strip off, remove one + // extension at a time, and use the first one that + // pathForResource:ofType:inDirectory likes. + NSString *base_name = [input_pathname_ lastPathComponent]; + NSString *dwarf_resource; + + do { + NSString *new_base_name = [base_name stringByDeletingPathExtension]; + + // If stringByDeletingPathExtension returned the name unchanged, then + // there's nothing more for us to strip off --- lose. + if ([new_base_name isEqualToString:base_name]) { + fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", + [input_pathname_ fileSystemRepresentation]); + return false; + } + + // Take the shortened result as our new base_name. + base_name = new_base_name; + + // Try to find a DWARF resource in the bundle under the new base_name. + dwarf_resource = [bundle pathForResource:base_name + ofType:nil inDirectory:@"DWARF"]; + } while (!dwarf_resource); + + object_filename_ = [dwarf_resource retain]; + } else { + object_filename_ = [input_pathname_ retain]; + } + + // Read the file's contents into memory. + // + // The documentation for dataWithContentsOfMappedFile says: + // + // Because of file mapping restrictions, this method should only be + // used if the file is guaranteed to exist for the duration of the + // data object’s existence. It is generally safer to use the + // dataWithContentsOfFile: method. + // + // I gather this means that OS X doesn't have (or at least, that method + // doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the + // process appears to get its own copy of the data, and changes to the + // file don't affect memory and vice versa). + NSError *error; + contents_ = [NSData dataWithContentsOfFile:object_filename_ + options:0 + error:&error]; + if (!contents_) { + fprintf(stderr, "Error reading object file: %s: %s\n", + [object_filename_ fileSystemRepresentation], + [[error localizedDescription] UTF8String]); + return false; + } + [contents_ retain]; + + // Get the list of object files present in the file. + FatReader::Reporter fat_reporter([object_filename_ + fileSystemRepresentation]); + FatReader fat_reader(&fat_reporter); + if (!fat_reader.Read(reinterpret_cast([contents_ bytes]), + [contents_ length])) { + return false; + } + + // Get our own copy of fat_reader's object file list. + size_t object_files_count; + const struct fat_arch *object_files = + fat_reader.object_files(&object_files_count); + if (object_files_count == 0) { + fprintf(stderr, "Fat binary file contains *no* architectures: %s\n", + [object_filename_ fileSystemRepresentation]); + return false; + } + object_files_.resize(object_files_count); + memcpy(&object_files_[0], object_files, + sizeof(struct fat_arch) * object_files_count); + + return true; +} + +bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype) { + // Find the best match for the architecture the user requested. + const struct fat_arch *best_match + = NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0], + static_cast(object_files_.size())); + if (!best_match) return false; + + // Record the selected object file. + selected_object_file_ = best_match; + return true; +} + +bool DumpSymbols::SetArchitecture(const std::string &arch_name) { + bool arch_set = false; + const NXArchInfo *arch_info = NXGetArchInfoFromName(arch_name.c_str()); + if (arch_info) { + arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); + } + return arch_set; +} + +string DumpSymbols::Identifier() { + FileID file_id([object_filename_ fileSystemRepresentation]); + unsigned char identifier_bytes[16]; + cpu_type_t cpu_type = selected_object_file_->cputype; + if (!file_id.MachoIdentifier(cpu_type, identifier_bytes)) { + fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n", + [object_filename_ fileSystemRepresentation]); + return ""; + } + + char identifier_string[40]; + FileID::ConvertIdentifierToString(identifier_bytes, identifier_string, + sizeof(identifier_string)); + + string compacted(identifier_string); + for(size_t i = compacted.find('-'); i != string::npos; + i = compacted.find('-', i)) + compacted.erase(i, 1); + + return compacted; +} + +// A line-to-module loader that accepts line number info parsed by +// dwarf2reader::LineInfo and populates a Module and a line vector +// with the results. +class DumpSymbols::DumperLineToModule: + public DwarfCUToModule::LineToModuleFunctor { + public: + // Create a line-to-module converter using BYTE_READER. + DumperLineToModule(dwarf2reader::ByteReader *byte_reader) + : byte_reader_(byte_reader) { } + void operator()(const char *program, uint64 length, + Module *module, vector *lines) { + DwarfLineToModule handler(module, lines); + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); + parser.Start(); + } + private: + dwarf2reader::ByteReader *byte_reader_; // WEAK +}; + +bool DumpSymbols::ReadDwarf(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::SectionMap &dwarf_sections) const { + // Build a byte reader of the appropriate endianness. + ByteReader byte_reader(macho_reader.big_endian() + ? dwarf2reader::ENDIANNESS_BIG + : dwarf2reader::ENDIANNESS_LITTLE); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(selected_object_name_, + module); + + // Build a dwarf2reader::SectionMap from our mach_o::SectionMap. + for (mach_o::SectionMap::const_iterator it = dwarf_sections.begin(); + it != dwarf_sections.end(); it++) { + file_context.section_map[it->first] = + make_pair(reinterpret_cast(it->second.contents.start), + it->second.contents.Size()); + } + + // Find the __debug_info section. + std::pair debug_info_section + = file_context.section_map["__debug_info"]; + // There had better be a __debug_info section! + if (!debug_info_section.first) { + fprintf(stderr, "%s: __DWARF segment of file has no __debug_info section\n", + selected_object_name_.c_str()); + return false; + } + + // Build a line-to-module loader for the root handler to use. + DumperLineToModule line_to_module(&byte_reader); + + // Walk the __debug_info section, one compilation unit at a time. + uint64 debug_info_length = debug_info_section.second; + for (uint64 offset = 0; offset < debug_info_length;) { + // Make a handler for the root DIE that populates MODULE with the + // debug info. + DwarfCUToModule::WarningReporter reporter(selected_object_name_, + offset); + DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); + // Make a Dwarf2Handler that drives our DIEHandler. + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map, + offset, + &byte_reader, + &die_dispatcher); + // Process the entire compilation unit; get the offset of the next. + offset += dwarf_reader.Start(); + } + + return true; +} + +bool DumpSymbols::ReadCFI(google_breakpad::Module *module, + const mach_o::Reader &macho_reader, + const mach_o::Section §ion, + bool eh_frame) const { + // Find the appropriate set of register names for this file's + // architecture. + vector register_names; + switch (macho_reader.cpu_type()) { + case CPU_TYPE_X86: + register_names = DwarfCFIToModule::RegisterNames::I386(); + break; + case CPU_TYPE_X86_64: + register_names = DwarfCFIToModule::RegisterNames::X86_64(); + break; + case CPU_TYPE_ARM: + register_names = DwarfCFIToModule::RegisterNames::ARM(); + break; + default: { + const NXArchInfo *arch = + NXGetArchInfoFromCpuType(macho_reader.cpu_type(), + macho_reader.cpu_subtype()); + fprintf(stderr, "%s: cannot convert DWARF call frame information for ", + selected_object_name_.c_str()); + if (arch) + fprintf(stderr, "architecture '%s'", arch->name); + else + fprintf(stderr, "architecture %d,%d", + macho_reader.cpu_type(), macho_reader.cpu_subtype()); + fprintf(stderr, " to Breakpad symbol file: no register name table\n"); + return false; + } + } + + // Find the call frame information and its size. + const char *cfi = reinterpret_cast(section.contents.start); + size_t cfi_size = section.contents.Size(); + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(selected_object_name_, + section.section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(macho_reader.big_endian() ? + dwarf2reader::ENDIANNESS_BIG : + dwarf2reader::ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(macho_reader.bits_64() ? 8 : 4); + // At the moment, according to folks at Apple and some cursory + // investigation, Mac OS X only uses DW_EH_PE_pcrel-based pointers, so + // this is the only base address the CFI parser will need. + byte_reader.SetCFIDataBase(section.address, cfi); + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, + section.section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + parser.Start(); + return true; +} + +// A LoadCommandHandler that loads whatever debugging data it finds into a +// Module. +class DumpSymbols::LoadCommandDumper: + public mach_o::Reader::LoadCommandHandler { + public: + // Create a load command dumper handling load commands from READER's + // file, and adding data to MODULE. + LoadCommandDumper(const DumpSymbols &dumper, + google_breakpad::Module *module, + const mach_o::Reader &reader) + : dumper_(dumper), module_(module), reader_(reader) { } + + bool SegmentCommand(const mach_o::Segment &segment); + bool SymtabCommand(const ByteBuffer &entries, const ByteBuffer &strings); + + private: + const DumpSymbols &dumper_; + google_breakpad::Module *module_; // WEAK + const mach_o::Reader &reader_; +}; + +bool DumpSymbols::LoadCommandDumper::SegmentCommand(const Segment &segment) { + mach_o::SectionMap section_map; + if (!reader_.MapSegmentSections(segment, §ion_map)) + return false; + + if (segment.name == "__TEXT") { + module_->SetLoadAddress(segment.vmaddr); + mach_o::SectionMap::const_iterator eh_frame = + section_map.find("__eh_frame"); + if (eh_frame != section_map.end()) { + // If there is a problem reading this, don't treat it as a fatal error. + dumper_.ReadCFI(module_, reader_, eh_frame->second, true); + } + return true; + } + + if (segment.name == "__DWARF") { + if (!dumper_.ReadDwarf(module_, reader_, section_map)) + return false; + mach_o::SectionMap::const_iterator debug_frame + = section_map.find("__debug_frame"); + if (debug_frame != section_map.end()) { + // If there is a problem reading this, don't treat it as a fatal error. + dumper_.ReadCFI(module_, reader_, debug_frame->second, false); + } + } + + return true; +} + +bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries, + const ByteBuffer &strings) { + StabsToModule stabs_to_module(module_); + // Mac OS X STABS are never "unitized", and the size of the 'value' field + // matches the address size of the executable. + StabsReader stabs_reader(entries.start, entries.Size(), + strings.start, strings.Size(), + reader_.big_endian(), + reader_.bits_64() ? 8 : 4, + true, + &stabs_to_module); + if (!stabs_reader.Process()) + return false; + stabs_to_module.Finalize(); + return true; +} + +bool DumpSymbols::WriteSymbolFile(std::ostream &stream, bool cfi) { + // Select an object file, if SetArchitecture hasn't been called to set one + // explicitly. + if (!selected_object_file_) { + // If there's only one architecture, that's the one. + if (object_files_.size() == 1) + selected_object_file_ = &object_files_[0]; + else { + // Look for an object file whose architecture matches our own. + const NXArchInfo *local_arch = NXGetLocalArchInfo(); + if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { + fprintf(stderr, "%s: object file contains more than one" + " architecture, none of which match the current" + " architecture; specify an architecture explicitly" + " with '-a ARCH' to resolve the ambiguity\n", + [object_filename_ fileSystemRepresentation]); + return false; + } + } + } + + assert(selected_object_file_); + + // Find the name of the selected file's architecture, to appear in + // the MODULE record and in error messages. + const NXArchInfo *selected_arch_info + = NXGetArchInfoFromCpuType(selected_object_file_->cputype, + selected_object_file_->cpusubtype); + + const char *selected_arch_name = selected_arch_info->name; + if (strcmp(selected_arch_name, "i386") == 0) + selected_arch_name = "x86"; + + // Produce a name to use in error messages that includes the + // filename, and the architecture, if there is more than one. + selected_object_name_ = [object_filename_ UTF8String]; + if (object_files_.size() > 1) { + selected_object_name_ += ", architecture "; + selected_object_name_ + selected_arch_name; + } + + // Compute a module name, to appear in the MODULE record. + NSString *module_name = [object_filename_ lastPathComponent]; + + // Choose an identifier string, to appear in the MODULE record. + string identifier = Identifier(); + if (identifier.empty()) + return false; + identifier += "0"; + + // Create a module to hold the debugging information. + Module module([module_name UTF8String], "mac", selected_arch_name, + identifier); + + // Parse the selected object file. + mach_o::Reader::Reporter reporter(selected_object_name_); + mach_o::Reader reader(&reporter); + if (!reader.Read(reinterpret_cast([contents_ bytes]) + + selected_object_file_->offset, + selected_object_file_->size, + selected_object_file_->cputype, + selected_object_file_->cpusubtype)) + return false; + + // Walk its load commands, and deal with whatever is there. + LoadCommandDumper load_command_dumper(*this, &module, reader); + if (!reader.WalkLoadCommands(&load_command_dumper)) + return false; + + return module.Write(stream, cfi); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/file_id.cc b/src/lib/crashdump/gbreakpad/common/mac/file_id.cc new file mode 100644 index 0000000..50502e4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/file_id.cc @@ -0,0 +1,101 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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_id.cc: Return a unique identifier for a file +// +// See file_id.h for documentation +// +// Author: Dan Waylonis + +#include +#include +#include + +#include "common/mac/file_id.h" +#include "common/mac/macho_id.h" + +using MacFileUtilities::MachoID; + +namespace google_breakpad { + +FileID::FileID(const char *path) { + strlcpy(path_, path, sizeof(path_)); +} + +bool FileID::FileIdentifier(unsigned char identifier[16]) { + int fd = open(path_, O_RDONLY); + if (fd == -1) + return false; + + MD5Context md5; + MD5Init(&md5); + + // Read 4k x 2 bytes at a time. This is faster than just 4k bytes, but + // doesn't seem to be an unreasonable size for the stack. + unsigned char buffer[4096 * 2]; + size_t buffer_size = sizeof(buffer); + while ((buffer_size = read(fd, buffer, buffer_size) > 0)) { + MD5Update(&md5, buffer, buffer_size); + } + + close(fd); + MD5Final(identifier, &md5); + + return true; +} + +bool FileID::MachoIdentifier(int cpu_type, unsigned char identifier[16]) { + MachoID macho(path_); + + if (macho.UUIDCommand(cpu_type, identifier)) + return true; + + return macho.MD5(cpu_type, identifier); +} + +// static +void FileID::ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length) { + int buffer_idx = 0; + for (int idx = 0; (buffer_idx < buffer_length) && (idx < 16); ++idx) { + int hi = (identifier[idx] >> 4) & 0x0F; + int lo = (identifier[idx]) & 0x0F; + + if (idx == 4 || idx == 6 || idx == 8 || idx == 10) + buffer[buffer_idx++] = '-'; + + buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; + buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; + } + + // NULL terminate + buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/file_id.h b/src/lib/crashdump/gbreakpad/common/mac/file_id.h new file mode 100644 index 0000000..eb06b0d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/file_id.h @@ -0,0 +1,78 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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_id.h: Return a unique identifier for a file +// +// Author: Dan Waylonis + +#ifndef COMMON_MAC_FILE_ID_H__ +#define COMMON_MAC_FILE_ID_H__ + +#include + +namespace google_breakpad { + +class FileID { + public: + FileID(const char *path); + ~FileID() {}; + + // Load the identifier for the file path specified in the constructor into + // |identifier|. Return false if the identifier could not be created for the + // file. + // The current implementation will return the MD5 hash of the file's bytes. + bool FileIdentifier(unsigned char identifier[16]); + + // Treat the file as a mach-o file that will contain one or more archicture. + // Accepted values for |cpu_type| (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC) + // are listed in /usr/include/mach/machine.h. + // If |cpu_type| is 0, then the native cpu type is used. + // Returns false if opening the file failed or if the |cpu_type| is not + // present in the file. + // Return the unique identifier in |identifier|. + // The current implementation will look for the (in order of priority): + // LC_UUID, LC_ID_DYLIB, or MD5 hash of the given |cpu_type|. + bool MachoIdentifier(int cpu_type, unsigned char identifier[16]); + + // Convert the |identifier| data to a NULL terminated string. The string will + // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). + // The |buffer| should be at least 37 bytes long to receive all of the data + // and termination. Shorter buffers will contain truncated data. + static void ConvertIdentifierToString(const unsigned char identifier[16], + char *buffer, int buffer_length); + + private: + // Storage for the path specified + char path_[PATH_MAX]; +}; + +} // namespace google_breakpad + +#endif // COMMON_MAC_FILE_ID_H__ + diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_id.cc b/src/lib/crashdump/gbreakpad/common/mac/macho_id.cc new file mode 100644 index 0000000..abe1fab --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_id.cc @@ -0,0 +1,366 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// macho_id.cc: Functions to gather identifying information from a macho file +// +// See macho_id.h for documentation +// +// Author: Dan Waylonis + +extern "C" { // necessary for Leopard + #include + #include + #include + #include + #include + #include + #include + #include + #include +} + +#include "common/mac/macho_id.h" +#include "common/mac/macho_walker.h" +#include "common/mac/macho_utilities.h" + +namespace MacFileUtilities { + +using google_breakpad::MD5Init; +using google_breakpad::MD5Update; +using google_breakpad::MD5Final; + +MachoID::MachoID(const char *path) + : memory_(0), + memory_size_(0), + crc_(0), + md5_context_(), + update_function_(NULL) { + strlcpy(path_, path, sizeof(path_)); +} + +MachoID::MachoID(const char *path, void *memory, size_t size) + : memory_(memory), + memory_size_(size), + crc_(0), + md5_context_(), + update_function_(NULL) { + strlcpy(path_, path, sizeof(path_)); +} + +MachoID::~MachoID() { +} + +// The CRC info is from http://en.wikipedia.org/wiki/Adler-32 +// With optimizations from http://www.zlib.net/ + +// The largest prime smaller than 65536 +#define MOD_ADLER 65521 +// MAX_BLOCK is the largest n such that 255n(n+1)/2 + (n+1)(MAX_BLOCK-1) <= 2^32-1 +#define MAX_BLOCK 5552 + +void MachoID::UpdateCRC(unsigned char *bytes, size_t size) { +// Unrolled loops for summing +#define DO1(buf,i) {sum1 += (buf)[i]; sum2 += sum1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + // Split up the crc + uint32_t sum1 = crc_ & 0xFFFF; + uint32_t sum2 = (crc_ >> 16) & 0xFFFF; + + // Do large blocks + while (size >= MAX_BLOCK) { + size -= MAX_BLOCK; + int block_count = MAX_BLOCK / 16; + do { + DO16(bytes); + bytes += 16; + } while (--block_count); + sum1 %= MOD_ADLER; + sum2 %= MOD_ADLER; + } + + // Do remaining bytes + if (size) { + while (size >= 16) { + size -= 16; + DO16(bytes); + bytes += 16; + } + while (size--) { + sum1 += *bytes++; + sum2 += sum1; + } + sum1 %= MOD_ADLER; + sum2 %= MOD_ADLER; + crc_ = (sum2 << 16) | sum1; + } +} + +void MachoID::UpdateMD5(unsigned char *bytes, size_t size) { + MD5Update(&md5_context_, bytes, size); +} + +void MachoID::Update(MachoWalker *walker, off_t offset, size_t size) { + if (!update_function_ || !size) + return; + + // Read up to 4k bytes at a time + unsigned char buffer[4096]; + size_t buffer_size; + off_t file_offset = offset; + while (size > 0) { + if (size > sizeof(buffer)) { + buffer_size = sizeof(buffer); + size -= buffer_size; + } else { + buffer_size = size; + size = 0; + } + + if (!walker->ReadBytes(buffer, buffer_size, file_offset)) + return; + + (this->*update_function_)(buffer, buffer_size); + file_offset += buffer_size; + } +} + +bool MachoID::UUIDCommand(int cpu_type, unsigned char bytes[16]) { + struct breakpad_uuid_command uuid_cmd; + uuid_cmd.cmd = 0; + if (!WalkHeader(cpu_type, UUIDWalkerCB, &uuid_cmd)) + return false; + + // If we found the command, we'll have initialized the uuid_command + // structure + if (uuid_cmd.cmd == LC_UUID) { + memcpy(bytes, uuid_cmd.uuid, sizeof(uuid_cmd.uuid)); + return true; + } + + return false; +} + +bool MachoID::IDCommand(int cpu_type, unsigned char identifier[16]) { + struct dylib_command dylib_cmd; + dylib_cmd.cmd = 0; + if (!WalkHeader(cpu_type, IDWalkerCB, &dylib_cmd)) + return false; + + // If we found the command, we'll have initialized the dylib_command + // structure + if (dylib_cmd.cmd == LC_ID_DYLIB) { + // Take the hashed filename, version, and compatability version bytes + // to form the first 12 bytes, pad the rest with zeros + + // create a crude hash of the filename to generate the first 4 bytes + identifier[0] = 0; + identifier[1] = 0; + identifier[2] = 0; + identifier[3] = 0; + + for (int j = 0, i = (int)strlen(path_)-1; i>=0 && path_[i]!='/'; ++j, --i) { + identifier[j%4] += path_[i]; + } + + identifier[4] = (dylib_cmd.dylib.current_version >> 24) & 0xFF; + identifier[5] = (dylib_cmd.dylib.current_version >> 16) & 0xFF; + identifier[6] = (dylib_cmd.dylib.current_version >> 8) & 0xFF; + identifier[7] = dylib_cmd.dylib.current_version & 0xFF; + identifier[8] = (dylib_cmd.dylib.compatibility_version >> 24) & 0xFF; + identifier[9] = (dylib_cmd.dylib.compatibility_version >> 16) & 0xFF; + identifier[10] = (dylib_cmd.dylib.compatibility_version >> 8) & 0xFF; + identifier[11] = dylib_cmd.dylib.compatibility_version & 0xFF; + identifier[12] = (cpu_type >> 24) & 0xFF; + identifier[13] = (cpu_type >> 16) & 0xFF; + identifier[14] = (cpu_type >> 8) & 0xFF; + identifier[15] = cpu_type & 0xFF; + + return true; + } + + return false; +} + +uint32_t MachoID::Adler32(int cpu_type) { + update_function_ = &MachoID::UpdateCRC; + crc_ = 0; + + if (!WalkHeader(cpu_type, WalkerCB, this)) + return 0; + + return crc_; +} + +bool MachoID::MD5(int cpu_type, unsigned char identifier[16]) { + update_function_ = &MachoID::UpdateMD5; + + MD5Init(&md5_context_); + + if (!WalkHeader(cpu_type, WalkerCB, this)) + return false; + + MD5Final(identifier, &md5_context_); + return true; +} + +bool MachoID::WalkHeader(int cpu_type, + MachoWalker::LoadCommandCallback callback, + void *context) { + if (memory_) { + MachoWalker walker(memory_, memory_size_, callback, context); + return walker.WalkHeader(cpu_type); + } else { + MachoWalker walker(path_, callback, context); + return walker.WalkHeader(cpu_type); + } +} + +// static +bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context) { + MachoID *macho_id = (MachoID *)context; + + if (cmd->cmd == LC_SEGMENT) { + struct segment_command seg; + + if (!walker->ReadBytes(&seg, sizeof(seg), offset)) + return false; + + if (swap) + swap_segment_command(&seg, NXHostByteOrder()); + + struct mach_header_64 header; + off_t header_offset; + + if (!walker->CurrentHeader(&header, &header_offset)) + return false; + + // Process segments that have sections: + // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) + offset += sizeof(struct segment_command); + struct section sec; + for (unsigned long i = 0; i < seg.nsects; ++i) { + if (!walker->ReadBytes(&sec, sizeof(sec), offset)) + return false; + + if (swap) + swap_section(&sec, 1, NXHostByteOrder()); + + // sections of type S_ZEROFILL are "virtual" and contain no data + // in the file itself + if ((sec.flags & SECTION_TYPE) != S_ZEROFILL && sec.offset != 0) + macho_id->Update(walker, header_offset + sec.offset, sec.size); + + offset += sizeof(struct section); + } + } else if (cmd->cmd == LC_SEGMENT_64) { + struct segment_command_64 seg64; + + if (!walker->ReadBytes(&seg64, sizeof(seg64), offset)) + return false; + + if (swap) + breakpad_swap_segment_command_64(&seg64, NXHostByteOrder()); + + struct mach_header_64 header; + off_t header_offset; + + if (!walker->CurrentHeader(&header, &header_offset)) + return false; + + // Process segments that have sections: + // (e.g., __TEXT, __DATA, __IMPORT, __OBJC) + offset += sizeof(struct segment_command_64); + struct section_64 sec64; + for (unsigned long i = 0; i < seg64.nsects; ++i) { + if (!walker->ReadBytes(&sec64, sizeof(sec64), offset)) + return false; + + if (swap) + breakpad_swap_section_64(&sec64, 1, NXHostByteOrder()); + + // sections of type S_ZEROFILL are "virtual" and contain no data + // in the file itself + if ((sec64.flags & SECTION_TYPE) != S_ZEROFILL && sec64.offset != 0) + macho_id->Update(walker, + header_offset + sec64.offset, + (size_t)sec64.size); + + offset += sizeof(struct section_64); + } + } + + // Continue processing + return true; +} + +// static +bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context) { + if (cmd->cmd == LC_UUID) { + struct breakpad_uuid_command *uuid_cmd = + (struct breakpad_uuid_command *)context; + + if (!walker->ReadBytes(uuid_cmd, sizeof(struct breakpad_uuid_command), + offset)) + return false; + + if (swap) + breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder()); + + return false; + } + + // Continue processing + return true; +} + +// static +bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context) { + if (cmd->cmd == LC_ID_DYLIB) { + struct dylib_command *dylib_cmd = (struct dylib_command *)context; + + if (!walker->ReadBytes(dylib_cmd, sizeof(struct dylib_command), offset)) + return false; + + if (swap) + swap_dylib_command(dylib_cmd, NXHostByteOrder()); + + return false; + } + + // Continue processing + return true; +} + +} // namespace MacFileUtilities diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_id.h b/src/lib/crashdump/gbreakpad/common/mac/macho_id.h new file mode 100644 index 0000000..ccb126d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_id.h @@ -0,0 +1,120 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// macho_id.h: Functions to gather identifying information from a macho file +// +// Author: Dan Waylonis + +#ifndef COMMON_MAC_MACHO_ID_H__ +#define COMMON_MAC_MACHO_ID_H__ + +#include +#include + +#include "common/mac/macho_walker.h" +#include "common/md5.h" + +namespace MacFileUtilities { + +class MachoID { + public: + MachoID(const char *path); + MachoID(const char *path, void *memory, size_t size); + ~MachoID(); + + // For the given |cpu_type|, return a UUID from the LC_UUID command. + // Return false if there isn't a LC_UUID command. + bool UUIDCommand(int cpu_type, unsigned char identifier[16]); + + // For the given |cpu_type|, return a UUID from the LC_ID_DYLIB command. + // Return false if there isn't a LC_ID_DYLIB command. + bool IDCommand(int cpu_type, unsigned char identifier[16]); + + // For the given |cpu_type|, return the Adler32 CRC for the mach-o data + // segment(s). + // Return 0 on error (e.g., if the file is not a mach-o file) + uint32_t Adler32(int cpu_type); + + // For the given |cpu_type|, return the MD5 for the mach-o data segment(s). + // Return true on success, false otherwise + bool MD5(int cpu_type, unsigned char identifier[16]); + + private: + // Signature of class member function to be called with data read from file + typedef void (MachoID::*UpdateFunction)(unsigned char *bytes, size_t size); + + // Update the CRC value by examining |size| |bytes| and applying the algorithm + // to each byte. + void UpdateCRC(unsigned char *bytes, size_t size); + + // Update the MD5 value by examining |size| |bytes| and applying the algorithm + // to each byte. + void UpdateMD5(unsigned char *bytes, size_t size); + + // Bottleneck for update routines + void Update(MachoWalker *walker, off_t offset, size_t size); + + // Factory for the MachoWalker + bool WalkHeader(int cpu_type, MachoWalker::LoadCommandCallback callback, + void *context); + + // The callback from the MachoWalker for CRC and MD5 + static bool WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context); + + // The callback from the MachoWalker for LC_UUID + static bool UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context); + + // The callback from the MachoWalker for LC_ID_DYLIB + static bool IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset, + bool swap, void *context); + + // File path + char path_[PATH_MAX]; + + // Memory region to read from + void *memory_; + + // Size of the memory region + size_t memory_size_; + + // The current crc value + uint32_t crc_; + + // The MD5 context + google_breakpad::MD5Context md5_context_; + + // The current update to call from the Update callback + UpdateFunction update_function_; +}; + +} // namespace MacFileUtilities + +#endif // COMMON_MAC_MACHO_ID_H__ diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_reader.cc b/src/lib/crashdump/gbreakpad/common/mac/macho_reader.cc new file mode 100644 index 0000000..f1f0a17 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_reader.cc @@ -0,0 +1,530 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// macho_reader.cc: Implementation of google_breakpad::Mach_O::FatReader and +// google_breakpad::Mach_O::Reader. See macho_reader.h for details. + +#include "common/mac/macho_reader.h" + +#include +#include +#include + +// Unfortunately, CPU_TYPE_ARM is not define for 10.4. +#if !defined(CPU_TYPE_ARM) +#define CPU_TYPE_ARM 12 +#endif + +namespace google_breakpad { +namespace mach_o { + +// If NDEBUG is #defined, then the 'assert' macro doesn't evaluate its +// arguments, so you can't place expressions that do necessary work in +// the argument of an assert. Nor can you assign the result of the +// expression to a variable and assert that the variable's value is +// true: you'll get unused variable warnings when NDEBUG is #defined. +// +// ASSERT_ALWAYS_EVAL always evaluates its argument, and asserts that +// the result is true if NDEBUG is not #defined. +#if defined(NDEBUG) +#define ASSERT_ALWAYS_EVAL(x) (x) +#else +#define ASSERT_ALWAYS_EVAL(x) assert(x) +#endif + +void FatReader::Reporter::BadHeader() { + fprintf(stderr, "%s: file is neither a fat binary file" + " nor a Mach-O object file\n", filename_.c_str()); +} + +void FatReader::Reporter::TooShort() { + fprintf(stderr, "%s: file too short for the data it claims to contain\n", + filename_.c_str()); +} + +void FatReader::Reporter::MisplacedObjectFile() { + fprintf(stderr, "%s: file too short for the object files it claims" + " to contain\n", filename_.c_str()); +} + +bool FatReader::Read(const uint8_t *buffer, size_t size) { + buffer_.start = buffer; + buffer_.end = buffer + size; + ByteCursor cursor(&buffer_); + + // Fat binaries always use big-endian, so read the magic number in + // that endianness. To recognize Mach-O magic numbers, which can use + // either endianness, check for both the proper and reversed forms + // of the magic numbers. + cursor.set_big_endian(true); + if (cursor >> magic_) { + if (magic_ == FAT_MAGIC) { + // How many object files does this fat binary contain? + uint32_t object_files_count; + if (!(cursor >> object_files_count)) { // nfat_arch + reporter_->TooShort(); + return false; + } + + // Read the list of object files. + object_files_.resize(object_files_count); + for (size_t i = 0; i < object_files_count; i++) { + struct fat_arch *objfile = &object_files_[i]; + + // Read this object file entry, byte-swapping as appropriate. + cursor >> objfile->cputype + >> objfile->cpusubtype + >> objfile->offset + >> objfile->size + >> objfile->align; + if (!cursor) { + reporter_->TooShort(); + return false; + } + // Does the file actually have the bytes this entry refers to? + size_t fat_size = buffer_.Size(); + if (objfile->offset > fat_size || + objfile->size > fat_size - objfile->offset) { + reporter_->MisplacedObjectFile(); + return false; + } + } + + return true; + } else if (magic_ == MH_MAGIC || magic_ == MH_MAGIC_64 || + magic_ == MH_CIGAM || magic_ == MH_CIGAM_64) { + // If this is a little-endian Mach-O file, fix the cursor's endianness. + if (magic_ == MH_CIGAM || magic_ == MH_CIGAM_64) + cursor.set_big_endian(false); + // Record the entire file as a single entry in the object file list. + object_files_.resize(1); + + // Get the cpu type and subtype from the Mach-O header. + if (!(cursor >> object_files_[0].cputype + >> object_files_[0].cpusubtype)) { + reporter_->TooShort(); + return false; + } + + object_files_[0].offset = 0; + object_files_[0].size = static_cast(buffer_.Size()); + // This alignment is correct for 32 and 64-bit x86 and ppc. + // See get_align in the lipo source for other architectures: + // http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c + object_files_[0].align = 12; // 2^12 == 4096 + + return true; + } + } + + reporter_->BadHeader(); + return false; +} + +void Reader::Reporter::BadHeader() { + fprintf(stderr, "%s: file is not a Mach-O object file\n", filename_.c_str()); +} + +void Reader::Reporter::CPUTypeMismatch(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + fprintf(stderr, "%s: CPU type %d, subtype %d does not match expected" + " type %d, subtype %d\n", + filename_.c_str(), cpu_type, cpu_subtype, + expected_cpu_type, expected_cpu_subtype); +} + +void Reader::Reporter::HeaderTruncated() { + fprintf(stderr, "%s: file does not contain a complete Mach-O header\n", + filename_.c_str()); +} + +void Reader::Reporter::LoadCommandRegionTruncated() { + fprintf(stderr, "%s: file too short to hold load command region" + " given in Mach-O header\n", filename_.c_str()); +} + +void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i, + LoadCommandType type) { + fprintf(stderr, "%s: file's header claims there are %ld" + " load commands, but load command #%ld", + filename_.c_str(), claimed, i); + if (type) fprintf(stderr, ", of type %d,", type); + fprintf(stderr, " extends beyond the end of the load command region\n"); +} + +void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) { + fprintf(stderr, "%s: the contents of load command #%ld, of type %d," + " extend beyond the size given in the load command's header\n", + filename_.c_str(), i, type); +} + +void Reader::Reporter::SectionsMissing(const string &name) { + fprintf(stderr, "%s: the load command for segment '%s'" + " is too short to hold the section headers it claims to have\n", + filename_.c_str(), name.c_str()); +} + +void Reader::Reporter::MisplacedSegmentData(const string &name) { + fprintf(stderr, "%s: the segment '%s' claims its contents lie beyond" + " the end of the file\n", filename_.c_str(), name.c_str()); +} + +void Reader::Reporter::MisplacedSectionData(const string §ion, + const string &segment) { + fprintf(stderr, "%s: the section '%s' in segment '%s'" + " claims its contents lie outside the segment's contents\n", + filename_.c_str(), section.c_str(), segment.c_str()); +} + +void Reader::Reporter::MisplacedSymbolTable() { + fprintf(stderr, "%s: the LC_SYMTAB load command claims that the symbol" + " table's contents are located beyond the end of the file\n", + filename_.c_str()); +} + +void Reader::Reporter::UnsupportedCPUType(cpu_type_t cpu_type) { + fprintf(stderr, "%s: CPU type %d is not supported\n", + filename_.c_str(), cpu_type); +} + +bool Reader::Read(const uint8_t *buffer, + size_t size, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + assert(!buffer_.start); + buffer_.start = buffer; + buffer_.end = buffer + size; + ByteCursor cursor(&buffer_, true); + uint32_t magic; + if (!(cursor >> magic)) { + reporter_->HeaderTruncated(); + return false; + } + + if (expected_cpu_type != CPU_TYPE_ANY) { + uint32_t expected_magic; + // validate that magic matches the expected cpu type + switch (expected_cpu_type) { + case CPU_TYPE_ARM: + case CPU_TYPE_I386: + expected_magic = MH_CIGAM; + break; + case CPU_TYPE_POWERPC: + expected_magic = MH_MAGIC; + break; + case CPU_TYPE_X86_64: + expected_magic = MH_CIGAM_64; + break; + case CPU_TYPE_POWERPC64: + expected_magic = MH_MAGIC_64; + break; + default: + reporter_->UnsupportedCPUType(expected_cpu_type); + return false; + } + + if (expected_magic != magic) { + reporter_->BadHeader(); + return false; + } + } + + // Since the byte cursor is in big-endian mode, a reversed magic number + // always indicates a little-endian file, regardless of our own endianness. + switch (magic) { + case MH_MAGIC: big_endian_ = true; bits_64_ = false; break; + case MH_CIGAM: big_endian_ = false; bits_64_ = false; break; + case MH_MAGIC_64: big_endian_ = true; bits_64_ = true; break; + case MH_CIGAM_64: big_endian_ = false; bits_64_ = true; break; + default: + reporter_->BadHeader(); + return false; + } + cursor.set_big_endian(big_endian_); + uint32_t commands_size, reserved; + cursor >> cpu_type_ >> cpu_subtype_ >> file_type_ >> load_command_count_ + >> commands_size >> flags_; + if (bits_64_) + cursor >> reserved; + if (!cursor) { + reporter_->HeaderTruncated(); + return false; + } + + if (expected_cpu_type != CPU_TYPE_ANY && + (expected_cpu_type != cpu_type_ || + expected_cpu_subtype != cpu_subtype_)) { + reporter_->CPUTypeMismatch(cpu_type_, cpu_subtype_, + expected_cpu_type, expected_cpu_subtype); + return false; + } + + cursor + .PointTo(&load_commands_.start, commands_size) + .PointTo(&load_commands_.end, 0); + if (!cursor) { + reporter_->LoadCommandRegionTruncated(); + return false; + } + + return true; +} + +bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const { + ByteCursor list_cursor(&load_commands_, big_endian_); + + for (size_t index = 0; index < load_command_count_; ++index) { + // command refers to this load command alone, so that cursor will + // refuse to read past the load command's end. But since we haven't + // read the size yet, let command initially refer to the entire + // remainder of the load command series. + ByteBuffer command(list_cursor.here(), list_cursor.Available()); + ByteCursor cursor(&command, big_endian_); + + // Read the command type and size --- fields common to all commands. + uint32_t type, size; + if (!(cursor >> type)) { + reporter_->LoadCommandsOverrun(load_command_count_, index, 0); + return false; + } + if (!(cursor >> size) || size > command.Size()) { + reporter_->LoadCommandsOverrun(load_command_count_, index, type); + return false; + } + + // Now that we've read the length, restrict command's range to this + // load command only. + command.end = command.start + size; + + switch (type) { + case LC_SEGMENT: + case LC_SEGMENT_64: { + Segment segment; + segment.bits_64 = (type == LC_SEGMENT_64); + size_t word_size = segment.bits_64 ? 8 : 4; + cursor.CString(&segment.name, 16); + size_t file_offset, file_size; + cursor + .Read(word_size, false, &segment.vmaddr) + .Read(word_size, false, &segment.vmsize) + .Read(word_size, false, &file_offset) + .Read(word_size, false, &file_size); + cursor >> segment.maxprot + >> segment.initprot + >> segment.nsects + >> segment.flags; + if (!cursor) { + reporter_->LoadCommandTooShort(index, type); + return false; + } + if (file_offset > buffer_.Size() || + file_size > buffer_.Size() - file_offset) { + reporter_->MisplacedSegmentData(segment.name); + return false; + } + // Mach-O files in .dSYM bundles have the contents of the loaded + // segments removed, and their file offsets and file sizes zeroed + // out. To help us handle this special case properly, give such + // segments' contents NULL starting and ending pointers. + if (file_offset == 0 && file_size == 0) { + segment.contents.start = segment.contents.end = NULL; + } else { + segment.contents.start = buffer_.start + file_offset; + segment.contents.end = segment.contents.start + file_size; + } + // The section list occupies the remainder of this load command's space. + segment.section_list.start = cursor.here(); + segment.section_list.end = command.end; + + if (!handler->SegmentCommand(segment)) + return false; + break; + } + + case LC_SYMTAB: { + uint32_t symoff, nsyms, stroff, strsize; + cursor >> symoff >> nsyms >> stroff >> strsize; + if (!cursor) { + reporter_->LoadCommandTooShort(index, type); + return false; + } + // How big are the entries in the symbol table? + // sizeof(struct nlist_64) : sizeof(struct nlist), + // but be paranoid about alignment vs. target architecture. + size_t symbol_size = bits_64_ ? 16 : 12; + // How big is the entire symbol array? + size_t symbols_size = nsyms * symbol_size; + if (symoff > buffer_.Size() || symbols_size > buffer_.Size() - symoff || + stroff > buffer_.Size() || strsize > buffer_.Size() - stroff) { + reporter_->MisplacedSymbolTable(); + return false; + } + ByteBuffer entries(buffer_.start + symoff, symbols_size); + ByteBuffer names(buffer_.start + stroff, strsize); + if (!handler->SymtabCommand(entries, names)) + return false; + break; + } + + default: { + if (!handler->UnknownCommand(type, command)) + return false; + break; + } + } + + list_cursor.set_here(command.end); + } + + return true; +} + +// A load command handler that looks for a segment of a given name. +class Reader::SegmentFinder : public LoadCommandHandler { + public: + // Create a load command handler that looks for a segment named NAME, + // and sets SEGMENT to describe it if found. + SegmentFinder(const string &name, Segment *segment) + : name_(name), segment_(segment), found_() { } + + // Return true if the traversal found the segment, false otherwise. + bool found() const { return found_; } + + bool SegmentCommand(const Segment &segment) { + if (segment.name == name_) { + *segment_ = segment; + found_ = true; + return false; + } + return true; + } + + private: + // The name of the segment our creator is looking for. + const string &name_; + + // Where we should store the segment if found. (WEAK) + Segment *segment_; + + // True if we found the segment. + bool found_; +}; + +bool Reader::FindSegment(const string &name, Segment *segment) const { + SegmentFinder finder(name, segment); + WalkLoadCommands(&finder); + return finder.found(); +} + +bool Reader::WalkSegmentSections(const Segment &segment, + SectionHandler *handler) const { + size_t word_size = segment.bits_64 ? 8 : 4; + ByteCursor cursor(&segment.section_list, big_endian_); + + for (size_t i = 0; i < segment.nsects; i++) { + Section section; + section.bits_64 = segment.bits_64; + uint64_t size; + uint32_t offset, dummy32; + cursor + .CString(§ion.section_name, 16) + .CString(§ion.segment_name, 16) + .Read(word_size, false, §ion.address) + .Read(word_size, false, &size) + >> offset + >> section.align + >> dummy32 + >> dummy32 + >> section.flags + >> dummy32 + >> dummy32; + if (section.bits_64) + cursor >> dummy32; + if (!cursor) { + reporter_->SectionsMissing(segment.name); + return false; + } + if ((section.flags & SECTION_TYPE) == S_ZEROFILL) { + // Zero-fill sections have a size, but no contents. + section.contents.start = section.contents.end = NULL; + } else if (segment.contents.start == NULL && + segment.contents.end == NULL) { + // Mach-O files in .dSYM bundles have the contents of the loaded + // segments removed, and their file offsets and file sizes zeroed + // out. However, the sections within those segments still have + // non-zero sizes. There's no reason to call MisplacedSectionData in + // this case; the caller may just need the section's load + // address. But do set the contents' limits to NULL, for safety. + section.contents.start = section.contents.end = NULL; + } else { + if (offset < size_t(segment.contents.start - buffer_.start) || + offset > size_t(segment.contents.end - buffer_.start) || + size > size_t(segment.contents.end - buffer_.start - offset)) { + reporter_->MisplacedSectionData(section.section_name, + section.segment_name); + return false; + } + section.contents.start = buffer_.start + offset; + section.contents.end = section.contents.start + size; + } + if (!handler->HandleSection(section)) + return false; + } + return true; +} + +// A SectionHandler that builds a SectionMap for the sections within a +// given segment. +class Reader::SectionMapper: public SectionHandler { + public: + // Create a SectionHandler that populates MAP with an entry for + // each section it is given. + SectionMapper(SectionMap *map) : map_(map) { } + bool HandleSection(const Section §ion) { + (*map_)[section.section_name] = section; + return true; + } + private: + // The map under construction. (WEAK) + SectionMap *map_; +}; + +bool Reader::MapSegmentSections(const Segment &segment, + SectionMap *section_map) const { + section_map->clear(); + SectionMapper mapper(section_map); + return WalkSegmentSections(segment, &mapper); +} + +} // namespace mach_o +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_reader.h b/src/lib/crashdump/gbreakpad/common/mac/macho_reader.h new file mode 100644 index 0000000..7537648 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_reader.h @@ -0,0 +1,459 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// macho_reader.h: A class for parsing Mach-O files. + +#ifndef BREAKPAD_COMMON_MAC_MACHO_READER_H_ +#define BREAKPAD_COMMON_MAC_MACHO_READER_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common/byte_cursor.h" + +namespace google_breakpad { +namespace mach_o { + +using std::map; +using std::string; +using std::vector; + +// The Mac headers don't specify particular types for these groups of +// constants, but defining them here provides some documentation +// value. We also give them the same width as the fields in which +// they appear, which makes them a bit easier to use with ByteCursors. +typedef uint32_t Magic; +typedef uint32_t FileType; +typedef uint32_t FileFlags; +typedef uint32_t LoadCommandType; +typedef uint32_t SegmentFlags; +typedef uint32_t SectionFlags; + +// A parser for fat binary files, used to store universal binaries. +// When applied to a (non-fat) Mach-O file, this behaves as if the +// file were a fat file containing a single object file. +class FatReader { + public: + + // A class for reporting errors found while parsing fat binary files. The + // default definitions of these methods print messages to stderr. + class Reporter { + public: + // Create a reporter that attributes problems to |filename|. + explicit Reporter(const string &filename) : filename_(filename) { } + + virtual ~Reporter() { } + + // The data does not begin with a fat binary or Mach-O magic number. + // This is a fatal error. + virtual void BadHeader(); + + // The Mach-O fat binary file ends abruptly, without enough space + // to contain an object file it claims is present. + virtual void MisplacedObjectFile(); + + // The file ends abruptly: either it is not large enough to hold a + // complete header, or the header implies that contents are present + // beyond the actual end of the file. + virtual void TooShort(); + + private: + // The filename to which the reader should attribute problems. + string filename_; + }; + + // Create a fat binary file reader that uses |reporter| to report problems. + explicit FatReader(Reporter *reporter) : reporter_(reporter) { } + + // Read the |size| bytes at |buffer| as a fat binary file. On success, + // return true; on failure, report the problem to reporter_ and return + // false. + // + // If the data is a plain Mach-O file, rather than a fat binary file, + // then the reader behaves as if it had found a fat binary file whose + // single object file is the Mach-O file. + bool Read(const uint8_t *buffer, size_t size); + + // Return an array of 'struct fat_arch' structures describing the + // object files present in this fat binary file. Set |size| to the + // number of elements in the array. + // + // Assuming Read returned true, the entries are validated: it is + // safe to assume that the offsets and sizes in each 'struct + // fat_arch' refer to subranges of the bytes passed to Read. + // + // If there are no object files in this fat binary, then this + // function can return NULL. + // + // The array is owned by this FatReader instance; it will be freed when + // this FatReader is destroyed. + // + // This function returns a C-style array instead of a vector to make it + // possible to use the result with OS X functions like NXFindBestFatArch, + // so that the symbol dumper will behave consistently with other OS X + // utilities that work with fat binaries. + const struct fat_arch *object_files(size_t *count) const { + *count = object_files_.size(); + if (object_files_.size() > 0) + return &object_files_[0]; + return NULL; + } + + private: + // We use this to report problems parsing the file's contents. (WEAK) + Reporter *reporter_; + + // The contents of the fat binary or Mach-O file we're parsing. We do not + // own the storage it refers to. + ByteBuffer buffer_; + + // The magic number of this binary, in host byte order. + Magic magic_; + + // The list of object files in this binary. + // object_files_.size() == fat_header.nfat_arch + vector object_files_; +}; + +// A segment in a Mach-O file. All these fields have been byte-swapped as +// appropriate for use by the executing architecture. +struct Segment { + // The ByteBuffers below point into the bytes passed to the Reader that + // created this Segment. + + ByteBuffer section_list; // This segment's section list. + ByteBuffer contents; // This segment's contents. + + // This segment's name. + string name; + + // The address at which this segment should be loaded in memory. If + // bits_64 is false, only the bottom 32 bits of this value are valid. + uint64_t vmaddr; + + // The size of this segment when loaded into memory. This may be larger + // than contents.Size(), in which case the extra area will be + // initialized with zeros. If bits_64 is false, only the bottom 32 bits + // of this value are valid. + uint64_t vmsize; + + // The maximum and initial VM protection of this segment's contents. + uint32_t maxprot; + uint32_t initprot; + + // The number of sections in section_list. + uint32_t nsects; + + // Flags describing this segment, from SegmentFlags. + uint32_t flags; + + // True if this is a 64-bit section; false if it is a 32-bit section. + bool bits_64; +}; + +// A section in a Mach-O file. All these fields have been byte-swapped as +// appropriate for use by the executing architecture. +struct Section { + // This section's contents. This points into the bytes passed to the + // Reader that created this Section. + ByteBuffer contents; + + // This section's name. + string section_name; // section[_64].sectname + // The name of the segment this section belongs to. + string segment_name; // section[_64].segname + + // The address at which this section's contents should be loaded in + // memory. If bits_64 is false, only the bottom 32 bits of this value + // are valid. + uint64_t address; + + // The contents of this section should be loaded into memory at an + // address which is a multiple of (two raised to this power). + uint32_t align; + + // Flags from SectionFlags describing the section's contents. + uint32_t flags; + + // We don't support reading relocations yet. + + // True if this is a 64-bit section; false if it is a 32-bit section. + bool bits_64; +}; + +// A map from section names to Sections. +typedef map SectionMap; + +// A reader for a Mach-O file. +// +// This does not handle fat binaries; see FatReader above. FatReader +// provides a friendly interface for parsing data that could be either a +// fat binary or a Mach-O file. +class Reader { + public: + + // A class for reporting errors found while parsing Mach-O files. The + // default definitions of these member functions print messages to + // stderr. + class Reporter { + public: + // Create a reporter that attributes problems to |filename|. + explicit Reporter(const string &filename) : filename_(filename) { } + virtual ~Reporter() { } + + // Reporter functions for fatal errors return void; the reader will + // definitely return an error to its caller after calling them + + // The data does not begin with a Mach-O magic number, or the magic + // number does not match the expected value for the cpu architecture. + // This is a fatal error. + virtual void BadHeader(); + + // The data contained in a Mach-O fat binary (|cpu_type|, |cpu_subtype|) + // does not match the expected CPU architecture + // (|expected_cpu_type|, |expected_cpu_subtype|). + virtual void CPUTypeMismatch(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype); + + // The file ends abruptly: either it is not large enough to hold a + // complete header, or the header implies that contents are present + // beyond the actual end of the file. + virtual void HeaderTruncated(); + + // The file's load command region, as given in the Mach-O header, is + // too large for the file. + virtual void LoadCommandRegionTruncated(); + + // The file's Mach-O header claims the file contains |claimed| load + // commands, but the I'th load command, of type |type|, extends beyond + // the end of the load command region, as given by the Mach-O header. + // If |type| is zero, the command's type was unreadable. + virtual void LoadCommandsOverrun(size_t claimed, size_t i, + LoadCommandType type); + + // The contents of the |i|'th load command, of type |type|, extend beyond + // the size given in the load command's header. + virtual void LoadCommandTooShort(size_t i, LoadCommandType type); + + // The LC_SEGMENT or LC_SEGMENT_64 load command for the segment named + // |name| is too short to hold the sections that its header says it does. + // (This more specific than LoadCommandTooShort.) + virtual void SectionsMissing(const string &name); + + // The segment named |name| claims that its contents lie beyond the end + // of the file. + virtual void MisplacedSegmentData(const string &name); + + // The section named |section| in the segment named |segment| claims that + // its contents do not lie entirely within the segment. + virtual void MisplacedSectionData(const string §ion, + const string &segment); + + // The LC_SYMTAB command claims that symbol table contents are located + // beyond the end of the file. + virtual void MisplacedSymbolTable(); + + // An attempt was made to read a Mach-O file of the unsupported + // CPU architecture |cpu_type|. + virtual void UnsupportedCPUType(cpu_type_t cpu_type); + + private: + string filename_; + }; + + // A handler for sections parsed from a segment. The WalkSegmentSections + // member function accepts an instance of this class, and applies it to + // each section defined in a given segment. + class SectionHandler { + public: + virtual ~SectionHandler() { } + + // Called to report that the segment's section list contains |section|. + // This should return true if the iteration should continue, or false + // if it should stop. + virtual bool HandleSection(const Section §ion) = 0; + }; + + // A handler for the load commands in a Mach-O file. + class LoadCommandHandler { + public: + LoadCommandHandler() { } + virtual ~LoadCommandHandler() { } + + // When called from WalkLoadCommands, the following handler functions + // should return true if they wish to continue iterating over the load + // command list, or false if they wish to stop iterating. + // + // When called from LoadCommandIterator::Handle or Reader::Handle, + // these functions' return values are simply passed through to Handle's + // caller. + // + // The definitions provided by this base class simply return true; the + // default is to silently ignore sections whose member functions the + // subclass doesn't override. + + // COMMAND is load command we don't recognize. We provide only the + // command type and a ByteBuffer enclosing the command's data (If we + // cannot parse the command type or its size, we call + // reporter_->IncompleteLoadCommand instead.) + virtual bool UnknownCommand(LoadCommandType type, + const ByteBuffer &contents) { + return true; + } + + // The load command is LC_SEGMENT or LC_SEGMENT_64, defining a segment + // with the properties given in |segment|. + virtual bool SegmentCommand(const Segment &segment) { + return true; + } + + // The load command is LC_SYMTAB. |entries| holds the array of nlist + // entries, and |names| holds the strings the entries refer to. + virtual bool SymtabCommand(const ByteBuffer &entries, + const ByteBuffer &names) { + return true; + } + + // Add handler functions for more load commands here as needed. + }; + + // Create a Mach-O file reader that reports problems to |reporter|. + explicit Reader(Reporter *reporter) + : reporter_(reporter) { } + + // Read the given data as a Mach-O file. The reader retains pointers + // into the data passed, so the data should live as long as the reader + // does. On success, return true; on failure, return false. + // + // At most one of these functions should be invoked once on each Reader + // instance. + bool Read(const uint8_t *buffer, + size_t size, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype); + bool Read(const ByteBuffer &buffer, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + return Read(buffer.start, + buffer.Size(), + expected_cpu_type, + expected_cpu_subtype); + } + + // Return this file's characteristics, as found in the Mach-O header. + cpu_type_t cpu_type() const { return cpu_type_; } + cpu_subtype_t cpu_subtype() const { return cpu_subtype_; } + FileType file_type() const { return file_type_; } + FileFlags flags() const { return flags_; } + + // Return true if this is a 64-bit Mach-O file, false if it is a 32-bit + // Mach-O file. + bool bits_64() const { return bits_64_; } + + // Return true if this is a big-endian Mach-O file, false if it is + // little-endian. + bool big_endian() const { return big_endian_; } + + // Apply |handler| to each load command in this Mach-O file, stopping when + // a handler function returns false. If we encounter a malformed load + // command, report it via reporter_ and return false. Return true if all + // load commands were parseable and all handlers returned true. + bool WalkLoadCommands(LoadCommandHandler *handler) const; + + // Set |segment| to describe the segment named |name|, if present. If + // found, |segment|'s byte buffers refer to a subregion of the bytes + // passed to Read. If we find the section, return true; otherwise, + // return false. + bool FindSegment(const string &name, Segment *segment) const; + + // Apply |handler| to each section defined in |segment|. If |handler| returns + // false, stop iterating and return false. If all calls to |handler| return + // true and we reach the end of the section list, return true. + bool WalkSegmentSections(const Segment &segment, SectionHandler *handler) + const; + + // Clear |section_map| and then populate it with a map of the sections + // in |segment|, from section names to Section structures. + // Each Section's contents refer to bytes in |segment|'s contents. + // On success, return true; if a problem occurs, report it and return false. + bool MapSegmentSections(const Segment &segment, SectionMap *section_map) + const; + + private: + // Used internally. + class SegmentFinder; + class SectionMapper; + + // We use this to report problems parsing the file's contents. (WEAK) + Reporter *reporter_; + + // The contents of the Mach-O file we're parsing. We do not own the + // storage it refers to. + ByteBuffer buffer_; + + // True if this file is big-endian. + bool big_endian_; + + // True if this file is a 64-bit Mach-O file. + bool bits_64_; + + // This file's cpu type and subtype. + cpu_type_t cpu_type_; // mach_header[_64].cputype + cpu_subtype_t cpu_subtype_; // mach_header[_64].cpusubtype + + // This file's type. + FileType file_type_; // mach_header[_64].filetype + + // The region of buffer_ occupied by load commands. + ByteBuffer load_commands_; + + // The number of load commands in load_commands_. + uint32_t load_command_count_; // mach_header[_64].ncmds + + // This file's header flags. + FileFlags flags_; +}; + +} // namespace mach_o +} // namespace google_breakpad + +#endif // BREAKPAD_COMMON_MAC_MACHO_READER_H_ diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_reader_unittest.cc b/src/lib/crashdump/gbreakpad/common/mac/macho_reader_unittest.cc new file mode 100644 index 0000000..9bc6d25 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_reader_unittest.cc @@ -0,0 +1,1898 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// macho_reader_unittest.cc: Unit tests for google_breakpad::Mach_O::FatReader +// and google_breakpad::Mach_O::Reader. + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/mac/macho_reader.h" +#include "common/test_assembler.h" + +namespace mach_o = google_breakpad::mach_o; +namespace test_assembler = google_breakpad::test_assembler; + +using mach_o::FatReader; +using mach_o::FileFlags; +using mach_o::FileType; +using mach_o::LoadCommandType; +using mach_o::Reader; +using mach_o::Section; +using mach_o::SectionMap; +using mach_o::Segment; +using test_assembler::Endianness; +using test_assembler::Label; +using test_assembler::kBigEndian; +using test_assembler::kLittleEndian; +using test_assembler::kUnsetEndian; +using google_breakpad::ByteBuffer; +using std::map; +using std::string; +using std::vector; +using testing::AllOf; +using testing::DoAll; +using testing::Field; +using testing::InSequence; +using testing::Matcher; +using testing::Return; +using testing::SaveArg; +using testing::Test; +using testing::_; + + +// Mock classes for the reader's various reporters and handlers. + +class MockFatReaderReporter: public FatReader::Reporter { + public: + MockFatReaderReporter(const string &filename) + : FatReader::Reporter(filename) { } + MOCK_METHOD0(BadHeader, void()); + MOCK_METHOD0(MisplacedObjectFile, void()); + MOCK_METHOD0(TooShort, void()); +}; + +class MockReaderReporter: public Reader::Reporter { + public: + MockReaderReporter(const string &filename) : Reader::Reporter(filename) { } + MOCK_METHOD0(BadHeader, void()); + MOCK_METHOD4(CPUTypeMismatch, void(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype)); + MOCK_METHOD0(HeaderTruncated, void()); + MOCK_METHOD0(LoadCommandRegionTruncated, void()); + MOCK_METHOD3(LoadCommandsOverrun, void(size_t claimed, size_t i, + LoadCommandType type)); + MOCK_METHOD2(LoadCommandTooShort, void(size_t i, LoadCommandType type)); + MOCK_METHOD1(SectionsMissing, void(const string &name)); + MOCK_METHOD1(MisplacedSegmentData, void(const string &name)); + MOCK_METHOD2(MisplacedSectionData, void(const string §ion, + const string &segment)); + MOCK_METHOD0(MisplacedSymbolTable, void()); + MOCK_METHOD1(UnsupportedCPUType, void(cpu_type_t cpu_type)); +}; + +class MockLoadCommandHandler: public Reader::LoadCommandHandler { + public: + MOCK_METHOD2(UnknownCommand, bool(LoadCommandType, const ByteBuffer &)); + MOCK_METHOD1(SegmentCommand, bool(const Segment &)); + MOCK_METHOD2(SymtabCommand, bool(const ByteBuffer &, const ByteBuffer &)); +}; + +class MockSectionHandler: public Reader::SectionHandler { + public: + MOCK_METHOD1(HandleSection, bool(const Section §ion)); +}; + + +// Tests for mach_o::FatReader. + +// Since the effect of these functions is to write to stderr, the +// results of these tests must be inspected by hand. +TEST(FatReaderReporter, BadHeader) { + FatReader::Reporter reporter("filename"); + reporter.BadHeader(); +} + +TEST(FatReaderReporter, MisplacedObjectFile) { + FatReader::Reporter reporter("filename"); + reporter.MisplacedObjectFile(); +} + +TEST(FatReaderReporter, TooShort) { + FatReader::Reporter reporter("filename"); + reporter.TooShort(); +} + +TEST(MachOReaderReporter, BadHeader) { + Reader::Reporter reporter("filename"); + reporter.BadHeader(); +} + +TEST(MachOReaderReporter, CPUTypeMismatch) { + Reader::Reporter reporter("filename"); + reporter.CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, + CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); +} + +TEST(MachOReaderReporter, HeaderTruncated) { + Reader::Reporter reporter("filename"); + reporter.HeaderTruncated(); +} + +TEST(MachOReaderReporter, LoadCommandRegionTruncated) { + Reader::Reporter reporter("filename"); + reporter.LoadCommandRegionTruncated(); +} + +TEST(MachOReaderReporter, LoadCommandsOverrun) { + Reader::Reporter reporter("filename"); + reporter.LoadCommandsOverrun(10, 9, LC_DYSYMTAB); + reporter.LoadCommandsOverrun(10, 9, 0); +} + +TEST(MachOReaderReporter, LoadCommandTooShort) { + Reader::Reporter reporter("filename"); + reporter.LoadCommandTooShort(11, LC_SYMTAB); +} + +TEST(MachOReaderReporter, SectionsMissing) { + Reader::Reporter reporter("filename"); + reporter.SectionsMissing("segment name"); +} + +TEST(MachOReaderReporter, MisplacedSegmentData) { + Reader::Reporter reporter("filename"); + reporter.MisplacedSegmentData("segment name"); +} + +TEST(MachOReaderReporter, MisplacedSectionData) { + Reader::Reporter reporter("filename"); + reporter.MisplacedSectionData("section name", "segment name"); +} + +TEST(MachOReaderReporter, MisplacedSymbolTable) { + Reader::Reporter reporter("filename"); + reporter.MisplacedSymbolTable(); +} + +TEST(MachOReaderReporter, UnsupportedCPUType) { + Reader::Reporter reporter("filename"); + reporter.UnsupportedCPUType(CPU_TYPE_HPPA); +} + +struct FatReaderFixture { + FatReaderFixture() + : fat(kBigEndian), + reporter("reporter filename"), + reader(&reporter), object_files(), object_files_size() { + EXPECT_CALL(reporter, BadHeader()).Times(0); + EXPECT_CALL(reporter, TooShort()).Times(0); + + // here, start, and Mark are file offsets in 'fat'. + fat.start() = 0; + } + // Append a 'fat_arch' entry to 'fat', with the given field values. + void AppendFatArch(cpu_type_t type, cpu_subtype_t subtype, + Label offset, Label size, uint32_t align) { + fat + .B32(type) + .B32(subtype) + .B32(offset) + .B32(size) + .B32(align); + } + // Append |n| dummy 'fat_arch' entries to 'fat'. The cpu type and + // subtype have unrealistic values. + void AppendDummyArchEntries(int n) { + for (int i = 0; i < n; i++) + AppendFatArch(0xb68ad617, 0x715a0840, 0, 0, 1); + } + void ReadFat(bool expect_parse_success = true) { + ASSERT_TRUE(fat.GetContents(&contents)); + fat_bytes = reinterpret_cast(contents.data()); + if (expect_parse_success) { + EXPECT_TRUE(reader.Read(fat_bytes, contents.size())); + object_files = reader.object_files(&object_files_size); + } + else + EXPECT_FALSE(reader.Read(fat_bytes, contents.size())); + } + test_assembler::Section fat; + MockFatReaderReporter reporter; + FatReader reader; + string contents; + const uint8_t *fat_bytes; + const struct fat_arch *object_files; + size_t object_files_size; +}; + +class FatReaderTest: public FatReaderFixture, public Test { }; + +TEST_F(FatReaderTest, BadMagic) { + EXPECT_CALL(reporter, BadHeader()).Times(1); + fat + .B32(0xcafed00d) // magic number (incorrect) + .B32(10); // number of architectures + AppendDummyArchEntries(10); + ReadFat(false); +} + +TEST_F(FatReaderTest, HeaderTooShort) { + EXPECT_CALL(reporter, TooShort()).Times(1); + fat + .B32(0xcafebabe); // magic number + ReadFat(false); +} + +TEST_F(FatReaderTest, ObjectListTooShort) { + EXPECT_CALL(reporter, TooShort()).Times(1); + fat + .B32(0xcafebabe) // magic number + .B32(10); // number of architectures + AppendDummyArchEntries(9); // nine dummy architecture entries... + fat // and a tenth, missing a byte at the end + .B32(0x3d46c8fc) // cpu type + .B32(0x8a7bfb01) // cpu subtype + .B32(0) // offset + .B32(0) // size + .Append(3, '*'); // one byte short of a four-byte alignment + ReadFat(false); +} + +TEST_F(FatReaderTest, DataTooShort) { + EXPECT_CALL(reporter, MisplacedObjectFile()).Times(1); + Label arch_data; + fat + .B32(0xcafebabe) // magic number + .B32(1); // number of architectures + AppendFatArch(0xb4d4a366, 0x4ba4f525, arch_data, 40, 0); + fat + .Mark(&arch_data) // file data begins here + .Append(30, '*'); // only 30 bytes, not 40 as header claims + ReadFat(false); +} + +TEST_F(FatReaderTest, NoObjectFiles) { + fat + .B32(0xcafebabe) // magic number + .B32(0); // number of architectures + ReadFat(); + EXPECT_EQ(0U, object_files_size); +} + +TEST_F(FatReaderTest, OneObjectFile) { + Label obj1_offset; + fat + .B32(0xcafebabe) // magic number + .B32(1); // number of architectures + // First object file list entry + AppendFatArch(0x5e3a6e91, 0x52ccd852, obj1_offset, 0x42, 0x355b15b2); + // First object file data + fat + .Mark(&obj1_offset) + .Append(0x42, '*'); // dummy contents + ReadFat(); + ASSERT_EQ(1U, object_files_size); + EXPECT_EQ(0x5e3a6e91, object_files[0].cputype); + EXPECT_EQ(0x52ccd852, object_files[0].cpusubtype); + EXPECT_EQ(obj1_offset.Value(), object_files[0].offset); + EXPECT_EQ(0x42U, object_files[0].size); + EXPECT_EQ(0x355b15b2U, object_files[0].align); +} + +TEST_F(FatReaderTest, ThreeObjectFiles) { + Label obj1, obj2, obj3; + fat + .B32(0xcafebabe) // magic number + .B32(3); // number of architectures + // Three object file list entries. + AppendFatArch(0x0cb92c30, 0x6a159a71, obj1, 0xfb4, 0x2615dbe8); + AppendFatArch(0x0f3f1cbb, 0x6c55e90f, obj2, 0xc31, 0x83af6ffd); + AppendFatArch(0x3717276d, 0x10ecdc84, obj3, 0x4b3, 0x035267d7); + fat + // First object file data + .Mark(&obj1) + .Append(0xfb4, '*') // dummy contents + // Second object file data + .Mark(&obj2) + .Append(0xc31, '%') // dummy contents + // Third object file data + .Mark(&obj3) + .Append(0x4b3, '^'); // dummy contents + + ReadFat(); + + ASSERT_EQ(3U, object_files_size); + + // First object file. + EXPECT_EQ(0x0cb92c30, object_files[0].cputype); + EXPECT_EQ(0x6a159a71, object_files[0].cpusubtype); + EXPECT_EQ(obj1.Value(), object_files[0].offset); + EXPECT_EQ(0xfb4U, object_files[0].size); + EXPECT_EQ(0x2615dbe8U, object_files[0].align); + + // Second object file. + EXPECT_EQ(0x0f3f1cbb, object_files[1].cputype); + EXPECT_EQ(0x6c55e90f, object_files[1].cpusubtype); + EXPECT_EQ(obj2.Value(), object_files[1].offset); + EXPECT_EQ(0xc31U, object_files[1].size); + EXPECT_EQ(0x83af6ffdU, object_files[1].align); + + // Third object file. + EXPECT_EQ(0x3717276d, object_files[2].cputype); + EXPECT_EQ(0x10ecdc84, object_files[2].cpusubtype); + EXPECT_EQ(obj3.Value(), object_files[2].offset); + EXPECT_EQ(0x4b3U, object_files[2].size); + EXPECT_EQ(0x035267d7U, object_files[2].align); +} + +TEST_F(FatReaderTest, BigEndianMachO32) { + fat.set_endianness(kBigEndian); + fat + .D32(0xfeedface) // Mach-O file magic number + .D32(0x1a9d0518) // cpu type + .D32(0x1b779357) // cpu subtype + .D32(0x009df67e) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x21987a99); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files_size); + EXPECT_EQ(0x1a9d0518, object_files[0].cputype); + EXPECT_EQ(0x1b779357, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, BigEndianMachO64) { + fat.set_endianness(kBigEndian); + fat + .D32(0xfeedfacf) // Mach-O 64-bit file magic number + .D32(0x5aff8487) // cpu type + .D32(0x4c6a57f7) // cpu subtype + .D32(0x4392d2c8) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x1b033eea); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files_size); + EXPECT_EQ(0x5aff8487, object_files[0].cputype); + EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, LittleEndianMachO32) { + fat.set_endianness(kLittleEndian); + fat + .D32(0xfeedface) // Mach-O file magic number + .D32(0x1a9d0518) // cpu type + .D32(0x1b779357) // cpu subtype + .D32(0x009df67e) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x21987a99); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files_size); + EXPECT_EQ(0x1a9d0518, object_files[0].cputype); + EXPECT_EQ(0x1b779357, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, LittleEndianMachO64) { + fat.set_endianness(kLittleEndian); + fat + .D32(0xfeedfacf) // Mach-O 64-bit file magic number + .D32(0x5aff8487) // cpu type + .D32(0x4c6a57f7) // cpu subtype + .D32(0x4392d2c8) // file type + .D32(0) // no load commands + .D32(0) // the load commands occupy no bytes + .D32(0x1b033eea); // flags + + ReadFat(); + + // FatReader should treat a Mach-O file as if it were a fat binary file + // containing one object file --- the whole thing. + ASSERT_EQ(1U, object_files_size); + EXPECT_EQ(0x5aff8487, object_files[0].cputype); + EXPECT_EQ(0x4c6a57f7, object_files[0].cpusubtype); + EXPECT_EQ(0U, object_files[0].offset); + EXPECT_EQ(contents.size(), object_files[0].size); +} + +TEST_F(FatReaderTest, IncompleteMach) { + fat.set_endianness(kLittleEndian); + fat + .D32(0xfeedfacf) // Mach-O 64-bit file magic number + .D32(0x5aff8487); // cpu type + // Truncated! + + EXPECT_CALL(reporter, TooShort()).WillOnce(Return()); + + ReadFat(false); +} + + +// General mach_o::Reader tests. + +// Dynamically scoped configuration data. +class WithConfiguration { + public: + // Establish the given parameters as the default for SizedSections + // created within the dynamic scope of this instance. + WithConfiguration(Endianness endianness, size_t word_size) + : endianness_(endianness), word_size_(word_size), saved_(current_) { + current_ = this; + } + ~WithConfiguration() { current_ = saved_; } + static Endianness endianness() { + assert(current_); + return current_->endianness_; + } + static size_t word_size() { + assert(current_); + return current_->word_size_; + } + + private: + // The innermost WithConfiguration in whose dynamic scope we are + // currently executing. + static WithConfiguration *current_; + + // The innermost WithConfiguration whose dynamic scope encloses this + // WithConfiguration. + Endianness endianness_; + size_t word_size_; + WithConfiguration *saved_; +}; + +WithConfiguration *WithConfiguration::current_ = NULL; + +// A test_assembler::Section with a size that we can cite. The start(), +// Here() and Mark() member functions of a SizedSection always represent +// offsets within the overall file. +class SizedSection: public test_assembler::Section { + public: + // Construct a section of the given endianness and word size. + explicit SizedSection(Endianness endianness, size_t word_size) + : test_assembler::Section(endianness), word_size_(word_size) { + assert(word_size_ == 32 || word_size_ == 64); + } + SizedSection() + : test_assembler::Section(WithConfiguration::endianness()), + word_size_(WithConfiguration::word_size()) { + assert(word_size_ == 32 || word_size_ == 64); + } + + // Access/set this section's word size. + size_t word_size() const { return word_size_; } + void set_word_size(size_t word_size) { + assert(word_size_ == 32 || word_size_ == 64); + word_size_ = word_size; + } + + // Return a label representing the size this section will have when it + // is Placed in some containing section. + Label final_size() const { return final_size_; } + + // Append SECTION to the end of this section, and call its Finish member. + // Return a reference to this section. + SizedSection &Place(SizedSection *section) { + assert(section->endianness() == endianness()); + section->Finish(); + section->start() = Here(); + test_assembler::Section::Append(*section); + return *this; + } + + protected: + // Mark this section's contents as complete. For plain SizedSections, we + // set SECTION's start to its position in this section, and its final_size + // label to its current size. Derived classes can extend this as needed + // for their additional semantics. + virtual void Finish() { + final_size_ = Size(); + } + + // The word size for this data: either 32 or 64. + size_t word_size_; + + private: + // This section's final size, set when we are placed in some other + // SizedSection. + Label final_size_; +}; + +// A SizedSection that is loaded into memory at a particular address. +class LoadedSection: public SizedSection { + public: + explicit LoadedSection(Label address = Label()) : address_(address) { } + + // Return a label representing this section's address. + Label address() const { return address_; } + + // Placing a loaded section within a loaded section sets the relationship + // between their addresses. + LoadedSection &Place(LoadedSection *section) { + section->address() = address() + Size(); + SizedSection::Place(section); + return *this; + } + + protected: + // The address at which this section's contents will be loaded. + Label address_; +}; + +// A SizedSection representing a segment load command. +class SegmentLoadCommand: public SizedSection { + public: + SegmentLoadCommand() : section_count_(0) { } + + // Append a segment load command header with the given characteristics. + // The load command will refer to CONTENTS, which must be Placed in the + // file separately, at the desired position. Return a reference to this + // section. + SegmentLoadCommand &Header(const string &name, const LoadedSection &contents, + uint32_t maxprot, uint32_t initprot, + uint32_t flags) { + assert(contents.word_size() == word_size()); + D32(word_size() == 32 ? LC_SEGMENT : LC_SEGMENT_64); + D32(final_size()); + AppendCString(name, 16); + Append(endianness(), word_size() / 8, contents.address()); + Append(endianness(), word_size() / 8, vmsize_); + Append(endianness(), word_size() / 8, contents.start()); + Append(endianness(), word_size() / 8, contents.final_size()); + D32(maxprot); + D32(initprot); + D32(final_section_count_); + D32(flags); + + content_final_size_ = contents.final_size(); + + return *this; + } + + // Return a label representing the size of this segment when loaded into + // memory. If this label is still undefined by the time we place this + // segment, it defaults to the final size of the segment's in-file + // contents. Return a reference to this load command. + Label &vmsize() { return vmsize_; } + + // Add a section entry with the given characteristics to this segment + // load command. Return a reference to this. The section entry will refer + // to CONTENTS, which must be Placed in the segment's contents + // separately, at the desired position. + SegmentLoadCommand &AppendSectionEntry(const string §ion_name, + const string &segment_name, + uint32_t alignment, uint32_t flags, + const LoadedSection &contents) { + AppendCString(section_name, 16); + AppendCString(segment_name, 16); + Append(endianness(), word_size() / 8, contents.address()); + Append(endianness(), word_size() / 8, contents.final_size()); + D32(contents.start()); + D32(alignment); + D32(0); // relocations start + D32(0); // relocations size + D32(flags); + D32(0x93656b95); // reserved1 + D32(0xc35a2473); // reserved2 + if (word_size() == 64) + D32(0x70284b95); // reserved3 + + section_count_++; + + return *this; + } + + protected: + void Finish() { + final_section_count_ = section_count_; + if (!vmsize_.IsKnownConstant()) + vmsize_ = content_final_size_; + SizedSection::Finish(); + } + + private: + // The number of sections that have been added to this segment so far. + size_t section_count_; + + // A label representing the final number of sections this segment will hold. + Label final_section_count_; + + // The size of the contents for this segment present in the file. + Label content_final_size_; + + // A label representing the size of this segment when loaded; this can be + // larger than the size of its file contents, the difference being + // zero-filled. If not set explicitly by calling set_vmsize, this is set + // equal to the size of the contents. + Label vmsize_; +}; + +// A SizedSection holding a list of Mach-O load commands. +class LoadCommands: public SizedSection { + public: + LoadCommands() : command_count_(0) { } + + // Return a label representing the final load command count. + Label final_command_count() const { return final_command_count_; } + + // Increment the command count; return a reference to this section. + LoadCommands &CountCommand() { + command_count_++; + return *this; + } + + // Place COMMAND, containing a load command, at the end of this section. + // Return a reference to this section. + LoadCommands &Place(SizedSection *section) { + SizedSection::Place(section); + CountCommand(); + return *this; + } + + protected: + // Mark this load command list as complete. + void Finish() { + SizedSection::Finish(); + final_command_count_ = command_count_; + } + + private: + // The number of load commands we have added to this file so far. + size_t command_count_; + + // A label representing the final command count. + Label final_command_count_; +}; + +// A SizedSection holding the contents of a Mach-O file. Within a +// MachOFile, the start, Here, and Mark members refer to file offsets. +class MachOFile: public SizedSection { + public: + MachOFile() { + start() = 0; + } + + // Create a Mach-O file header using the given characteristics and load + // command list. This Places COMMANDS immediately after the header. + // Return a reference to this section. + MachOFile &Header(LoadCommands *commands, + cpu_type_t cpu_type = CPU_TYPE_X86, + cpu_subtype_t cpu_subtype = CPU_SUBTYPE_I386_ALL, + FileType file_type = MH_EXECUTE, + uint32_t file_flags = (MH_TWOLEVEL | + MH_DYLDLINK | + MH_NOUNDEFS)) { + D32(word_size() == 32 ? 0xfeedface : 0xfeedfacf); // magic number + D32(cpu_type); // cpu type + D32(cpu_subtype); // cpu subtype + D32(file_type); // file type + D32(commands->final_command_count()); // number of load commands + D32(commands->final_size()); // their size in bytes + D32(file_flags); // flags + if (word_size() == 64) + D32(0x55638b90); // reserved + Place(commands); + return *this; + } +}; + + +struct ReaderFixture { + ReaderFixture() + : reporter("reporter filename"), + reader(&reporter) { + EXPECT_CALL(reporter, BadHeader()).Times(0); + EXPECT_CALL(reporter, CPUTypeMismatch(_, _, _, _)).Times(0); + EXPECT_CALL(reporter, HeaderTruncated()).Times(0); + EXPECT_CALL(reporter, LoadCommandRegionTruncated()).Times(0); + EXPECT_CALL(reporter, LoadCommandsOverrun(_, _, _)).Times(0); + EXPECT_CALL(reporter, LoadCommandTooShort(_, _)).Times(0); + EXPECT_CALL(reporter, SectionsMissing(_)).Times(0); + EXPECT_CALL(reporter, MisplacedSegmentData(_)).Times(0); + EXPECT_CALL(reporter, MisplacedSectionData(_, _)).Times(0); + EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(0); + EXPECT_CALL(reporter, UnsupportedCPUType(_)).Times(0); + + EXPECT_CALL(load_command_handler, UnknownCommand(_, _)).Times(0); + EXPECT_CALL(load_command_handler, SegmentCommand(_)).Times(0); + } + + void ReadFile(MachOFile *file, + bool expect_parse_success, + cpu_type_t expected_cpu_type, + cpu_subtype_t expected_cpu_subtype) { + ASSERT_TRUE(file->GetContents(&file_contents)); + file_bytes = reinterpret_cast(file_contents.data()); + if (expect_parse_success) { + EXPECT_TRUE(reader.Read(file_bytes, + file_contents.size(), + expected_cpu_type, + expected_cpu_subtype)); + } else { + EXPECT_FALSE(reader.Read(file_bytes, + file_contents.size(), + expected_cpu_type, + expected_cpu_subtype)); + } + } + + string file_contents; + const uint8_t *file_bytes; + MockReaderReporter reporter; + Reader reader; + MockLoadCommandHandler load_command_handler; + MockSectionHandler section_handler; +}; + +class ReaderTest: public ReaderFixture, public Test { }; + +TEST_F(ReaderTest, BadMagic) { + WithConfiguration config(kLittleEndian, 32); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0x67bdebe1) // Not a proper magic number. + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + EXPECT_CALL(reporter, BadHeader()).WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType); +} + +TEST_F(ReaderTest, MismatchedMagic) { + WithConfiguration config(kLittleEndian, 32); + const cpu_type_t kCPUType = CPU_TYPE_I386; + const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL; + MachOFile file; + file + .D32(MH_CIGAM) // Right magic, but winds up wrong + // due to bitswapping + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + EXPECT_CALL(reporter, BadHeader()).WillOnce(Return()); + ReadFile(&file, false, kCPUType, kCPUSubType); +} + +TEST_F(ReaderTest, ShortMagic) { + WithConfiguration config(kBigEndian, 32); + MachOFile file; + file + .D16(0xfeed); // magic number + // truncated! + EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_ANY, 0); +} + +TEST_F(ReaderTest, ShortHeader) { + WithConfiguration config(kBigEndian, 32); + const cpu_type_t kCPUType = CPU_TYPE_ANY; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedface) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0); // they occupy no bytes + EXPECT_CALL(reporter, HeaderTruncated()).WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType); +} + +TEST_F(ReaderTest, MismatchedCPU) { + WithConfiguration config(kBigEndian, 32); + const cpu_type_t kCPUType = CPU_TYPE_I386; + const cpu_subtype_t kCPUSubType = CPU_SUBTYPE_X86_ALL; + MachOFile file; + file + .D32(MH_MAGIC) // Right magic for PPC (once bitswapped) + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + EXPECT_CALL(reporter, + CPUTypeMismatch(CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL, + CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL)) + .WillOnce(Return()); + ReadFile(&file, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL); +} + +TEST_F(ReaderTest, LittleEndian32Bit) { + WithConfiguration config(kLittleEndian, 32); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedface) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_FALSE(reader.bits_64()); + EXPECT_FALSE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + +TEST_F(ReaderTest, LittleEndian64Bit) { + WithConfiguration config(kLittleEndian, 64); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedfacf) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_TRUE(reader.bits_64()); + EXPECT_FALSE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + +TEST_F(ReaderTest, BigEndian32Bit) { + WithConfiguration config(kBigEndian, 32); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedface) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_FALSE(reader.bits_64()); + EXPECT_TRUE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + +TEST_F(ReaderTest, BigEndian64Bit) { + WithConfiguration config(kBigEndian, 64); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedfacf) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(0) // no load commands + .D32(0) // they occupy no bytes + .D32(0x80e71d64) // flags + .D32(0); // reserved + ReadFile(&file, true, CPU_TYPE_ANY, kCPUSubType); + EXPECT_TRUE(reader.bits_64()); + EXPECT_TRUE(reader.big_endian()); + EXPECT_EQ(kCPUType, reader.cpu_type()); + EXPECT_EQ(kCPUSubType, reader.cpu_subtype()); + EXPECT_EQ(FileType(0x149fc717), reader.file_type()); + EXPECT_EQ(FileFlags(0x80e71d64), reader.flags()); +} + + +// Load command tests. + +class LoadCommand: public ReaderFixture, public Test { }; + +TEST_F(LoadCommand, RegionTruncated) { + WithConfiguration config(kBigEndian, 64); + const cpu_type_t kCPUType = 0x46b760df; + const cpu_subtype_t kCPUSubType = 0x76a0e7f7; + MachOFile file; + file + .D32(0xfeedfacf) // magic number + .D32(kCPUType) // cpu type + .D32(kCPUSubType) // cpu subtype + .D32(0x149fc717) // file type + .D32(1) // one load command + .D32(40) // occupying 40 bytes + .D32(0x80e71d64) // flags + .D32(0) // reserved + .Append(20, 0); // load command region, not as long as + // Mach-O header promised + + EXPECT_CALL(reporter, LoadCommandRegionTruncated()).WillOnce(Return()); + + ReadFile(&file, false, CPU_TYPE_ANY, kCPUSubType); +} + +TEST_F(LoadCommand, None) { + WithConfiguration config(kLittleEndian, 32); + LoadCommands load_commands; + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_X86, CPU_SUBTYPE_I386_ALL); + + EXPECT_FALSE(reader.bits_64()); + EXPECT_FALSE(reader.big_endian()); + EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type()); + EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype()); + EXPECT_EQ(static_cast(MH_EXECUTE), reader.file_type()); + EXPECT_EQ(FileFlags(MH_TWOLEVEL | + MH_DYLDLINK | + MH_NOUNDEFS), + FileFlags(reader.flags())); + + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, Unknown) { + WithConfiguration config(kBigEndian, 32); + LoadCommands load_commands; + load_commands + .CountCommand() + .D32(0x33293d4a) // unknown load command + .D32(40) // total size in bytes + .Append(32, '*'); // dummy data + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_FALSE(reader.bits_64()); + EXPECT_TRUE(reader.big_endian()); + EXPECT_EQ(CPU_TYPE_X86, reader.cpu_type()); + EXPECT_EQ(CPU_SUBTYPE_I386_ALL, reader.cpu_subtype()); + EXPECT_EQ(static_cast(MH_EXECUTE), reader.file_type()); + EXPECT_EQ(FileFlags(MH_TWOLEVEL | + MH_DYLDLINK | + MH_NOUNDEFS), + reader.flags()); + + ByteBuffer expected; + expected.start = file_bytes + load_commands.start().Value(); + expected.end = expected.start + load_commands.final_size().Value(); + EXPECT_CALL(load_command_handler, UnknownCommand(0x33293d4a, + expected)) + .WillOnce(Return(true)); + + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, TypeIncomplete) { + WithConfiguration config(kLittleEndian, 32); + LoadCommands load_commands; + load_commands + .CountCommand() + .Append(3, 0); // load command type, incomplete + + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, 0)) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, LengthIncomplete) { + WithConfiguration config(kBigEndian, 64); + LoadCommands load_commands; + load_commands + .CountCommand() + .D32(LC_SEGMENT); // load command + // no length + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT)) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, ContentIncomplete) { + WithConfiguration config(kLittleEndian, 64); + LoadCommands load_commands; + load_commands + .CountCommand() + .D32(LC_SEGMENT) // load command + .D32(40) // total size in bytes + .Append(28, '*'); // not enough dummy data + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandsOverrun(1, 0, LC_SEGMENT)) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, SegmentBE32) { + WithConfiguration config(kBigEndian, 32); + LoadedSection segment; + segment.address() = 0x1891139c; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("froon", segment, 0x94d6dd22, 0x8bdbc319, 0x990a16dd); + segment_command.vmsize() = 0xcb76584fU; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(false, actual_segment.bits_64); + EXPECT_EQ("froon", actual_segment.name); + EXPECT_EQ(0x1891139cU, actual_segment.vmaddr); + EXPECT_EQ(0xcb76584fU, actual_segment.vmsize); + EXPECT_EQ(0x94d6dd22U, actual_segment.maxprot); + EXPECT_EQ(0x8bdbc319U, actual_segment.initprot); + EXPECT_EQ(0x990a16ddU, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentLE32) { + WithConfiguration config(kLittleEndian, 32); + LoadedSection segment; + segment.address() = 0x4b877866; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("sixteenprecisely", segment, + 0x350759ed, 0x6cf5a62e, 0x990a16dd); + segment_command.vmsize() = 0xcb76584fU; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(false, actual_segment.bits_64); + EXPECT_EQ("sixteenprecisely", actual_segment.name); + EXPECT_EQ(0x4b877866U, actual_segment.vmaddr); + EXPECT_EQ(0xcb76584fU, actual_segment.vmsize); + EXPECT_EQ(0x350759edU, actual_segment.maxprot); + EXPECT_EQ(0x6cf5a62eU, actual_segment.initprot); + EXPECT_EQ(0x990a16ddU, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentBE64) { + WithConfiguration config(kBigEndian, 64); + LoadedSection segment; + segment.address() = 0x79f484f77009e511ULL; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("froon", segment, 0x42b45da5, 0x8bdbc319, 0xb2335220); + segment_command.vmsize() = 0x8d92397ce6248abaULL; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(true, actual_segment.bits_64); + EXPECT_EQ("froon", actual_segment.name); + EXPECT_EQ(0x79f484f77009e511ULL, actual_segment.vmaddr); + EXPECT_EQ(0x8d92397ce6248abaULL, actual_segment.vmsize); + EXPECT_EQ(0x42b45da5U, actual_segment.maxprot); + EXPECT_EQ(0x8bdbc319U, actual_segment.initprot); + EXPECT_EQ(0xb2335220U, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentLE64) { + WithConfiguration config(kLittleEndian, 64); + LoadedSection segment; + segment.address() = 0x50c0501dc5922d35ULL; + segment.Append(42, '*'); // segment contents + SegmentLoadCommand segment_command; + segment_command + .Header("sixteenprecisely", segment, + 0x917c339d, 0xdbc446fa, 0xb650b563); + segment_command.vmsize() = 0x84ae73e7c75469bfULL; + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(true, actual_segment.bits_64); + EXPECT_EQ("sixteenprecisely", actual_segment.name); + EXPECT_EQ(0x50c0501dc5922d35ULL, actual_segment.vmaddr); + EXPECT_EQ(0x84ae73e7c75469bfULL, actual_segment.vmsize); + EXPECT_EQ(0x917c339dU, actual_segment.maxprot); + EXPECT_EQ(0xdbc446faU, actual_segment.initprot); + EXPECT_EQ(0xb650b563U, actual_segment.flags); + EXPECT_EQ(0U, actual_segment.nsects); + EXPECT_EQ(0U, actual_segment.section_list.Size()); + EXPECT_EQ(segment.final_size().Value(), actual_segment.contents.Size()); +} + +TEST_F(LoadCommand, SegmentCommandTruncated) { + WithConfiguration config(kBigEndian, 32); + LoadedSection segment_contents; + segment_contents.Append(20, '*'); // lah di dah + SizedSection command; + command + .D32(LC_SEGMENT) // command type + .D32(command.final_size()) // command size + .AppendCString("too-short", 16) // segment name + .D32(0x9c759211) // vmaddr + .D32(segment_contents.final_size()) // vmsize + .D32(segment_contents.start()) // file offset + .D32(segment_contents.final_size()) // file size + .D32(0x56f28446) // max protection + .D32(0xe7910dcb) // initial protection + .D32(0) // no sections + .Append(3, 0); // flags (one byte short!) + LoadCommands load_commands; + load_commands.Place(&command); + MachOFile file; + file + .Header(&load_commands) + .Place(&segment_contents); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, LoadCommandTooShort(0, LC_SEGMENT)) + .WillOnce(Return()); + + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, SegmentBadContentOffset) { + WithConfiguration config(kLittleEndian, 32); + // Instead of letting a Place call set the segment's file offset and size, + // set them ourselves, to check that the parser catches invalid offsets + // instead of handing us bogus pointers. + LoadedSection segment; + segment.address() = 0x4db5489c; + segment.start() = 0x7e189e76; // beyond end of file + segment.final_size() = 0x98b9c3ab; + SegmentLoadCommand segment_command; + segment_command + .Header("notmerelyfifteen", segment, 0xcbab25ee, 0x359a20db, 0x68a3933f); + LoadCommands load_commands; + load_commands.Place(&segment_command); + MachOFile file; + file.Header(&load_commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, MisplacedSegmentData("notmerelyfifteen")) + .WillOnce(Return()); + + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(LoadCommand, ThreeLoadCommands) { + WithConfiguration config(kBigEndian, 32); + LoadedSection seg1, seg2, seg3; + SegmentLoadCommand cmd1, cmd2, cmd3; + + seg1.Append(128, '@'); + seg1.address() = 0xa7f61ef6; + cmd1.Header("head", seg1, 0x88bf1cc7, 0x889a26a4, 0xe9b80d87); + // Include some dummy data at the end of the load command. Since we + // didn't claim to have any sections, the reader should ignore this. But + // making sure the commands have different lengths ensures that we're + // using the right command's length to advance the LoadCommandIterator. + cmd1.Append(128, '!'); + + seg2.Append(42, '*'); + seg2.address() = 0xc70fc909; + cmd2.Header("thorax", seg2, 0xde7327f4, 0xfdaf771d, 0x65e74b30); + // More dummy data at the end of the load command. + cmd2.Append(32, '^'); + + seg3.Append(42, '%'); + seg3.address() = 0x46b3ab05; + cmd3.Header("abdomen", seg3, 0x7098b70d, 0x8d8d7728, 0x5131419b); + // More dummy data at the end of the load command. + cmd3.Append(64, '&'); + + LoadCommands load_commands; + load_commands.Place(&cmd1).Place(&cmd2).Place(&cmd3); + + MachOFile file; + file.Header(&load_commands).Place(&seg1).Place(&seg2).Place(&seg3); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + { + InSequence s; + EXPECT_CALL(load_command_handler, + SegmentCommand(Field(&Segment::name, "head"))) + .WillOnce(Return(true)); + EXPECT_CALL(load_command_handler, + SegmentCommand(Field(&Segment::name, "thorax"))) + .WillOnce(Return(true)); + EXPECT_CALL(load_command_handler, + SegmentCommand(Field(&Segment::name, "abdomen"))) + .WillOnce(Return(true)); + } + + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); +} + +static inline Matcher MatchSection( + Matcher bits_64, + Matcher section_name, + Matcher segment_name, + Matcher address, + Matcher alignment, + Matcher flags, + Matcher contents) { + return AllOf(AllOf(Field(&Section::bits_64, bits_64), + Field(&Section::section_name, section_name), + Field(&Section::segment_name, segment_name), + Field(&Section::address, address)), + AllOf(Field(&Section::align, alignment), + Field(&Section::flags, flags), + Field(&Section::contents, contents))); +} + +static inline Matcher MatchSection( + Matcher bits_64, + Matcher section_name, + Matcher segment_name, + Matcher address) { + return AllOf(Field(&Section::bits_64, bits_64), + Field(&Section::section_name, section_name), + Field(&Section::segment_name, segment_name), + Field(&Section::address, address)); +} + +TEST_F(LoadCommand, OneSegmentTwoSections) { + WithConfiguration config(kBigEndian, 64); + + // Create some sections with some data. + LoadedSection section1, section2; + section1.Append("buddha's hand"); + section2.Append("kumquat"); + + // Create a segment to hold them. + LoadedSection segment; + segment.address() = 0xe1d0eeec; + segment.Place(§ion2).Place(§ion1); + + SegmentLoadCommand segment_command; + segment_command + .Header("head", segment, 0x92c9568c, 0xa89f2627, 0x4dc7a1e2) + .AppendSectionEntry("mandarin", "kishu", 12, 0x8cd4604bU, section1) + .AppendSectionEntry("bergamot", "cara cara", 12, 0x98746efaU, section2); + + LoadCommands commands; + commands.Place(&segment_command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_CALL(load_command_handler, SegmentCommand(_)) + .WillOnce(DoAll(SaveArg<0>(&actual_segment), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + { + InSequence s; + ByteBuffer contents1; + contents1.start = file_bytes + section1.start().Value(); + contents1.end = contents1.start + section1.final_size().Value(); + EXPECT_EQ("buddha's hand", + string(reinterpret_cast(contents1.start), + contents1.Size())); + EXPECT_CALL(section_handler, + HandleSection(MatchSection(true, "mandarin", "kishu", + section1.address().Value(), 12, + 0x8cd4604bU, contents1))) + .WillOnce(Return(true)); + + ByteBuffer contents2; + contents2.start = file_bytes + section2.start().Value(); + contents2.end = contents2.start + section2.final_size().Value(); + EXPECT_EQ("kumquat", + string(reinterpret_cast(contents2.start), + contents2.Size())); + EXPECT_CALL(section_handler, + HandleSection(MatchSection(true, "bergamot", "cara cara", + section2.address().Value(), 12, + 0x98746efaU, contents2))) + .WillOnce(Return(true)); + } + + EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionBefore) { + WithConfiguration config(kLittleEndian, 64); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.Append(10, '0'); + + // The contents of the following sections don't matter, because + // we're not really going to Place them in segment; we're just going + // to set all their labels by hand to get the (impossible) + // configurations we want. + + // A section whose starting offset is before that of its section. + LoadedSection before; + before.Append(10, '1'); + before.start() = segment.start() - 1; + before.address() = segment.address() - 1; + before.final_size() = before.Size(); + + SegmentLoadCommand command; + command + .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("before", "segment", 0, 0x686c6921, before); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("segment", &actual_segment)); + + EXPECT_CALL(reporter, MisplacedSectionData("before", "segment")) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionAfter) { + WithConfiguration config(kLittleEndian, 64); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.Append(10, '0'); + + // The contents of the following sections don't matter, because + // we're not really going to Place them in segment; we're just going + // to set all their labels by hand to get the (impossible) + // configurations we want. + + // A section whose starting offset is after the end of its section. + LoadedSection after; + after.Append(10, '2'); + after.start() = segment.start() + 11; + after.address() = segment.address() + 11; + after.final_size() = after.Size(); + + SegmentLoadCommand command; + command + .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("after", "segment", 0, 0x2ee50124, after); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("segment", &actual_segment)); + + EXPECT_CALL(reporter, MisplacedSectionData("after", "segment")) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionTooBig) { + WithConfiguration config(kLittleEndian, 64); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.Append(10, '0'); + + // The contents of the following sections don't matter, because + // we're not really going to Place them in segment; we're just going + // to set all their labels by hand to get the (impossible) + // configurations we want. + + // A section that extends beyond the end of its section. + LoadedSection too_big; + too_big.Append(10, '3'); + too_big.start() = segment.start() + 1; + too_big.address() = segment.address() + 1; + too_big.final_size() = too_big.Size(); + + SegmentLoadCommand command; + command + .Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("too big", "segment", 0, 0x8b53ae5c, too_big); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands).Place(&segment); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("segment", &actual_segment)); + + EXPECT_CALL(reporter, MisplacedSectionData("too big", "segment")) + .WillOnce(Return()); + EXPECT_FALSE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + + +// The segments in a .dSYM bundle's Mach-O file have their file offset +// and size set to zero, but the sections don't. The reader shouldn't +// report an error in this case. +TEST_F(LoadCommand, ZappedSegment) { + WithConfiguration config(kBigEndian, 32); + + // The segment. + LoadedSection segment; + segment.address() = 0x696d83cc; + segment.start() = 0; + segment.final_size() = 0; + + // The section. + LoadedSection section; + section.address() = segment.address(); + section.start() = 0; + section.final_size() = 1000; // extends beyond its segment + + SegmentLoadCommand command; + command + .Header("zapped", segment, 0x0861a5cb, 0x68ccff67, 0x0b66255c) + .AppendSectionEntry("twitching", "zapped", 0, 0x93b3bd42, section); + + LoadCommands commands; + commands.Place(&command); + + MachOFile file; + file.Header(&commands); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + EXPECT_TRUE(reader.FindSegment("zapped", &actual_segment)); + + ByteBuffer zapped_extent(NULL, 0); + EXPECT_CALL(section_handler, + HandleSection(MatchSection(false, "twitching", "zapped", + 0x696d83cc, 0, 0x93b3bd42, + zapped_extent))) + .WillOnce(Return(true)); + + EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MapSegmentSections) { + WithConfiguration config(kLittleEndian, 32); + + // Create some sections with some data. + LoadedSection section1, section2, section3, section4; + section1.Append("buddha's hand"); + section2.start() = 0; // Section 2 is an S_ZEROFILL section. + section2.final_size() = 0; + section3.Append("shasta gold"); + section4.Append("satsuma"); + + // Create two segments to hold them. + LoadedSection segment1, segment2; + segment1.address() = 0x13e6c8a9; + segment1.Place(§ion3).Place(§ion1); + segment2.set_word_size(64); + segment2.address() = 0x04d462e2; + segment2.Place(§ion4); + section2.address() = segment2.address() + segment2.Size(); + + SegmentLoadCommand segment_command1, segment_command2; + segment_command1 + .Header("head", segment1, 0x67d955a6, 0x7a61c13e, 0xe3e50c64) + .AppendSectionEntry("mandarin", "head", 12, 0x5bb565d7, section1) + .AppendSectionEntry("bergamot", "head", 12, 0x8620de73, section3); + segment_command2.set_word_size(64); + segment_command2 + .Header("thorax", segment2, 0x7aab2419, 0xe908007f, 0x17961d33) + .AppendSectionEntry("sixteenprecisely", "thorax", + 12, S_ZEROFILL, section2) + .AppendSectionEntry("cara cara", "thorax", 12, 0xb6c5dd8a, section4); + + LoadCommands commands; + commands.Place(&segment_command1).Place(&segment_command2); + + MachOFile file; + file.Header(&commands).Place(&segment1).Place(&segment2); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment segment; + SectionMap section_map; + + EXPECT_FALSE(reader.FindSegment("smoot", &segment)); + + ASSERT_TRUE(reader.FindSegment("thorax", &segment)); + ASSERT_TRUE(reader.MapSegmentSections(segment, §ion_map)); + + EXPECT_FALSE(section_map.find("sixteenpreciselyandthensome") + != section_map.end()); + EXPECT_FALSE(section_map.find("mandarin") != section_map.end()); + ASSERT_TRUE(section_map.find("cara cara") != section_map.end()); + EXPECT_THAT(section_map["cara cara"], + MatchSection(true, "cara cara", "thorax", 0x04d462e2)); + ASSERT_TRUE(section_map.find("sixteenprecisely") + != section_map.end()); + ByteBuffer sixteenprecisely_contents(NULL, 0); + EXPECT_THAT(section_map["sixteenprecisely"], + MatchSection(true, "sixteenprecisely", "thorax", + 0x04d462e2 + 7, 12, S_ZEROFILL, + sixteenprecisely_contents)); + + ASSERT_TRUE(reader.FindSegment("head", &segment)); + ASSERT_TRUE(reader.MapSegmentSections(segment, §ion_map)); + + ASSERT_TRUE(section_map.find("mandarin") != section_map.end()); + EXPECT_THAT(section_map["mandarin"], + MatchSection(false, "mandarin", "head", 0x13e6c8a9 + 11)); + ASSERT_TRUE(section_map.find("bergamot") != section_map.end()); + EXPECT_THAT(section_map["bergamot"], + MatchSection(false, "bergamot", "head", 0x13e6c8a9)); +} + +TEST_F(LoadCommand, FindSegment) { + WithConfiguration config(kBigEndian, 32); + + LoadedSection segment1, segment2, segment3; + segment1.address() = 0xb8ae5752; + segment1.Append("Some contents!"); + segment2.address() = 0xd6b0ce83; + segment2.Append("Different stuff."); + segment3.address() = 0x7374fd2a; + segment3.Append("Further materials."); + + SegmentLoadCommand cmd1, cmd2, cmd3; + cmd1.Header("first", segment1, 0xfadb6932, 0x175bf529, 0x0de790ad); + cmd2.Header("second", segment2, 0xeef716e0, 0xe103a9d7, 0x7d38a8ef); + cmd3.Header("third", segment3, 0xe172b39e, 0x86012f07, 0x080ac94d); + + LoadCommands commands; + commands.Place(&cmd1).Place(&cmd2).Place(&cmd3); + + MachOFile file; + file.Header(&commands).Place(&segment1).Place(&segment2).Place(&segment3); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + Segment actual_segment; + + EXPECT_FALSE(reader.FindSegment("murphy", &actual_segment)); + + EXPECT_TRUE(reader.FindSegment("second", &actual_segment)); + EXPECT_EQ(0xd6b0ce83, actual_segment.vmaddr); +} + + +// Symtab tests. + +// A StringAssembler is a class for generating .stabstr sections to present +// as input to the STABS parser. +class StringAssembler: public SizedSection { + public: + // Add the string S to this StringAssembler, and return the string's + // offset within this compilation unit's strings. + size_t Add(const string &s) { + size_t offset = Size(); + AppendCString(s); + return offset; + } +}; + +// A SymbolAssembler is a class for generating .stab sections to present as +// test input for the STABS parser. +class SymbolAssembler: public SizedSection { + public: + // Create a SymbolAssembler that uses StringAssembler for its strings. + explicit SymbolAssembler(StringAssembler *string_assembler) + : string_assembler_(string_assembler), + entry_count_(0) { } + + // Append a STAB entry to the end of this section with the given + // characteristics. NAME is the offset of this entry's name string within + // its compilation unit's portion of the .stabstr section; this can be a + // value generated by a StringAssembler. Return a reference to this + // SymbolAssembler. + SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor, + Label value, Label name) { + D32(name); + D8(type); + D8(other); + D16(descriptor); + Append(endianness(), word_size_ / 8, value); + entry_count_++; + return *this; + } + + // As above, but automatically add NAME to our StringAssembler. + SymbolAssembler &Symbol(uint8_t type, uint8_t other, Label descriptor, + Label value, const string &name) { + return Symbol(type, other, descriptor, value, string_assembler_->Add(name)); + } + + private: + // The strings for our STABS entries. + StringAssembler *string_assembler_; + + // The number of entries in this compilation unit so far. + size_t entry_count_; +}; + +class Symtab: public ReaderFixture, public Test { }; + +TEST_F(Symtab, Symtab32) { + WithConfiguration config(kLittleEndian, 32); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0x52, 0x7c, 0x3470, 0x9bb02e7c, "hrududu") + .Symbol(0x50, 0x90, 0x7520, 0x1122525d, "Frith"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(2) // symbol count + .D32(strings.start()) // file offset of strings + .D32(strings.final_size()); // strings size + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + file.Header(&load_commands).Place(&symbols).Place(&strings); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + ByteBuffer symbols_found, strings_found; + EXPECT_CALL(load_command_handler, SymtabCommand(_, _)) + .WillOnce(DoAll(SaveArg<0>(&symbols_found), + SaveArg<1>(&strings_found), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(24U, symbols_found.Size()); + EXPECT_EQ(14U, strings_found.Size()); +} + +TEST_F(Symtab, Symtab64) { + WithConfiguration config(kBigEndian, 64); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo") + .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(2) // symbol count + .D32(strings.start()) // file offset of strings + .D32(strings.final_size()); // strings size + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + file.Header(&load_commands).Place(&symbols).Place(&strings); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + ByteBuffer symbols_found, strings_found; + EXPECT_CALL(load_command_handler, SymtabCommand(_, _)) + .WillOnce(DoAll(SaveArg<0>(&symbols_found), + SaveArg<1>(&strings_found), + Return(true))); + EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler)); + + EXPECT_EQ(32U, symbols_found.Size()); + EXPECT_EQ(8U, strings_found.Size()); +} + +TEST_F(Symtab, SymtabMisplacedSymbols) { + WithConfiguration config(kBigEndian, 32); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo") + .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(3) // symbol count (too many) + .D32(strings.start()) // file offset of strings + .D32(strings.final_size()); // strings size + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + // Put symbols at end, so the excessive length will be noticed. + file.Header(&load_commands).Place(&strings).Place(&symbols); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + +TEST_F(Symtab, SymtabMisplacedStrings) { + WithConfiguration config(kLittleEndian, 32); + + StringAssembler strings; + SymbolAssembler symbols(&strings); + symbols + .Symbol(0xa7, 0xaf, 0x03af, 0x42f3072c74335181ULL, "foo") + .Symbol(0xb0, 0x9a, 0x2aa7, 0x2e2d349b3d5744a0ULL, "bar"); + + SizedSection symtab_command; + symtab_command + .D32(LC_SYMTAB) // command + .D32(symtab_command.final_size()) // size + .D32(symbols.start()) // file offset of symbols + .D32(2) // symbol count + .D32(strings.start()) // file offset of strings + .D32(strings.final_size() + 1); // strings size (too long) + + LoadCommands load_commands; + load_commands.Place(&symtab_command); + + MachOFile file; + // Put strings at end, so the excessive length will be noticed. + file.Header(&load_commands).Place(&symbols).Place(&strings); + + ReadFile(&file, true, CPU_TYPE_ANY, 0); + + EXPECT_CALL(reporter, MisplacedSymbolTable()).Times(1); + EXPECT_FALSE(reader.WalkLoadCommands(&load_command_handler)); +} + diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_utilities.cc b/src/lib/crashdump/gbreakpad/common/mac/macho_utilities.cc new file mode 100644 index 0000000..89f9e77 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_utilities.cc @@ -0,0 +1,90 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// macho_utilties.cc: Utilities for dealing with mach-o files +// +// Author: Dave Camp + +#include "common/mac/byteswap.h" +#include "common/mac/macho_utilities.h" + +void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc, + enum NXByteOrder target_byte_order) +{ + uc->cmd = ByteSwap(uc->cmd); + uc->cmdsize = ByteSwap(uc->cmdsize); +} + +void breakpad_swap_segment_command_64(struct segment_command_64 *sg, + enum NXByteOrder target_byte_order) +{ + sg->cmd = ByteSwap(sg->cmd); + sg->cmdsize = ByteSwap(sg->cmdsize); + + sg->vmaddr = ByteSwap(sg->vmaddr); + sg->vmsize = ByteSwap(sg->vmsize); + sg->fileoff = ByteSwap(sg->fileoff); + sg->filesize = ByteSwap(sg->filesize); + + sg->maxprot = ByteSwap(sg->maxprot); + sg->initprot = ByteSwap(sg->initprot); + sg->nsects = ByteSwap(sg->nsects); + sg->flags = ByteSwap(sg->flags); +} + +void breakpad_swap_mach_header_64(struct mach_header_64 *mh, + enum NXByteOrder target_byte_order) +{ + mh->magic = ByteSwap(mh->magic); + mh->cputype = ByteSwap(mh->cputype); + mh->cpusubtype = ByteSwap(mh->cpusubtype); + mh->filetype = ByteSwap(mh->filetype); + mh->ncmds = ByteSwap(mh->ncmds); + mh->sizeofcmds = ByteSwap(mh->sizeofcmds); + mh->flags = ByteSwap(mh->flags); + mh->reserved = ByteSwap(mh->reserved); +} + +void breakpad_swap_section_64(struct section_64 *s, + uint32_t nsects, + enum NXByteOrder target_byte_order) +{ + for (uint32_t i = 0; i < nsects; i++) { + s[i].addr = ByteSwap(s[i].addr); + s[i].size = ByteSwap(s[i].size); + + s[i].offset = ByteSwap(s[i].offset); + s[i].align = ByteSwap(s[i].align); + s[i].reloff = ByteSwap(s[i].reloff); + s[i].nreloc = ByteSwap(s[i].nreloc); + s[i].flags = ByteSwap(s[i].flags); + s[i].reserved1 = ByteSwap(s[i].reserved1); + s[i].reserved2 = ByteSwap(s[i].reserved2); + } +} diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_utilities.h b/src/lib/crashdump/gbreakpad/common/mac/macho_utilities.h new file mode 100644 index 0000000..a07945f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_utilities.h @@ -0,0 +1,92 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// macho_utilities.h: Utilities for dealing with mach-o files +// +// Author: Dave Camp + +#ifndef COMMON_MAC_MACHO_UTILITIES_H__ +#define COMMON_MAC_MACHO_UTILITIES_H__ + +#include +#include + +/* Some #defines and structs that aren't defined in older SDKs */ +#ifndef CPU_ARCH_ABI64 +# define CPU_ARCH_ABI64 0x01000000 +#endif + +#ifndef CPU_TYPE_X86 +# define CPU_TYPE_X86 CPU_TYPE_I386 +#endif + +#ifndef CPU_TYPE_POWERPC64 +# define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) +#endif + +#ifndef LC_UUID +# define LC_UUID 0x1b /* the uuid */ +#endif + +#if TARGET_CPU_X86 +# define BREAKPAD_MACHINE_THREAD_STATE i386_THREAD_STATE +#elif TARGET_CPU_X86_64 +# define BREAKPAD_MACHINE_THREAD_STATE x86_THREAD_STATE64 +#else +# define BREAKPAD_MACHINE_THREAD_STATE MACHINE_THREAD_STATE +#endif + +// The uuid_command struct/swap routines were added during the 10.4 series. +// Their presence isn't guaranteed. +struct breakpad_uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; + +void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc, + enum NXByteOrder target_byte_order); + +// Older SDKs defines thread_state_data_t as an int[] instead +// of the natural_t[] it should be. +typedef natural_t breakpad_thread_state_data_t[THREAD_STATE_MAX]; + +// The 64-bit swap routines were added during the 10.4 series, their +// presence isn't guaranteed. +void breakpad_swap_segment_command_64(struct segment_command_64 *sg, + enum NXByteOrder target_byte_order); + +void breakpad_swap_mach_header_64(struct mach_header_64 *mh, + enum NXByteOrder target_byte_order); + +void breakpad_swap_section_64(struct section_64 *s, + uint32_t nsects, + enum NXByteOrder target_byte_order); + +#endif diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_walker.cc b/src/lib/crashdump/gbreakpad/common/mac/macho_walker.cc new file mode 100644 index 0000000..92da7b1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_walker.cc @@ -0,0 +1,267 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// macho_walker.cc: Iterate over the load commands in a mach-o file +// +// See macho_walker.h for documentation +// +// Author: Dan Waylonis + +extern "C" { // necessary for Leopard + #include + #include + #include + #include + #include + #include + #include +} + +#include "common/mac/byteswap.h" +#include "common/mac/macho_walker.h" +#include "common/mac/macho_utilities.h" + +namespace MacFileUtilities { + +MachoWalker::MachoWalker(const char *path, LoadCommandCallback callback, + void *context) + : file_(0), + memory_(NULL), + memory_size_(0), + callback_(callback), + callback_context_(context), + current_header_(NULL), + current_header_size_(0), + current_header_offset_(0) { + file_ = open(path, O_RDONLY); +} + +MachoWalker::MachoWalker(void *memory, size_t size, + LoadCommandCallback callback, void *context) + : file_(0), + memory_(memory), + memory_size_(size), + callback_(callback), + callback_context_(context), + current_header_(NULL), + current_header_size_(0), + current_header_offset_(0) { +} + +MachoWalker::~MachoWalker() { + if (file_ != -1) + close(file_); +} + +int MachoWalker::ValidateCPUType(int cpu_type) { + // If the user didn't specify, use the local architecture. + if (cpu_type == 0) { + const NXArchInfo *arch = NXGetLocalArchInfo(); + assert(arch); + cpu_type = arch->cputype; + } + + return cpu_type; +} + +bool MachoWalker::WalkHeader(int cpu_type) { + int valid_cpu_type = ValidateCPUType(cpu_type); + off_t offset; + if (FindHeader(valid_cpu_type, offset)) { + if (cpu_type & CPU_ARCH_ABI64) + return WalkHeader64AtOffset(offset); + + return WalkHeaderAtOffset(offset); + } + + return false; +} + +bool MachoWalker::ReadBytes(void *buffer, size_t size, off_t offset) { + if (memory_) { + if (offset < 0) + return false; + bool result = true; + if (offset + size > memory_size_) { + if (static_cast(offset) >= memory_size_) + return false; + size = memory_size_ - offset; + result = false; + } + memcpy(buffer, static_cast(memory_) + offset, size); + return result; + } else { + return pread(file_, buffer, size, offset) == (ssize_t)size; + } +} + +bool MachoWalker::CurrentHeader(struct mach_header_64 *header, off_t *offset) { + if (current_header_) { + memcpy(header, current_header_, sizeof(mach_header_64)); + *offset = current_header_offset_; + return true; + } + + return false; +} + +bool MachoWalker::FindHeader(int cpu_type, off_t &offset) { + int valid_cpu_type = ValidateCPUType(cpu_type); + // Read the magic bytes that's common amongst all mach-o files + uint32_t magic; + if (!ReadBytes(&magic, sizeof(magic), 0)) + return false; + + offset = sizeof(magic); + + // Figure out what type of file we've got + bool is_fat = false; + if (magic == FAT_MAGIC || magic == FAT_CIGAM) { + is_fat = true; + } + else if (magic != MH_MAGIC && magic != MH_CIGAM && magic != MH_MAGIC_64 && + magic != MH_CIGAM_64) { + return false; + } + + if (!is_fat) { + // If we don't have a fat header, check if the cpu type matches the single + // header + cpu_type_t header_cpu_type; + if (!ReadBytes(&header_cpu_type, sizeof(header_cpu_type), offset)) + return false; + + if (magic == MH_CIGAM || magic == MH_CIGAM_64) + header_cpu_type = ByteSwap(header_cpu_type); + + if (valid_cpu_type != header_cpu_type) + return false; + + offset = 0; + return true; + } else { + // Read the fat header and find an appropriate architecture + offset = 0; + struct fat_header fat; + if (!ReadBytes(&fat, sizeof(fat), offset)) + return false; + + if (NXHostByteOrder() != NX_BigEndian) + swap_fat_header(&fat, NXHostByteOrder()); + + offset += sizeof(fat); + + // Search each architecture for the desired one + struct fat_arch arch; + for (uint32_t i = 0; i < fat.nfat_arch; ++i) { + if (!ReadBytes(&arch, sizeof(arch), offset)) + return false; + + if (NXHostByteOrder() != NX_BigEndian) + swap_fat_arch(&arch, 1, NXHostByteOrder()); + + if (arch.cputype == valid_cpu_type) { + offset = arch.offset; + return true; + } + + offset += sizeof(arch); + } + } + + return false; +} + +bool MachoWalker::WalkHeaderAtOffset(off_t offset) { + struct mach_header header; + if (!ReadBytes(&header, sizeof(header), offset)) + return false; + + bool swap = (header.magic == MH_CIGAM); + if (swap) + swap_mach_header(&header, NXHostByteOrder()); + + // Copy the data into the mach_header_64 structure. Since the 32-bit and + // 64-bit only differ in the last field (reserved), this is safe to do. + struct mach_header_64 header64; + memcpy((void *)&header64, (const void *)&header, sizeof(header)); + header64.reserved = 0; + + current_header_ = &header64; + current_header_size_ = sizeof(header); // 32-bit, not 64-bit + current_header_offset_ = offset; + offset += current_header_size_; + bool result = WalkHeaderCore(offset, header.ncmds, swap); + current_header_ = NULL; + current_header_size_ = 0; + current_header_offset_ = 0; + return result; +} + +bool MachoWalker::WalkHeader64AtOffset(off_t offset) { + struct mach_header_64 header; + if (!ReadBytes(&header, sizeof(header), offset)) + return false; + + bool swap = (header.magic == MH_CIGAM_64); + if (swap) + breakpad_swap_mach_header_64(&header, NXHostByteOrder()); + + current_header_ = &header; + current_header_size_ = sizeof(header); + current_header_offset_ = offset; + offset += current_header_size_; + bool result = WalkHeaderCore(offset, header.ncmds, swap); + current_header_ = NULL; + current_header_size_ = 0; + current_header_offset_ = 0; + return result; +} + +bool MachoWalker::WalkHeaderCore(off_t offset, uint32_t number_of_commands, + bool swap) { + for (uint32_t i = 0; i < number_of_commands; ++i) { + struct load_command cmd; + if (!ReadBytes(&cmd, sizeof(cmd), offset)) + return false; + + if (swap) + swap_load_command(&cmd, NXHostByteOrder()); + + // Call the user callback + if (callback_ && !callback_(this, &cmd, offset, swap, callback_context_)) + break; + + offset += cmd.cmdsize; + } + + return true; +} + +} // namespace MacFileUtilities diff --git a/src/lib/crashdump/gbreakpad/common/mac/macho_walker.h b/src/lib/crashdump/gbreakpad/common/mac/macho_walker.h new file mode 100644 index 0000000..cee3eb8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/macho_walker.h @@ -0,0 +1,117 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// macho_walker.h: Iterate over the load commands in a mach-o file +// +// Author: Dan Waylonis + +#ifndef COMMON_MAC_MACHO_WALKER_H__ +#define COMMON_MAC_MACHO_WALKER_H__ + +#include +#include + +namespace MacFileUtilities { + +class MachoWalker { + public: + // A callback function executed when a new load command is read. If no + // further processing of load commands is desired, return false. Otherwise, + // return true. + // |cmd| is the current command, and |offset| is the location relative to the + // beginning of the file (not header) where the command was read. If |swap| + // is set, then any command data (other than the returned load_command) should + // be swapped when read + typedef bool (*LoadCommandCallback)(MachoWalker *walker, load_command *cmd, + off_t offset, bool swap, void *context); + + MachoWalker(const char *path, LoadCommandCallback callback, void *context); + MachoWalker(void *memory, size_t size, LoadCommandCallback callback, + void *context); + ~MachoWalker(); + + // Begin walking the header for |cpu_type|. If |cpu_type| is 0, then the + // native cpu type is used. Otherwise, accepted values are listed in + // /usr/include/mach/machine.h (e.g., CPU_TYPE_X86 or CPU_TYPE_POWERPC). + // Returns false if opening the file failed or if the |cpu_type| is not + // present in the file. + bool WalkHeader(int cpu_type); + + // Locate (if any) the header offset for |cpu_type| and return in |offset|. + // Return true if found, false otherwise. + bool FindHeader(int cpu_type, off_t &offset); + + // Read |size| bytes from the opened file at |offset| into |buffer| + bool ReadBytes(void *buffer, size_t size, off_t offset); + + // Return the current header and header offset + bool CurrentHeader(struct mach_header_64 *header, off_t *offset); + + private: + // Validate the |cpu_type| + int ValidateCPUType(int cpu_type); + + // Process an individual header starting at |offset| from the start of the + // file. Return true if successful, false otherwise. + bool WalkHeaderAtOffset(off_t offset); + bool WalkHeader64AtOffset(off_t offset); + + // Bottleneck for walking the load commands + bool WalkHeaderCore(off_t offset, uint32_t number_of_commands, bool swap); + + // File descriptor to the opened file + int file_; + + // Memory location to read from. + void *memory_; + + // Size of the memory segment we can read from. + size_t memory_size_; + + // User specified callback & context + LoadCommandCallback callback_; + void *callback_context_; + + // Current header, size, and offset. The mach_header_64 is used for both + // 32-bit and 64-bit headers because they only differ in their last field + // (reserved). By adding the |current_header_size_| and the + // |current_header_offset_|, you can determine the offset in the file just + // after the header. + struct mach_header_64 *current_header_; + unsigned long current_header_size_; + off_t current_header_offset_; + + private: + MachoWalker(const MachoWalker &); + MachoWalker &operator=(const MachoWalker &); +}; + +} // namespace MacFileUtilities + +#endif // COMMON_MAC_MACHO_WALKER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/mac/scoped_task_suspend-inl.h b/src/lib/crashdump/gbreakpad/common/mac/scoped_task_suspend-inl.h new file mode 100644 index 0000000..d6d1bef --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/scoped_task_suspend-inl.h @@ -0,0 +1,56 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Inline implementation of ScopedTaskSuspend, which suspends a Mach +// task for the duration of its scope. + +#ifndef GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_ +#define GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_ + +#include + +namespace google_breakpad { + +class ScopedTaskSuspend { + public: + explicit ScopedTaskSuspend(mach_port_t target) : target_(target) { + task_suspend(target_); + } + + ~ScopedTaskSuspend() { + task_resume(target_); + } + + private: + mach_port_t target_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_MAC_SCOPED_TASK_SUSPEND_H_ diff --git a/src/lib/crashdump/gbreakpad/common/mac/string_utilities.cc b/src/lib/crashdump/gbreakpad/common/mac/string_utilities.cc new file mode 100644 index 0000000..e1f63a9 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/string_utilities.cc @@ -0,0 +1,84 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "processor/scoped_ptr.h" +#include "common/mac/string_utilities.h" + +namespace MacStringUtils { + +using google_breakpad::scoped_array; + +std::string ConvertToString(CFStringRef str) { + CFIndex length = CFStringGetLength(str); + std::string result; + + if (!length) + return result; + + CFIndex maxUTF8Length = + CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); + scoped_array buffer(new UInt8[maxUTF8Length + 1]); + CFIndex actualUTF8Length; + CFStringGetBytes(str, CFRangeMake(0, length), kCFStringEncodingUTF8, 0, + false, buffer.get(), maxUTF8Length, &actualUTF8Length); + buffer[actualUTF8Length] = 0; + result.assign((const char *)buffer.get()); + + return result; +} + +unsigned int IntegerValueAtIndex(string &str, unsigned int idx) { + string digits("0123456789"), temp; + size_t start = 0; + size_t end; + size_t found = 0; + unsigned int result = 0; + + for (; found <= idx; ++found) { + end = str.find_first_not_of(digits, start); + + if (end == string::npos) + end = str.size(); + + temp = str.substr(start, end - start); + + if (found == idx) { + result = atoi(temp.c_str()); + } + + start = str.find_first_of(digits, end + 1); + + if (start == string::npos) + break; + } + + return result; +} + +} // namespace MacStringUtils diff --git a/src/lib/crashdump/gbreakpad/common/mac/string_utilities.h b/src/lib/crashdump/gbreakpad/common/mac/string_utilities.h new file mode 100644 index 0000000..6d89c83 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/string_utilities.h @@ -0,0 +1,52 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// string_utilities.h: Utilities for strings for Mac platform + +#ifndef COMMON_MAC_STRING_UTILITIES_H__ +#define COMMON_MAC_STRING_UTILITIES_H__ + +#include + +#include + +namespace MacStringUtils { + +using std::string; + +// Convert a CoreFoundation string into a std::string +string ConvertToString(CFStringRef str); + +// Return the idx'th decimal integer in str, separated by non-decimal-digits +// E.g., str = 10.4.8, idx = 1 -> 4 +unsigned int IntegerValueAtIndex(string &str, unsigned int idx); + +} // namespace MacStringUtils + +#endif // COMMON_MAC_STRING_UTILITIES_H__ diff --git a/src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.h b/src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.h new file mode 100644 index 0000000..d425f59 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.h @@ -0,0 +1,1004 @@ +// +// GTMSenTestCase.h +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +// Portions of this file fall under the following license, marked with +// SENTE_BEGIN - SENTE_END +// +// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. +// +// Use of this source code is governed by the following license: +// +// 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 COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL Sente SA 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. +// +// Note: this license is equivalent to the FreeBSD license. +// +// This notice may not be removed from this file. + +// Some extra test case macros that would have been convenient for SenTestingKit +// to provide. I didn't stick GTM in front of the Macro names, so that they would +// be easy to remember. + +#import "GTMDefines.h" + +#if (!GTM_IPHONE_SDK) +#import +#else +#import +NSString *STComposeString(NSString *, ...); +#endif + +// Generates a failure when a1 != noErr +// Args: +// a1: should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNoErr(a1, description, ...) \ +do { \ + @try {\ + OSStatus a1value = (a1); \ + if (a1value != noErr) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 != a2 +// Args: +// a1: received value. Should be either an OSErr or an OSStatus +// a2: expected value. Should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertErr(a1, a2, description, ...) \ +do { \ + @try {\ + OSStatus a1value = (a1); \ + OSStatus a2value = (a2); \ + if (a1value != a2value) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +// Generates a failure when a1 is NULL +// Args: +// a1: should be a pointer (use STAssertNotNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotNULL(a1, description, ...) \ +do { \ + @try {\ + const void* a1value = (a1); \ + if (a1value == NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not NULL +// Args: +// a1: should be a pointer (use STAssertNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNULL(a1, description, ...) \ +do { \ + @try {\ + const void* a1value = (a1); \ + if (a1value != NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for C scalars, +// structs and unions. +// Args: +// a1: argument 1 +// a2: argument 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEquals(a1, a2, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + if ([a1encoded isEqualToValue:a2encoded]) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for objects. +// Args: +// a1: argument 1. object. +// a2: argument 2. object. +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualObjects(a1, a2, desc, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ( (@encode(__typeof__(a1value)) == @encode(id)) && \ + (@encode(__typeof__(a2value)) == @encode(id)) && \ + ![(id)a1value isEqual:(id)a2value] ) continue; \ + NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \ + if (desc) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(desc, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(desc, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not 'op' to a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertOperation(a1, a2, op, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + if (!(a1value op a2value)) { \ + double a1DoubleValue = a1value; \ + double a2DoubleValue = a2value; \ + NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException \ + failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not > a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not >= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >=, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not < a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not <= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <=, description, ##__VA_ARGS__) + +// Generates a failure when string a1 is not equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualStrings(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] == NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when string a1 is equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualStrings(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] != NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when c-string a1 is not equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualCStrings(a1, a2, description, ...) \ +do { \ + @try {\ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (a1value == a2value) continue; \ + if (strcmp(a1value, a2value) == 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ + andObject: [NSString stringWithUTF8String:a2value] \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when c-string a1 is equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualCStrings(a1, a2, description, ...) \ +do { \ + @try {\ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (strcmp(a1value, a2value) != 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ + andObject: [NSString stringWithUTF8String:a2value] \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +#if GTM_IPHONE_SDK + +// SENTE_BEGIN +/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false + (or one is nil and the other is not). + _{a1 The object on the left.} + _{a2 The object on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEqualObjects(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ( (@encode(__typeof__(a1value)) == @encode(id)) && \ + (@encode(__typeof__(a2value)) == @encode(id)) && \ + [(id)a1value isEqual: (id)a2value] ) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when a1 is not equal to a2. This test is for + C scalars, structs and unions. + _{a1 The argument on the left.} + _{a2 The argument on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEquals(a1, a2, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType: @encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType: @encode(__typeof__(a2))]; \ + if (![a1encoded isEqualToValue:a2encoded]) { \ + [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \ + andValue: a2encoded \ + withAccuracy: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right)) + + +/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false. + This test is for scalars such as floats and doubles where small differences + could make these items not exactly equal, but also works for all scalars. + _{a1 The scalar on the left.} + _{a2 The scalar on the right.} + _{accuracy The maximum difference between a1 and a2 for these values to be + considered equal.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ + +#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + __typeof__(accuracy) accuracyvalue = (accuracy); \ + if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \ + [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \ + andValue: a2encoded \ + withAccuracy: accuracyencoded \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + + +/*" Generates a failure unconditionally. + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STFail(description, ...) \ +[self failWithException:[NSException failureInFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]] + + + +/*" Generates a failure when a1 is not nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNil(a1, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + if (a1value != nil) { \ + NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when a1 is nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNotNil(a1, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + if (a1value == nil) { \ + NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when expression evaluates to false. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrue(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr);\ + if (!_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when expression evaluates to false and in addition will + generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrueNoThrow(expr, description, ...) \ +do { \ + @try {\ + BOOL _evaluatedExpression = (expr);\ + if (!_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when the expression evaluates to true. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalse(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr);\ + if (_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: YES \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when the expression evaluates to true and in addition + will generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalseNoThrow(expr, description, ...) \ +do { \ + @try {\ + BOOL _evaluatedExpression = (expr);\ + if (_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: YES \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when expression does not throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent. +"*/ +#define STAssertThrows(expr, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (id anException) { \ + continue; \ + }\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class. + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertThrowsSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + continue; \ + }\ + @catch (id anException) {\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString: [anException name]]) continue; \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + @catch (id anException) {\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrow(expr, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when expression does throw an exception of the specitied + class. Any other exception is okay (i.e. does not generate a failure). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrowSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anythingElse) {\ + ; \ + }\ +} while (0) + + +/*" Generates a failure when expression does throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString: [anException name]]) { \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + } \ + continue; \ + }\ + @catch (id anythingElse) {\ + ; \ + }\ +} while (0) + + + +@interface NSException (GTMSenTestAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int) ineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; +@end + +// SENTE_END + +@interface SenTestCase : NSObject { + SEL currentSelector_; +} + +- (void)setUp; +- (void)invokeTest; +- (void)tearDown; +- (void)performTest:(SEL)sel; +- (void)failWithException:(NSException*)exception; +@end + +GTM_EXTERN NSString *const SenTestFailureException; + +GTM_EXTERN NSString *const SenTestFilenameKey; +GTM_EXTERN NSString *const SenTestLineNumberKey; + +#endif // GTM_IPHONE_SDK + +// All unittest cases in GTM should inherit from GTMTestCase. It makes sure +// to set up our logging system correctly to verify logging calls. +// See GTMUnitTestDevLog.h for details +@interface GTMTestCase : SenTestCase +@end diff --git a/src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.m b/src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.m new file mode 100644 index 0000000..99b9db0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/mac/testing/GTMSenTestCase.m @@ -0,0 +1,366 @@ +// +// GTMSenTestCase.m +// +// Copyright 2007-2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMSenTestCase.h" +#import + +#if !GTM_IPHONE_SDK +#import "GTMGarbageCollection.h" +#endif // !GTM_IPHONE_SDK + +#if GTM_IPHONE_SDK +#import + +@interface NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason; +@end + +@implementation NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason { + NSDictionary *userInfo = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, + filename, SenTestFilenameKey, + nil]; + + return [self exceptionWithName:SenTestFailureException + reason:reason + userInfo:userInfo]; +} +@end + +@implementation NSException (GTMSenTestAdditions) + ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = testDescription; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", + condition, isTrue ? "TRUE" : "FALSE", testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + [left description], [right description], testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if (accuracy) { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + left, right, testDescription]; + } else { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", + left, right, accuracy, testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", + expression, testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if ([[exception name] isEqualToString:SenTestFailureException]) { + // it's our exception, assume it has the right description on it. + reason = [exception reason]; + } else { + // not one of our exception, use the exceptions reason and our description + reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", + expression, [exception reason], testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + +@end + +NSString *STComposeString(NSString *formatString, ...) { + NSString *reason = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + reason = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + return reason; +} + +NSString *const SenTestFailureException = @"SenTestFailureException"; +NSString *const SenTestFilenameKey = @"SenTestFilenameKey"; +NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; + +@interface SenTestCase (SenTestCasePrivate) +// our method of logging errors ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name; +@end + +@implementation SenTestCase +- (void)failWithException:(NSException*)exception { + [exception raise]; +} + +- (void)setUp { +} + +- (void)performTest:(SEL)sel { + currentSelector_ = sel; + @try { + [self invokeTest]; + } @catch (NSException *exception) { + [[self class] printException:exception + fromTestName:NSStringFromSelector(sel)]; + [exception raise]; + } +} + ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name { + NSDictionary *userInfo = [exception userInfo]; + NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; + NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; + NSString *className = NSStringFromClass([self class]); + if ([filename length] == 0) { + filename = @"Unknown.m"; + } + fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", + [filename UTF8String], + (long)[lineNumber integerValue], + [className UTF8String], + [name UTF8String], + [[exception reason] UTF8String]); + fflush(stderr); +} + +- (void)invokeTest { + NSException *e = nil; + @try { + // Wrap things in autorelease pools because they may + // have an STMacro in their dealloc which may get called + // when the pool is cleaned up + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + // We don't log exceptions here, instead we let the person that called + // this log the exception. This ensures they are only logged once but the + // outer layers get the exceptions to report counts, etc. + @try { + [self setUp]; + @try { + [self performSelector:currentSelector_]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [self tearDown]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [pool release]; + } @catch (NSException *exception) { + e = [exception retain]; + } + if (e) { + [e autorelease]; + [e raise]; + } +} + +- (void)tearDown { +} + +- (NSString *)description { + // This matches the description OCUnit would return to you + return [NSString stringWithFormat:@"-[%@ %@]", [self class], + NSStringFromSelector(currentSelector_)]; +} +@end + +#endif // GTM_IPHONE_SDK + +@implementation GTMTestCase : SenTestCase +- (void)invokeTest { + Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); + if (devLogClass) { + [devLogClass performSelector:@selector(enableTracking)]; + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + + } + [super invokeTest]; + if (devLogClass) { + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + [devLogClass performSelector:@selector(disableTracking)]; + } +} +@end + +// Leak detection +#if !GTM_IPHONE_DEVICE +// Don't want to get leaks on the iPhone Device as the device doesn't +// have 'leaks'. The simulator does though. + +// COV_NF_START +// We don't have leak checking on by default, so this won't be hit. +static void _GTMRunLeaks(void) { + // This is an atexit handler. It runs leaks for us to check if we are + // leaking anything in our tests. + const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE"); + NSMutableString *exclusions = [NSMutableString string]; + if (cExclusionsEnv) { + NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv]; + NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","]; + NSString *exclusion; + NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet]; + GTM_FOREACH_OBJECT(exclusion, exclusionsArray) { + exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet]; + [exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; + } + } + NSString *string + = [NSString stringWithFormat:@"/usr/bin/leaks %@%d" + @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'", + exclusions, getpid()]; + int ret = system([string UTF8String]); + if (ret) { + fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d", + __FILE__, __LINE__, ret); + fflush(stderr); + } +} +// COV_NF_END + +static __attribute__((constructor)) void _GTMInstallLeaks(void) { + BOOL checkLeaks = YES; +#if !GTM_IPHONE_SDK + checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES; +#endif // !GTM_IPHONE_SDK + if (checkLeaks) { + checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO; + if (checkLeaks) { + // COV_NF_START + // We don't have leak checking on by default, so this won't be hit. + fprintf(stderr, "Leak Checking Enabled\n"); + fflush(stderr); + int ret = atexit(&_GTMRunLeaks); + _GTMDevAssert(ret == 0, + @"Unable to install _GTMRunLeaks as an atexit handler (%d)", + errno); + // COV_NF_END + } + } +} + +#endif // !GTM_IPHONE_DEVICE diff --git a/src/lib/crashdump/gbreakpad/common/md5.cc b/src/lib/crashdump/gbreakpad/common/md5.cc new file mode 100644 index 0000000..bccf61c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/md5.cc @@ -0,0 +1,251 @@ +/* + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include + +#include "common/md5.h" + +namespace google_breakpad { + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +static void MD5Transform(u32 buf[4], u32 const in[16]); + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +} // namespace google_breakpad + diff --git a/src/lib/crashdump/gbreakpad/common/md5.h b/src/lib/crashdump/gbreakpad/common/md5.h new file mode 100644 index 0000000..e96521e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/md5.h @@ -0,0 +1,27 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: liuli@google.com (Liu Li) +#ifndef COMMON_MD5_H__ +#define COMMON_MD5_H__ + +#include + +namespace google_breakpad { + +typedef uint32_t u32; +typedef uint8_t u8; + +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +void MD5Init(struct MD5Context *ctx); + +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len); + +void MD5Final(unsigned char digest[16], struct MD5Context *ctx); + +} // namespace google_breakpad + +#endif // COMMON_MD5_H__ diff --git a/src/lib/crashdump/gbreakpad/common/memory.h b/src/lib/crashdump/gbreakpad/common/memory.h new file mode 100644 index 0000000..e90bd52 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/memory.h @@ -0,0 +1,218 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 GOOGLE_BREAKPAD_COMMON_MEMORY_H_ +#define GOOGLE_BREAKPAD_COMMON_MEMORY_H_ + +#include +#include +#include +#include + +#ifdef __APPLE__ +#define sys_mmap mmap +#define sys_mmap2 mmap +#define sys_munmap munmap +#define MAP_ANONYMOUS MAP_ANON +#else +#include "third_party/lss/linux_syscall_support.h" +#endif + +namespace google_breakpad { + +// This is very simple allocator which fetches pages from the kernel directly. +// Thus, it can be used even when the heap may be corrupted. +// +// There is no free operation. The pages are only freed when the object is +// destroyed. +class PageAllocator { + public: + PageAllocator() + : page_size_(getpagesize()), + last_(NULL), + current_page_(NULL), + page_offset_(0) { + } + + ~PageAllocator() { + FreeAll(); + } + + void *Alloc(unsigned bytes) { + if (!bytes) + return NULL; + + if (current_page_ && page_size_ - page_offset_ >= bytes) { + uint8_t *const ret = current_page_ + page_offset_; + page_offset_ += bytes; + if (page_offset_ == page_size_) { + page_offset_ = 0; + current_page_ = NULL; + } + + return ret; + } + + const unsigned pages = + (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_; + uint8_t *const ret = GetNPages(pages); + if (!ret) + return NULL; + + page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_; + current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL; + + return ret + sizeof(PageHeader); + } + + private: + uint8_t *GetNPages(unsigned num_pages) { +#ifdef __x86_64 + void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#else + void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (a == MAP_FAILED) + return NULL; + + struct PageHeader *header = reinterpret_cast(a); + header->next = last_; + header->num_pages = num_pages; + last_ = header; + + return reinterpret_cast(a); + } + + void FreeAll() { + PageHeader *next; + + for (PageHeader *cur = last_; cur; cur = next) { + next = cur->next; + sys_munmap(cur, cur->num_pages * page_size_); + } + } + + struct PageHeader { + PageHeader *next; // pointer to the start of the next set of pages. + unsigned num_pages; // the number of pages in this set. + }; + + const unsigned page_size_; + PageHeader *last_; + uint8_t *current_page_; + unsigned page_offset_; +}; + +// A wasteful vector is like a normal std::vector, except that it's very much +// simplier and it allocates memory from a PageAllocator. It's wasteful +// because, when resizing, it always allocates a whole new array since the +// PageAllocator doesn't support realloc. +template +class wasteful_vector { + public: + wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16) + : allocator_(allocator), + a_((T*) allocator->Alloc(sizeof(T) * size_hint)), + allocated_(size_hint), + used_(0) { + } + + T& back() { + return a_[used_ - 1]; + } + + const T& back() const { + return a_[used_ - 1]; + } + + bool empty() const { + return used_ == 0; + } + + void push_back(const T& new_element) { + if (used_ == allocated_) + Realloc(allocated_ * 2); + a_[used_++] = new_element; + } + + size_t size() const { + return used_; + } + + void resize(unsigned sz, T c = T()) { + // No need to test "sz >= 0", as "sz" is unsigned. + if (sz <= used_) { + used_ = sz; + } else { + unsigned a = allocated_; + if (sz > a) { + while (sz > a) { + a *= 2; + } + Realloc(a); + } + while (sz > used_) { + a_[used_++] = c; + } + } + } + + T& operator[](size_t index) { + return a_[index]; + } + + const T& operator[](size_t index) const { + return a_[index]; + } + + private: + void Realloc(unsigned new_size) { + T *new_array = + reinterpret_cast(allocator_->Alloc(sizeof(T) * new_size)); + memcpy(new_array, a_, used_ * sizeof(T)); + a_ = new_array; + allocated_ = new_size; + } + + PageAllocator *const allocator_; + T *a_; // pointer to an array of |allocated_| elements. + unsigned allocated_; // size of |a_|, in elements. + unsigned used_; // number of used slots in |a_|. +}; + +} // namespace google_breakpad + +inline void* operator new(size_t nbytes, + google_breakpad::PageAllocator& allocator) { + return allocator.Alloc(nbytes); +} + +#endif // GOOGLE_BREAKPAD_COMMON_MEMORY_H_ diff --git a/src/lib/crashdump/gbreakpad/common/memory_range.h b/src/lib/crashdump/gbreakpad/common/memory_range.h new file mode 100644 index 0000000..86bd08c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/memory_range.h @@ -0,0 +1,145 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// memory_range.h: Define the google_breakpad::MemoryRange class, which +// is a lightweight wrapper with a pointer and a length to encapsulate +// a contiguous range of memory. + +#ifndef COMMON_MEMORY_RANGE_H_ +#define COMMON_MEMORY_RANGE_H_ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +// A lightweight wrapper with a pointer and a length to encapsulate a +// contiguous range of memory. It provides helper methods for checked +// access of a subrange of the memory. Its implemementation does not +// allocate memory or call into libc functions, and is thus safer to use +// in a crashed environment. +class MemoryRange { + public: + MemoryRange() : data_(NULL), length_(0) {} + + MemoryRange(const void* data, size_t length) { + Set(data, length); + } + + // Returns true if this memory range contains no data. + bool IsEmpty() const { + // Set() guarantees that |length_| is zero if |data_| is NULL. + return length_ == 0; + } + + // Resets to an empty range. + void Reset() { + data_ = NULL; + length_ = 0; + } + + // Sets this memory range to point to |data| and its length to |length|. + void Set(const void* data, size_t length) { + data_ = reinterpret_cast(data); + // Always set |length_| to zero if |data_| is NULL. + length_ = data ? length : 0; + } + + // Returns true if this range covers a subrange of |sub_length| bytes + // at |sub_offset| bytes of this memory range, or false otherwise. + bool Covers(size_t sub_offset, size_t sub_length) const { + // The following checks verify that: + // 1. sub_offset is within [ 0 .. length_ - 1 ] + // 2. sub_offset + sub_length is within + // [ sub_offset .. length_ ] + return sub_offset < length_ && + sub_offset + sub_length >= sub_offset && + sub_offset + sub_length <= length_; + } + + // Returns a raw data pointer to a subrange of |sub_length| bytes at + // |sub_offset| bytes of this memory range, or NULL if the subrange + // is out of bounds. + const void* GetData(size_t sub_offset, size_t sub_length) const { + return Covers(sub_offset, sub_length) ? (data_ + sub_offset) : NULL; + } + + // Same as the two-argument version of GetData() but uses sizeof(DataType) + // as the subrange length and returns an |DataType| pointer for convenience. + template + const DataType* GetData(size_t sub_offset) const { + return reinterpret_cast( + GetData(sub_offset, sizeof(DataType))); + } + + // Returns a raw pointer to the |element_index|-th element of an array + // of elements of length |element_size| starting at |sub_offset| bytes + // of this memory range, or NULL if the element is out of bounds. + const void* GetArrayElement(size_t element_offset, + size_t element_size, + unsigned element_index) const { + size_t sub_offset = element_offset + element_index * element_size; + return GetData(sub_offset, element_size); + } + + // Same as the three-argument version of GetArrayElement() but deduces + // the element size using sizeof(ElementType) and returns an |ElementType| + // pointer for convenience. + template + const ElementType* GetArrayElement(size_t element_offset, + unsigned element_index) const { + return reinterpret_cast( + GetArrayElement(element_offset, sizeof(ElementType), element_index)); + } + + // Returns a subrange of |sub_length| bytes at |sub_offset| bytes of + // this memory range, or an empty range if the subrange is out of bounds. + MemoryRange Subrange(size_t sub_offset, size_t sub_length) const { + return Covers(sub_offset, sub_length) ? + MemoryRange(data_ + sub_offset, sub_length) : MemoryRange(); + } + + // Returns a pointer to the beginning of this memory range. + const u_int8_t* data() const { return data_; } + + // Returns the length, in bytes, of this memory range. + size_t length() const { return length_; } + + private: + // Pointer to the beginning of this memory range. + const u_int8_t* data_; + + // Length, in bytes, of this memory range. + size_t length_; +}; + +} // namespace google_breakpad + +#endif // COMMON_MEMORY_RANGE_H_ diff --git a/src/lib/crashdump/gbreakpad/common/memory_range_unittest.cc b/src/lib/crashdump/gbreakpad/common/memory_range_unittest.cc new file mode 100644 index 0000000..f0831e8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/memory_range_unittest.cc @@ -0,0 +1,193 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// memory_range_unittest.cc: Unit tests for google_breakpad::MemoryRange. + +#include "breakpad_googletest_includes.h" +#include "common/memory_range.h" + +using google_breakpad::MemoryRange; +using testing::Message; + +namespace { + +const u_int32_t kBuffer[10] = { 0 }; +const size_t kBufferSize = sizeof(kBuffer); +const u_int8_t* kBufferPointer = reinterpret_cast(kBuffer); + +// Test vectors for verifying Covers, GetData, and Subrange. +const struct { + bool valid; + size_t offset; + size_t length; +} kSubranges[] = { + { true, 0, 0 }, + { true, 0, 2 }, + { true, 0, kBufferSize }, + { true, 2, 0 }, + { true, 2, 4 }, + { true, 2, kBufferSize - 2 }, + { true, kBufferSize - 1, 1 }, + { false, kBufferSize, 0 }, + { false, kBufferSize, -1 }, + { false, kBufferSize + 1, 0 }, + { false, -1, 2 }, + { false, 1, kBufferSize }, + { false, kBufferSize - 1, 2 }, + { false, 0, -1 }, + { false, 1, -1 }, +}; +const size_t kNumSubranges = sizeof(kSubranges) / sizeof(kSubranges[0]); + +// Test vectors for verifying GetArrayElement. +const struct { + size_t offset; + size_t size; + size_t index; + const void* const pointer; +} kElements[] = { + // Valid array elemenets + { 0, 1, 0, kBufferPointer }, + { 0, 1, 1, kBufferPointer + 1 }, + { 0, 1, kBufferSize - 1, kBufferPointer + kBufferSize - 1 }, + { 0, 2, 1, kBufferPointer + 2 }, + { 0, 4, 2, kBufferPointer + 8 }, + { 0, 4, 9, kBufferPointer + 36 }, + { kBufferSize - 1, 1, 0, kBufferPointer + kBufferSize - 1 }, + // Invalid array elemenets + { 0, 1, kBufferSize, NULL }, + { 0, 4, 10, NULL }, + { kBufferSize - 1, 1, 1, NULL }, + { kBufferSize - 1, 2, 0, NULL }, + { kBufferSize, 1, 0, NULL }, +}; +const size_t kNumElements = sizeof(kElements) / sizeof(kElements[0]); + +} // namespace + +TEST(MemoryRangeTest, DefaultConstructor) { + MemoryRange range; + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); +} + +TEST(MemoryRangeTest, ConstructorWithDataAndLength) { + MemoryRange range(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); +} + +TEST(MemoryRangeTest, Reset) { + MemoryRange range; + range.Reset(); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); + + range.Set(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); + + range.Reset(); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); +} + +TEST(MemoryRangeTest, Set) { + MemoryRange range; + range.Set(kBuffer, kBufferSize); + EXPECT_EQ(kBufferPointer, range.data()); + EXPECT_EQ(kBufferSize, range.length()); + + range.Set(NULL, 0); + EXPECT_EQ(NULL, range.data()); + EXPECT_EQ(0, range.length()); +} + +TEST(MemoryRangeTest, SubrangeOfEmptyMemoryRange) { + MemoryRange range; + MemoryRange subrange = range.Subrange(0, 10); + EXPECT_EQ(NULL, subrange.data()); + EXPECT_EQ(0, subrange.length()); +} + +TEST(MemoryRangeTest, SubrangeAndGetData) { + MemoryRange range(kBuffer, kBufferSize); + for (size_t i = 0; i < kNumSubranges; ++i) { + bool valid = kSubranges[i].valid; + size_t sub_offset = kSubranges[i].offset; + size_t sub_length = kSubranges[i].length; + SCOPED_TRACE(Message() << "offset=" << sub_offset + << ", length=" << sub_length); + + MemoryRange subrange = range.Subrange(sub_offset, sub_length); + if (valid) { + EXPECT_TRUE(range.Covers(sub_offset, sub_length)); + EXPECT_EQ(kBufferPointer + sub_offset, + range.GetData(sub_offset, sub_length)); + EXPECT_EQ(kBufferPointer + sub_offset, subrange.data()); + EXPECT_EQ(sub_length, subrange.length()); + } else { + EXPECT_FALSE(range.Covers(sub_offset, sub_length)); + EXPECT_EQ(NULL, range.GetData(sub_offset, sub_length)); + EXPECT_EQ(NULL, subrange.data()); + EXPECT_EQ(0, subrange.length()); + } + } +} + +TEST(MemoryRangeTest, GetDataWithTemplateType) { + MemoryRange range(kBuffer, kBufferSize); + const char* char_pointer = range.GetData(0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), char_pointer); + const int* int_pointer = range.GetData(0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), int_pointer); +} + +TEST(MemoryRangeTest, GetArrayElement) { + MemoryRange range(kBuffer, kBufferSize); + for (size_t i = 0; i < kNumElements; ++i) { + size_t element_offset = kElements[i].offset; + size_t element_size = kElements[i].size; + unsigned element_index = kElements[i].index; + const void* const element_pointer = kElements[i].pointer; + SCOPED_TRACE(Message() << "offset=" << element_offset + << ", size=" << element_size + << ", index=" << element_index); + EXPECT_EQ(element_pointer, range.GetArrayElement( + element_offset, element_size, element_index)); + } +} + +TEST(MemoryRangeTest, GetArrayElmentWithTemplateType) { + MemoryRange range(kBuffer, kBufferSize); + const char* char_pointer = range.GetArrayElement(0, 0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), char_pointer); + const int* int_pointer = range.GetArrayElement(0, 0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), int_pointer); +} diff --git a/src/lib/crashdump/gbreakpad/common/memory_unittest.cc b/src/lib/crashdump/gbreakpad/common/memory_unittest.cc new file mode 100644 index 0000000..d580c1f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/memory_unittest.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2009, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/memory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test PageAllocatorTest; +} + +TEST(PageAllocatorTest, Setup) { + PageAllocator allocator; +} + +TEST(PageAllocatorTest, SmallObjects) { + PageAllocator allocator; + + for (unsigned i = 1; i < 1024; ++i) { + uint8_t *p = reinterpret_cast(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +TEST(PageAllocatorTest, LargeObject) { + PageAllocator allocator; + + uint8_t *p = reinterpret_cast(allocator.Alloc(10000)); + ASSERT_FALSE(p == NULL); + for (unsigned i = 1; i < 10; ++i) { + uint8_t *p = reinterpret_cast(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +namespace { +typedef testing::Test WastefulVectorTest; +} + +TEST(WastefulVectorTest, Setup) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + ASSERT_TRUE(v.empty()); + ASSERT_EQ(v.size(), 0u); +} + +TEST(WastefulVectorTest, Simple) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + + for (unsigned i = 0; i < 256; ++i) { + v.push_back(i); + ASSERT_EQ(i, v.back()); + ASSERT_EQ(&v.back(), &v[i]); + } + ASSERT_FALSE(v.empty()); + ASSERT_EQ(v.size(), 256u); + for (unsigned i = 0; i < 256; ++i) + ASSERT_EQ(v[i], i); +} diff --git a/src/lib/crashdump/gbreakpad/common/module.cc b/src/lib/crashdump/gbreakpad/common/module.cc new file mode 100644 index 0000000..4e257d1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/module.cc @@ -0,0 +1,294 @@ +// Copyright (c) 2011 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// module.cc: Implement google_breakpad::Module. See module.h. + +#include "common/module.h" + +#include +#include +#include +#include + +#include +#include + +namespace google_breakpad { + +using std::dec; +using std::endl; +using std::hex; + + +Module::Module(const string &name, const string &os, + const string &architecture, const string &id) : + name_(name), + os_(os), + architecture_(architecture), + id_(id), + load_address_(0) { } + +Module::~Module() { + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) + delete it->second; + for (FunctionSet::iterator it = functions_.begin(); + it != functions_.end(); ++it) { + delete *it; + } + for (vector::iterator it = stack_frame_entries_.begin(); + it != stack_frame_entries_.end(); ++it) { + delete *it; + } + for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it) + delete *it; +} + +void Module::SetLoadAddress(Address address) { + load_address_ = address; +} + +void Module::AddFunction(Function *function) { + // FUNC lines must not hold an empty name, so catch the problem early if + // callers try to add one. + assert(!function->name.empty()); + std::pair ret = functions_.insert(function); + if (!ret.second) { + // Free the duplicate that was not inserted because this Module + // now owns it. + delete function; + } +} + +void Module::AddFunctions(vector::iterator begin, + vector::iterator end) { + for (vector::iterator it = begin; it != end; ++it) + AddFunction(*it); +} + +void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) { + stack_frame_entries_.push_back(stack_frame_entry); +} + +void Module::AddExtern(Extern *ext) { + std::pair ret = externs_.insert(ext); + if (!ret.second) { + // Free the duplicate that was not inserted because this Module + // now owns it. + delete ext; + } +} + +void Module::GetFunctions(vector *vec, + vector::iterator i) { + vec->insert(i, functions_.begin(), functions_.end()); +} + +void Module::GetExterns(vector *vec, + vector::iterator i) { + vec->insert(i, externs_.begin(), externs_.end()); +} + +Module::File *Module::FindFile(const string &name) { + // A tricky bit here. The key of each map entry needs to be a + // pointer to the entry's File's name string. This means that we + // can't do the initial lookup with any operation that would create + // an empty entry for us if the name isn't found (like, say, + // operator[] or insert do), because such a created entry's key will + // be a pointer the string passed as our argument. Since the key of + // a map's value type is const, we can't fix it up once we've + // created our file. lower_bound does the lookup without doing an + // insertion, and returns a good hint iterator to pass to insert. + // Our "destiny" is where we belong, whether we're there or not now. + FileByNameMap::iterator destiny = files_.lower_bound(&name); + if (destiny == files_.end() + || *destiny->first != name) { // Repeated string comparison, boo hoo. + File *file = new File; + file->name = name; + file->source_id = -1; + destiny = files_.insert(destiny, + FileByNameMap::value_type(&file->name, file)); + } + return destiny->second; +} + +Module::File *Module::FindFile(const char *name) { + string name_string = name; + return FindFile(name_string); +} + +Module::File *Module::FindExistingFile(const string &name) { + FileByNameMap::iterator it = files_.find(&name); + return (it == files_.end()) ? NULL : it->second; +} + +void Module::GetFiles(vector *vec) { + vec->clear(); + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) + vec->push_back(it->second); +} + +void Module::GetStackFrameEntries(vector *vec) { + *vec = stack_frame_entries_; +} + +void Module::AssignSourceIds() { + // First, give every source file an id of -1. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + file_it->second->source_id = -1; + } + + // Next, mark all files actually cited by our functions' line number + // info, by setting each one's source id to zero. + for (FunctionSet::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); ++func_it) { + Function *func = *func_it; + for (vector::iterator line_it = func->lines.begin(); + line_it != func->lines.end(); ++line_it) + line_it->file->source_id = 0; + } + + // Finally, assign source ids to those files that have been marked. + // We could have just assigned source id numbers while traversing + // the line numbers, but doing it this way numbers the files in + // lexicographical order by name, which is neat. + int next_source_id = 0; + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + if (!file_it->second->source_id) + file_it->second->source_id = next_source_id++; + } +} + +bool Module::ReportError() { + fprintf(stderr, "error writing symbol file: %s\n", + strerror(errno)); + return false; +} + +bool Module::WriteRuleMap(const RuleMap &rule_map, std::ostream &stream) { + for (RuleMap::const_iterator it = rule_map.begin(); + it != rule_map.end(); ++it) { + if (it != rule_map.begin()) + stream << ' '; + stream << it->first << ": " << it->second; + } + return stream.good(); +} + +bool Module::Write(std::ostream &stream, bool cfi) { + stream << "MODULE " << os_ << " " << architecture_ << " " + << id_ << " " << name_ << endl; + if (!stream.good()) + return ReportError(); + + AssignSourceIds(); + + // Write out files. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); ++file_it) { + File *file = file_it->second; + if (file->source_id >= 0) { + stream << "FILE " << file->source_id << " " << file->name << endl; + if (!stream.good()) + return ReportError(); + } + } + + // Write out functions and their lines. + for (FunctionSet::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); ++func_it) { + Function *func = *func_it; + stream << "FUNC " << hex + << (func->address - load_address_) << " " + << func->size << " " + << func->parameter_size << " " + << func->name << dec << endl; + + if (!stream.good()) + return ReportError(); + for (vector::iterator line_it = func->lines.begin(); + line_it != func->lines.end(); ++line_it) { + stream << hex + << (line_it->address - load_address_) << " " + << line_it->size << " " + << dec + << line_it->number << " " + << line_it->file->source_id << endl; + if (!stream.good()) + return ReportError(); + } + } + + // Write out 'PUBLIC' records. + for (ExternSet::const_iterator extern_it = externs_.begin(); + extern_it != externs_.end(); ++extern_it) { + Extern *ext = *extern_it; + stream << "PUBLIC " << hex + << (ext->address - load_address_) << " 0 " + << ext->name << dec << endl; + if (!stream.good()) + return ReportError(); + } + + if (cfi) { + // Write out 'STACK CFI INIT' and 'STACK CFI' records. + vector::const_iterator frame_it; + for (frame_it = stack_frame_entries_.begin(); + frame_it != stack_frame_entries_.end(); ++frame_it) { + StackFrameEntry *entry = *frame_it; + stream << "STACK CFI INIT " << hex + << (entry->address - load_address_) << " " + << entry->size << " " << dec; + if (!stream.good() + || !WriteRuleMap(entry->initial_rules, stream)) + return ReportError(); + + stream << endl; + + // Write out this entry's delta rules as 'STACK CFI' records. + for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); + delta_it != entry->rule_changes.end(); ++delta_it) { + stream << "STACK CFI " << hex + << (delta_it->first - load_address_) << " " << dec; + if (!stream.good() + || !WriteRuleMap(delta_it->second, stream)) + return ReportError(); + + stream << endl; + } + } + } + + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/module.h b/src/lib/crashdump/gbreakpad/common/module.h new file mode 100644 index 0000000..734a1af --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/module.h @@ -0,0 +1,321 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// module.h: Define google_breakpad::Module. A Module holds debugging +// information, and can write that information out as a Breakpad +// symbol file. + +#ifndef COMMON_LINUX_MODULE_H__ +#define COMMON_LINUX_MODULE_H__ + +#include +#include +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::set; +using std::string; +using std::vector; +using std::map; + +// A Module represents the contents of a module, and supports methods +// for adding information produced by parsing STABS or DWARF data +// --- possibly both from the same file --- and then writing out the +// unified contents as a Breakpad-format symbol file. +class Module { + public: + // The type of addresses and sizes in a symbol table. + typedef u_int64_t Address; + struct File; + struct Function; + struct Line; + struct Extern; + + // Addresses appearing in File, Function, and Line structures are + // absolute, not relative to the the module's load address. That + // is, if the module were loaded at its nominal load address, the + // addresses would be correct. + + // A source file. + struct File { + // The name of the source file. + string name; + + // The file's source id. The Write member function clears this + // field and assigns source ids a fresh, so any value placed here + // before calling Write will be lost. + int source_id; + }; + + // A function. + struct Function { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Function *x, const Function *y) { + return x->address < y->address; + } + + // The function's name. + string name; + + // The start address and length of the function's code. + Address address, size; + + // The function's parameter size. + Address parameter_size; + + // Source lines belonging to this function, sorted by increasing + // address. + vector lines; + }; + + // A source line. + struct Line { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Module::Line &x, const Module::Line &y) { + return x.address < y.address; + } + + Address address, size; // The address and size of the line's code. + File *file; // The source file. + int number; // The source line number. + }; + + // An exported symbol. + struct Extern { + Address address; + string name; + }; + + // A map from register names to postfix expressions that recover + // their their values. This can represent a complete set of rules to + // follow at some address, or a set of changes to be applied to an + // extant set of rules. + typedef map RuleMap; + + // A map from addresses to RuleMaps, representing changes that take + // effect at given addresses. + typedef map RuleChangeMap; + + // A range of 'STACK CFI' stack walking information. An instance of + // this structure corresponds to a 'STACK CFI INIT' record and the + // subsequent 'STACK CFI' records that fall within its range. + struct StackFrameEntry { + // The starting address and number of bytes of machine code this + // entry covers. + Address address, size; + + // The initial register recovery rules, in force at the starting + // address. + RuleMap initial_rules; + + // A map from addresses to rule changes. To find the rules in + // force at a given address, start with initial_rules, and then + // apply the changes given in this map for all addresses up to and + // including the address you're interested in. + RuleChangeMap rule_changes; + }; + + struct FunctionCompare { + bool operator() (const Function *lhs, + const Function *rhs) const { + if (lhs->address == rhs->address) + return lhs->name < rhs->name; + return lhs->address < rhs->address; + } + }; + + struct ExternCompare { + bool operator() (const Extern *lhs, + const Extern *rhs) const { + return lhs->address < rhs->address; + } + }; + + // Create a new module with the given name, operating system, + // architecture, and ID string. + Module(const string &name, const string &os, const string &architecture, + const string &id); + ~Module(); + + // Set the module's load address to LOAD_ADDRESS; addresses given + // for functions and lines will be written to the Breakpad symbol + // file as offsets from this address. Construction initializes this + // module's load address to zero: addresses written to the symbol + // file will be the same as they appear in the Function, Line, and + // StackFrameEntry structures. + // + // Note that this member function has no effect on addresses stored + // in the data added to this module; the Write member function + // simply subtracts off the load address from addresses before it + // prints them. Only the last load address given before calling + // Write is used. + void SetLoadAddress(Address load_address); + + // Add FUNCTION to the module. FUNCTION's name must not be empty. + // This module owns all Function objects added with this function: + // destroying the module destroys them as well. + void AddFunction(Function *function); + + // Add all the functions in [BEGIN,END) to the module. + // This module owns all Function objects added with this function: + // destroying the module destroys them as well. + void AddFunctions(vector::iterator begin, + vector::iterator end); + + // Add STACK_FRAME_ENTRY to the module. + // This module owns all StackFrameEntry objects added with this + // function: destroying the module destroys them as well. + void AddStackFrameEntry(StackFrameEntry *stack_frame_entry); + + // Add PUBLIC to the module. + // This module owns all Extern objects added with this function: + // destroying the module destroys them as well. + void AddExtern(Extern *ext); + + // If this module has a file named NAME, return a pointer to it. If + // it has none, then create one and return a pointer to the new + // file. This module owns all File objects created using these + // functions; destroying the module destroys them as well. + File *FindFile(const string &name); + File *FindFile(const char *name); + + // If this module has a file named NAME, return a pointer to it. + // Otherwise, return NULL. + File *FindExistingFile(const string &name); + + // Insert pointers to the functions added to this module at I in + // VEC. The pointed-to Functions are still owned by this module. + // (Since this is effectively a copy of the function list, this is + // mostly useful for testing; other uses should probably get a more + // appropriate interface.) + void GetFunctions(vector *vec, vector::iterator i); + + // Insert pointers to the externs added to this module at I in + // VEC. The pointed-to Externs are still owned by this module. + // (Since this is effectively a copy of the extern list, this is + // mostly useful for testing; other uses should probably get a more + // appropriate interface.) + void GetExterns(vector *vec, vector::iterator i); + + // Clear VEC and fill it with pointers to the Files added to this + // module, sorted by name. The pointed-to Files are still owned by + // this module. (Since this is effectively a copy of the file list, + // this is mostly useful for testing; other uses should probably get + // a more appropriate interface.) + void GetFiles(vector *vec); + + // Clear VEC and fill it with pointers to the StackFrameEntry + // objects that have been added to this module. (Since this is + // effectively a copy of the stack frame entry list, this is mostly + // useful for testing; other uses should probably get + // a more appropriate interface.) + void GetStackFrameEntries(vector *vec); + + // Find those files in this module that are actually referred to by + // functions' line number data, and assign them source id numbers. + // Set the source id numbers for all other files --- unused by the + // source line data --- to -1. We do this before writing out the + // symbol file, at which point we omit any unused files. + void AssignSourceIds(); + + // Call AssignSourceIds, and write this module to STREAM in the + // breakpad symbol format. Return true if all goes well, or false if + // an error occurs. This method writes out: + // - a header based on the values given to the constructor, + // - the source files added via FindFile, + // - the functions added via AddFunctions, each with its lines, + // - all public records, + // - and if CFI is true, all CFI records. + // Addresses in the output are all relative to the load address + // established by SetLoadAddress. + bool Write(std::ostream &stream, bool cfi); + + private: + // Report an error that has occurred writing the symbol file, using + // errno to find the appropriate cause. Return false. + static bool ReportError(); + + // Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI' + // records, without a final newline. Return true if all goes well; + // if an error occurs, return false, and leave errno set. + static bool WriteRuleMap(const RuleMap &rule_map, std::ostream &stream); + + // Module header entries. + string name_, os_, architecture_, id_; + + // The module's nominal load address. Addresses for functions and + // lines are absolute, assuming the module is loaded at this + // address. + Address load_address_; + + // Relation for maps whose keys are strings shared with some other + // structure. + struct CompareStringPtrs { + bool operator()(const string *x, const string *y) { return *x < *y; } + }; + + // A map from filenames to File structures. The map's keys are + // pointers to the Files' names. + typedef map FileByNameMap; + + // A set containing Function structures, sorted by address. + typedef set FunctionSet; + + // A set containing Extern structures, sorted by address. + typedef set ExternSet; + + // The module owns all the files and functions that have been added + // to it; destroying the module frees the Files and Functions these + // point to. + FileByNameMap files_; // This module's source files. + FunctionSet functions_; // This module's functions. + + // The module owns all the call frame info entries that have been + // added to it. + vector stack_frame_entries_; + + // The module owns all the externs that have been added to it; + // destroying the module frees the Externs these point to. + ExternSet externs_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MODULE_H__ diff --git a/src/lib/crashdump/gbreakpad/common/module_unittest.cc b/src/lib/crashdump/gbreakpad/common/module_unittest.cc new file mode 100644 index 0000000..85c3b1c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/module_unittest.cc @@ -0,0 +1,490 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// module_unittest.cc: Unit tests for google_breakpad::Module. + +#include +#include +#include +#include + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/module.h" + +using google_breakpad::Module; +using std::string; +using std::stringstream; +using std::vector; +using testing::ContainerEq; + +static Module::Function *generate_duplicate_function(const string &name) { + const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL; + const Module::Address DUP_SIZE = 0x200b26e605f99071LL; + const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99LL; + + Module::Function *function = new(Module::Function); + function->name = name; + function->address = DUP_ADDRESS; + function->size = DUP_SIZE; + function->parameter_size = DUP_PARAMETER_SIZE; + return function; +} + +#define MODULE_NAME "name with spaces" +#define MODULE_OS "os-name" +#define MODULE_ARCH "architecture" +#define MODULE_ID "id-string" + +TEST(Write, Header) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n", + contents.c_str()); +} + +TEST(Write, OneLineFunc) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + Module::File *file = m.FindFile("file_name.cc"); + Module::Function *function = new(Module::Function); + function->name = "function_name"; + function->address = 0xe165bf8023b9d9abLL; + function->size = 0x1e4bb0eb1cbf5b09LL; + function->parameter_size = 0x772beee89114358aLL; + Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL, + file, 67519080 }; + function->lines.push_back(line); + m.AddFunction(function); + + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 file_name.cc\n" + "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a" + " function_name\n" + "e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n", + contents.c_str()); +} + +TEST(Write, RelativeLoadAddress) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Some source files. We will expect to see them in lexicographic order. + Module::File *file1 = m.FindFile("filename-b.cc"); + Module::File *file2 = m.FindFile("filename-a.cc"); + + // A function. + Module::Function *function = new(Module::Function); + function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)"; + function->address = 0xbec774ea5dd935f3LL; + function->size = 0x2922088f98d3f6fcLL; + function->parameter_size = 0xe5e9aa008bd5f0d0LL; + + // Some source lines. The module should not sort these. + Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL, + file1, 41676901 }; + Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL, + file2, 67519080 }; + function->lines.push_back(line2); + function->lines.push_back(line1); + + m.AddFunction(function); + + // Some stack information. + Module::StackFrameEntry *entry = new Module::StackFrameEntry(); + entry->address = 0x30f9e5c83323973dULL; + entry->size = 0x49fc9ca7c7c13dc2ULL; + entry->initial_rules[".cfa"] = "he was a handsome man"; + entry->initial_rules["and"] = "what i want to know is"; + entry->rule_changes[0x30f9e5c83323973eULL]["how"] = + "do you like your blueeyed boy"; + entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; + m.AddStackFrameEntry(entry); + + // Set the load address. Doing this after adding all the data to + // the module must work fine. + m.SetLoadAddress(0x2ab698b0b6407073LL); + + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename-a.cc\n" + "FILE 1 filename-b.cc\n" + "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" + " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" + "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n" + "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n" + "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2" + " .cfa: he was a handsome man" + " and: what i want to know is\n" + "STACK CFI 6434d177ce326cb" + " Mister: Death" + " how: do you like your blueeyed boy\n", + contents.c_str()); +} + +TEST(Write, OmitUnusedFiles) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Create some source files. + Module::File *file1 = m.FindFile("filename1"); + m.FindFile("filename2"); // not used by any line + Module::File *file3 = m.FindFile("filename3"); + + // Create a function. + Module::Function *function = new(Module::Function); + function->name = "function_name"; + function->address = 0x9b926d464f0b9384LL; + function->size = 0x4f524a4ba795e6a6LL; + function->parameter_size = 0xbbe8133a6641c9b7LL; + + // Source files that refer to some files, but not others. + Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL, + file1, 137850127 }; + Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL, + file3, 28113549 }; + function->lines.push_back(line1); + function->lines.push_back(line2); + m.AddFunction(function); + + m.AssignSourceIds(); + + vector vec; + m.GetFiles(&vec); + EXPECT_EQ((size_t) 3, vec.size()); + EXPECT_STREQ("filename1", vec[0]->name.c_str()); + EXPECT_NE(-1, vec[0]->source_id); + // Expect filename2 not to be used. + EXPECT_STREQ("filename2", vec[1]->name.c_str()); + EXPECT_EQ(-1, vec[1]->source_id); + EXPECT_STREQ("filename3", vec[2]->name.c_str()); + EXPECT_NE(-1, vec[2]->source_id); + + stringstream s; + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename1\n" + "FILE 1 filename3\n" + "FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7" + " function_name\n" + "595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n" + "401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n", + contents.c_str()); +} + +TEST(Write, NoCFI) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Some source files. We will expect to see them in lexicographic order. + Module::File *file1 = m.FindFile("filename.cc"); + + // A function. + Module::Function *function = new(Module::Function); + function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)"; + function->address = 0xbec774ea5dd935f3LL; + function->size = 0x2922088f98d3f6fcLL; + function->parameter_size = 0xe5e9aa008bd5f0d0LL; + + // Some source lines. The module should not sort these. + Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL, + file1, 41676901 }; + function->lines.push_back(line1); + + m.AddFunction(function); + + // Some stack information. + Module::StackFrameEntry *entry = new Module::StackFrameEntry(); + entry->address = 0x30f9e5c83323973dULL; + entry->size = 0x49fc9ca7c7c13dc2ULL; + entry->initial_rules[".cfa"] = "he was a handsome man"; + entry->initial_rules["and"] = "what i want to know is"; + entry->rule_changes[0x30f9e5c83323973eULL]["how"] = + "do you like your blueeyed boy"; + entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; + m.AddStackFrameEntry(entry); + + // Set the load address. Doing this after adding all the data to + // the module must work fine. + m.SetLoadAddress(0x2ab698b0b6407073LL); + + m.Write(s, false); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename.cc\n" + "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" + " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" + "9410dc39a798c580 1c2be6d6c5af2611 41676901 0\n", + contents.c_str()); +} + +TEST(Construct, AddFunctions) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function *function1 = new(Module::Function); + function1->name = "_without_form"; + function1->address = 0xd35024aa7ca7da5cLL; + function1->size = 0x200b26e605f99071LL; + function1->parameter_size = 0xf14ac4fed48c4a99LL; + + Module::Function *function2 = new(Module::Function); + function2->name = "_and_void"; + function2->address = 0x2987743d0b35b13fLL; + function2->size = 0xb369db048deb3010LL; + function2->parameter_size = 0x938e556cb5a79988LL; + + // Put them in a vector. + vector vec; + vec.push_back(function1); + vec.push_back(function2); + + m.AddFunctions(vec.begin(), vec.end()); + + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988" + " _and_void\n" + "FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n", + contents.c_str()); + + // Check that m.GetFunctions returns the functions we expect. + vec.clear(); + m.GetFunctions(&vec, vec.end()); + EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1)); + EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2)); + EXPECT_EQ((size_t) 2, vec.size()); +} + +TEST(Construct, AddFrames) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // First STACK CFI entry, with no initial rules or deltas. + Module::StackFrameEntry *entry1 = new Module::StackFrameEntry(); + entry1->address = 0xddb5f41285aa7757ULL; + entry1->size = 0x1486493370dc5073ULL; + m.AddStackFrameEntry(entry1); + + // Second STACK CFI entry, with initial rules but no deltas. + Module::StackFrameEntry *entry2 = new Module::StackFrameEntry(); + entry2->address = 0x8064f3af5e067e38ULL; + entry2->size = 0x0de2a5ee55509407ULL; + entry2->initial_rules[".cfa"] = "I think that I shall never see"; + entry2->initial_rules["stromboli"] = "a poem lovely as a tree"; + entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest"; + m.AddStackFrameEntry(entry2); + + // Third STACK CFI entry, with initial rules and deltas. + Module::StackFrameEntry *entry3 = new Module::StackFrameEntry(); + entry3->address = 0x5e8d0db0a7075c6cULL; + entry3->size = 0x1c7edb12a7aea229ULL; + entry3->initial_rules[".cfa"] = "Whose woods are these"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] = + "the village though"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] = + "his house is in"; + entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] = + "I think I know"; + m.AddStackFrameEntry(entry3); + + // Check that Write writes STACK CFI records properly. + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n" + "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407" + " .cfa: I think that I shall never see" + " cannoli: a tree whose hungry mouth is prest" + " stromboli: a poem lovely as a tree\n" + "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229" + " .cfa: Whose woods are these\n" + "STACK CFI 36682fad3763ffff" + " .cfa: I think I know" + " stromboli: his house is in\n" + "STACK CFI 47ceb0f63c269d7f" + " calzone: the village though" + " cannoli: he will not see me stopping here\n", + contents.c_str()); + + // Check that GetStackFrameEntries works. + vector entries; + m.GetStackFrameEntries(&entries); + ASSERT_EQ(3U, entries.size()); + // Check first entry. + EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address); + EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size); + ASSERT_EQ(0U, entries[0]->initial_rules.size()); + ASSERT_EQ(0U, entries[0]->rule_changes.size()); + // Check second entry. + EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address); + EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size); + ASSERT_EQ(3U, entries[1]->initial_rules.size()); + Module::RuleMap entry2_initial; + entry2_initial[".cfa"] = "I think that I shall never see"; + entry2_initial["stromboli"] = "a poem lovely as a tree"; + entry2_initial["cannoli"] = "a tree whose hungry mouth is prest"; + EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial)); + ASSERT_EQ(0U, entries[1]->rule_changes.size()); + // Check third entry. + EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address); + EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size); + Module::RuleMap entry3_initial; + entry3_initial[".cfa"] = "Whose woods are these"; + EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial)); + Module::RuleChangeMap entry3_changes; + entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know"; + entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in"; + entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though"; + entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes)); +} + +TEST(Construct, UniqueFiles) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + Module::File *file1 = m.FindFile("foo"); + Module::File *file2 = m.FindFile(string("bar")); + Module::File *file3 = m.FindFile(string("foo")); + Module::File *file4 = m.FindFile("bar"); + EXPECT_NE(file1, file2); + EXPECT_EQ(file1, file3); + EXPECT_EQ(file2, file4); + EXPECT_EQ(file1, m.FindExistingFile("foo")); + EXPECT_TRUE(m.FindExistingFile("baz") == NULL); +} + +TEST(Construct, DuplicateFunctions) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function *function1 = generate_duplicate_function("_without_form"); + Module::Function *function2 = generate_duplicate_function("_without_form"); + + m.AddFunction(function1); + m.AddFunction(function2); + + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n", + contents.c_str()); +} + +TEST(Construct, FunctionsWithSameAddress) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function *function1 = generate_duplicate_function("_without_form"); + Module::Function *function2 = generate_duplicate_function("_and_void"); + + m.AddFunction(function1); + m.AddFunction(function2); + + m.Write(s, true); + string contents = s.str(); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" + " _and_void\n" + "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n", + contents.c_str()); +} + +// Externs should be written out as PUBLIC records, sorted by +// address. +TEST(Construct, Externs) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two externs. + Module::Extern *extern1 = new(Module::Extern); + extern1->address = 0xffff; + extern1->name = "_abc"; + Module::Extern *extern2 = new(Module::Extern); + extern2->address = 0xaaaa; + extern2->name = "_xyz"; + + m.AddExtern(extern1); + m.AddExtern(extern2); + + m.Write(s, true); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " + MODULE_ID " " MODULE_NAME "\n" + "PUBLIC aaaa 0 _xyz\n" + "PUBLIC ffff 0 _abc\n", + contents.c_str()); +} + +// Externs with the same address should only keep the first entry +// added. +TEST(Construct, DuplicateExterns) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two externs. + Module::Extern *extern1 = new(Module::Extern); + extern1->address = 0xffff; + extern1->name = "_xyz"; + Module::Extern *extern2 = new(Module::Extern); + extern2->address = 0xffff; + extern2->name = "_abc"; + + m.AddExtern(extern1); + m.AddExtern(extern2); + + m.Write(s, true); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " + MODULE_ID " " MODULE_NAME "\n" + "PUBLIC ffff 0 _xyz\n", + contents.c_str()); +} diff --git a/src/lib/crashdump/gbreakpad/common/stabs_reader.cc b/src/lib/crashdump/gbreakpad/common/stabs_reader.cc new file mode 100644 index 0000000..4897361 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/stabs_reader.cc @@ -0,0 +1,311 @@ +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// This file implements the google_breakpad::StabsReader class. +// See stabs_reader.h. + +#include "common/stabs_reader.h" + +#include +#include +#include + +using std::vector; + +namespace google_breakpad { + +StabsReader::EntryIterator::EntryIterator(const ByteBuffer *buffer, + bool big_endian, size_t value_size) + : value_size_(value_size), cursor_(buffer, big_endian) { + // Actually, we could handle weird sizes just fine, but they're + // probably mistakes --- expressed in bits, say. + assert(value_size == 4 || value_size == 8); + entry_.index = 0; + Fetch(); +} + +void StabsReader::EntryIterator::Fetch() { + cursor_ + .Read(4, false, &entry_.name_offset) + .Read(1, false, &entry_.type) + .Read(1, false, &entry_.other) + .Read(2, false, &entry_.descriptor) + .Read(value_size_, false, &entry_.value); + entry_.at_end = !cursor_; +} + +StabsReader::StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + bool big_endian, size_t value_size, bool unitized, + StabsHandler *handler) + : entries_(stab, stab_size), + strings_(stabstr, stabstr_size), + iterator_(&entries_, big_endian, value_size), + unitized_(unitized), + handler_(handler), + string_offset_(0), + next_cu_string_offset_(0), + current_source_file_(NULL) { } + +const char *StabsReader::SymbolString() { + ptrdiff_t offset = string_offset_ + iterator_->name_offset; + if (offset < 0 || (size_t) offset >= strings_.Size()) { + handler_->Warning("symbol %d: name offset outside the string section\n", + iterator_->index); + // Return our null string, to keep our promise about all names being + // taken from the string section. + offset = 0; + } + return reinterpret_cast(strings_.start + offset); +} + +bool StabsReader::Process() { + while (!iterator_->at_end) { + if (iterator_->type == N_SO) { + if (! ProcessCompilationUnit()) + return false; + } else if (iterator_->type == N_UNDF && unitized_) { + // In unitized STABS (including Linux STABS, and pretty much anything + // else that puts STABS data in sections), at the head of each + // compilation unit's entries there is an N_UNDF stab giving the + // number of symbols in the compilation unit, and the number of bytes + // that compilation unit's strings take up in the .stabstr section. + // Each CU's strings are separate; the n_strx values are offsets + // within the current CU's portion of the .stabstr section. + // + // As an optimization, the GNU linker combines all the + // compilation units into one, with a single N_UNDF at the + // beginning. However, other linkers, like Gold, do not perform + // this optimization. + string_offset_ = next_cu_string_offset_; + next_cu_string_offset_ = iterator_->value; + ++iterator_; + } +#if defined(HAVE_MACH_O_NLIST_H) + // Export symbols in Mach-O binaries look like this. + // This is necessary in order to be able to dump symbols + // from OS X system libraries. + else if ((iterator_->type & N_STAB) == 0 && + (iterator_->type & N_TYPE) == N_SECT) { + ProcessExtern(); + } +#endif + else { + ++iterator_; + } + } + return true; +} + +bool StabsReader::ProcessCompilationUnit() { + assert(!iterator_->at_end && iterator_->type == N_SO); + + // There may be an N_SO entry whose name ends with a slash, + // indicating the directory in which the compilation occurred. + // The build directory defaults to NULL. + const char *build_directory = NULL; + { + const char *name = SymbolString(); + if (name[0] && name[strlen(name) - 1] == '/') { + build_directory = name; + ++iterator_; + } + } + + // We expect to see an N_SO entry with a filename next, indicating + // the start of the compilation unit. + { + if (iterator_->at_end || iterator_->type != N_SO) + return true; + const char *name = SymbolString(); + if (name[0] == '\0') { + // This seems to be a stray end-of-compilation-unit marker; + // consume it, but don't report the end, since we didn't see a + // beginning. + ++iterator_; + return true; + } + current_source_file_ = name; + } + + if (! handler_->StartCompilationUnit(current_source_file_, + iterator_->value, + build_directory)) + return false; + + ++iterator_; + + // The STABS documentation says that some compilers may emit + // additional N_SO entries with names immediately following the + // first, and that they should be ignored. However, the original + // Breakpad STABS reader doesn't ignore them, so we won't either. + + // Process the body of the compilation unit, up to the next N_SO. + while (!iterator_->at_end && iterator_->type != N_SO) { + if (iterator_->type == N_FUN) { + if (! ProcessFunction()) + return false; + } else if (iterator_->type == N_SLINE) { + // Mac OS X STABS place SLINE records before functions. + Line line; + // The value of an N_SLINE entry that appears outside a function is + // the absolute address of the line. + line.address = iterator_->value; + line.filename = current_source_file_; + // The n_desc of a N_SLINE entry is the line number. It's a + // signed 16-bit field; line numbers from 32768 to 65535 are + // stored as n-65536. + line.number = (uint16_t) iterator_->descriptor; + queued_lines_.push_back(line); + ++iterator_; + } else if (iterator_->type == N_SOL) { + current_source_file_ = SymbolString(); + ++iterator_; + } else { + // Ignore anything else. + ++iterator_; + } + } + + // An N_SO with an empty name indicates the end of the compilation + // unit. Default to zero. + uint64_t ending_address = 0; + if (!iterator_->at_end) { + assert(iterator_->type == N_SO); + const char *name = SymbolString(); + if (name[0] == '\0') { + ending_address = iterator_->value; + ++iterator_; + } + } + + if (! handler_->EndCompilationUnit(ending_address)) + return false; + + queued_lines_.clear(); + + return true; +} + +bool StabsReader::ProcessFunction() { + assert(!iterator_->at_end && iterator_->type == N_FUN); + + uint64_t function_address = iterator_->value; + // The STABS string for an N_FUN entry is the name of the function, + // followed by a colon, followed by type information for the + // function. We want to pass the name alone to StartFunction. + const char *stab_string = SymbolString(); + const char *name_end = strchr(stab_string, ':'); + if (! name_end) + name_end = stab_string + strlen(stab_string); + std::string name(stab_string, name_end - stab_string); + if (! handler_->StartFunction(name, function_address)) + return false; + ++iterator_; + + // If there were any SLINE records given before the function, report them now. + for (vector::const_iterator it = queued_lines_.begin(); + it != queued_lines_.end(); it++) { + if (!handler_->Line(it->address, it->filename, it->number)) + return false; + } + queued_lines_.clear(); + + while (!iterator_->at_end) { + if (iterator_->type == N_SO || iterator_->type == N_FUN) + break; + else if (iterator_->type == N_SLINE) { + // The value of an N_SLINE entry is the offset of the line from + // the function's start address. + uint64_t line_address = function_address + iterator_->value; + // The n_desc of a N_SLINE entry is the line number. It's a + // signed 16-bit field; line numbers from 32768 to 65535 are + // stored as n-65536. + uint16_t line_number = iterator_->descriptor; + if (! handler_->Line(line_address, current_source_file_, line_number)) + return false; + ++iterator_; + } else if (iterator_->type == N_SOL) { + current_source_file_ = SymbolString(); + ++iterator_; + } else + // Ignore anything else. + ++iterator_; + } + + // We've reached the end of the function. See if we can figure out its + // ending address. + uint64_t ending_address = 0; + if (!iterator_->at_end) { + assert(iterator_->type == N_SO || iterator_->type == N_FUN); + if (iterator_->type == N_FUN) { + const char *symbol_name = SymbolString(); + if (symbol_name[0] == '\0') { + // An N_FUN entry with no name is a terminator for this function; + // its value is the function's size. + ending_address = function_address + iterator_->value; + ++iterator_; + } else { + // An N_FUN entry with a name is the next function, and we can take + // its value as our ending address. Don't advance the iterator, as + // we'll use this symbol to start the next function as well. + ending_address = iterator_->value; + } + } else { + // An N_SO entry could be an end-of-compilation-unit marker, or the + // start of the next compilation unit, but in either case, its value + // is our ending address. We don't advance the iterator; + // ProcessCompilationUnit will decide what to do with this symbol. + ending_address = iterator_->value; + } + } + + if (! handler_->EndFunction(ending_address)) + return false; + + return true; +} + +bool StabsReader::ProcessExtern() { +#if defined(HAVE_MACH_O_NLIST_H) + assert(!iterator_->at_end && + (iterator_->type & N_STAB) == 0 && + (iterator_->type & N_TYPE) == N_SECT); +#endif + + // TODO(mark): only do symbols in the text section? + if (!handler_->Extern(SymbolString(), iterator_->value)) + return false; + + ++iterator_; + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/stabs_reader.h b/src/lib/crashdump/gbreakpad/common/stabs_reader.h new file mode 100644 index 0000000..b22ebfd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/stabs_reader.h @@ -0,0 +1,325 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// stabs_reader.h: Define StabsReader, a parser for STABS debugging +// information. A description of the STABS debugging format can be +// found at: +// +// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html +// +// The comments here assume you understand the format. +// +// This parser can handle big-endian and little-endian data, and the symbol +// values may be either 32 or 64 bits long. It handles both STABS in +// sections (as used on Linux) and STABS appearing directly in an +// a.out-like symbol table (as used in Darwin OS X Mach-O files). + +#ifndef COMMON_STABS_READER_H__ +#define COMMON_STABS_READER_H__ + +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_A_OUT_H +#include +#endif +#ifdef HAVE_MACH_O_NLIST_H +#include +#endif + +#include +#include + +#include "common/byte_cursor.h" + +namespace google_breakpad { + +class StabsHandler; + +class StabsReader { + public: + // Create a reader for the STABS debug information whose .stab section is + // being traversed by ITERATOR, and whose .stabstr section is referred to + // by STRINGS. The reader will call the member functions of HANDLER to + // report the information it finds, when the reader's 'Process' member + // function is called. + // + // BIG_ENDIAN should be true if the entries in the .stab section are in + // big-endian form, or false if they are in little-endian form. + // + // VALUE_SIZE should be either 4 or 8, indicating the size of the 'value' + // field in each entry in bytes. + // + // UNITIZED should be true if the STABS data is stored in units with + // N_UNDF headers. This is usually the case for STABS stored in sections, + // like .stab/.stabstr, and usually not the case for STABS stored in the + // actual symbol table; UNITIZED should be true when parsing Linux stabs, + // false when parsing Mac OS X STABS. For details, see: + // http://sourceware.org/gdb/current/onlinedocs/stabs/Stab-Section-Basics.html + // + // Note that, in ELF, the .stabstr section should be found using the + // 'sh_link' field of the .stab section header, not by name. + StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + bool big_endian, size_t value_size, bool unitized, + StabsHandler *handler); + + // Process the STABS data, calling the handler's member functions to + // report what we find. While the handler functions return true, + // continue to process until we reach the end of the section. If we + // processed the entire section and all handlers returned true, + // return true. If any handler returned false, return false. + // + // This is only meant to be called once per StabsReader instance; + // resuming a prior processing pass that stopped abruptly isn't supported. + bool Process(); + + private: + + // An class for walking arrays of STABS entries. This isolates the main + // STABS reader from the exact format (size; endianness) of the entries + // themselves. + class EntryIterator { + public: + // The contents of a STABS entry, adjusted for the host's endianness, + // word size, 'struct nlist' layout, and so on. + struct Entry { + // True if this iterator has reached the end of the entry array. When + // this is set, the other members of this structure are not valid. + bool at_end; + + // The number of this entry within the list. + size_t index; + + // The current entry's name offset. This is the offset within the + // current compilation unit's strings, as establish by the N_UNDF entries. + size_t name_offset; + + // The current entry's type, 'other' field, descriptor, and value. + unsigned char type; + unsigned char other; + short descriptor; + uint64_t value; + }; + + // Create a EntryIterator walking the entries in BUFFER. Treat the + // entries as big-endian if BIG_ENDIAN is true, as little-endian + // otherwise. Assume each entry has a 'value' field whose size is + // VALUE_SIZE. + // + // This would not be terribly clean to extend to other format variations, + // but it's enough to handle Linux and Mac, and we'd like STABS to die + // anyway. + // + // For the record: on Linux, STABS entry values are always 32 bits, + // regardless of the architecture address size (don't ask me why); on + // Mac, they are 32 or 64 bits long. Oddly, the section header's entry + // size for a Linux ELF .stab section varies according to the ELF class + // from 12 to 20 even as the actual entries remain unchanged. + EntryIterator(const ByteBuffer *buffer, bool big_endian, size_t value_size); + + // Move to the next entry. This function's behavior is undefined if + // at_end() is true when it is called. + EntryIterator &operator++() { Fetch(); entry_.index++; return *this; } + + // Dereferencing this iterator produces a reference to an Entry structure + // that holds the current entry's values. The entry is owned by this + // EntryIterator, and will be invalidated at the next call to operator++. + const Entry &operator*() const { return entry_; } + const Entry *operator->() const { return &entry_; } + + private: + // Read the STABS entry at cursor_, and set entry_ appropriately. + void Fetch(); + + // The size of entries' value field, in bytes. + size_t value_size_; + + // A byte cursor traversing buffer_. + ByteCursor cursor_; + + // Values for the entry this iterator refers to. + Entry entry_; + }; + + // A source line, saved to be reported later. + struct Line { + uint64_t address; + const char *filename; + int number; + }; + + // Return the name of the current symbol. + const char *SymbolString(); + + // Process a compilation unit starting at symbol_. Return true + // to continue processing, or false to abort. + bool ProcessCompilationUnit(); + + // Process a function in current_source_file_ starting at symbol_. + // Return true to continue processing, or false to abort. + bool ProcessFunction(); + + // Process an exported function symbol. + // Return true to continue processing, or false to abort. + bool ProcessExtern(); + + // The STABS entries being parsed. + ByteBuffer entries_; + + // The string section to which the entries refer. + ByteBuffer strings_; + + // The iterator walking the STABS entries. + EntryIterator iterator_; + + // True if the data is "unitized"; see the explanation in the comment for + // StabsReader::StabsReader. + bool unitized_; + + StabsHandler *handler_; + + // The offset of the current compilation unit's strings within stabstr_. + size_t string_offset_; + + // The value string_offset_ should have for the next compilation unit, + // as established by N_UNDF entries. + size_t next_cu_string_offset_; + + // The current source file name. + const char *current_source_file_; + + // Mac OS X STABS place SLINE records before functions; we accumulate a + // vector of these until we see the FUN record, and then report them + // after the StartFunction call. + std::vector queued_lines_; +}; + +// Consumer-provided callback structure for the STABS reader. Clients +// of the STABS reader provide an instance of this structure. The +// reader then invokes the member functions of that instance to report +// the information it finds. +// +// The default definitions of the member functions do nothing, and return +// true so processing will continue. +class StabsHandler { + public: + StabsHandler() { } + virtual ~StabsHandler() { } + + // Some general notes about the handler callback functions: + + // Processing proceeds until the end of the .stabs section, or until + // one of these functions returns false. + + // The addresses given are as reported in the STABS info, without + // regard for whether the module may be loaded at different + // addresses at different times (a shared library, say). When + // processing STABS from an ELF shared library, the addresses given + // all assume the library is loaded at its nominal load address. + // They are *not* offsets from the nominal load address. If you + // want offsets, you must subtract off the library's nominal load + // address. + + // The arguments to these functions named FILENAME are all + // references to strings stored in the .stabstr section. Because + // both the Linux and Solaris linkers factor out duplicate strings + // from the .stabstr section, the consumer can assume that if two + // FILENAME values are different addresses, they represent different + // file names. + // + // Thus, it's safe to use (say) std::map, which does + // string address comparisons, not string content comparisons. + // Since all the strings are in same array of characters --- the + // .stabstr section --- comparing their addresses produces + // predictable, if not lexicographically meaningful, results. + + // Begin processing a compilation unit whose main source file is + // named FILENAME, and whose base address is ADDRESS. If + // BUILD_DIRECTORY is non-NULL, it is the name of the build + // directory in which the compilation occurred. + virtual bool StartCompilationUnit(const char *filename, uint64_t address, + const char *build_directory) { + return true; + } + + // Finish processing the compilation unit. If ADDRESS is non-zero, + // it is the ending address of the compilation unit. If ADDRESS is + // zero, then the compilation unit's ending address is not + // available, and the consumer must infer it by other means. + virtual bool EndCompilationUnit(uint64_t address) { return true; } + + // Begin processing a function named NAME, whose starting address is + // ADDRESS. This function belongs to the compilation unit that was + // most recently started but not ended. + // + // Note that, unlike filenames, NAME is not a pointer into the + // .stabstr section; this is because the name as it appears in the + // STABS data is followed by type information. The value passed to + // StartFunction is the function name alone. + // + // In languages that use name mangling, like C++, NAME is mangled. + virtual bool StartFunction(const std::string &name, uint64_t address) { + return true; + } + + // Finish processing the function. If ADDRESS is non-zero, it is + // the ending address for the function. If ADDRESS is zero, then + // the function's ending address is not available, and the consumer + // must infer it by other means. + virtual bool EndFunction(uint64_t address) { return true; } + + // Report that the code at ADDRESS is attributable to line NUMBER of + // the source file named FILENAME. The caller must infer the ending + // address of the line. + virtual bool Line(uint64_t address, const char *filename, int number) { + return true; + } + + // Report that an exported function NAME is present at ADDRESS. + // The size of the function is unknown. + virtual bool Extern(const std::string &name, uint64_t address) { + return true; + } + + // Report a warning. FORMAT is a printf-like format string, + // specifying how to format the subsequent arguments. + virtual void Warning(const char *format, ...) = 0; +}; + +} // namespace google_breakpad + +#endif // COMMON_STABS_READER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/stabs_reader_unittest.cc b/src/lib/crashdump/gbreakpad/common/stabs_reader_unittest.cc new file mode 100644 index 0000000..2a9b5d4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/stabs_reader_unittest.cc @@ -0,0 +1,610 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/stabs_reader.h" +#include "common/test_assembler.h" + +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::_; +using google_breakpad::StabsHandler; +using google_breakpad::StabsReader; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using std::map; +using std::string; + +namespace { + +// A StringAssembler is a class for generating .stabstr sections to present +// as input to the STABS parser. +class StringAssembler: public Section { + public: + StringAssembler() : in_cu_(false) { StartCU(); } + + // Add the string S to this StringAssembler, and return the string's + // offset within this compilation unit's strings. If S has been added + // already, this returns the offset of its first instance. + size_t Add(const string &s) { + map::iterator it = added_.find(s); + if (it != added_.end()) + return it->second; + size_t offset = Size() - cu_start_; + AppendCString(s); + added_[s] = offset; + return offset; + } + + // Start a fresh compilation unit string collection. + void StartCU() { + // Ignore duplicate calls to StartCU. Our test data don't always call + // StartCU at all, meaning that our constructor has to take care of it, + // meaning that tests that *do* call StartCU call it twice at the + // beginning. This is not worth smoothing out. + if (in_cu_) return; + + added_.clear(); + cu_start_ = Size(); + + // Each compilation unit's strings start with an empty string. + AppendCString(""); + added_[""] = 0; + + in_cu_ = true; + } + + // Finish off the current CU's strings. + size_t EndCU() { + assert(in_cu_); + in_cu_ = false; + return Size() - cu_start_; + } + + private: + // The offset of the start of this compilation unit's strings. + size_t cu_start_; + + // True if we're in a CU. + bool in_cu_; + + // A map from the strings that have been added to this section to + // their starting indices within their compilation unit. + map added_; +}; + +// A StabsAssembler is a class for generating .stab sections to present as +// test input for the STABS parser. +class StabsAssembler: public Section { + public: + // Create a StabsAssembler that uses StringAssembler for its strings. + StabsAssembler(StringAssembler *string_assembler) + : Section(string_assembler->endianness()), + string_assembler_(string_assembler), + value_size_(0), + entry_count_(0), + cu_header_(NULL) { } + ~StabsAssembler() { assert(!cu_header_); } + + // Accessor and setter for value_size_. + size_t value_size() const { return value_size_; } + StabsAssembler &set_value_size(size_t value_size) { + value_size_ = value_size; + return *this; + } + + // Append a STAB entry to the end of this section with the given + // characteristics. NAME is the offset of this entry's name string within + // its compilation unit's portion of the .stabstr section; this can be a + // value generated by a StringAssembler. Return a reference to this + // StabsAssembler. + StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor, + Label value, Label name) { + D32(name); + D8(type); + D8(other); + D16(descriptor); + Append(endianness(), value_size_, value); + entry_count_++; + return *this; + } + + // As above, but automatically add NAME to our StringAssembler. + StabsAssembler &Stab(uint8_t type, uint8_t other, Label descriptor, + Label value, const string &name) { + return Stab(type, other, descriptor, value, string_assembler_->Add(name)); + } + + // Start a compilation unit named NAME, with an N_UNDF symbol to start + // it, and its own portion of the string section. Return a reference to + // this StabsAssembler. + StabsAssembler &StartCU(const string &name) { + assert(!cu_header_); + cu_header_ = new CUHeader; + string_assembler_->StartCU(); + entry_count_ = 0; + return Stab(N_UNDF, 0, + cu_header_->final_entry_count, + cu_header_->final_string_size, + string_assembler_->Add(name)); + } + + // Close off the current compilation unit. Return a reference to this + // StabsAssembler. + StabsAssembler &EndCU() { + assert(cu_header_); + cu_header_->final_entry_count = entry_count_; + cu_header_->final_string_size = string_assembler_->EndCU(); + delete cu_header_; + cu_header_ = NULL; + return *this; + } + + private: + // Data used in a compilation unit header STAB that we won't know until + // we've finished the compilation unit. + struct CUHeader { + // The final number of entries this compilation unit will hold. + Label final_entry_count; + + // The final size of this compilation unit's strings. + Label final_string_size; + }; + + // The strings for our STABS entries. + StringAssembler *string_assembler_; + + // The size of the 'value' field of stabs entries in this section. + size_t value_size_; + + // The number of entries in this compilation unit so far. + size_t entry_count_; + + // Header labels for this compilation unit, if we've started one but not + // finished it. + CUHeader *cu_header_; +}; + +class MockStabsReaderHandler: public StabsHandler { + public: + MOCK_METHOD3(StartCompilationUnit, + bool(const char *, uint64_t, const char *)); + MOCK_METHOD1(EndCompilationUnit, bool(uint64_t)); + MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t)); + MOCK_METHOD1(EndFunction, bool(uint64_t)); + MOCK_METHOD3(Line, bool(uint64_t, const char *, int)); + MOCK_METHOD2(Extern, bool(const std::string &, uint64_t)); + void Warning(const char *format, ...) { MockWarning(format); } + MOCK_METHOD1(MockWarning, void(const char *)); +}; + +struct StabsFixture { + StabsFixture() : stabs(&strings), unitized(true) { } + + // Create a StabsReader to parse the mock stabs data in stabs and + // strings, and pass the parsed information to mock_handler. Use the + // endianness and value size of stabs to parse the data. If all goes + // well, return the result of calling the reader's Process member + // function. Otherwise, return false. + bool ApplyHandlerToMockStabsData() { + string stabs_contents, stabstr_contents; + if (!stabs.GetContents(&stabs_contents) || + !strings.GetContents(&stabstr_contents)) + return false; + + // Run the parser on the test input, passing whatever we find to HANDLER. + StabsReader reader( + reinterpret_cast(stabs_contents.data()), + stabs_contents.size(), + reinterpret_cast(stabstr_contents.data()), + stabstr_contents.size(), + stabs.endianness() == kBigEndian, stabs.value_size(), unitized, + &mock_handler); + return reader.Process(); + } + + StringAssembler strings; + StabsAssembler stabs; + bool unitized; + MockStabsReaderHandler mock_handler; +}; + +class Stabs: public StabsFixture, public Test { }; + +TEST_F(Stabs, MockStabsInput) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + stabs + .Stab(N_SO, 149, 40232, 0x18a2a72bU, "builddir/") + .Stab(N_FUN, 83, 50010, 0x91a5353fU, + "not the SO with source file name we expected ") + .Stab(N_SO, 165, 24791, 0xfe69d23cU, "") + .Stab(N_SO, 184, 34178, 0xca4d883aU, "builddir1/") + .Stab(N_SO, 83, 40859, 0xd2fe5df3U, "file1.c") + .Stab(N_LSYM, 147, 39565, 0x60d4bb8aU, "not the FUN we're looking for") + .Stab(N_FUN, 120, 50271, 0xa049f4b1U, "fun1") + .Stab(N_BINCL, 150, 15694, 0xef65c659U, + "something to ignore in a FUN body") + .Stab(N_SLINE, 147, 4967, 0xd904b3f, "") + .Stab(N_SOL, 177, 56135, 0xbd97b1dcU, "header.h") + .Stab(N_SLINE, 130, 24610, 0x90f145b, "") + .Stab(N_FUN, 45, 32441, 0xbf27cf93U, + "fun2:some stabs type info here:to trim from the name") + .Stab(N_SLINE, 138, 39002, 0x8148b87, "") + .Stab(N_SOL, 60, 49318, 0x1d06e025U, "file1.c") + .Stab(N_SLINE, 29, 52163, 0x6eebbb7, "") + .Stab(N_SO, 167, 4647, 0xd04b7448U, "") + .Stab(N_LSYM, 58, 37837, 0xe6b14d37U, "") + .Stab(N_SO, 152, 7810, 0x11759f10U, "file3.c") + .Stab(N_SO, 218, 12447, 0x11cfe4b5U, ""); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file1.c"), 0xd2fe5df3U, + StrEq("builddir1/"))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0xa049f4b1U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xa049f4b1U + 0xd904b3f, StrEq("file1.c"), 4967)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xa049f4b1U + 0x90f145b, StrEq("header.h"), 24610)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xbf27cf93U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0xbf27cf93U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xbf27cf93U + 0x8148b87, StrEq("header.h"), 39002)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0xbf27cf93U + 0x6eebbb7, StrEq("file1.c"), 52163)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xd04b7448U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xd04b7448U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"), + 0x11759f10U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x11cfe4b5U)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, AbruptCU) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(4); + stabs.Stab(N_SO, 177, 23446, 0xbf10d5e4, "file2-1.c"); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file2-1.c"), 0xbf10d5e4, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, AbruptFunction) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(8); + stabs + .Stab(N_SO, 218, 26631, 0xb83ddf10U, "file3-1.c") + .Stab(N_FUN, 113, 24765, 0xbbd4a145U, "fun3_1"); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file3-1.c"), 0xb83ddf10U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0xbbd4a145U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, NoCU) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(8); + stabs.Stab(N_SO, 161, 25673, 0x8f676e7bU, "build-directory/"); + + EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _)) + .Times(0); + EXPECT_CALL(mock_handler, StartFunction(_, _)) + .Times(0); + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, NoCUEnd) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(8); + stabs + .Stab(N_SO, 116, 58280, 0x2f7493c9U, "file5-1.c") + .Stab(N_SO, 224, 23057, 0xf9f1d50fU, "file5-2.c"); + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file5-1.c"), 0x2f7493c9U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file5-2.c"), 0xf9f1d50fU, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +// On systems that store STABS in sections, string offsets are relative to +// the beginning of that compilation unit's strings, marked with N_UNDF +// symbols; see the comments for StabsReader::StabsReader. +TEST_F(Stabs, Unitized) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(4); + stabs + .StartCU("antimony") + .Stab(N_SO, 49, 26043, 0x7e259f1aU, "antimony") + .Stab(N_FUN, 101, 63253, 0x7fbcccaeU, "arsenic") + .Stab(N_SO, 124, 37175, 0x80b0014cU, "") + .EndCU() + .StartCU("aluminum") + .Stab(N_SO, 72, 23084, 0x86756839U, "aluminum") + .Stab(N_FUN, 59, 3305, 0xa8e120b0U, "selenium") + .Stab(N_SO, 178, 56949, 0xbffff983U, "") + .EndCU(); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("antimony"), 0x7e259f1aU, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x7fbcccaeU)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0x80b0014cU)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x80b0014cU)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("aluminum"), 0x86756839U, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0xa8e120b0U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xbffff983U)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xbffff983U)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +// On systems that store STABS entries in the real symbol table, the N_UNDF +// entries have no special meaning, and shouldn't mess up the string +// indices. +TEST_F(Stabs, NonUnitized) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + unitized = false; + stabs + .Stab(N_UNDF, 21, 11551, 0x9bad2b2e, "") + .Stab(N_UNDF, 21, 11551, 0x9bad2b2e, "") + .Stab(N_SO, 71, 45139, 0x11a97352, "Tanzania") + .Stab(N_SO, 221, 41976, 0x21a97352, ""); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("Tanzania"), + 0x11a97352, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x21a97352)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, FunctionEnd) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(8); + stabs + .Stab(N_SO, 102, 62362, 0x52a830d644cd6942ULL, "compilation unit") + // This function is terminated by the start of the next function. + .Stab(N_FUN, 216, 38405, 0xbb5ab70ecdd23bfeULL, "function 1") + // This function is terminated by an explicit end-of-function stab, + // whose value is a size in bytes. + .Stab(N_FUN, 240, 10973, 0xc954de9b8fb3e5e2ULL, "function 2") + .Stab(N_FUN, 14, 36749, 0xc1ab, "") + // This function is terminated by the end of the compilation unit. + .Stab(N_FUN, 143, 64514, 0xdff98c9a35386e1fULL, "function 3") + .Stab(N_SO, 164, 60142, 0xfdacb856e78bbf57ULL, ""); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("compilation unit"), + 0x52a830d644cd6942ULL, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("function 1"), 0xbb5ab70ecdd23bfeULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xc954de9b8fb3e5e2ULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("function 2"), 0xc954de9b8fb3e5e2ULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xc954de9b8fb3e5e2ULL + 0xc1ab)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("function 3"), 0xdff98c9a35386e1fULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xfdacb856e78bbf57ULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xfdacb856e78bbf57ULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +// On Mac OS X, SLINE records can appear before the FUN stab to which they +// belong, and their values are absolute addresses, not offsets. +TEST_F(Stabs, LeadingLine) { + stabs.set_endianness(kBigEndian); + stabs.set_value_size(4); + stabs + .Stab(N_SO, 179, 27357, 0x8adabc15, "build directory/") + .Stab(N_SO, 52, 53058, 0x4c7e3bf4, "compilation unit") + .Stab(N_SOL, 165, 12086, 0x6a797ca3, "source file name") + .Stab(N_SLINE, 229, 20015, 0x4cb3d7e0, "") + .Stab(N_SLINE, 89, 43802, 0x4cba8b88, "") + .Stab(N_FUN, 251, 51639, 0xce1b98fa, "rutabaga") + .Stab(N_FUN, 218, 16113, 0x5798, "") + .Stab(N_SO, 52, 53058, 0xd4af4415, ""); + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("compilation unit"), + 0x4c7e3bf4, StrEq("build directory/"))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartFunction(Eq("rutabaga"), 0xce1b98fa)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0x4cb3d7e0, StrEq("source file name"), 20015)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Line(0x4cba8b88, StrEq("source file name"), 43802)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0xce1b98fa + 0x5798)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0xd4af4415)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + + +#if defined(HAVE_MACH_O_NLIST_H) +// These tests have no meaning on non-Mach-O-based systems, as +// only Mach-O uses N_SECT to represent public symbols. +TEST_F(Stabs, OnePublicSymbol) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + + const u_int32_t kExpectedAddress = 0x9000; + const string kExpectedFunctionName("public_function"); + stabs + .Stab(N_SECT, 1, 0, kExpectedAddress, kExpectedFunctionName); + + { + InSequence s; + EXPECT_CALL(mock_handler, + Extern(StrEq(kExpectedFunctionName), + kExpectedAddress)) + .WillOnce(Return(true)); + } + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +TEST_F(Stabs, TwoPublicSymbols) { + stabs.set_endianness(kLittleEndian); + stabs.set_value_size(4); + + const u_int32_t kExpectedAddress1 = 0xB0B0B0B0; + const string kExpectedFunctionName1("public_function"); + const u_int32_t kExpectedAddress2 = 0xF0F0F0F0; + const string kExpectedFunctionName2("something else"); + stabs + .Stab(N_SECT, 1, 0, kExpectedAddress1, kExpectedFunctionName1) + .Stab(N_SECT, 1, 0, kExpectedAddress2, kExpectedFunctionName2); + + { + InSequence s; + EXPECT_CALL(mock_handler, + Extern(StrEq(kExpectedFunctionName1), + kExpectedAddress1)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + Extern(StrEq(kExpectedFunctionName2), + kExpectedAddress2)) + .WillOnce(Return(true)); + } + ASSERT_TRUE(ApplyHandlerToMockStabsData()); +} + +#endif + +} // anonymous namespace diff --git a/src/lib/crashdump/gbreakpad/common/stabs_to_module.cc b/src/lib/crashdump/gbreakpad/common/stabs_to_module.cc new file mode 100644 index 0000000..67cd13b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/stabs_to_module.cc @@ -0,0 +1,201 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dump_stabs.cc --- implement the StabsToModule class. + +#include +#include +#include +#include + +#include + +#include "common/stabs_to_module.h" + +namespace google_breakpad { + +using std::string; + +// Demangle using abi call. +// Older GCC may not support it. +static string Demangle(const string &mangled) { + int status = 0; + char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); + if (status == 0 && demangled != NULL) { + string str(demangled); + free(demangled); + return str; + } + return string(mangled); +} + +StabsToModule::~StabsToModule() { + // Free any functions we've accumulated but not added to the module. + for (vector::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); func_it++) + delete *func_it; + // Free any function that we're currently within. + delete current_function_; +} + +bool StabsToModule::StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory) { + assert(!in_compilation_unit_); + in_compilation_unit_ = true; + current_source_file_name_ = name; + current_source_file_ = module_->FindFile(name); + comp_unit_base_address_ = address; + boundaries_.push_back(static_cast(address)); + return true; +} + +bool StabsToModule::EndCompilationUnit(uint64_t address) { + assert(in_compilation_unit_); + in_compilation_unit_ = false; + comp_unit_base_address_ = 0; + current_source_file_ = NULL; + current_source_file_name_ = NULL; + if (address) + boundaries_.push_back(static_cast(address)); + return true; +} + +bool StabsToModule::StartFunction(const string &name, + uint64_t address) { + assert(!current_function_); + Module::Function *f = new Module::Function; + f->name = Demangle(name); + f->address = address; + f->size = 0; // We compute this in StabsToModule::Finalize(). + f->parameter_size = 0; // We don't provide this information. + current_function_ = f; + boundaries_.push_back(static_cast(address)); + return true; +} + +bool StabsToModule::EndFunction(uint64_t address) { + assert(current_function_); + // Functions in this compilation unit should have address bigger + // than the compilation unit's starting address. There may be a lot + // of duplicated entries for functions in the STABS data. We will + // count on the Module to remove the duplicates. + if (current_function_->address >= comp_unit_base_address_) + functions_.push_back(current_function_); + else + delete current_function_; + current_function_ = NULL; + if (address) + boundaries_.push_back(static_cast(address)); + return true; +} + +bool StabsToModule::Line(uint64_t address, const char *name, int number) { + assert(current_function_); + assert(current_source_file_); + if (name != current_source_file_name_) { + current_source_file_ = module_->FindFile(name); + current_source_file_name_ = name; + } + Module::Line line; + line.address = address; + line.size = 0; // We compute this in StabsToModule::Finalize(). + line.file = current_source_file_; + line.number = number; + current_function_->lines.push_back(line); + return true; +} + +bool StabsToModule::Extern(const string &name, uint64_t address) { + Module::Extern *ext = new Module::Extern; + // Older libstdc++ demangle implementations can crash on unexpected + // input, so be careful about what gets passed in. + if (name.compare(0, 3, "__Z") == 0) { + ext->name = Demangle(name.substr(1)); + } else if (name[0] == '_') { + ext->name = name.substr(1); + } else { + ext->name = name; + } + ext->address = address; + module_->AddExtern(ext); + return true; +} + +void StabsToModule::Warning(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +void StabsToModule::Finalize() { + // Sort our boundary list, so we can search it quickly. + sort(boundaries_.begin(), boundaries_.end()); + // Sort all functions by address, just for neatness. + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + + for (vector::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); + func_it++) { + Module::Function *f = *func_it; + // Compute the function f's size. + vector::const_iterator boundary + = std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address); + if (boundary != boundaries_.end()) + f->size = *boundary - f->address; + else + // If this is the last function in the module, and the STABS + // reader was unable to give us its ending address, then assign + // it a bogus, very large value. This will happen at most once + // per module: since we've added all functions' addresses to the + // boundary table, only one can be the last. + f->size = kFallbackSize; + + // Compute sizes for each of the function f's lines --- if it has any. + if (!f->lines.empty()) { + stable_sort(f->lines.begin(), f->lines.end(), + Module::Line::CompareByAddress); + vector::iterator last_line = f->lines.end() - 1; + for (vector::iterator line_it = f->lines.begin(); + line_it != last_line; line_it++) + line_it[0].size = line_it[1].address - line_it[0].address; + // Compute the size of the last line from f's end address. + last_line->size = (f->address + f->size) - last_line->address; + } + } + // Now that everything has a size, add our functions to the module, and + // dispose of our private list. + module_->AddFunctions(functions_.begin(), functions_.end()); + functions_.clear(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/stabs_to_module.h b/src/lib/crashdump/gbreakpad/common/stabs_to_module.h new file mode 100644 index 0000000..632f4d0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/stabs_to_module.h @@ -0,0 +1,143 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dump_stabs.h: Define the StabsToModule class, which receives +// STABS debugging information from a parser and adds it to a Breakpad +// symbol file. + +#ifndef BREAKPAD_COMMON_STABS_TO_MODULE_H_ +#define BREAKPAD_COMMON_STABS_TO_MODULE_H_ + +#include + +#include +#include + +#include "common/module.h" +#include "common/stabs_reader.h" + +namespace google_breakpad { + +using std::string; +using std::vector; + +// A StabsToModule is a handler that receives parsed STABS debugging +// information from a StabsReader, and uses that to populate +// a Module. (All classes are in the google_breakpad namespace.) A +// Module represents the contents of a Breakpad symbol file, and knows +// how to write itself out as such. A StabsToModule thus acts as +// the bridge between STABS and Breakpad data. +// When processing Darwin Mach-O files, this also receives public linker +// symbols, like those found in system libraries. +class StabsToModule: public google_breakpad::StabsHandler { + public: + // Receive parsed debugging information from a StabsReader, and + // store it all in MODULE. + StabsToModule(Module *module) : + module_(module), + in_compilation_unit_(false), + comp_unit_base_address_(0), + current_function_(NULL), + current_source_file_(NULL), + current_source_file_name_(NULL) { } + ~StabsToModule(); + + // The standard StabsHandler virtual member functions. + bool StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory); + bool EndCompilationUnit(uint64_t address); + bool StartFunction(const string &name, uint64_t address); + bool EndFunction(uint64_t address); + bool Line(uint64_t address, const char *name, int number); + bool Extern(const string &name, uint64_t address); + void Warning(const char *format, ...); + + // Do any final processing necessary to make module_ contain all the + // data provided by the STABS reader. + // + // Because STABS does not provide reliable size information for + // functions and lines, we need to make a pass over the data after + // processing all the STABS to compute those sizes. We take care of + // that here. + void Finalize(); + + private: + + // An arbitrary, but very large, size to use for functions whose + // size we can't compute properly. + static const uint64_t kFallbackSize = 0x10000000; + + // The module we're contributing debugging info to. + Module *module_; + + // The functions we've generated so far. We don't add these to + // module_ as we parse them. Instead, we wait until we've computed + // their ending address, and their lines' ending addresses. + // + // We could just stick them in module_ from the outset, but if + // module_ already contains data gathered from other debugging + // formats, that would complicate the size computation. + vector functions_; + + // Boundary addresses. STABS doesn't necessarily supply sizes for + // functions and lines, so we need to compute them ourselves by + // finding the next object. + vector boundaries_; + + // True if we are currently within a compilation unit: we have gotten a + // StartCompilationUnit call, but no matching EndCompilationUnit call + // yet. We use this for sanity checks. + bool in_compilation_unit_; + + // The base address of the current compilation unit. We use this to + // recognize functions we should omit from the symbol file. (If you + // know the details of why we omit these, please patch this + // comment.) + Module::Address comp_unit_base_address_; + + // The function we're currently contributing lines to. + Module::Function *current_function_; + + // The last Module::File we got a line number in. + Module::File *current_source_file_; + + // The pointer in the .stabstr section of the name that + // current_source_file_ is built from. This allows us to quickly + // recognize when the current line is in the same file as the + // previous one (which it usually is). + const char *current_source_file_name_; +}; + +} // namespace google_breakpad + +#endif // BREAKPAD_COMMON_STABS_TO_MODULE_H_ diff --git a/src/lib/crashdump/gbreakpad/common/stabs_to_module_unittest.cc b/src/lib/crashdump/gbreakpad/common/stabs_to_module_unittest.cc new file mode 100644 index 0000000..d445d1d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/stabs_to_module_unittest.cc @@ -0,0 +1,258 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// dump_stabs_unittest.cc: Unit tests for StabsToModule. + +#include + +#include "breakpad_googletest_includes.h" +#include "common/stabs_to_module.h" + +using google_breakpad::Module; +using google_breakpad::StabsToModule; +using std::vector; + +TEST(StabsToModule, SimpleCU) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Feed in a simple compilation unit that defines a function with + // one line. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL)); + EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314)); + EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL)); + EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL)); + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("source-file-name"); + ASSERT_TRUE(file != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.c_str()); + EXPECT_EQ(0xfde4abbed390c394LL, function->address); + EXPECT_EQ(0x10U, function->size); + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ((size_t) 1, function->lines.size()); + Module::Line *line = &function->lines[0]; + EXPECT_EQ(0xfde4abbed390c394LL, line->address); + EXPECT_EQ(0x10U, line->size); // derived from EndFunction + EXPECT_TRUE(line->file == file); + EXPECT_EQ(174823314, line->number); +} + +#ifdef __GNUC__ +// Function name mangling can vary by compiler, so only run mangled-name +// tests on GCC for simplicity's sake. +TEST(StabsToModule, Externs) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Feed in a few Extern symbols. + EXPECT_TRUE(h.Extern("_foo", 0xffff)); + EXPECT_TRUE(h.Extern("__Z21dyldGlobalLockAcquirev", 0xaaaa)); + EXPECT_TRUE(h.Extern("_MorphTableGetNextMorphChain", 0x1111)); + h.Finalize(); + + // Now check to see what has been added to the Module. + vector externs; + m.GetExterns(&externs, externs.end()); + ASSERT_EQ((size_t) 3, externs.size()); + Module::Extern *extern1 = externs[0]; + EXPECT_STREQ("MorphTableGetNextMorphChain", extern1->name.c_str()); + EXPECT_EQ((Module::Address)0x1111, extern1->address); + Module::Extern *extern2 = externs[1]; + EXPECT_STREQ("dyldGlobalLockAcquire()", extern2->name.c_str()); + EXPECT_EQ((Module::Address)0xaaaa, extern2->address); + Module::Extern *extern3 = externs[2]; + EXPECT_STREQ("foo", extern3->name.c_str()); + EXPECT_EQ((Module::Address)0xffff, extern3->address); +} +#endif // __GNUC__ + +TEST(StabsToModule, DuplicateFunctionNames) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Compilation unit with one function, mangled name. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda36ecf7f46cLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("funcfoo", + 0xf2cfda36ecf7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.StartFunction("funcfoo", + 0xf2cfda36ecf7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); + + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("compilation-unit"); + ASSERT_TRUE(file != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ(1U, functions.size()); + + Module::Function *function = functions[0]; + EXPECT_EQ(0xf2cfda36ecf7f46dLL, function->address); + EXPECT_LT(0U, function->size); // should have used dummy size + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ(0U, function->lines.size()); +} + +TEST(InferSizes, LineSize) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Feed in a simple compilation unit that defines a function with + // one line. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL)); + EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614)); + EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088)); + EXPECT_TRUE(h.EndFunction(0)); // unknown function end address + EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL, + "build-directory-2")); // next boundary + EXPECT_TRUE(h.EndCompilationUnit(0)); + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file1 = m.FindExistingFile("source-file-name-1"); + ASSERT_TRUE(file1 != NULL); + Module::File *file2 = m.FindExistingFile("source-file-name-2"); + ASSERT_TRUE(file2 != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.c_str()); + EXPECT_EQ(0xb4513962eff94e92LL, function->address); + EXPECT_EQ(0x1000100000000ULL, function->size); // inferred from CU end + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ((size_t) 2, function->lines.size()); + + Module::Line *line1 = &function->lines[0]; + EXPECT_EQ(0xb4513962eff94e92LL, line1->address); + EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction + EXPECT_TRUE(line1->file == file1); + EXPECT_EQ(77396614, line1->number); + + Module::Line *line2 = &function->lines[1]; + EXPECT_EQ(0xb4513963eff94e92LL, line2->address); + EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction + EXPECT_TRUE(line2->file == file2); + EXPECT_EQ(87660088, line2->number); +} + +#ifdef __GNUC__ +// Function name mangling can vary by compiler, so only run mangled-name +// tests on GCC for simplicity's sake. +TEST(FunctionNames, Mangled) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // Compilation unit with one function, mangled name. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy", + 0xf2cfda63cef7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); + + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("compilation-unit"); + ASSERT_TRUE(file != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ(1U, functions.size()); + + Module::Function *function = functions[0]; + // This is GCC-specific, but we shouldn't be seeing STABS data anywhere + // but Linux. + EXPECT_STREQ("std::vector >::" + "push_back(unsigned long long const&)", + function->name.c_str()); + EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address); + EXPECT_LT(0U, function->size); // should have used dummy size + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ(0U, function->lines.size()); +} +#endif // __GNUC__ + +// The GNU toolchain can omit functions that are not used; however, +// when it does so, it doesn't clean up the debugging information that +// refers to them. In STABS, this results in compilation units whose +// SO addresses are zero. +TEST(Omitted, Function) { + Module m("name", "os", "arch", "id"); + StabsToModule h(&m); + + // The StartCompilationUnit and EndCompilationUnit calls may both have an + // address of zero if the compilation unit has had sections removed. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0x2a133596)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); +} + +// TODO --- if we actually cared about STABS. Even without these we've +// got full coverage of non-failure source lines in dump_stabs.cc. + +// Line size from next line +// Line size from function end +// Line size from next function start +// line size from cu end +// line size from next cu start +// fallback size is something plausible + +// function size from function end +// function size from next function start +// function size from cu end +// function size from next cu start +// fallback size is something plausible + +// omitting functions outside the compilation unit's address range +// zero-line, one-line, many-line functions diff --git a/src/lib/crashdump/gbreakpad/common/string_conversion.cc b/src/lib/crashdump/gbreakpad/common/string_conversion.cc new file mode 100644 index 0000000..ac4f1da --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/string_conversion.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/convert_UTF.h" +#include "processor/scoped_ptr.h" +#include "common/string_conversion.h" +#include + +namespace google_breakpad { + +using std::string; +using std::vector; + +void UTF8ToUTF16(const char *in, vector *out) { + size_t source_length = strlen(in); + const UTF8 *source_ptr = reinterpret_cast(in); + const UTF8 *source_end_ptr = source_ptr + source_length; + // Erase the contents and zero fill to the expected size + out->clear(); + out->insert(out->begin(), source_length, 0); + u_int16_t *target_ptr = &(*out)[0]; + u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t); + ConversionResult result = ConvertUTF8toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + // Resize to be the size of the # of converted characters + NULL + out->resize(result == conversionOK ? target_ptr - &(*out)[0] + 1: 0); +} + +int UTF8ToUTF16Char(const char *in, int in_length, u_int16_t out[2]) { + const UTF8 *source_ptr = reinterpret_cast(in); + const UTF8 *source_end_ptr = source_ptr + sizeof(char); + u_int16_t *target_ptr = out; + u_int16_t *target_end_ptr = target_ptr + 2 * sizeof(u_int16_t); + out[0] = out[1] = 0; + + // Process one character at a time + while (1) { + ConversionResult result = ConvertUTF8toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + if (result == conversionOK) + return static_cast(source_ptr - reinterpret_cast(in)); + + // Add another character to the input stream and try again + source_ptr = reinterpret_cast(in); + ++source_end_ptr; + + if (source_end_ptr > reinterpret_cast(in) + in_length) + break; + } + + return 0; +} + +void UTF32ToUTF16(const wchar_t *in, vector *out) { + size_t source_length = wcslen(in); + const UTF32 *source_ptr = reinterpret_cast(in); + const UTF32 *source_end_ptr = source_ptr + source_length; + // Erase the contents and zero fill to the expected size + out->clear(); + out->insert(out->begin(), source_length, 0); + u_int16_t *target_ptr = &(*out)[0]; + u_int16_t *target_end_ptr = target_ptr + out->capacity() * sizeof(u_int16_t); + ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + // Resize to be the size of the # of converted characters + NULL + out->resize(result == conversionOK ? target_ptr - &(*out)[0] + 1: 0); +} + +void UTF32ToUTF16Char(wchar_t in, u_int16_t out[2]) { + const UTF32 *source_ptr = reinterpret_cast(&in); + const UTF32 *source_end_ptr = source_ptr + 1; + u_int16_t *target_ptr = out; + u_int16_t *target_end_ptr = target_ptr + 2 * sizeof(u_int16_t); + out[0] = out[1] = 0; + ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + if (result != conversionOK) { + out[0] = out[1] = 0; + } +} + +static inline u_int16_t Swap(u_int16_t value) { + return (value >> 8) | (value << 8); +} + +string UTF16ToUTF8(const vector &in, bool swap) { + const UTF16 *source_ptr = &in[0]; + scoped_ptr source_buffer; + + // If we're to swap, we need to make a local copy and swap each byte pair + if (swap) { + int idx = 0; + source_buffer.reset(new u_int16_t[in.size()]); + UTF16 *source_buffer_ptr = source_buffer.get(); + for (vector::const_iterator it = in.begin(); + it != in.end(); ++it, ++idx) + source_buffer_ptr[idx] = Swap(*it); + + source_ptr = source_buffer.get(); + } + + // The maximum expansion would be 4x the size of the input string. + const UTF16 *source_end_ptr = source_ptr + in.size(); + size_t target_capacity = in.size() * 4; + scoped_array target_buffer(new UTF8[target_capacity]); + UTF8 *target_ptr = target_buffer.get(); + UTF8 *target_end_ptr = target_ptr + target_capacity; + ConversionResult result = ConvertUTF16toUTF8(&source_ptr, source_end_ptr, + &target_ptr, target_end_ptr, + strictConversion); + + if (result == conversionOK) { + const char *targetPtr = reinterpret_cast(target_buffer.get()); + return targetPtr; + } + + return ""; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/string_conversion.h b/src/lib/crashdump/gbreakpad/common/string_conversion.h new file mode 100644 index 0000000..d51f46b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/string_conversion.h @@ -0,0 +1,66 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// string_conversion.h: Conversion between different UTF-8/16/32 encodings. + +#ifndef COMMON_STRING_CONVERSION_H__ +#define COMMON_STRING_CONVERSION_H__ + +#include +#include +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::vector; + +// Convert |in| to UTF-16 into |out|. Use platform byte ordering. If the +// conversion failed, |out| will be zero length. +void UTF8ToUTF16(const char *in, vector *out); + +// Convert at least one character (up to a maximum of |in_length|) from |in| +// to UTF-16 into |out|. Return the number of characters consumed from |in|. +// Any unused characters in |out| will be initialized to 0. No memory will +// be allocated by this routine. +int UTF8ToUTF16Char(const char *in, int in_length, u_int16_t out[2]); + +// Convert |in| to UTF-16 into |out|. Use platform byte ordering. If the +// conversion failed, |out| will be zero length. +void UTF32ToUTF16(const wchar_t *in, vector *out); + +// Convert |in| to UTF-16 into |out|. Any unused characters in |out| will be +// initialized to 0. No memory will be allocated by this routine. +void UTF32ToUTF16Char(wchar_t in, u_int16_t out[2]); + +// Convert |in| to UTF-8. If |swap| is true, swap bytes before converting. +std::string UTF16ToUTF8(const vector &in, bool swap); + +} // namespace google_breakpad + +#endif // COMMON_STRING_CONVERSION_H__ diff --git a/src/lib/crashdump/gbreakpad/common/test_assembler.cc b/src/lib/crashdump/gbreakpad/common/test_assembler.cc new file mode 100644 index 0000000..7f5dd38 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/test_assembler.cc @@ -0,0 +1,359 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// test_assembler.cc: Implementation of google_breakpad::TestAssembler. +// See test_assembler.h for details. + +#include "common/test_assembler.h" + +#include +#include + +#include + +namespace google_breakpad { +namespace test_assembler { + +using std::back_insert_iterator; + +Label::Label() : value_(new Binding()) { } +Label::Label(u_int64_t value) : value_(new Binding(value)) { } +Label::Label(const Label &label) { + value_ = label.value_; + value_->Acquire(); +} +Label::~Label() { + if (value_->Release()) delete value_; +} + +Label &Label::operator=(u_int64_t value) { + value_->Set(NULL, value); + return *this; +} + +Label &Label::operator=(const Label &label) { + value_->Set(label.value_, 0); + return *this; +} + +Label Label::operator+(u_int64_t addend) const { + Label l; + l.value_->Set(this->value_, addend); + return l; +} + +Label Label::operator-(u_int64_t subtrahend) const { + Label l; + l.value_->Set(this->value_, -subtrahend); + return l; +} + +// When NDEBUG is #defined, assert doesn't evaluate its argument. This +// means you can't simply use assert to check the return value of a +// function with necessary side effects. +// +// ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether +// NDEBUG is #defined; when NDEBUG is not #defined, it further asserts +// that x is true. +#ifdef NDEBUG +#define ALWAYS_EVALUATE_AND_ASSERT(x) x +#else +#define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x) +#endif + +u_int64_t Label::operator-(const Label &label) const { + u_int64_t offset; + ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset)); + return offset; +} + +u_int64_t Label::Value() const { + u_int64_t v = 0; + ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v)); + return v; +}; + +bool Label::IsKnownConstant(u_int64_t *value_p) const { + Binding *base; + u_int64_t addend; + value_->Get(&base, &addend); + if (base != NULL) return false; + if (value_p) *value_p = addend; + return true; +} + +bool Label::IsKnownOffsetFrom(const Label &label, u_int64_t *offset_p) const +{ + Binding *label_base, *this_base; + u_int64_t label_addend, this_addend; + label.value_->Get(&label_base, &label_addend); + value_->Get(&this_base, &this_addend); + // If this and label are related, Get will find their final + // common ancestor, regardless of how indirect the relation is. This + // comparison also handles the constant vs. constant case. + if (this_base != label_base) return false; + if (offset_p) *offset_p = this_addend - label_addend; + return true; +} + +Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { } + +Label::Binding::Binding(u_int64_t addend) + : base_(NULL), addend_(addend), reference_count_(1) { } + +Label::Binding::~Binding() { + assert(reference_count_ == 0); + if (base_ && base_ != this && base_->Release()) + delete base_; +} + +void Label::Binding::Set(Binding *binding, u_int64_t addend) { + if (!base_ && !binding) { + // We're equating two constants. This could be okay. + assert(addend_ == addend); + } else if (!base_) { + // We are a known constant, but BINDING may not be, so turn the + // tables and try to set BINDING's value instead. + binding->Set(NULL, addend_ - addend); + } else { + if (binding) { + // Find binding's final value. Since the final value is always either + // completely unconstrained or a constant, never a reference to + // another variable (otherwise, it wouldn't be final), this + // guarantees we won't create cycles here, even for code like this: + // l = m, m = n, n = l; + u_int64_t binding_addend; + binding->Get(&binding, &binding_addend); + addend += binding_addend; + } + + // It seems likely that setting a binding to itself is a bug + // (although I can imagine this might turn out to be helpful to + // permit). + assert(binding != this); + + if (base_ != this) { + // Set the other bindings on our chain as well. Note that this + // is sufficient even though binding relationships form trees: + // All binding operations traverse their chains to the end, and + // all bindings related to us share some tail of our chain, so + // they will see the changes we make here. + base_->Set(binding, addend - addend_); + // We're not going to use base_ any more. + if (base_->Release()) delete base_; + } + + // Adopt BINDING as our base. Note that it should be correct to + // acquire here, after the release above, even though the usual + // reference-counting rules call for acquiring first, and then + // releasing: the self-reference assertion above should have + // complained if BINDING were 'this' or anywhere along our chain, + // so we didn't release BINDING. + if (binding) binding->Acquire(); + base_ = binding; + addend_ = addend; + } +} + +void Label::Binding::Get(Binding **base, u_int64_t *addend) { + if (base_ && base_ != this) { + // Recurse to find the end of our reference chain (the root of our + // tree), and then rewrite every binding along the chain to refer + // to it directly, adjusting addends appropriately. (This is why + // this member function isn't this-const.) + Binding *final_base; + u_int64_t final_addend; + base_->Get(&final_base, &final_addend); + if (final_base) final_base->Acquire(); + if (base_->Release()) delete base_; + base_ = final_base; + addend_ += final_addend; + } + *base = base_; + *addend = addend_; +} + +template +static inline void InsertEndian(test_assembler::Endianness endianness, + size_t size, u_int64_t number, Inserter dest) { + assert(size > 0); + if (endianness == kLittleEndian) { + for (size_t i = 0; i < size; i++) { + *dest++ = (char) (number & 0xff); + number >>= 8; + } + } else { + assert(endianness == kBigEndian); + // The loop condition is odd, but it's correct for size_t. + for (size_t i = size - 1; i < size; i--) + *dest++ = (char) ((number >> (i * 8)) & 0xff); + } +} + +Section &Section::Append(Endianness endianness, size_t size, u_int64_t number) { + InsertEndian(endianness, size, number, + back_insert_iterator(contents_)); + return *this; +} + +Section &Section::Append(Endianness endianness, size_t size, + const Label &label) { + // If this label's value is known, there's no reason to waste an + // entry in references_ on it. + u_int64_t value; + if (label.IsKnownConstant(&value)) + return Append(endianness, size, value); + + // This will get caught when the references are resolved, but it's + // nicer to find out earlier. + assert(endianness != kUnsetEndian); + + references_.push_back(Reference(contents_.size(), endianness, size, label)); + contents_.append(size, 0); + return *this; +} + +#define ENDIANNESS_L kLittleEndian +#define ENDIANNESS_B kBigEndian +#define ENDIANNESS(e) ENDIANNESS_ ## e + +#define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ + Section &Section::e ## bits(u_int ## bits ## _t v) { \ + InsertEndian(ENDIANNESS(e), bits / 8, v, \ + back_insert_iterator(contents_)); \ + return *this; \ + } + +#define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \ + Section &Section::e ## bits(const Label &v) { \ + return Append(ENDIANNESS(e), bits / 8, v); \ + } + +// Define L16, B32, and friends. +#define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \ + DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ + DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) + +DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8); +DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8); +DEFINE_SHORT_APPEND_ENDIAN(L, 16); +DEFINE_SHORT_APPEND_ENDIAN(L, 32); +DEFINE_SHORT_APPEND_ENDIAN(L, 64); +DEFINE_SHORT_APPEND_ENDIAN(B, 16); +DEFINE_SHORT_APPEND_ENDIAN(B, 32); +DEFINE_SHORT_APPEND_ENDIAN(B, 64); + +#define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ + Section &Section::D ## bits(u_int ## bits ## _t v) { \ + InsertEndian(endianness_, bits / 8, v, \ + back_insert_iterator(contents_)); \ + return *this; \ + } +#define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \ + Section &Section::D ## bits(const Label &v) { \ + return Append(endianness_, bits / 8, v); \ + } +#define DEFINE_SHORT_APPEND_DEFAULT(bits) \ + DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ + DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) + +DEFINE_SHORT_APPEND_LABEL_DEFAULT(8) +DEFINE_SHORT_APPEND_DEFAULT(16); +DEFINE_SHORT_APPEND_DEFAULT(32); +DEFINE_SHORT_APPEND_DEFAULT(64); + +Section &Section::Append(const Section §ion) { + size_t base = contents_.size(); + contents_.append(section.contents_); + for (vector::const_iterator it = section.references_.begin(); + it != section.references_.end(); it++) + references_.push_back(Reference(base + it->offset, it->endianness, + it->size, it->label)); + return *this; +} + +Section &Section::LEB128(long long value) { + while (value < -0x40 || 0x3f < value) { + contents_ += (value & 0x7f) | 0x80; + if (value < 0) + value = (value >> 7) | ~(((unsigned long long) -1) >> 7); + else + value = (value >> 7); + } + contents_ += value & 0x7f; + return *this; +} + +Section &Section::ULEB128(u_int64_t value) { + while (value > 0x7f) { + contents_ += (value & 0x7f) | 0x80; + value = (value >> 7); + } + contents_ += value; + return *this; +} + +Section &Section::Align(size_t alignment, u_int8_t pad_byte) { + // ALIGNMENT must be a power of two. + assert(((alignment - 1) & alignment) == 0); + size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1); + contents_.append(new_size - contents_.size(), pad_byte); + assert((contents_.size() & (alignment - 1)) == 0); + return *this; +} + +void Section::Clear() { + contents_.clear(); + references_.clear(); +} + +bool Section::GetContents(string *contents) { + // For each label reference, find the label's value, and patch it into + // the section's contents. + for (size_t i = 0; i < references_.size(); i++) { + Reference &r = references_[i]; + u_int64_t value; + if (!r.label.IsKnownConstant(&value)) { + fprintf(stderr, "Undefined label #%zu at offset 0x%zx\n", i, r.offset); + return false; + } + assert(r.offset < contents_.size()); + assert(contents_.size() - r.offset >= r.size); + InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset); + } + contents->clear(); + std::swap(contents_, *contents); + references_.clear(); + return true; +} + +} // namespace test_assembler +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/test_assembler.h b/src/lib/crashdump/gbreakpad/common/test_assembler.h new file mode 100644 index 0000000..5ce6b0c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/test_assembler.h @@ -0,0 +1,481 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// test-assembler.h: interface to class for building complex binary streams. + +// To test the Breakpad symbol dumper and processor thoroughly, for +// all combinations of host system and minidump processor +// architecture, we need to be able to easily generate complex test +// data like debugging information and minidump files. +// +// For example, if we want our unit tests to provide full code +// coverage for stack walking, it may be difficult to persuade the +// compiler to generate every possible sort of stack walking +// information that we want to support; there are probably DWARF CFI +// opcodes that GCC never emits. Similarly, if we want to test our +// error handling, we will need to generate damaged minidumps or +// debugging information that (we hope) the client or compiler will +// never produce on its own. +// +// google_breakpad::TestAssembler provides a predictable and +// (relatively) simple way to generate complex formatted data streams +// like minidumps and CFI. Furthermore, because TestAssembler is +// portable, developers without access to (say) Visual Studio or a +// SPARC assembler can still work on test data for those targets. + +#ifndef PROCESSOR_TEST_ASSEMBLER_H_ +#define PROCESSOR_TEST_ASSEMBLER_H_ + +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::list; +using std::string; +using std::vector; + +namespace test_assembler { + +// A Label represents a value not yet known that we need to store in a +// section. As long as all the labels a section refers to are defined +// by the time we retrieve its contents as bytes, we can use undefined +// labels freely in that section's construction. +// +// A label can be in one of three states: +// - undefined, +// - defined as the sum of some other label and a constant, or +// - a constant. +// +// A label's value never changes, but it can accumulate constraints. +// Adding labels and integers is permitted, and yields a label. +// Subtracting a constant from a label is permitted, and also yields a +// label. Subtracting two labels that have some relationship to each +// other is permitted, and yields a constant. +// +// For example: +// +// Label a; // a's value is undefined +// Label b; // b's value is undefined +// { +// Label c = a + 4; // okay, even though a's value is unknown +// b = c + 4; // also okay; b is now a+8 +// } +// Label d = b - 2; // okay; d == a+6, even though c is gone +// d.Value(); // error: d's value is not yet known +// d - a; // is 6, even though their values are not known +// a = 12; // now b == 20, and d == 18 +// d.Value(); // 18: no longer an error +// b.Value(); // 20 +// d = 10; // error: d is already defined. +// +// Label objects' lifetimes are unconstrained: notice that, in the +// above example, even though a and b are only related through c, and +// c goes out of scope, the assignment to a sets b's value as well. In +// particular, it's not necessary to ensure that a Label lives beyond +// Sections that refer to it. +class Label { + public: + Label(); // An undefined label. + Label(u_int64_t value); // A label with a fixed value + Label(const Label &value); // A label equal to another. + ~Label(); + + // Return this label's value; it must be known. + // + // Providing this as a cast operator is nifty, but the conversions + // happen in unexpected places. In particular, ISO C++ says that + // Label + size_t becomes ambigious, because it can't decide whether + // to convert the Label to a u_int64_t and then to a size_t, or use + // the overloaded operator that returns a new label, even though the + // former could fail if the label is not yet defined and the latter won't. + u_int64_t Value() const; + + Label &operator=(u_int64_t value); + Label &operator=(const Label &value); + Label operator+(u_int64_t addend) const; + Label operator-(u_int64_t subtrahend) const; + u_int64_t operator-(const Label &subtrahend) const; + + // We could also provide == and != that work on undefined, but + // related, labels. + + // Return true if this label's value is known. If VALUE_P is given, + // set *VALUE_P to the known value if returning true. + bool IsKnownConstant(u_int64_t *value_p = NULL) const; + + // Return true if the offset from LABEL to this label is known. If + // OFFSET_P is given, set *OFFSET_P to the offset when returning true. + // + // You can think of l.KnownOffsetFrom(m, &d) as being like 'd = l-m', + // except that it also returns a value indicating whether the + // subtraction is possible given what we currently know of l and m. + // It can be possible even if we don't know l and m's values. For + // example: + // + // Label l, m; + // m = l + 10; + // l.IsKnownConstant(); // false + // m.IsKnownConstant(); // false + // u_int64_t d; + // l.IsKnownOffsetFrom(m, &d); // true, and sets d to -10. + // l-m // -10 + // m-l // 10 + // m.Value() // error: m's value is not known + bool IsKnownOffsetFrom(const Label &label, u_int64_t *offset_p = NULL) const; + + private: + // A label's value, or if that is not yet known, how the value is + // related to other labels' values. A binding may be: + // - a known constant, + // - constrained to be equal to some other binding plus a constant, or + // - unconstrained, and free to take on any value. + // + // Many labels may point to a single binding, and each binding may + // refer to another, so bindings and labels form trees whose leaves + // are labels, whose interior nodes (and roots) are bindings, and + // where links point from children to parents. Bindings are + // reference counted, allowing labels to be lightweight, copyable, + // assignable, placed in containers, and so on. + class Binding { + public: + Binding(); + Binding(u_int64_t addend); + ~Binding(); + + // Increment our reference count. + void Acquire() { reference_count_++; }; + // Decrement our reference count, and return true if it is zero. + bool Release() { return --reference_count_ == 0; } + + // Set this binding to be equal to BINDING + ADDEND. If BINDING is + // NULL, then set this binding to the known constant ADDEND. + // Update every binding on this binding's chain to point directly + // to BINDING, or to be a constant, with addends adjusted + // appropriately. + void Set(Binding *binding, u_int64_t value); + + // Return what we know about the value of this binding. + // - If this binding's value is a known constant, set BASE to + // NULL, and set ADDEND to its value. + // - If this binding is not a known constant but related to other + // bindings, set BASE to the binding at the end of the relation + // chain (which will always be unconstrained), and set ADDEND to the + // value to add to that binding's value to get this binding's + // value. + // - If this binding is unconstrained, set BASE to this, and leave + // ADDEND unchanged. + void Get(Binding **base, u_int64_t *addend); + + private: + // There are three cases: + // + // - A binding representing a known constant value has base_ NULL, + // and addend_ equal to the value. + // + // - A binding representing a completely unconstrained value has + // base_ pointing to this; addend_ is unused. + // + // - A binding whose value is related to some other binding's + // value has base_ pointing to that other binding, and addend_ + // set to the amount to add to that binding's value to get this + // binding's value. We only represent relationships of the form + // x = y+c. + // + // Thus, the bind_ links form a chain terminating in either a + // known constant value or a completely unconstrained value. Most + // operations on bindings do path compression: they change every + // binding on the chain to point directly to the final value, + // adjusting addends as appropriate. + Binding *base_; + u_int64_t addend_; + + // The number of Labels and Bindings pointing to this binding. + // (When a binding points to itself, indicating a completely + // unconstrained binding, that doesn't count as a reference.) + int reference_count_; + }; + + // This label's value. + Binding *value_; +}; + +inline Label operator+(u_int64_t a, const Label &l) { return l + a; } +// Note that int-Label isn't defined, as negating a Label is not an +// operation we support. + +// Conventions for representing larger numbers as sequences of bytes. +enum Endianness { + kBigEndian, // Big-endian: the most significant byte comes first. + kLittleEndian, // Little-endian: the least significant byte comes first. + kUnsetEndian, // used internally +}; + +// A section is a sequence of bytes, constructed by appending bytes +// to the end. Sections have a convenient and flexible set of member +// functions for appending data in various formats: big-endian and +// little-endian signed and unsigned values of different sizes; +// LEB128 and ULEB128 values (see below), and raw blocks of bytes. +// +// If you need to append a value to a section that is not convenient +// to compute immediately, you can create a label, append the +// label's value to the section, and then set the label's value +// later, when it's convenient to do so. Once a label's value is +// known, the section class takes care of updating all previously +// appended references to it. +// +// Once all the labels to which a section refers have had their +// values determined, you can get a copy of the section's contents +// as a string. +// +// Note that there is no specified "start of section" label. This is +// because there are typically several different meanings for "the +// start of a section": the offset of the section within an object +// file, the address in memory at which the section's content appear, +// and so on. It's up to the code that uses the Section class to +// keep track of these explicitly, as they depend on the application. +class Section { + public: + Section(Endianness endianness = kUnsetEndian) + : endianness_(endianness) { }; + ~Section() { }; + + // Set the default endianness of this section to ENDIANNESS. This + // sets the behavior of the D appending functions. If the + // assembler's default endianness was set, this is the + void set_endianness(Endianness endianness) { + endianness_ = endianness; + } + + // Return the default endianness of this section. + Endianness endianness() const { return endianness_; } + + // Append the SIZE bytes at DATA or the contents of STRING to the + // end of this section. Return a reference to this section. + Section &Append(const u_int8_t *data, size_t size) { + contents_.append(reinterpret_cast(data), size); + return *this; + }; + Section &Append(const string &data) { + contents_.append(data); + return *this; + }; + + // Append SIZE copies of BYTE to the end of this section. Return a + // reference to this section. + Section &Append(size_t size, u_int8_t byte) { + contents_.append(size, (char) byte); + return *this; + } + + // Append NUMBER to this section. ENDIANNESS is the endianness to + // use to write the number. SIZE is the length of the number in + // bytes. Return a reference to this section. + Section &Append(Endianness endianness, size_t size, u_int64_t number); + Section &Append(Endianness endianness, size_t size, const Label &label); + + // Append SECTION to the end of this section. The labels SECTION + // refers to need not be defined yet. + // + // Note that this has no effect on any Labels' values, or on + // SECTION. If placing SECTION within 'this' provides new + // constraints on existing labels' values, then it's up to the + // caller to fiddle with those labels as needed. + Section &Append(const Section §ion); + + // Append the contents of DATA as a series of bytes terminated by + // a NULL character. + Section &AppendCString(const string &data) { + Append(data); + contents_ += '\0'; + return *this; + } + + // Append at most SIZE bytes from DATA; if DATA is less than SIZE bytes + // long, pad with '\0' characters. + Section &AppendCString(const string &data, size_t size) { + contents_.append(data, 0, size); + if (data.size() < size) + Append(size - data.size(), 0); + return *this; + } + + // Append VALUE or LABEL to this section, with the given bit width and + // endianness. Return a reference to this section. + // + // The names of these functions have the form : + // is either 'L' (little-endian, least significant byte first), + // 'B' (big-endian, most significant byte first), or + // 'D' (default, the section's default endianness) + // is 8, 16, 32, or 64. + // + // Since endianness doesn't matter for a single byte, all the + // =8 functions are equivalent. + // + // These can be used to write both signed and unsigned values, as + // the compiler will properly sign-extend a signed value before + // passing it to the function, at which point the function's + // behavior is the same either way. + Section &L8(u_int8_t value) { contents_ += value; return *this; } + Section &B8(u_int8_t value) { contents_ += value; return *this; } + Section &D8(u_int8_t value) { contents_ += value; return *this; } + Section &L16(u_int16_t), &L32(u_int32_t), &L64(u_int64_t), + &B16(u_int16_t), &B32(u_int32_t), &B64(u_int64_t), + &D16(u_int16_t), &D32(u_int32_t), &D64(u_int64_t); + Section &L8(const Label &label), &L16(const Label &label), + &L32(const Label &label), &L64(const Label &label), + &B8(const Label &label), &B16(const Label &label), + &B32(const Label &label), &B64(const Label &label), + &D8(const Label &label), &D16(const Label &label), + &D32(const Label &label), &D64(const Label &label); + + // Append VALUE in a signed LEB128 (Little-Endian Base 128) form. + // + // The signed LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between -0x40 and 0x3f, then its signed LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its signed LEB128 representation is (N & 0x7f) | + // 0x80, followed by the signed LEB128 representation of N / 128, + // rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + // + // Note that VALUE cannot be a Label (we would have to implement + // relaxation). + Section &LEB128(long long value); + + // Append VALUE in unsigned LEB128 (Little-Endian Base 128) form. + // + // The unsigned LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between 0 and 0x7f, then its unsigned LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | + // 0x80, followed by the unsigned LEB128 representation of N / + // 128, rounded towards negative infinity. + // + // Note that VALUE cannot be a Label (we would have to implement + // relaxation). + Section &ULEB128(u_int64_t value); + + // Jump to the next location aligned on an ALIGNMENT-byte boundary, + // relative to the start of the section. Fill the gap with PAD_BYTE. + // ALIGNMENT must be a power of two. Return a reference to this + // section. + Section &Align(size_t alignment, u_int8_t pad_byte = 0); + + // Clear the contents of this section. + void Clear(); + + // Return the current size of the section. + size_t Size() const { return contents_.size(); } + + // Return a label representing the start of the section. + // + // It is up to the user whether this label represents the section's + // position in an object file, the section's address in memory, or + // what have you; some applications may need both, in which case + // this simple-minded interface won't be enough. This class only + // provides a single start label, for use with the Here and Mark + // member functions. + // + // Ideally, we'd provide this in a subclass that actually knows more + // about the application at hand and can provide an appropriate + // collection of start labels. But then the appending member + // functions like Append and D32 would return a reference to the + // base class, not the derived class, and the chaining won't work. + // Since the only value here is in pretty notation, that's a fatal + // flaw. + Label start() const { return start_; } + + // Return a label representing the point at which the next Appended + // item will appear in the section, relative to start(). + Label Here() const { return start_ + Size(); } + + // Set *LABEL to Here, and return a reference to this section. + Section &Mark(Label *label) { *label = Here(); return *this; } + + // If there are no undefined label references left in this + // section, set CONTENTS to the contents of this section, as a + // string, and clear this section. Return true on success, or false + // if there were still undefined labels. + bool GetContents(string *contents); + + private: + // Used internally. A reference to a label's value. + struct Reference { + Reference(size_t set_offset, Endianness set_endianness, size_t set_size, + const Label &set_label) + : offset(set_offset), endianness(set_endianness), size(set_size), + label(set_label) { } + + // The offset of the reference within the section. + size_t offset; + + // The endianness of the reference. + Endianness endianness; + + // The size of the reference. + size_t size; + + // The label to which this is a reference. + Label label; + }; + + // The default endianness of this section. + Endianness endianness_; + + // The contents of the section. + string contents_; + + // References to labels within those contents. + vector references_; + + // A label referring to the beginning of the section. + Label start_; +}; + +} // namespace test_assembler +} // namespace google_breakpad + +#endif // PROCESSOR_TEST_ASSEMBLER_H_ diff --git a/src/lib/crashdump/gbreakpad/common/test_assembler_unittest.cc b/src/lib/crashdump/gbreakpad/common/test_assembler_unittest.cc new file mode 100644 index 0000000..5db1326 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/test_assembler_unittest.cc @@ -0,0 +1,1662 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// test_assembler_unittest.cc: Unit tests for google_breakpad::TestAssembler. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/test_assembler.h" + +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using std::string; +using testing::Test; + +TEST(ConstructLabel, Simple) { + Label l; +} + +TEST(ConstructLabel, Undefined) { + Label l; + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(ConstructLabelDeathTest, Undefined) { + Label l; + ASSERT_DEATH(l.Value(), "IsKnownConstant\\(&v\\)"); +} + +TEST(ConstructLabel, Constant) { + Label l(0x060b9f974eaf301eULL); + u_int64_t v; + EXPECT_TRUE(l.IsKnownConstant(&v)); + EXPECT_EQ(v, 0x060b9f974eaf301eULL); + EXPECT_EQ(l.Value(), 0x060b9f974eaf301eULL); +} + +TEST(ConstructLabel, Copy) { + Label l; + Label m(l); + u_int64_t v; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &v)); + EXPECT_EQ(0U, v); +} + +// The left-hand-side of a label assignment can be either +// unconstrained, related, or known. The right-hand-side can be any of +// those, or an integer. +TEST(Assignment, UnconstrainedToUnconstrained) { + Label l, m; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, UnconstrainedToRelated) { + Label l, m, n; + l = n; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, UnconstrainedToKnown) { + Label l, m; + l = 0x8fd16e55b20a39c1ULL; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x8fd16e55b20a39c1ULL, m.Value()); +} + +TEST(Assignment, RelatedToUnconstrained) { + Label l, m, n; + m = n; + l = m; + EXPECT_EQ(0U, l-n); + EXPECT_TRUE(l.IsKnownOffsetFrom(n)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(n, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, RelatedToRelated) { + Label l, m, n, o; + l = n; + m = o; + l = m; + EXPECT_EQ(0U, n-o); + EXPECT_TRUE(n.IsKnownOffsetFrom(o)); + u_int64_t d; + EXPECT_TRUE(n.IsKnownOffsetFrom(o, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, RelatedToKnown) { + Label l, m, n; + m = n; + l = 0xd2011f8c82ad56f2ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, m.Value()); + EXPECT_TRUE(n.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, n.Value()); +} + +TEST(Assignment, KnownToUnconstrained) { + Label l, m; + m = 0x50b024c0d6073887ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x50b024c0d6073887ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x50b024c0d6073887ULL, m.Value()); +} + +TEST(Assignment, KnownToRelated) { + Label l, m, n; + l = n; + m = 0x5348883655c727e5ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, m.Value()); + EXPECT_TRUE(n.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, n.Value()); +} + +TEST(Assignment, KnownToKnown) { + Label l, m; + l = 0x36c209c20987564eULL; + m = 0x36c209c20987564eULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x36c209c20987564eULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x36c209c20987564eULL, m.Value()); +} + +TEST(Assignment, ConstantToUnconstrained) { + Label l; + l = 0xc02495f4d7f5a957ULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xc02495f4d7f5a957ULL, l.Value()); +} + +TEST(Assignment, ConstantToRelated) { + Label l, m; + l = m; + l = 0x4577901cf275488dULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x4577901cf275488dULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x4577901cf275488dULL, m.Value()); +} + +TEST(Assignment, ConstantToKnown) { + Label l; + l = 0xec0b9c369b7e8ea7ULL; + l = 0xec0b9c369b7e8ea7ULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xec0b9c369b7e8ea7ULL, l.Value()); +} + +TEST(AssignmentDeathTest, Self) { + Label l; + ASSERT_DEATH(l = l, "binding != this"); +} + +TEST(AssignmentDeathTest, IndirectCycle) { + Label l, m, n; + l = m; + m = n; + ASSERT_DEATH(n = l, "binding != this"); +} + +TEST(AssignmentDeathTest, Cycle) { + Label l, m, n, o; + l = m; + m = n; + o = n; + ASSERT_DEATH(o = l, "binding != this"); +} + +TEST(Addition, LabelConstant) { + Label l, m; + m = l + 0x5248d93e8bbe9497ULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x5248d93e8bbe9497ULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Addition, ConstantLabel) { + Label l, m; + m = 0xf51e94e00d6e3c84ULL + l; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0xf51e94e00d6e3c84ULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Addition, KnownLabelConstant) { + Label l, m; + l = 0x16286307042ce0d8ULL; + m = l + 0x3fdddd91306719d7ULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x3fdddd91306719d7ULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x16286307042ce0d8ULL + 0x3fdddd91306719d7ULL, m.Value()); +} + +TEST(Addition, ConstantKnownLabel) { + Label l, m; + l = 0x50f62d0cdd1031deULL; + m = 0x1b13462d8577c538ULL + l; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x1b13462d8577c538ULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x50f62d0cdd1031deULL + 0x1b13462d8577c538ULL, m.Value()); +} + +TEST(Subtraction, LabelConstant) { + Label l, m; + m = l - 0x0620884d21d3138eULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(-0x0620884d21d3138eULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Subtraction, KnownLabelConstant) { + Label l, m; + l = 0x6237fbaf9ef7929eULL; + m = l - 0x317730995d2ab6eeULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(-0x317730995d2ab6eeULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x6237fbaf9ef7929eULL - 0x317730995d2ab6eeULL, m.Value()); +} + +TEST(SubtractionDeathTest, LabelLabel) { + Label l, m; + ASSERT_DEATH(l - m, "IsKnownOffsetFrom\\(label, &offset\\)"); +} + +TEST(Subtraction, LabelLabel) { + Label l, m; + l = m + 0x7fa77ec63e28a17aULL; + EXPECT_EQ(0x7fa77ec63e28a17aULL, l - m); + EXPECT_EQ(-0x7fa77ec63e28a17aULL, m - l); +} + +TEST(IsKnownConstant, Undefined) { + Label l; + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(IsKnownConstant, RelatedLabel) { + Label l, m; + l = m; + EXPECT_FALSE(l.IsKnownConstant()); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(IsKnownConstant, Constant) { + Label l; + l = 0xf374b1bdd6a22576ULL; + EXPECT_TRUE(l.IsKnownConstant()); +} + +TEST(IsKnownOffsetFrom, Unrelated) { + Label l, m; + EXPECT_FALSE(l.IsKnownOffsetFrom(m)); +} + +TEST(IsKnownOffsetFrom, Related) { + Label l, m; + l = m; + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); +} + +// Test the construction of chains of related labels, and the +// propagation of values through them. +// +// Although the relations between labels are supposed to behave +// symmetrically --- that is, 'a = b' should put a and b in +// indistinguishable states --- there's a distinction made internally +// between the target (a) and the source (b). +// +// So there are five test axes to cover: +// +// - Do we construct the chain with assignment ("Assign") or with constructors +// ("Construct")? +// +// - Do we set the value of the label at the start of the chain +// ("Start") or the label at the end ("End")? +// +// - Are we testing the propagation of a relationship between variable +// values ("Relation"), or the propagation of a known constant value +// ("Value")? +// +// - Do we set the value before building the chain ("Before") or after +// the chain has been built ("After")? +// +// - Do we add new relationships to the end of the existing chain +// ("Forward") or to the beginning ("Backward")? +// +// Of course, "Construct" and "Backward" can't be combined, which +// eliminates eight combinations, and "Construct", "End", and "Before" +// can't be combined, which eliminates two more, so there are are 22 +// combinations, not 32. + +TEST(LabelChain, AssignStartRelationBeforeForward) { + Label a, b, c, d; + Label x; + a = x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationBeforeBackward) { + Label a, b, c, d; + Label x; + a = x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationAfterForward) { + Label a, b, c, d; + Label x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationAfterBackward) { + Label a, b, c, d; + Label x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartValueBeforeForward) { + Label a, b, c, d; + a = 0xa131200190546ac2ULL; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0xa131200190546ac2ULL + 0x111U, d.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0x11U, c.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0x1U, b.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueBeforeBackward) { + Label a, b, c, d; + a = 0x8da17e1670ad4fa2ULL; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x111U, d.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x11U, c.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x1U, b.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueAfterForward) { + Label a, b, c, d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + a = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x111U, d.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x11U, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x1U, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueAfterBackward) { + Label a, b, c, d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + a = 0xc86ca1d97ab5df6eULL; + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x111U, d.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x11U, c.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x1U, b.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignEndRelationBeforeForward) { + Label a, b, c, d; + Label x; + x = d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0U, d-x); +} + +TEST(LabelChain, AssignEndRelationBeforeBackward) { + Label a, b, c, d; + Label x; + x = d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0U, d-x); +} + +TEST(LabelChain, AssignEndRelationAfterForward) { + Label a, b, c, d; + Label x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + x = d; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0x000U, d-x); +} + +TEST(LabelChain, AssignEndRelationAfterBackward) { + Label a, b, c, d; + Label x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + x = d; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0x000U, d-x); +} + +TEST(LabelChain, AssignEndValueBeforeForward) { + Label a, b, c, d; + d = 0xa131200190546ac2ULL; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0xa131200190546ac2ULL - 0x111, a.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x110, b.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x100, c.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueBeforeBackward) { + Label a, b, c, d; + d = 0x8da17e1670ad4fa2ULL; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x111, a.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x110, b.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x100, c.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueAfterForward) { + Label a, b, c, d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + d = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueAfterBackward) { + Label a, b, c, d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + d = 0xc86ca1d97ab5df6eULL; + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x111, a.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x110, b.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x100, c.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x000, d.Value()); +} + +TEST(LabelChain, ConstructStartRelationBeforeForward) { + Label x; + Label a(x); + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, ConstructStartRelationAfterForward) { + Label x; + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, ConstructStartValueBeforeForward) { + Label a(0x5d234d177d01ccc8ULL); + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x111U, d.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x011U, c.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x001U, b.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x000U, a.Value()); +} + +TEST(LabelChain, ConstructStartValueAfterForward) { + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + a = 0xded85d54586e84fcULL; + EXPECT_EQ(0xded85d54586e84fcULL + 0x111U, d.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x011U, c.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x001U, b.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x000U, a.Value()); +} + +TEST(LabelChain, ConstructEndRelationAfterForward) { + Label x; + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + x = d; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0x000U, d-x); +} + +TEST(LabelChain, ConstructEndValueAfterForward) { + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + d = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value()); +} + +TEST(LabelTree, KnownValue) { + Label l, m, n, o, p; + l = m; + m = n; + o = p; + p = n; + l = 0x536b5de3d468a1b5ULL; + EXPECT_EQ(0x536b5de3d468a1b5ULL, o.Value()); +} + +TEST(LabelTree, Related) { + Label l, m, n, o, p; + l = m - 1; + m = n - 10; + o = p + 100; + p = n + 1000; + EXPECT_EQ(1111U, o - l); +} + +TEST(EquationDeathTest, EqualConstants) { + Label m = 0x0d3962f280f07d24ULL; + Label n = 0x0d3962f280f07d24ULL; + m = n; // no death expected +} + +TEST(EquationDeathTest, EqualIndirectConstants) { + Label m = 0xa347f1e5238fe6a1ULL; + Label n; + Label o = n; + n = 0xa347f1e5238fe6a1ULL; + n = m; // no death expected +} + +TEST(EquationDeathTest, ConstantClash) { + Label m = 0xd4cc0f4f630ec741ULL; + Label n = 0x934cd2d8254fc3eaULL; + ASSERT_DEATH(m = n, "addend_ == addend"); +} + +TEST(EquationDeathTest, IndirectConstantClash) { + Label m = 0xd4cc0f4f630ec741ULL; + Label n, o; + n = o; + o = 0xcfbe3b83ac49ce86ULL; + ASSERT_DEATH(m = n, "addend_ == addend"); +} + +// Assigning to a related label may free the next Binding on its +// chain. This test always passes; it is interesting to memory +// checkers and coverage analysis. +TEST(LabelReferenceCount, AssignmentFree) { + Label l; + { + Label m; + l = m; + } + // This should free m's Binding. + l = 0xca8bae92f0376d4fULL; + ASSERT_EQ(0xca8bae92f0376d4fULL, l.Value()); +} + +// Finding the value of a label may free the Binding it refers to. This test +// always passes; it is interesting to memory checkers and coverage analysis. +TEST(LabelReferenceCount, FindValueFree) { + Label l; + { + Label m, n; + l = m; + m = n; + n = 0x7a0b0c576672daafULL; + // At this point, l's Binding refers to m's Binding, which refers + // to n's binding. + } + // Now, l is the only reference keeping the three Bindings alive. + // Resolving its value should free l's and m's original bindings. + ASSERT_EQ(0x7a0b0c576672daafULL, l.Value()); +} + +TEST(ConstructSection, Simple) { + Section s; +} + +TEST(ConstructSection, WithEndian) { + Section s(kBigEndian); +} + +// A fixture class for TestAssembler::Section tests. +class SectionFixture { + public: + Section section; + string contents; + static const u_int8_t data[]; + static const size_t data_size; +}; + +const u_int8_t SectionFixture::data[] = { + 0x87, 0x4f, 0x43, 0x67, 0x30, 0xd0, 0xd4, 0x0e +}; + +#define I0() +#define I1(a) { a } +#define I2(a,b) { a,b } +#define I3(a,b,c) { a,b,c } +#define I4(a,b,c,d) { a,b,c,d } +#define I5(a,b,c,d,e) { a,b,c,d,e } +#define I6(a,b,c,d,e,f) { a,b,c,d,e,f } +#define I7(a,b,c,d,e,f,g) { a,b,c,d,e,f,g } +#define I8(a,b,c,d,e,f,g,h) { a,b,c,d,e,f,g,h } +#define I9(a,b,c,d,e,f,g,h,i) { a,b,c,d,e,f,g,h,i } +#define ASSERT_BYTES(s, b) \ + do \ + { \ + static const u_int8_t expected_bytes[] = b; \ + ASSERT_EQ(sizeof(expected_bytes), s.size()); \ + ASSERT_TRUE(memcmp(s.data(), (const char *) expected_bytes, \ + sizeof(expected_bytes)) == 0); \ + } \ + while(0) + +class Append: public SectionFixture, public Test { }; + +TEST_F(Append, Bytes) { + section.Append(data, sizeof(data)); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(sizeof(data), contents.size()); + EXPECT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data))); +} + +TEST_F(Append, BytesTwice) { + section.Append(data, sizeof(data)); + section.Append(data, sizeof(data)); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(2 * sizeof(data), contents.size()); + ASSERT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data))); + ASSERT_TRUE(0 == memcmp(contents.data() + sizeof(data), + (const char *) data, sizeof(data))); +} + +TEST_F(Append, String) { + string s1 = "howdy "; + string s2 = "there"; + section.Append(s1); + section.Append(s2); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_STREQ(contents.c_str(), "howdy there"); +} + +TEST_F(Append, CString) { + section.AppendCString("howdy"); + section.AppendCString(""); + section.AppendCString("there"); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("howdy\0\0there\0", 13), contents); +} + +TEST_F(Append, CStringSize) { + section.AppendCString("howdy", 3); + section.AppendCString("there", 5); + section.AppendCString("fred", 6); + section.AppendCString("natalie", 0); + section.AppendCString("", 10); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("howtherefred\0\0\0\0\0\0\0\0\0\0\0\0", 24), contents); +} + +TEST_F(Append, RepeatedBytes) { + section.Append((size_t) 10, '*'); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_STREQ(contents.c_str(), "**********"); +} + +TEST_F(Append, GeneralLE1) { + section.Append(kLittleEndian, 1, 42); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); +} + +TEST_F(Append, GeneralLE2) { + section.Append(kLittleEndian, 2, 0x15a1); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0xa1, 0x15)); +} + +TEST_F(Append, GeneralLE3) { + section.Append(kLittleEndian, 3, 0x59ae8d); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59)); +} + +TEST_F(Append, GeneralLE4) { + section.Append(kLittleEndian, 4, 0x51603c56); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51)); +} + +TEST_F(Append, GeneralLE5) { + section.Append(kLittleEndian, 5, 0x385e2803b4ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38)); +} + +TEST_F(Append, GeneralLE6) { + section.Append(kLittleEndian, 6, 0xc7db9534dd1fULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7)); +} + +TEST_F(Append, GeneralLE7) { + section.Append(kLittleEndian, 7, 0x1445c9f1b843e6ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14)); +} + +TEST_F(Append, GeneralLE8) { + section.Append(kLittleEndian, 8, 0xaf48019dfe5c01e5ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf)); +} + +TEST_F(Append, GeneralBE1) { + section.Append(kBigEndian, 1, 0xd0ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(0xd0)); +} + +TEST_F(Append, GeneralBE2) { + section.Append(kBigEndian, 2, 0x2e7eULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2e, 0x7e)); +} + +TEST_F(Append, GeneralBE3) { + section.Append(kBigEndian, 3, 0x37dad6ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6)); +} + +TEST_F(Append, GeneralBE4) { + section.Append(kBigEndian, 4, 0x715935c7ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7)); +} + +TEST_F(Append, GeneralBE5) { + section.Append(kBigEndian, 5, 0x42baeb02b7ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7)); +} + +TEST_F(Append, GeneralBE6) { + section.Append(kBigEndian, 6, 0xf1cdf10e7b18ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18)); +} + +TEST_F(Append, GeneralBE7) { + section.Append(kBigEndian, 7, 0xf50a724f0b0d20ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20)); +} + +TEST_F(Append, GeneralBE8) { + section.Append(kBigEndian, 8, 0xa6b2cb5e98dc9c16ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16)); +} + +TEST_F(Append, GeneralLE1Label) { + Label l; + section.Append(kLittleEndian, 1, l); + l = 42; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); +} + +TEST_F(Append, GeneralLE2Label) { + Label l; + section.Append(kLittleEndian, 2, l); + l = 0x15a1; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0xa1, 0x15)); +} + +TEST_F(Append, GeneralLE3Label) { + Label l; + section.Append(kLittleEndian, 3, l); + l = 0x59ae8d; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59)); +} + +TEST_F(Append, GeneralLE4Label) { + Label l; + section.Append(kLittleEndian, 4, l); + l = 0x51603c56; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51)); +} + +TEST_F(Append, GeneralLE5Label) { + Label l; + section.Append(kLittleEndian, 5, l); + l = 0x385e2803b4ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38)); +} + +TEST_F(Append, GeneralLE6Label) { + Label l; + section.Append(kLittleEndian, 6, l); + l = 0xc7db9534dd1fULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7)); +} + +TEST_F(Append, GeneralLE7Label) { + Label l; + section.Append(kLittleEndian, 7, l); + l = 0x1445c9f1b843e6ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14)); +} + +TEST_F(Append, GeneralLE8Label) { + Label l; + section.Append(kLittleEndian, 8, l); + l = 0xaf48019dfe5c01e5ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf)); +} + +TEST_F(Append, GeneralBE1Label) { + Label l; + section.Append(kBigEndian, 1, l); + l = 0xd0ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(0xd0)); +} + +TEST_F(Append, GeneralBE2Label) { + Label l; + section.Append(kBigEndian, 2, l); + l = 0x2e7eULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2e, 0x7e)); +} + +TEST_F(Append, GeneralBE3Label) { + Label l; + section.Append(kBigEndian, 3, l); + l = 0x37dad6ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6)); +} + +TEST_F(Append, GeneralBE4Label) { + Label l; + section.Append(kBigEndian, 4, l); + l = 0x715935c7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7)); +} + +TEST_F(Append, GeneralBE5Label) { + Label l; + section.Append(kBigEndian, 5, l); + l = 0x42baeb02b7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7)); +} + +TEST_F(Append, GeneralBE6Label) { + Label l; + section.Append(kBigEndian, 6, l); + l = 0xf1cdf10e7b18ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18)); +} + +TEST_F(Append, GeneralBE7Label) { + Label l; + section.Append(kBigEndian, 7, l); + l = 0xf50a724f0b0d20ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20)); +} + +TEST_F(Append, GeneralBE8Label) { + Label l; + section.Append(kBigEndian, 8, l); + l = 0xa6b2cb5e98dc9c16ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16)); +} + +TEST_F(Append, B8) { + section.Append(1, 0x2a); + section.B8(0xd3U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xd3)); +} + +TEST_F(Append, B8Label) { + Label l; + section.Append(1, 0x2a); + section.B8(l); + l = 0x4bU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x4b)); +} + +TEST_F(Append, B16) { + section.Append(1, 0x2a); + section.B16(0x472aU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x47, 0x2a)); +} + +TEST_F(Append, B16Label) { + Label l; + section.Append(1, 0x2a); + section.B16(l); + l = 0x55e8U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x55, 0xe8)); +} + +TEST_F(Append, B32) { + section.Append(1, 0x2a); + section.B32(0xbd412cbcU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xbd, 0x41, 0x2c, 0xbc)); +} + +TEST_F(Append, B32Label) { + Label l; + section.Append(1, 0x2a); + section.B32(l); + l = 0x208e37d5U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x20, 0x8e, 0x37, 0xd5)); +} + +TEST_F(Append, B64) { + section.Append(1, 0x2a); + section.B64(0x3402a013111e68adULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x34, 0x02, 0xa0, 0x13, 0x11, 0x1e, 0x68, 0xad)); +} + +TEST_F(Append, B64Label) { + Label l; + section.Append(1, 0x2a); + section.B64(l); + l = 0x355dbfbb4ac6d57fULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x35, 0x5d, 0xbf, 0xbb, 0x4a, 0xc6, 0xd5, 0x7f)); +} + +TEST_F(Append, L8) { + section.Append(1, 0x2a); + section.L8(0x26U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x26)); +} + +TEST_F(Append, L8Label) { + Label l; + section.Append(1, 0x2a); + section.L8(l); + l = 0xa8U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xa8)); +} + +TEST_F(Append, L16) { + section.Append(1, 0x2a); + section.L16(0xca6dU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x6d, 0xca)); +} + +TEST_F(Append, L16Label) { + Label l; + section.Append(1, 0x2a); + section.L16(l); + l = 0xd21fU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x1f, 0xd2)); +} + +TEST_F(Append, L32) { + section.Append(1, 0x2a); + section.L32(0x558f6181U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x81, 0x61, 0x8f, 0x55)); +} + +TEST_F(Append, L32Label) { + Label l; + section.Append(1, 0x2a); + section.L32(l); + l = 0x4b810f82U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x82, 0x0f, 0x81, 0x4b)); +} + +TEST_F(Append, L64) { + section.Append(1, 0x2a); + section.L64(0x564384f7579515bfULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xbf, 0x15, 0x95, 0x57, 0xf7, 0x84, 0x43, 0x56)); +} + +TEST_F(Append, L64Label) { + Label l; + section.Append(1, 0x2a); + section.L64(l); + l = 0x424b1d020667c8dbULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xdb, 0xc8, 0x67, 0x06, 0x02, 0x1d, 0x4b, 0x42)); +} + +TEST_F(Append, D8Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D8(0xe6U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xe6)); +} + +TEST_F(Append, D8BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D8(l); + l = 0xeeU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xee)); +} + +TEST_F(Append, D16Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D16(0x83b1U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x83, 0xb1)); +} + +TEST_F(Append, D16BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D16(l); + l = 0x5b55U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x5b, 0x55)); +} + +TEST_F(Append, D32Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D32(0xd0b0e431U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xd0, 0xb0, 0xe4, 0x31)); +} + +TEST_F(Append, D32BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D32(l); + l = 0x312fb340U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x31, 0x2f, 0xb3, 0x40)); +} + +TEST_F(Append, D64Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D64(0xb109843500dbcb16ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xb1, 0x09, 0x84, 0x35, 0x00, 0xdb, 0xcb, 0x16)); +} + +TEST_F(Append, D64BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D64(l); + l = 0x9a0d61b70f671fd7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x9a, 0x0d, 0x61, 0xb7, 0x0f, 0x67, 0x1f, 0xd7)); +} + +TEST_F(Append, D8Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D8(0x42U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x42)); +} + +TEST_F(Append, D8LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D8(l); + l = 0x05U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x05)); +} + +TEST_F(Append, D16Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D16(0xc5c5U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0xc5, 0xc5)); +} + +TEST_F(Append, D16LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D16(l); + l = 0xb620U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x20, 0xb6)); +} + +TEST_F(Append, D32Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D32(0x1a87d0feU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xfe, 0xd0, 0x87, 0x1a)); +} + +TEST_F(Append, D32LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D32(l); + l = 0xb8012d6bU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x6b, 0x2d, 0x01, 0xb8)); +} + +TEST_F(Append, D64Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D64(0x42de75c61375a1deULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xde, 0xa1, 0x75, 0x13, 0xc6, 0x75, 0xde, 0x42)); +} + +TEST_F(Append, D64LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D64(l); + l = 0x8b3bececf3fb5312ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x12, 0x53, 0xfb, 0xf3, 0xec, 0xec, 0x3b, 0x8b)); +} + +TEST_F(Append, Variety) { + Label a, b, c, d, e, f, g, h; + section.Append(kBigEndian, 1, a) + .Append(kLittleEndian, 8, h) + .Append(kBigEndian, 1, 0x8bULL) + .Append(kLittleEndian, 8, 0x0ea56540448f4439ULL) + .Append(kBigEndian, 2, b) + .Append(kLittleEndian, 7, g) + .Append(kBigEndian, 2, 0xcf15ULL) + .Append(kLittleEndian, 7, 0x29694f04c5724aULL) + .Append(kBigEndian, 3, c) + .Append(kLittleEndian, 6, f) + .Append(kBigEndian, 3, 0x8c3ffdULL) + .Append(kLittleEndian, 6, 0x6f11ba80187aULL) + .Append(kBigEndian, 4, d) + .Append(kLittleEndian, 5, e) + .Append(kBigEndian, 4, 0x2fda2472ULL) + .Append(kLittleEndian, 5, 0x0aa02d423fULL) + .Append(kBigEndian, 5, e) + .Append(kLittleEndian, 4, d) + .Append(kBigEndian, 5, 0x53ba432138ULL) + .Append(kLittleEndian, 4, 0xf139ae60ULL) + .Append(kBigEndian, 6, f) + .Append(kLittleEndian, 3, c) + .Append(kBigEndian, 6, 0x168e436af716ULL) + .Append(kLittleEndian, 3, 0x3ef189ULL) + .Append(kBigEndian, 7, g) + .Append(kLittleEndian, 2, b) + .Append(kBigEndian, 7, 0xacd4ef233e47d9ULL) + .Append(kLittleEndian, 2, 0x5311ULL) + .Append(kBigEndian, 8, h) + .Append(kLittleEndian, 1, a) + .Append(kBigEndian, 8, 0x4668d5f1c93637a1ULL) + .Append(kLittleEndian, 1, 0x65ULL); + a = 0x79ac9bd8aa256b35ULL; + b = 0x22d13097ef86c91cULL; + c = 0xf204968b0a05862fULL; + d = 0x163177f15a0eb4ecULL; + e = 0xbd1b0f1d977f2246ULL; + f = 0x2b0842eee83c6461ULL; + g = 0x92f4b928a4bf875eULL; + h = 0x61a199a8f7286ba6ULL; + ASSERT_EQ(8 * 18U, section.Size()); + ASSERT_TRUE(section.GetContents(&contents)); + + static const u_int8_t expected[] = { + 0x35, 0xa6, 0x6b, 0x28, 0xf7, 0xa8, 0x99, 0xa1, 0x61, + 0x8b, 0x39, 0x44, 0x8f, 0x44, 0x40, 0x65, 0xa5, 0x0e, + 0xc9, 0x1c, 0x5e, 0x87, 0xbf, 0xa4, 0x28, 0xb9, 0xf4, + 0xcf, 0x15, 0x4a, 0x72, 0xc5, 0x04, 0x4f, 0x69, 0x29, + 0x05, 0x86, 0x2f, 0x61, 0x64, 0x3c, 0xe8, 0xee, 0x42, + 0x8c, 0x3f, 0xfd, 0x7a, 0x18, 0x80, 0xba, 0x11, 0x6f, + 0x5a, 0x0e, 0xb4, 0xec, 0x46, 0x22, 0x7f, 0x97, 0x1d, + 0x2f, 0xda, 0x24, 0x72, 0x3f, 0x42, 0x2d, 0xa0, 0x0a, + 0x1d, 0x97, 0x7f, 0x22, 0x46, 0xec, 0xb4, 0x0e, 0x5a, + 0x53, 0xba, 0x43, 0x21, 0x38, 0x60, 0xae, 0x39, 0xf1, + 0x42, 0xee, 0xe8, 0x3c, 0x64, 0x61, 0x2f, 0x86, 0x05, + 0x16, 0x8e, 0x43, 0x6a, 0xf7, 0x16, 0x89, 0xf1, 0x3e, + 0xf4, 0xb9, 0x28, 0xa4, 0xbf, 0x87, 0x5e, 0x1c, 0xc9, + 0xac, 0xd4, 0xef, 0x23, 0x3e, 0x47, 0xd9, 0x11, 0x53, + 0x61, 0xa1, 0x99, 0xa8, 0xf7, 0x28, 0x6b, 0xa6, 0x35, + 0x46, 0x68, 0xd5, 0xf1, 0xc9, 0x36, 0x37, 0xa1, 0x65, + }; + + ASSERT_TRUE(0 == memcmp(contents.data(), expected, sizeof(expected))); +} + +TEST_F(Append, Section) { + section.Append("murder"); + { + Section middle; + middle.Append(" she"); + section.Append(middle); + } + section.Append(" wrote"); + EXPECT_EQ(16U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_STREQ(contents.c_str(), "murder she wrote"); +} + +TEST_F(Append, SectionRefs) { + section.Append("sugar "); + Label l; + { + Section middle; + Label m; + middle.B32(m); + section.Append(middle); + m = 0x66726565; + } + section.Append(" jazz"); + EXPECT_EQ(15U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_STREQ(contents.c_str(), "sugar free jazz"); +} + +TEST_F(Append, LEB128_0) { + section.LEB128(0); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\0", 1), contents); +} + +TEST_F(Append, LEB128_0x3f) { + section.LEB128(0x3f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x3f", 1), contents); +} + +TEST_F(Append, LEB128_0x40) { + section.LEB128(0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xc0\x00", 2), contents); +} + +TEST_F(Append, LEB128_0x7f) { + section.LEB128(0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x00", 2), contents); +} + +TEST_F(Append, LEB128_0x80) { + section.LEB128(0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x01", 2), contents); +} + +TEST_F(Append, LEB128_0xff) { + section.LEB128(0xff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x01", 2), contents); +} + +TEST_F(Append, LEB128_0x1fff) { + section.LEB128(0x1fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x3f", 2), contents); +} + +TEST_F(Append, LEB128_0x2000) { + section.LEB128(0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\xc0\x00", 3), contents); +} + +TEST_F(Append, LEB128_n1) { + section.LEB128(-1); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x7f", 1), contents); +} + +TEST_F(Append, LEB128_n0x40) { + section.LEB128(-0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x40", 1), contents); +} + +TEST_F(Append, LEB128_n0x41) { + section.LEB128(-0x41); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xbf\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x7f) { + section.LEB128(-0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x81\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x80) { + section.LEB128(-0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x2000) { + section.LEB128(-0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x40", 2), contents); +} + +TEST_F(Append, LEB128_n0x2001) { + section.LEB128(-0x2001); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\xbf\x7f", 3), contents); +} + +TEST_F(Append,ULEB128_0) { + section.ULEB128(0); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\0", 1), contents); +} + +TEST_F(Append,ULEB128_1) { + section.ULEB128(1); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x01", 1), contents); +} + +TEST_F(Append,ULEB128_0x3f) { + section.ULEB128(0x3f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x3f", 1), contents); +} + +TEST_F(Append,ULEB128_0x40) { + section.ULEB128(0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x40", 1), contents); +} + +TEST_F(Append,ULEB128_0x7f) { + section.ULEB128(0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x7f", 1), contents); +} + +TEST_F(Append,ULEB128_0x80) { + section.ULEB128(0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x01", 2), contents); +} + +TEST_F(Append,ULEB128_0xff) { + section.ULEB128(0xff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x01", 2), contents); +} + +TEST_F(Append,ULEB128_0x100) { + section.ULEB128(0x100); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x02", 2), contents); +} + +TEST_F(Append,ULEB128_0x1fff) { + section.ULEB128(0x1fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x3f", 2), contents); +} + +TEST_F(Append,ULEB128_0x2000) { + section.ULEB128(0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x40", 2), contents); +} + +TEST_F(Append,ULEB128_0x3fff) { + section.ULEB128(0x3fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x7f", 2), contents); +} + +TEST_F(Append,ULEB128_0x4000) { + section.ULEB128(0x4000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x80\x01", 3), contents); +} + +TEST_F(Append,ULEB128_12857) { + section.ULEB128(12857); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xb9\x64", 2), contents); +} + +TEST_F(Append, LEBChain) { + section.LEB128(-0x80).ULEB128(12857).Append("*"); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x7f\xb9\x64*", 5), contents); +} + + +class GetContents: public SectionFixture, public Test { }; + +TEST_F(GetContents, Undefined) { + Label l; + section.Append(kLittleEndian, 8, l); + ASSERT_FALSE(section.GetContents(&contents)); +} + +TEST_F(GetContents, ClearsContents) { + section.Append((size_t) 10, '*'); + EXPECT_EQ(10U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(0U, section.Size()); +} + +TEST_F(GetContents, ClearsReferences) { + Label l; + section.Append(kBigEndian, 1, l); + l = 42; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); + ASSERT_TRUE(section.GetContents(&contents)); // should not die +} + +class Miscellanea: public SectionFixture, public Test { }; + +TEST_F(Miscellanea, Clear) { + section.Append("howdy"); + Label l; + section.L32(l); + EXPECT_EQ(9U, section.Size()); + section.Clear(); + EXPECT_EQ(0U, section.Size()); + l = 0x8d231bf0U; + ASSERT_TRUE(section.GetContents(&contents)); // should not die +} + +TEST_F(Miscellanea, Align) { + section.Append("*"); + EXPECT_EQ(1U, section.Size()); + section.Align(4).Append("*"); + EXPECT_EQ(5U, section.Size()); + section.Append("*").Align(2); + EXPECT_EQ(6U, section.Size()); +} + +TEST_F(Miscellanea, AlignPad) { + section.Append("*"); + EXPECT_EQ(1U, section.Size()); + section.Align(4, ' ').Append("*"); + EXPECT_EQ(5U, section.Size()); + section.Append("*").Align(2, ' '); + EXPECT_EQ(6U, section.Size()); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("* **"), contents); +} + +TEST_F(Miscellanea, StartHereMark) { + Label m; + section.Append(42, ' ').Mark(&m).Append(13, '+'); + EXPECT_EQ(42U, m - section.start()); + EXPECT_EQ(42U + 13U, section.Here() - section.start()); + EXPECT_FALSE(section.start().IsKnownConstant()); + EXPECT_FALSE(m.IsKnownConstant()); + EXPECT_FALSE(section.Here().IsKnownConstant()); +} + +TEST_F(Miscellanea, Endianness) { + section.set_endianness(kBigEndian); + EXPECT_EQ(kBigEndian, section.endianness()); + section.set_endianness(kLittleEndian); + EXPECT_EQ(kLittleEndian, section.endianness()); +} diff --git a/src/lib/crashdump/gbreakpad/common/testdata/func-line-pairing.h b/src/lib/crashdump/gbreakpad/common/testdata/func-line-pairing.h new file mode 100644 index 0000000..05538f9 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/testdata/func-line-pairing.h @@ -0,0 +1,676 @@ +// -*- mode: c++ -*- + +// Test data for pairing functions and lines. +// +// For a pair of functions that are adjacent (10,20),(20,25) and a +// pair that are not (10,15),(20,25), we include a test case for every +// possible arrangement of two lines relative to those functions. We +// include cases only for non-empty ranges, since empty functions and +// lines are dropped before we do any pairing. +// +// Each test case is represented by a macro call of the form: +// +// PAIRING(func1_start, func1_end, func2_start, func2_end, +// line1_start, line1_end, line2_start, line2_end, +// func1_num_lines, func2_num_lines, +// func1_line1_start, func1_line1_end, +// func1_line2_start, func1_line2_end, +// func2_line1_start, func2_line1_end, +// func2_line2_start, func2_line2_end, +// uncovered_funcs, uncovered_lines) +// +// where: +// - funcN_{start,end} is the range of the N'th function +// - lineN_{start,end} is the range of the N'th function +// - funcN_num_lines is the number of source lines that should be +// paired with the N'th function +// - funcN_lineM_{start,end} is the range of the M'th line +// paired with the N'th function, where 0,0 indicates that +// there should be no such line paired +// - uncovered_funcs is the number of functions with area that is +// uncovered by any line, and +// - uncovered_lines is the reverse. + +// func1 func2 line1 line2 num pairing1 pairing2 uncovered +PAIRING(10, 20, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #0 +PAIRING(10, 20, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #1 +PAIRING(10, 20, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #2 +PAIRING(10, 20, 20, 25, 6, 7, 7, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #3 +PAIRING(10, 20, 20, 25, 6, 7, 7, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #4 +PAIRING(10, 20, 20, 25, 6, 7, 7, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #5 +PAIRING(10, 20, 20, 25, 6, 7, 7, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #6 +PAIRING(10, 20, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #7 +PAIRING(10, 20, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #8 +PAIRING(10, 20, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #9 +PAIRING(10, 20, 20, 25, 6, 7, 8, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #10 +PAIRING(10, 20, 20, 25, 6, 7, 8, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #11 +PAIRING(10, 20, 20, 25, 6, 7, 8, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #12 +PAIRING(10, 20, 20, 25, 6, 7, 8, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #13 +PAIRING(10, 20, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #14 +PAIRING(10, 20, 20, 25, 6, 7, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #15 +PAIRING(10, 20, 20, 25, 6, 7, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #16 +PAIRING(10, 20, 20, 25, 6, 7, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #17 +PAIRING(10, 20, 20, 25, 6, 7, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #18 +PAIRING(10, 20, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #19 +PAIRING(10, 20, 20, 25, 6, 7, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #20 +PAIRING(10, 20, 20, 25, 6, 7, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #21 +PAIRING(10, 20, 20, 25, 6, 7, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #22 +PAIRING(10, 20, 20, 25, 6, 7, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #23 +PAIRING(10, 20, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #24 +PAIRING(10, 20, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #25 +PAIRING(10, 20, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #26 +PAIRING(10, 20, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #27 +PAIRING(10, 20, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #28 +PAIRING(10, 20, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #29 +PAIRING(10, 20, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #30 +PAIRING(10, 20, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #31 +PAIRING(10, 20, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #32 +PAIRING(10, 20, 20, 25, 6, 10, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #33 +PAIRING(10, 20, 20, 25, 6, 10, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #34 +PAIRING(10, 20, 20, 25, 6, 10, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #35 +PAIRING(10, 20, 20, 25, 6, 10, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #36 +PAIRING(10, 20, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #37 +PAIRING(10, 20, 20, 25, 6, 10, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #38 +PAIRING(10, 20, 20, 25, 6, 10, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #39 +PAIRING(10, 20, 20, 25, 6, 10, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #40 +PAIRING(10, 20, 20, 25, 6, 10, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #41 +PAIRING(10, 20, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #42 +PAIRING(10, 20, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #43 +PAIRING(10, 20, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #44 +PAIRING(10, 20, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #45 +PAIRING(10, 20, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #46 +PAIRING(10, 20, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #47 +PAIRING(10, 20, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #48 +PAIRING(10, 20, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #49 +PAIRING(10, 20, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #50 +PAIRING(10, 20, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 1) // #51 +PAIRING(10, 20, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 1) // #52 +PAIRING(10, 20, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #53 +PAIRING(10, 20, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 2) // #54 +PAIRING(10, 20, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #55 +PAIRING(10, 20, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 1) // #56 +PAIRING(10, 20, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 1) // #57 +PAIRING(10, 20, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #58 +PAIRING(10, 20, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 2) // #59 +PAIRING(10, 20, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #60 +PAIRING(10, 20, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #61 +PAIRING(10, 20, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #62 +PAIRING(10, 20, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #63 +PAIRING(10, 20, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #64 +PAIRING(10, 20, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #65 +PAIRING(10, 20, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #66 +PAIRING(10, 20, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #67 +PAIRING(10, 20, 20, 25, 6, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #68 +PAIRING(10, 20, 20, 25, 6, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #69 +PAIRING(10, 20, 20, 25, 6, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #70 +PAIRING(10, 20, 20, 25, 6, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 1) // #71 +PAIRING(10, 20, 20, 25, 6, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #72 +PAIRING(10, 20, 20, 25, 6, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 2) // #73 +PAIRING(10, 20, 20, 25, 6, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #74 +PAIRING(10, 20, 20, 25, 6, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #75 +PAIRING(10, 20, 20, 25, 6, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 1) // #76 +PAIRING(10, 20, 20, 25, 6, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #77 +PAIRING(10, 20, 20, 25, 6, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 2) // #78 +PAIRING(10, 20, 20, 25, 6, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 1) // #79 +PAIRING(10, 20, 20, 25, 6, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #80 +PAIRING(10, 20, 20, 25, 6, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 2) // #81 +PAIRING(10, 20, 20, 25, 6, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #82 +PAIRING(10, 20, 20, 25, 6, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #83 +PAIRING(10, 20, 20, 25, 6, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #84 +PAIRING(10, 20, 20, 25, 6, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #85 +PAIRING(10, 20, 20, 25, 6, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #86 +PAIRING(10, 20, 20, 25, 6, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #87 +PAIRING(10, 20, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #88 +PAIRING(10, 20, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 0) // #89 +PAIRING(10, 20, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 0) // #90 +PAIRING(10, 20, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 0) // #91 +PAIRING(10, 20, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #92 +PAIRING(10, 20, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #93 +PAIRING(10, 20, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 0) // #94 +PAIRING(10, 20, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 0) // #95 +PAIRING(10, 20, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 0) // #96 +PAIRING(10, 20, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #97 +PAIRING(10, 20, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #98 +PAIRING(10, 20, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #99 +PAIRING(10, 20, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #100 +PAIRING(10, 20, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #101 +PAIRING(10, 20, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #102 +PAIRING(10, 20, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #103 +PAIRING(10, 20, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #104 +PAIRING(10, 20, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #105 +PAIRING(10, 20, 20, 25, 10, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 0) // #106 +PAIRING(10, 20, 20, 25, 10, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 0) // #107 +PAIRING(10, 20, 20, 25, 10, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #108 +PAIRING(10, 20, 20, 25, 10, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 0) // #109 +PAIRING(10, 20, 20, 25, 10, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 0) // #110 +PAIRING(10, 20, 20, 25, 10, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #111 +PAIRING(10, 20, 20, 25, 10, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #112 +PAIRING(10, 20, 20, 25, 10, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #113 +PAIRING(10, 20, 20, 25, 10, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 0) // #114 +PAIRING(10, 20, 20, 25, 10, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 0) // #115 +PAIRING(10, 20, 20, 25, 10, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #116 +PAIRING(10, 20, 20, 25, 10, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 0) // #117 +PAIRING(10, 20, 20, 25, 10, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 0) // #118 +PAIRING(10, 20, 20, 25, 10, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #119 +PAIRING(10, 20, 20, 25, 10, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #120 +PAIRING(10, 20, 20, 25, 10, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #121 +PAIRING(10, 20, 20, 25, 10, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #122 +PAIRING(10, 20, 20, 25, 10, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #123 +PAIRING(10, 20, 20, 25, 10, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #124 +PAIRING(10, 20, 20, 25, 10, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #125 +PAIRING(10, 20, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #126 +PAIRING(10, 20, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 20, 0, 0, 0, 0, 2, 0) // #127 +PAIRING(10, 20, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 20, 20, 21, 0, 0, 2, 0) // #128 +PAIRING(10, 20, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 0) // #129 +PAIRING(10, 20, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 1) // #130 +PAIRING(10, 20, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #131 +PAIRING(10, 20, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 20, 0, 0, 0, 0, 2, 0) // #132 +PAIRING(10, 20, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 20, 20, 21, 0, 0, 2, 0) // #133 +PAIRING(10, 20, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 0) // #134 +PAIRING(10, 20, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 1) // #135 +PAIRING(10, 20, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #136 +PAIRING(10, 20, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #137 +PAIRING(10, 20, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #138 +PAIRING(10, 20, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #139 +PAIRING(10, 20, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #140 +PAIRING(10, 20, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #141 +PAIRING(10, 20, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #142 +PAIRING(10, 20, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #143 +PAIRING(10, 20, 20, 25, 11, 20, 20, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 0) // #144 +PAIRING(10, 20, 20, 25, 11, 20, 20, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 0) // #145 +PAIRING(10, 20, 20, 25, 11, 20, 20, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #146 +PAIRING(10, 20, 20, 25, 11, 20, 21, 22, 1, 1, 11, 20, 0, 0, 21, 22, 0, 0, 2, 0) // #147 +PAIRING(10, 20, 20, 25, 11, 20, 21, 25, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 0) // #148 +PAIRING(10, 20, 20, 25, 11, 20, 21, 26, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 1) // #149 +PAIRING(10, 20, 20, 25, 11, 20, 25, 26, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #150 +PAIRING(10, 20, 20, 25, 11, 20, 26, 27, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #151 +PAIRING(10, 20, 20, 25, 11, 21, 21, 22, 1, 2, 11, 20, 0, 0, 20, 21, 21, 22, 2, 0) // #152 +PAIRING(10, 20, 20, 25, 11, 21, 21, 25, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 0) // #153 +PAIRING(10, 20, 20, 25, 11, 21, 21, 26, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 1) // #154 +PAIRING(10, 20, 20, 25, 11, 21, 22, 23, 1, 2, 11, 20, 0, 0, 20, 21, 22, 23, 2, 0) // #155 +PAIRING(10, 20, 20, 25, 11, 21, 22, 25, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 0) // #156 +PAIRING(10, 20, 20, 25, 11, 21, 22, 26, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 1) // #157 +PAIRING(10, 20, 20, 25, 11, 21, 25, 26, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #158 +PAIRING(10, 20, 20, 25, 11, 21, 26, 27, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #159 +PAIRING(10, 20, 20, 25, 11, 25, 25, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #160 +PAIRING(10, 20, 20, 25, 11, 25, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #161 +PAIRING(10, 20, 20, 25, 11, 26, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #162 +PAIRING(10, 20, 20, 25, 11, 26, 27, 28, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #163 +PAIRING(10, 20, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #164 +PAIRING(10, 20, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #165 +PAIRING(10, 20, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #166 +PAIRING(10, 20, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #167 +PAIRING(10, 20, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #168 +PAIRING(10, 20, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #169 +PAIRING(10, 20, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #170 +PAIRING(10, 20, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #171 +PAIRING(10, 20, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #172 +PAIRING(10, 20, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #173 +PAIRING(10, 20, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #174 +PAIRING(10, 20, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #175 +PAIRING(10, 20, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #176 +PAIRING(10, 20, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #177 +PAIRING(10, 20, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #178 +PAIRING(10, 20, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #179 +PAIRING(10, 20, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #180 +PAIRING(10, 20, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #181 +PAIRING(10, 20, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #182 +PAIRING(10, 20, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #183 +PAIRING(10, 20, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #184 +PAIRING(10, 20, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #185 +PAIRING(10, 20, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #186 +PAIRING(10, 20, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #187 +PAIRING(10, 20, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #188 +PAIRING(10, 20, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #189 +PAIRING(10, 20, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #190 +PAIRING(10, 20, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #191 +PAIRING(10, 15, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #192 +PAIRING(10, 15, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #193 +PAIRING(10, 15, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #194 +PAIRING(10, 15, 20, 25, 6, 7, 7, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #195 +PAIRING(10, 15, 20, 25, 6, 7, 7, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #196 +PAIRING(10, 15, 20, 25, 6, 7, 7, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #197 +PAIRING(10, 15, 20, 25, 6, 7, 7, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #198 +PAIRING(10, 15, 20, 25, 6, 7, 7, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #199 +PAIRING(10, 15, 20, 25, 6, 7, 7, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #200 +PAIRING(10, 15, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #201 +PAIRING(10, 15, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #202 +PAIRING(10, 15, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #203 +PAIRING(10, 15, 20, 25, 6, 7, 8, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #204 +PAIRING(10, 15, 20, 25, 6, 7, 8, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #205 +PAIRING(10, 15, 20, 25, 6, 7, 8, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #206 +PAIRING(10, 15, 20, 25, 6, 7, 8, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #207 +PAIRING(10, 15, 20, 25, 6, 7, 8, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #208 +PAIRING(10, 15, 20, 25, 6, 7, 8, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #209 +PAIRING(10, 15, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #210 +PAIRING(10, 15, 20, 25, 6, 7, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #211 +PAIRING(10, 15, 20, 25, 6, 7, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #212 +PAIRING(10, 15, 20, 25, 6, 7, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #213 +PAIRING(10, 15, 20, 25, 6, 7, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #214 +PAIRING(10, 15, 20, 25, 6, 7, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #215 +PAIRING(10, 15, 20, 25, 6, 7, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #216 +PAIRING(10, 15, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #217 +PAIRING(10, 15, 20, 25, 6, 7, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #218 +PAIRING(10, 15, 20, 25, 6, 7, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #219 +PAIRING(10, 15, 20, 25, 6, 7, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #220 +PAIRING(10, 15, 20, 25, 6, 7, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #221 +PAIRING(10, 15, 20, 25, 6, 7, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #222 +PAIRING(10, 15, 20, 25, 6, 7, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #223 +PAIRING(10, 15, 20, 25, 6, 7, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #224 +PAIRING(10, 15, 20, 25, 6, 7, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #225 +PAIRING(10, 15, 20, 25, 6, 7, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #226 +PAIRING(10, 15, 20, 25, 6, 7, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #227 +PAIRING(10, 15, 20, 25, 6, 7, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #228 +PAIRING(10, 15, 20, 25, 6, 7, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #229 +PAIRING(10, 15, 20, 25, 6, 7, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #230 +PAIRING(10, 15, 20, 25, 6, 7, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #231 +PAIRING(10, 15, 20, 25, 6, 7, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #232 +PAIRING(10, 15, 20, 25, 6, 7, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #233 +PAIRING(10, 15, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #234 +PAIRING(10, 15, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #235 +PAIRING(10, 15, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #236 +PAIRING(10, 15, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #237 +PAIRING(10, 15, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #238 +PAIRING(10, 15, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #239 +PAIRING(10, 15, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #240 +PAIRING(10, 15, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #241 +PAIRING(10, 15, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #242 +PAIRING(10, 15, 20, 25, 6, 10, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #243 +PAIRING(10, 15, 20, 25, 6, 10, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #244 +PAIRING(10, 15, 20, 25, 6, 10, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #245 +PAIRING(10, 15, 20, 25, 6, 10, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #246 +PAIRING(10, 15, 20, 25, 6, 10, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #247 +PAIRING(10, 15, 20, 25, 6, 10, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #248 +PAIRING(10, 15, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #249 +PAIRING(10, 15, 20, 25, 6, 10, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #250 +PAIRING(10, 15, 20, 25, 6, 10, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #251 +PAIRING(10, 15, 20, 25, 6, 10, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #252 +PAIRING(10, 15, 20, 25, 6, 10, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #253 +PAIRING(10, 15, 20, 25, 6, 10, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #254 +PAIRING(10, 15, 20, 25, 6, 10, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #255 +PAIRING(10, 15, 20, 25, 6, 10, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #256 +PAIRING(10, 15, 20, 25, 6, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #257 +PAIRING(10, 15, 20, 25, 6, 10, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #258 +PAIRING(10, 15, 20, 25, 6, 10, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #259 +PAIRING(10, 15, 20, 25, 6, 10, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #260 +PAIRING(10, 15, 20, 25, 6, 10, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #261 +PAIRING(10, 15, 20, 25, 6, 10, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #262 +PAIRING(10, 15, 20, 25, 6, 10, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #263 +PAIRING(10, 15, 20, 25, 6, 10, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #264 +PAIRING(10, 15, 20, 25, 6, 10, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #265 +PAIRING(10, 15, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #266 +PAIRING(10, 15, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #267 +PAIRING(10, 15, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #268 +PAIRING(10, 15, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #269 +PAIRING(10, 15, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #270 +PAIRING(10, 15, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #271 +PAIRING(10, 15, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #272 +PAIRING(10, 15, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #273 +PAIRING(10, 15, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #274 +PAIRING(10, 15, 20, 25, 6, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #275 +PAIRING(10, 15, 20, 25, 6, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 2) // #276 +PAIRING(10, 15, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #277 +PAIRING(10, 15, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 2) // #278 +PAIRING(10, 15, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #279 +PAIRING(10, 15, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #280 +PAIRING(10, 15, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #281 +PAIRING(10, 15, 20, 25, 6, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #282 +PAIRING(10, 15, 20, 25, 6, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 2) // #283 +PAIRING(10, 15, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #284 +PAIRING(10, 15, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 2) // #285 +PAIRING(10, 15, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #286 +PAIRING(10, 15, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #287 +PAIRING(10, 15, 20, 25, 6, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #288 +PAIRING(10, 15, 20, 25, 6, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #289 +PAIRING(10, 15, 20, 25, 6, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #290 +PAIRING(10, 15, 20, 25, 6, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #291 +PAIRING(10, 15, 20, 25, 6, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #292 +PAIRING(10, 15, 20, 25, 6, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #293 +PAIRING(10, 15, 20, 25, 6, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #294 +PAIRING(10, 15, 20, 25, 6, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #295 +PAIRING(10, 15, 20, 25, 6, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #296 +PAIRING(10, 15, 20, 25, 6, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #297 +PAIRING(10, 15, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #298 +PAIRING(10, 15, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #299 +PAIRING(10, 15, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #300 +PAIRING(10, 15, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #301 +PAIRING(10, 15, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #302 +PAIRING(10, 15, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #303 +PAIRING(10, 15, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #304 +PAIRING(10, 15, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #305 +PAIRING(10, 15, 20, 25, 6, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #306 +PAIRING(10, 15, 20, 25, 6, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #307 +PAIRING(10, 15, 20, 25, 6, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #308 +PAIRING(10, 15, 20, 25, 6, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #309 +PAIRING(10, 15, 20, 25, 6, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #310 +PAIRING(10, 15, 20, 25, 6, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #311 +PAIRING(10, 15, 20, 25, 6, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #312 +PAIRING(10, 15, 20, 25, 6, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #313 +PAIRING(10, 15, 20, 25, 6, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #314 +PAIRING(10, 15, 20, 25, 6, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #315 +PAIRING(10, 15, 20, 25, 6, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #316 +PAIRING(10, 15, 20, 25, 6, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #317 +PAIRING(10, 15, 20, 25, 6, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #318 +PAIRING(10, 15, 20, 25, 6, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #319 +PAIRING(10, 15, 20, 25, 6, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #320 +PAIRING(10, 15, 20, 25, 6, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #321 +PAIRING(10, 15, 20, 25, 6, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #322 +PAIRING(10, 15, 20, 25, 6, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #323 +PAIRING(10, 15, 20, 25, 6, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #324 +PAIRING(10, 15, 20, 25, 6, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #325 +PAIRING(10, 15, 20, 25, 6, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #326 +PAIRING(10, 15, 20, 25, 6, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #327 +PAIRING(10, 15, 20, 25, 6, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #328 +PAIRING(10, 15, 20, 25, 6, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #329 +PAIRING(10, 15, 20, 25, 6, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #330 +PAIRING(10, 15, 20, 25, 6, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #331 +PAIRING(10, 15, 20, 25, 6, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #332 +PAIRING(10, 15, 20, 25, 6, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #333 +PAIRING(10, 15, 20, 25, 6, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #334 +PAIRING(10, 15, 20, 25, 6, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #335 +PAIRING(10, 15, 20, 25, 6, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #336 +PAIRING(10, 15, 20, 25, 6, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #337 +PAIRING(10, 15, 20, 25, 6, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #338 +PAIRING(10, 15, 20, 25, 6, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #339 +PAIRING(10, 15, 20, 25, 6, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #340 +PAIRING(10, 15, 20, 25, 6, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #341 +PAIRING(10, 15, 20, 25, 6, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #342 +PAIRING(10, 15, 20, 25, 6, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #343 +PAIRING(10, 15, 20, 25, 6, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #344 +PAIRING(10, 15, 20, 25, 6, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #345 +PAIRING(10, 15, 20, 25, 6, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #346 +PAIRING(10, 15, 20, 25, 6, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #347 +PAIRING(10, 15, 20, 25, 6, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #348 +PAIRING(10, 15, 20, 25, 6, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #349 +PAIRING(10, 15, 20, 25, 6, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #350 +PAIRING(10, 15, 20, 25, 6, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #351 +PAIRING(10, 15, 20, 25, 6, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #352 +PAIRING(10, 15, 20, 25, 6, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #353 +PAIRING(10, 15, 20, 25, 6, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #354 +PAIRING(10, 15, 20, 25, 6, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #355 +PAIRING(10, 15, 20, 25, 6, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #356 +PAIRING(10, 15, 20, 25, 6, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #357 +PAIRING(10, 15, 20, 25, 6, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #358 +PAIRING(10, 15, 20, 25, 6, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #359 +PAIRING(10, 15, 20, 25, 6, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #360 +PAIRING(10, 15, 20, 25, 6, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #361 +PAIRING(10, 15, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #362 +PAIRING(10, 15, 20, 25, 10, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #363 +PAIRING(10, 15, 20, 25, 10, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #364 +PAIRING(10, 15, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #365 +PAIRING(10, 15, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 1) // #366 +PAIRING(10, 15, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #367 +PAIRING(10, 15, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #368 +PAIRING(10, 15, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #369 +PAIRING(10, 15, 20, 25, 10, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #370 +PAIRING(10, 15, 20, 25, 10, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #371 +PAIRING(10, 15, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #372 +PAIRING(10, 15, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 1) // #373 +PAIRING(10, 15, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #374 +PAIRING(10, 15, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #375 +PAIRING(10, 15, 20, 25, 10, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #376 +PAIRING(10, 15, 20, 25, 10, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #377 +PAIRING(10, 15, 20, 25, 10, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #378 +PAIRING(10, 15, 20, 25, 10, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #379 +PAIRING(10, 15, 20, 25, 10, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #380 +PAIRING(10, 15, 20, 25, 10, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #381 +PAIRING(10, 15, 20, 25, 10, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #382 +PAIRING(10, 15, 20, 25, 10, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #383 +PAIRING(10, 15, 20, 25, 10, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #384 +PAIRING(10, 15, 20, 25, 10, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #385 +PAIRING(10, 15, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #386 +PAIRING(10, 15, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #387 +PAIRING(10, 15, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #388 +PAIRING(10, 15, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #389 +PAIRING(10, 15, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #390 +PAIRING(10, 15, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #391 +PAIRING(10, 15, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #392 +PAIRING(10, 15, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #393 +PAIRING(10, 15, 20, 25, 10, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #394 +PAIRING(10, 15, 20, 25, 10, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #395 +PAIRING(10, 15, 20, 25, 10, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #396 +PAIRING(10, 15, 20, 25, 10, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #397 +PAIRING(10, 15, 20, 25, 10, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #398 +PAIRING(10, 15, 20, 25, 10, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #399 +PAIRING(10, 15, 20, 25, 10, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #400 +PAIRING(10, 15, 20, 25, 10, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #401 +PAIRING(10, 15, 20, 25, 10, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #402 +PAIRING(10, 15, 20, 25, 10, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #403 +PAIRING(10, 15, 20, 25, 10, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #404 +PAIRING(10, 15, 20, 25, 10, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #405 +PAIRING(10, 15, 20, 25, 10, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #406 +PAIRING(10, 15, 20, 25, 10, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #407 +PAIRING(10, 15, 20, 25, 10, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #408 +PAIRING(10, 15, 20, 25, 10, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #409 +PAIRING(10, 15, 20, 25, 10, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #410 +PAIRING(10, 15, 20, 25, 10, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #411 +PAIRING(10, 15, 20, 25, 10, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #412 +PAIRING(10, 15, 20, 25, 10, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #413 +PAIRING(10, 15, 20, 25, 10, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #414 +PAIRING(10, 15, 20, 25, 10, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #415 +PAIRING(10, 15, 20, 25, 10, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #416 +PAIRING(10, 15, 20, 25, 10, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #417 +PAIRING(10, 15, 20, 25, 10, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #418 +PAIRING(10, 15, 20, 25, 10, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #419 +PAIRING(10, 15, 20, 25, 10, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #420 +PAIRING(10, 15, 20, 25, 10, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #421 +PAIRING(10, 15, 20, 25, 10, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #422 +PAIRING(10, 15, 20, 25, 10, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #423 +PAIRING(10, 15, 20, 25, 10, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #424 +PAIRING(10, 15, 20, 25, 10, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #425 +PAIRING(10, 15, 20, 25, 10, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #426 +PAIRING(10, 15, 20, 25, 10, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #427 +PAIRING(10, 15, 20, 25, 10, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #428 +PAIRING(10, 15, 20, 25, 10, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #429 +PAIRING(10, 15, 20, 25, 10, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #430 +PAIRING(10, 15, 20, 25, 10, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #431 +PAIRING(10, 15, 20, 25, 10, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #432 +PAIRING(10, 15, 20, 25, 10, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #433 +PAIRING(10, 15, 20, 25, 10, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #434 +PAIRING(10, 15, 20, 25, 10, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #435 +PAIRING(10, 15, 20, 25, 10, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #436 +PAIRING(10, 15, 20, 25, 10, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #437 +PAIRING(10, 15, 20, 25, 10, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #438 +PAIRING(10, 15, 20, 25, 10, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #439 +PAIRING(10, 15, 20, 25, 10, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #440 +PAIRING(10, 15, 20, 25, 10, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #441 +PAIRING(10, 15, 20, 25, 10, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #442 +PAIRING(10, 15, 20, 25, 10, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #443 +PAIRING(10, 15, 20, 25, 10, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #444 +PAIRING(10, 15, 20, 25, 10, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #445 +PAIRING(10, 15, 20, 25, 10, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #446 +PAIRING(10, 15, 20, 25, 10, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #447 +PAIRING(10, 15, 20, 25, 10, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #448 +PAIRING(10, 15, 20, 25, 10, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #449 +PAIRING(10, 15, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #450 +PAIRING(10, 15, 20, 25, 11, 12, 12, 15, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #451 +PAIRING(10, 15, 20, 25, 11, 12, 12, 16, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 1) // #452 +PAIRING(10, 15, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #453 +PAIRING(10, 15, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 15, 20, 21, 0, 0, 2, 1) // #454 +PAIRING(10, 15, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #455 +PAIRING(10, 15, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #456 +PAIRING(10, 15, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #457 +PAIRING(10, 15, 20, 25, 11, 12, 13, 15, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #458 +PAIRING(10, 15, 20, 25, 11, 12, 13, 16, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 1) // #459 +PAIRING(10, 15, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #460 +PAIRING(10, 15, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 15, 20, 21, 0, 0, 2, 1) // #461 +PAIRING(10, 15, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #462 +PAIRING(10, 15, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #463 +PAIRING(10, 15, 20, 25, 11, 12, 15, 16, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #464 +PAIRING(10, 15, 20, 25, 11, 12, 15, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #465 +PAIRING(10, 15, 20, 25, 11, 12, 15, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #466 +PAIRING(10, 15, 20, 25, 11, 12, 15, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #467 +PAIRING(10, 15, 20, 25, 11, 12, 15, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #468 +PAIRING(10, 15, 20, 25, 11, 12, 16, 17, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #469 +PAIRING(10, 15, 20, 25, 11, 12, 16, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #470 +PAIRING(10, 15, 20, 25, 11, 12, 16, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #471 +PAIRING(10, 15, 20, 25, 11, 12, 16, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #472 +PAIRING(10, 15, 20, 25, 11, 12, 16, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #473 +PAIRING(10, 15, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #474 +PAIRING(10, 15, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #475 +PAIRING(10, 15, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #476 +PAIRING(10, 15, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #477 +PAIRING(10, 15, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #478 +PAIRING(10, 15, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #479 +PAIRING(10, 15, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #480 +PAIRING(10, 15, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #481 +PAIRING(10, 15, 20, 25, 11, 15, 15, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #482 +PAIRING(10, 15, 20, 25, 11, 15, 15, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #483 +PAIRING(10, 15, 20, 25, 11, 15, 15, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #484 +PAIRING(10, 15, 20, 25, 11, 15, 15, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #485 +PAIRING(10, 15, 20, 25, 11, 15, 15, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #486 +PAIRING(10, 15, 20, 25, 11, 15, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #487 +PAIRING(10, 15, 20, 25, 11, 15, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #488 +PAIRING(10, 15, 20, 25, 11, 15, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #489 +PAIRING(10, 15, 20, 25, 11, 15, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #490 +PAIRING(10, 15, 20, 25, 11, 15, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #491 +PAIRING(10, 15, 20, 25, 11, 15, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #492 +PAIRING(10, 15, 20, 25, 11, 15, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #493 +PAIRING(10, 15, 20, 25, 11, 15, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #494 +PAIRING(10, 15, 20, 25, 11, 15, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #495 +PAIRING(10, 15, 20, 25, 11, 15, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #496 +PAIRING(10, 15, 20, 25, 11, 15, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #497 +PAIRING(10, 15, 20, 25, 11, 15, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #498 +PAIRING(10, 15, 20, 25, 11, 15, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #499 +PAIRING(10, 15, 20, 25, 11, 16, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #500 +PAIRING(10, 15, 20, 25, 11, 16, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #501 +PAIRING(10, 15, 20, 25, 11, 16, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #502 +PAIRING(10, 15, 20, 25, 11, 16, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #503 +PAIRING(10, 15, 20, 25, 11, 16, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #504 +PAIRING(10, 15, 20, 25, 11, 16, 17, 18, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #505 +PAIRING(10, 15, 20, 25, 11, 16, 17, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #506 +PAIRING(10, 15, 20, 25, 11, 16, 17, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #507 +PAIRING(10, 15, 20, 25, 11, 16, 17, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #508 +PAIRING(10, 15, 20, 25, 11, 16, 17, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #509 +PAIRING(10, 15, 20, 25, 11, 16, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #510 +PAIRING(10, 15, 20, 25, 11, 16, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #511 +PAIRING(10, 15, 20, 25, 11, 16, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #512 +PAIRING(10, 15, 20, 25, 11, 16, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 1) // #513 +PAIRING(10, 15, 20, 25, 11, 16, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #514 +PAIRING(10, 15, 20, 25, 11, 16, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 2) // #515 +PAIRING(10, 15, 20, 25, 11, 16, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #516 +PAIRING(10, 15, 20, 25, 11, 16, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #517 +PAIRING(10, 15, 20, 25, 11, 20, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #518 +PAIRING(10, 15, 20, 25, 11, 20, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #519 +PAIRING(10, 15, 20, 25, 11, 20, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #520 +PAIRING(10, 15, 20, 25, 11, 20, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #521 +PAIRING(10, 15, 20, 25, 11, 20, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #522 +PAIRING(10, 15, 20, 25, 11, 20, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #523 +PAIRING(10, 15, 20, 25, 11, 20, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #524 +PAIRING(10, 15, 20, 25, 11, 20, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #525 +PAIRING(10, 15, 20, 25, 11, 21, 21, 22, 1, 2, 11, 15, 0, 0, 20, 21, 21, 22, 2, 1) // #526 +PAIRING(10, 15, 20, 25, 11, 21, 21, 25, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 1) // #527 +PAIRING(10, 15, 20, 25, 11, 21, 21, 26, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 2) // #528 +PAIRING(10, 15, 20, 25, 11, 21, 22, 23, 1, 2, 11, 15, 0, 0, 20, 21, 22, 23, 2, 1) // #529 +PAIRING(10, 15, 20, 25, 11, 21, 22, 25, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 1) // #530 +PAIRING(10, 15, 20, 25, 11, 21, 22, 26, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 2) // #531 +PAIRING(10, 15, 20, 25, 11, 21, 25, 26, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #532 +PAIRING(10, 15, 20, 25, 11, 21, 26, 27, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #533 +PAIRING(10, 15, 20, 25, 11, 25, 25, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #534 +PAIRING(10, 15, 20, 25, 11, 25, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #535 +PAIRING(10, 15, 20, 25, 11, 26, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #536 +PAIRING(10, 15, 20, 25, 11, 26, 27, 28, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #537 +PAIRING(10, 15, 20, 25, 15, 16, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #538 +PAIRING(10, 15, 20, 25, 15, 16, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #539 +PAIRING(10, 15, 20, 25, 15, 16, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #540 +PAIRING(10, 15, 20, 25, 15, 16, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #541 +PAIRING(10, 15, 20, 25, 15, 16, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #542 +PAIRING(10, 15, 20, 25, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #543 +PAIRING(10, 15, 20, 25, 15, 16, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #544 +PAIRING(10, 15, 20, 25, 15, 16, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #545 +PAIRING(10, 15, 20, 25, 15, 16, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #546 +PAIRING(10, 15, 20, 25, 15, 16, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #547 +PAIRING(10, 15, 20, 25, 15, 16, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #548 +PAIRING(10, 15, 20, 25, 15, 16, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #549 +PAIRING(10, 15, 20, 25, 15, 16, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #550 +PAIRING(10, 15, 20, 25, 15, 16, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #551 +PAIRING(10, 15, 20, 25, 15, 16, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #552 +PAIRING(10, 15, 20, 25, 15, 16, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #553 +PAIRING(10, 15, 20, 25, 15, 16, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #554 +PAIRING(10, 15, 20, 25, 15, 16, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #555 +PAIRING(10, 15, 20, 25, 15, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #556 +PAIRING(10, 15, 20, 25, 15, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #557 +PAIRING(10, 15, 20, 25, 15, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #558 +PAIRING(10, 15, 20, 25, 15, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #559 +PAIRING(10, 15, 20, 25, 15, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #560 +PAIRING(10, 15, 20, 25, 15, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #561 +PAIRING(10, 15, 20, 25, 15, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #562 +PAIRING(10, 15, 20, 25, 15, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #563 +PAIRING(10, 15, 20, 25, 15, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #564 +PAIRING(10, 15, 20, 25, 15, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #565 +PAIRING(10, 15, 20, 25, 15, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #566 +PAIRING(10, 15, 20, 25, 15, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #567 +PAIRING(10, 15, 20, 25, 15, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #568 +PAIRING(10, 15, 20, 25, 15, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #569 +PAIRING(10, 15, 20, 25, 15, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #570 +PAIRING(10, 15, 20, 25, 15, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #571 +PAIRING(10, 15, 20, 25, 15, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #572 +PAIRING(10, 15, 20, 25, 15, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #573 +PAIRING(10, 15, 20, 25, 15, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #574 +PAIRING(10, 15, 20, 25, 15, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #575 +PAIRING(10, 15, 20, 25, 16, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #576 +PAIRING(10, 15, 20, 25, 16, 17, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #577 +PAIRING(10, 15, 20, 25, 16, 17, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #578 +PAIRING(10, 15, 20, 25, 16, 17, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #579 +PAIRING(10, 15, 20, 25, 16, 17, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #580 +PAIRING(10, 15, 20, 25, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #581 +PAIRING(10, 15, 20, 25, 16, 17, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #582 +PAIRING(10, 15, 20, 25, 16, 17, 18, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #583 +PAIRING(10, 15, 20, 25, 16, 17, 18, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #584 +PAIRING(10, 15, 20, 25, 16, 17, 18, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #585 +PAIRING(10, 15, 20, 25, 16, 17, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #586 +PAIRING(10, 15, 20, 25, 16, 17, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #587 +PAIRING(10, 15, 20, 25, 16, 17, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #588 +PAIRING(10, 15, 20, 25, 16, 17, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #589 +PAIRING(10, 15, 20, 25, 16, 17, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #590 +PAIRING(10, 15, 20, 25, 16, 17, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #591 +PAIRING(10, 15, 20, 25, 16, 17, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #592 +PAIRING(10, 15, 20, 25, 16, 17, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #593 +PAIRING(10, 15, 20, 25, 16, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #594 +PAIRING(10, 15, 20, 25, 16, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #595 +PAIRING(10, 15, 20, 25, 16, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #596 +PAIRING(10, 15, 20, 25, 16, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #597 +PAIRING(10, 15, 20, 25, 16, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #598 +PAIRING(10, 15, 20, 25, 16, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #599 +PAIRING(10, 15, 20, 25, 16, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #600 +PAIRING(10, 15, 20, 25, 16, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #601 +PAIRING(10, 15, 20, 25, 16, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #602 +PAIRING(10, 15, 20, 25, 16, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #603 +PAIRING(10, 15, 20, 25, 16, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #604 +PAIRING(10, 15, 20, 25, 16, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #605 +PAIRING(10, 15, 20, 25, 16, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #606 +PAIRING(10, 15, 20, 25, 16, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #607 +PAIRING(10, 15, 20, 25, 16, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #608 +PAIRING(10, 15, 20, 25, 16, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #609 +PAIRING(10, 15, 20, 25, 16, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #610 +PAIRING(10, 15, 20, 25, 16, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #611 +PAIRING(10, 15, 20, 25, 16, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #612 +PAIRING(10, 15, 20, 25, 16, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #613 +PAIRING(10, 15, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #614 +PAIRING(10, 15, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #615 +PAIRING(10, 15, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #616 +PAIRING(10, 15, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #617 +PAIRING(10, 15, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #618 +PAIRING(10, 15, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #619 +PAIRING(10, 15, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #620 +PAIRING(10, 15, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #621 +PAIRING(10, 15, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #622 +PAIRING(10, 15, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #623 +PAIRING(10, 15, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #624 +PAIRING(10, 15, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #625 +PAIRING(10, 15, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #626 +PAIRING(10, 15, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #627 +PAIRING(10, 15, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #628 +PAIRING(10, 15, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #629 +PAIRING(10, 15, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #630 +PAIRING(10, 15, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #631 +PAIRING(10, 15, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #632 +PAIRING(10, 15, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #633 +PAIRING(10, 15, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #634 +PAIRING(10, 15, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #635 +PAIRING(10, 15, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #636 +PAIRING(10, 15, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #637 +PAIRING(10, 15, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #638 +PAIRING(10, 15, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #639 +PAIRING(10, 15, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #640 +PAIRING(10, 15, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #641 diff --git a/src/lib/crashdump/gbreakpad/common/tests/auto_tempdir.h b/src/lib/crashdump/gbreakpad/common/tests/auto_tempdir.h new file mode 100644 index 0000000..ccfd81b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/tests/auto_tempdir.h @@ -0,0 +1,98 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Utility class for creating a temporary directory for unit tests +// that is deleted in the destructor. +#ifndef GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TEMPDIR +#define GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TEMPDIR + +#include +#include + +#include + +#include "breakpad_googletest_includes.h" + +#if !defined(__ANDROID__) +#define TEMPDIR "/tmp" +#else +#define TEMPDIR "/data/local/tmp" +#endif + +namespace google_breakpad { + +class AutoTempDir { + public: + AutoTempDir() { + char temp_dir[] = TEMPDIR "/breakpad.XXXXXXXXXX"; + EXPECT_TRUE(mkdtemp(temp_dir) != NULL); + path_.assign(temp_dir); + } + + ~AutoTempDir() { + DeleteRecursively(path_); + } + + const std::string& path() const { + return path_; + } + + private: + void DeleteRecursively(const std::string& path) { + // First remove any files in the dir + DIR* dir = opendir(path.c_str()); + if (!dir) + return; + + dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) + continue; + std::string entry_path = path + "/" + entry->d_name; + struct stat stats; + EXPECT_TRUE(lstat(entry_path.c_str(), &stats) == 0); + if (S_ISDIR(stats.st_mode)) + DeleteRecursively(entry_path); + else + EXPECT_TRUE(unlink(entry_path.c_str()) == 0); + } + EXPECT_TRUE(closedir(dir) == 0); + EXPECT_TRUE(rmdir(path.c_str()) == 0); + } + + // prevent copy construction and assignment + AutoTempDir(const AutoTempDir&); + AutoTempDir& operator=(const AutoTempDir&); + + std::string path_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_TESTS_AUTO_TEMPDIR diff --git a/src/lib/crashdump/gbreakpad/common/tests/file_utils.cc b/src/lib/crashdump/gbreakpad/common/tests/file_utils.cc new file mode 100644 index 0000000..1264b07 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/tests/file_utils.cc @@ -0,0 +1,152 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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_utils.cc: Implement utility functions for file manipulation. +// See file_utils.h for details. + +#include +#include +#include +#include + +#include "common/linux/eintr_wrapper.h" +#include "common/tests/file_utils.h" + +namespace google_breakpad { + +bool CopyFile(const char* from_path, const char* to_path) { + int infile = HANDLE_EINTR(open(from_path, O_RDONLY)); + if (infile < 0) { + perror("open"); + return false; + } + + int outfile = HANDLE_EINTR(creat(to_path, 0666)); + if (outfile < 0) { + perror("creat"); + if (HANDLE_EINTR(close(infile)) < 0) { + perror("close"); + } + return false; + } + + char buffer[1024]; + bool result = true; + + while (result) { + ssize_t bytes_read = HANDLE_EINTR(read(infile, buffer, sizeof(buffer))); + if (bytes_read < 0) { + perror("read"); + result = false; + break; + } + if (bytes_read == 0) + break; + ssize_t bytes_written_per_read = 0; + do { + ssize_t bytes_written_partial = HANDLE_EINTR(write( + outfile, + &buffer[bytes_written_per_read], + bytes_read - bytes_written_per_read)); + if (bytes_written_partial < 0) { + perror("write"); + result = false; + break; + } + bytes_written_per_read += bytes_written_partial; + } while (bytes_written_per_read < bytes_read); + } + + if (HANDLE_EINTR(close(infile)) == -1) { + perror("close"); + result = false; + } + if (HANDLE_EINTR(close(outfile)) == -1) { + perror("close"); + result = false; + } + + return result; +} + +bool ReadFile(const char* path, void* buffer, ssize_t* buffer_size) { + int fd = HANDLE_EINTR(open(path, O_RDONLY)); + if (fd == -1) { + perror("open"); + return false; + } + + bool ok = true; + if (buffer && buffer_size && *buffer_size > 0) { + memset(buffer, 0, sizeof(*buffer_size)); + *buffer_size = HANDLE_EINTR(read(fd, buffer, *buffer_size)); + if (*buffer_size == -1) { + perror("read"); + ok = false; + } + } + if (HANDLE_EINTR(close(fd)) == -1) { + perror("close"); + ok = false; + } + return ok; +} + +bool WriteFile(const char* path, const void* buffer, size_t buffer_size) { + int fd = HANDLE_EINTR(open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU)); + if (fd == -1) { + perror("open"); + return false; + } + + bool ok = true; + if (buffer) { + size_t bytes_written_total = 0; + ssize_t bytes_written_partial = 0; + const char* data = reinterpret_cast(buffer); + while (bytes_written_total < buffer_size) { + bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + buffer_size - bytes_written_total)); + if (bytes_written_partial < 0) { + perror("write"); + ok = false; + break; + } + bytes_written_total += bytes_written_partial; + } + } + if (HANDLE_EINTR(close(fd)) == -1) { + perror("close"); + ok = false; + } + return ok; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/tests/file_utils.h b/src/lib/crashdump/gbreakpad/common/tests/file_utils.h new file mode 100644 index 0000000..c98a9bf --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/tests/file_utils.h @@ -0,0 +1,52 @@ +// Copyright (c) 2011, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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_utils.h: Define utility functions for file manipulation, which +// are used for testing. + +#ifndef COMMON_TESTS_FILE_UTILS_H_ +#define COMMON_TESTS_FILE_UTILS_H_ + +namespace google_breakpad { + +// Copies a file from |from_path| to |to_path|. Returns true on success. +bool CopyFile(const char* from_path, const char* to_path); + +// Reads the content of a file at |path| into |buffer|. |buffer_size| specifies +// the size of |buffer| in bytes and returns the number of bytes read from the +// file on success. Returns true on success. +bool ReadFile(const char* path, void* buffer, ssize_t* buffer_size); + +// Writes |buffer_size| bytes of the content in |buffer| to a file at |path|. +// Returns true on success. +bool WriteFile(const char* path, const void* buffer, size_t buffer_size); + +} // namespace google_breakpad + +#endif // COMMON_TESTS_FILE_UTILS_H_ diff --git a/src/lib/crashdump/gbreakpad/common/windows/guid_string.cc b/src/lib/crashdump/gbreakpad/common/windows/guid_string.cc new file mode 100644 index 0000000..b7f877e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/guid_string.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// guid_string.cc: Convert GUIDs to strings. +// +// See guid_string.h for documentation. + +#include + +#include "common/windows/string_utils-inl.h" + +#include "common/windows/guid_string.h" + +namespace google_breakpad { + +// static +wstring GUIDString::GUIDToWString(GUID *guid) { + wchar_t guid_string[37]; + swprintf( + guid_string, sizeof(guid_string) / sizeof(guid_string[0]), + L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], + guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); + + // remove when VC++7.1 is no longer supported + guid_string[sizeof(guid_string) / sizeof(guid_string[0]) - 1] = L'\0'; + + return wstring(guid_string); +} + +// static +wstring GUIDString::GUIDToSymbolServerWString(GUID *guid) { + wchar_t guid_string[33]; + swprintf( + guid_string, sizeof(guid_string) / sizeof(guid_string[0]), + L"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], + guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); + + // remove when VC++7.1 is no longer supported + guid_string[sizeof(guid_string) / sizeof(guid_string[0]) - 1] = L'\0'; + + return wstring(guid_string); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/windows/guid_string.h b/src/lib/crashdump/gbreakpad/common/windows/guid_string.h new file mode 100644 index 0000000..f8aa8a2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/guid_string.h @@ -0,0 +1,58 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// guid_string.cc: Convert GUIDs to strings. + +#ifndef COMMON_WINDOWS_GUID_STRING_H__ +#define COMMON_WINDOWS_GUID_STRING_H__ + +#include + +#include + +namespace google_breakpad { + +using std::wstring; + +class GUIDString { + public: + // Converts guid to a string in the format recommended by RFC 4122 and + // returns the string. + static wstring GUIDToWString(GUID *guid); + + // Converts guid to a string formatted as uppercase hexadecimal, with + // no separators, and returns the string. This is the format used for + // symbol server identifiers, although identifiers have an age tacked + // on to the string. + static wstring GUIDToSymbolServerWString(GUID *guid); +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_GUID_STRING_H__ diff --git a/src/lib/crashdump/gbreakpad/common/windows/http_upload.cc b/src/lib/crashdump/gbreakpad/common/windows/http_upload.cc new file mode 100644 index 0000000..ddf61f7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/http_upload.cc @@ -0,0 +1,412 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 + +// Disable exception handler warnings. +#pragma warning( disable : 4530 ) + +#include + +#include "common/windows/string_utils-inl.h" + +#include "common/windows/http_upload.h" + +namespace google_breakpad { + +using std::ifstream; +using std::ios; + +static const wchar_t kUserAgent[] = L"Breakpad/1.0 (Windows)"; + +// Helper class which closes an internet handle when it goes away +class HTTPUpload::AutoInternetHandle { + public: + explicit AutoInternetHandle(HINTERNET handle) : handle_(handle) {} + ~AutoInternetHandle() { + if (handle_) { + InternetCloseHandle(handle_); + } + } + + HINTERNET get() { return handle_; } + + private: + HINTERNET handle_; +}; + +// static +bool HTTPUpload::SendRequest(const wstring &url, + const map ¶meters, + const wstring &upload_file, + const wstring &file_part_name, + int *timeout, + wstring *response_body, + int *response_code) { + if (response_code) { + *response_code = 0; + } + + // TODO(bryner): support non-ASCII parameter names + if (!CheckParameters(parameters)) { + return false; + } + + // Break up the URL and make sure we can handle it + wchar_t scheme[16], host[256], path[256]; + URL_COMPONENTS components; + memset(&components, 0, sizeof(components)); + components.dwStructSize = sizeof(components); + components.lpszScheme = scheme; + components.dwSchemeLength = sizeof(scheme) / sizeof(scheme[0]); + components.lpszHostName = host; + components.dwHostNameLength = sizeof(host) / sizeof(host[0]); + components.lpszUrlPath = path; + components.dwUrlPathLength = sizeof(path) / sizeof(path[0]); + if (!InternetCrackUrl(url.c_str(), static_cast(url.size()), + 0, &components)) { + return false; + } + bool secure = false; + if (wcscmp(scheme, L"https") == 0) { + secure = true; + } else if (wcscmp(scheme, L"http") != 0) { + return false; + } + + AutoInternetHandle internet(InternetOpen(kUserAgent, + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, // proxy name + NULL, // proxy bypass + 0)); // flags + if (!internet.get()) { + return false; + } + + AutoInternetHandle connection(InternetConnect(internet.get(), + host, + components.nPort, + NULL, // user name + NULL, // password + INTERNET_SERVICE_HTTP, + 0, // flags + NULL)); // context + if (!connection.get()) { + return false; + } + + DWORD http_open_flags = secure ? INTERNET_FLAG_SECURE : 0; + http_open_flags |= INTERNET_FLAG_NO_COOKIES; + AutoInternetHandle request(HttpOpenRequest(connection.get(), + L"POST", + path, + NULL, // version + NULL, // referer + NULL, // agent type + http_open_flags, + NULL)); // context + if (!request.get()) { + return false; + } + + wstring boundary = GenerateMultipartBoundary(); + wstring content_type_header = GenerateRequestHeader(boundary); + HttpAddRequestHeaders(request.get(), + content_type_header.c_str(), + static_cast(-1), + HTTP_ADDREQ_FLAG_ADD); + + string request_body; + if (!GenerateRequestBody(parameters, upload_file, + file_part_name, boundary, &request_body)) { + return false; + } + + if (timeout) { + if (!InternetSetOption(request.get(), + INTERNET_OPTION_SEND_TIMEOUT, + timeout, + sizeof(timeout))) { + fwprintf(stderr, L"Could not unset send timeout, continuing...\n"); + } + + if (!InternetSetOption(request.get(), + INTERNET_OPTION_RECEIVE_TIMEOUT, + timeout, + sizeof(timeout))) { + fwprintf(stderr, L"Could not unset receive timeout, continuing...\n"); + } + } + + if (!HttpSendRequest(request.get(), NULL, 0, + const_cast(request_body.data()), + static_cast(request_body.size()))) { + return false; + } + + // The server indicates a successful upload with HTTP status 200. + wchar_t http_status[4]; + DWORD http_status_size = sizeof(http_status); + if (!HttpQueryInfo(request.get(), HTTP_QUERY_STATUS_CODE, + static_cast(&http_status), &http_status_size, + 0)) { + return false; + } + + int http_response = wcstol(http_status, NULL, 10); + if (response_code) { + *response_code = http_response; + } + + bool result = (http_response == 200); + + if (result) { + result = ReadResponse(request.get(), response_body); + } + + return result; +} + +// static +bool HTTPUpload::ReadResponse(HINTERNET request, wstring *response) { + bool has_content_length_header = false; + wchar_t content_length[32]; + DWORD content_length_size = sizeof(content_length); + DWORD claimed_size = 0; + string response_body; + + if (HttpQueryInfo(request, HTTP_QUERY_CONTENT_LENGTH, + static_cast(&content_length), + &content_length_size, 0)) { + has_content_length_header = true; + claimed_size = wcstol(content_length, NULL, 10); + response_body.reserve(claimed_size); + } + + + DWORD bytes_available; + DWORD total_read = 0; + BOOL return_code; + + while (((return_code = InternetQueryDataAvailable(request, &bytes_available, + 0, 0)) != 0) && bytes_available > 0) { + + vector response_buffer(bytes_available); + DWORD size_read; + + return_code = InternetReadFile(request, + &response_buffer[0], + bytes_available, &size_read); + + if (return_code && size_read > 0) { + total_read += size_read; + response_body.append(&response_buffer[0], size_read); + } else { + break; + } + } + + bool succeeded = return_code && (!has_content_length_header || + (total_read == claimed_size)); + if (succeeded && response) { + *response = UTF8ToWide(response_body); + } + + return succeeded; +} + +// static +wstring HTTPUpload::GenerateMultipartBoundary() { + // The boundary has 27 '-' characters followed by 16 hex digits + static const wchar_t kBoundaryPrefix[] = L"---------------------------"; + static const int kBoundaryLength = 27 + 16 + 1; + + // Generate some random numbers to fill out the boundary + int r0 = rand(); + int r1 = rand(); + + wchar_t temp[kBoundaryLength]; + swprintf(temp, kBoundaryLength, L"%s%08X%08X", kBoundaryPrefix, r0, r1); + + // remove when VC++7.1 is no longer supported + temp[kBoundaryLength - 1] = L'\0'; + + return wstring(temp); +} + +// static +wstring HTTPUpload::GenerateRequestHeader(const wstring &boundary) { + wstring header = L"Content-Type: multipart/form-data; boundary="; + header += boundary; + return header; +} + +// static +bool HTTPUpload::GenerateRequestBody(const map ¶meters, + const wstring &upload_file, + const wstring &file_part_name, + const wstring &boundary, + string *request_body) { + vector contents; + if (!GetFileContents(upload_file, &contents)) { + return false; + } + + string boundary_str = WideToUTF8(boundary); + if (boundary_str.empty()) { + return false; + } + + request_body->clear(); + + // Append each of the parameter pairs as a form-data part + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + request_body->append("--" + boundary_str + "\r\n"); + request_body->append("Content-Disposition: form-data; name=\"" + + WideToUTF8(pos->first) + "\"\r\n\r\n" + + WideToUTF8(pos->second) + "\r\n"); + } + + // Now append the upload file as a binary (octet-stream) part + string filename_utf8 = WideToUTF8(upload_file); + if (filename_utf8.empty()) { + return false; + } + + string file_part_name_utf8 = WideToUTF8(file_part_name); + if (file_part_name_utf8.empty()) { + return false; + } + + request_body->append("--" + boundary_str + "\r\n"); + request_body->append("Content-Disposition: form-data; " + "name=\"" + file_part_name_utf8 + "\"; " + "filename=\"" + filename_utf8 + "\"\r\n"); + request_body->append("Content-Type: application/octet-stream\r\n"); + request_body->append("\r\n"); + + if (!contents.empty()) { + request_body->append(&(contents[0]), contents.size()); + } + request_body->append("\r\n"); + request_body->append("--" + boundary_str + "--\r\n"); + return true; +} + +// static +bool HTTPUpload::GetFileContents(const wstring &filename, + vector *contents) { + // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a + // wchar_t* filename, so use _wfopen directly in that case. For VC8 and + // later, _wfopen has been deprecated in favor of _wfopen_s, which does + // not exist in earlier versions, so let the ifstream open the file itself. +#if _MSC_VER >= 1400 // MSVC 2005/8 + ifstream file; + file.open(filename.c_str(), ios::binary); +#else // _MSC_VER >= 1400 + ifstream file(_wfopen(filename.c_str(), L"rb")); +#endif // _MSC_VER >= 1400 + if (file.is_open()) { + file.seekg(0, ios::end); + std::streamoff length = file.tellg(); + contents->resize(length); + if (length != 0) { + file.seekg(0, ios::beg); + file.read(&((*contents)[0]), length); + } + file.close(); + return true; + } + return false; +} + +// static +wstring HTTPUpload::UTF8ToWide(const string &utf8) { + if (utf8.length() == 0) { + return wstring(); + } + + // compute the length of the buffer we'll need + int charcount = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, NULL, 0); + + if (charcount == 0) { + return wstring(); + } + + // convert + wchar_t* buf = new wchar_t[charcount]; + MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), -1, buf, charcount); + wstring result(buf); + delete[] buf; + return result; +} + +// static +string HTTPUpload::WideToUTF8(const wstring &wide) { + if (wide.length() == 0) { + return string(); + } + + // compute the length of the buffer we'll need + int charcount = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, + NULL, 0, NULL, NULL); + if (charcount == 0) { + return string(); + } + + // convert + char *buf = new char[charcount]; + WideCharToMultiByte(CP_UTF8, 0, wide.c_str(), -1, buf, charcount, + NULL, NULL); + + string result(buf); + delete[] buf; + return result; +} + +// static +bool HTTPUpload::CheckParameters(const map ¶meters) { + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + const wstring &str = pos->first; + if (str.size() == 0) { + return false; // disallow empty parameter names + } + for (unsigned int i = 0; i < str.size(); ++i) { + wchar_t c = str[i]; + if (c < 32 || c == '"' || c > 127) { + return false; + } + } + } + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/windows/http_upload.h b/src/lib/crashdump/gbreakpad/common/windows/http_upload.h new file mode 100644 index 0000000..8a17aab --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/http_upload.h @@ -0,0 +1,126 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST +// request using wininet. It currently supports requests that contain +// a set of string parameters (key/value pairs), and a file to upload. + +#ifndef COMMON_WINDOWS_HTTP_UPLOAD_H__ +#define COMMON_WINDOWS_HTTP_UPLOAD_H__ + +#pragma warning( push ) +// Disable exception handler warnings. +#pragma warning( disable : 4530 ) + +#include +#include + +#include +#include +#include + +namespace google_breakpad { + +using std::string; +using std::wstring; +using std::map; +using std::vector; + +class HTTPUpload { + public: + // Sends the given set of parameters, along with the contents of + // upload_file, as a multipart POST request to the given URL. + // file_part_name contains the name of the file part of the request + // (i.e. it corresponds to the name= attribute on an . + // Parameter names must contain only printable ASCII characters, + // and may not contain a quote (") character. + // Only HTTP(S) URLs are currently supported. Returns true on success. + // If the request is successful and response_body is non-NULL, + // the response body will be returned in response_body. + // If response_code is non-NULL, it will be set to the HTTP response code + // received (or 0 if the request failed before getting an HTTP response). + static bool SendRequest(const wstring &url, + const map ¶meters, + const wstring &upload_file, + const wstring &file_part_name, + int *timeout, + wstring *response_body, + int *response_code); + + private: + class AutoInternetHandle; + + // Retrieves the HTTP response. If NULL is passed in for response, + // this merely checks (via the return value) that we were successfully + // able to retrieve exactly as many bytes of content in the response as + // were specified in the Content-Length header. + static bool HTTPUpload::ReadResponse(HINTERNET request, wstring* response); + + // Generates a new multipart boundary for a POST request + static wstring GenerateMultipartBoundary(); + + // Generates a HTTP request header for a multipart form submit. + static wstring GenerateRequestHeader(const wstring &boundary); + + // Given a set of parameters, an upload filename, and a file part name, + // generates a multipart request body string with these parameters + // and minidump contents. Returns true on success. + static bool GenerateRequestBody(const map ¶meters, + const wstring &upload_file, + const wstring &file_part_name, + const wstring &boundary, + string *request_body); + + // Fills the supplied vector with the contents of filename. + static bool GetFileContents(const wstring &filename, vector *contents); + + // Converts a UTF8 string to UTF16. + static wstring UTF8ToWide(const string &utf8); + + // Converts a UTF16 string to UTF8. + static string WideToUTF8(const wstring &wide); + + // Checks that the given list of parameters has only printable + // ASCII characters in the parameter name, and does not contain + // any quote (") characters. Returns true if so. + static bool CheckParameters(const map ¶meters); + + // No instances of this class should be created. + // Disallow all constructors, destructors, and operator=. + HTTPUpload(); + explicit HTTPUpload(const HTTPUpload &); + void operator=(const HTTPUpload &); + ~HTTPUpload(); +}; + +} // namespace google_breakpad + +#pragma warning( pop ) + +#endif // COMMON_WINDOWS_HTTP_UPLOAD_H__ diff --git a/src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.cc b/src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.cc new file mode 100644 index 0000000..8d8e55c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.cc @@ -0,0 +1,1001 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/windows/string_utils-inl.h" + +#include "common/windows/pdb_source_line_writer.h" +#include "common/windows/guid_string.h" + +// This constant may be missing from DbgHelp.h. See the documentation for +// IDiaSymbol::get_undecoratedNameEx. +#ifndef UNDNAME_NO_ECSU +#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union. +#endif // UNDNAME_NO_ECSU + +namespace google_breakpad { + +using std::vector; + +// A helper class to scope a PLOADED_IMAGE. +class AutoImage { + public: + explicit AutoImage(PLOADED_IMAGE img) : img_(img) {} + ~AutoImage() { + if (img_) + ImageUnload(img_); + } + + operator PLOADED_IMAGE() { return img_; } + PLOADED_IMAGE operator->() { return img_; } + + private: + PLOADED_IMAGE img_; +}; + +PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) { +} + +PDBSourceLineWriter::~PDBSourceLineWriter() { +} + +bool PDBSourceLineWriter::Open(const wstring &file, FileFormat format) { + Close(); + + if (FAILED(CoInitialize(NULL))) { + fprintf(stderr, "CoInitialize failed\n"); + return false; + } + + CComPtr data_source; + if (FAILED(data_source.CoCreateInstance(CLSID_DiaSource))) { + const int kGuidSize = 64; + wchar_t classid[kGuidSize] = {0}; + StringFromGUID2(CLSID_DiaSource, classid, kGuidSize); + // vc80 uses bce36434-2c24-499e-bf49-8bd99b0eeb68. + // vc90 uses 4C41678E-887B-4365-A09E-925D28DB33C2. + fprintf(stderr, "CoCreateInstance CLSID_DiaSource %S failed " + "(msdia*.dll unregistered?)\n", classid); + return false; + } + + switch (format) { + case PDB_FILE: + if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { + fprintf(stderr, "loadDataFromPdb failed for %ws\n", file.c_str()); + return false; + } + break; + case EXE_FILE: + if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { + fprintf(stderr, "loadDataForExe failed for %ws\n", file.c_str()); + return false; + } + code_file_ = file; + break; + case ANY_FILE: + if (FAILED(data_source->loadDataFromPdb(file.c_str()))) { + if (FAILED(data_source->loadDataForExe(file.c_str(), NULL, NULL))) { + fprintf(stderr, "loadDataForPdb and loadDataFromExe failed for %ws\n", file.c_str()); + return false; + } + code_file_ = file; + } + break; + default: + fprintf(stderr, "Unknown file format\n"); + return false; + } + + if (FAILED(data_source->openSession(&session_))) { + fprintf(stderr, "openSession failed\n"); + } + + return true; +} + +bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) { + // The line number format is: + // + CComPtr line; + ULONG count; + + while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) { + DWORD rva; + if (FAILED(line->get_relativeVirtualAddress(&rva))) { + fprintf(stderr, "failed to get line rva\n"); + return false; + } + + DWORD length; + if (FAILED(line->get_length(&length))) { + fprintf(stderr, "failed to get line code length\n"); + return false; + } + + DWORD dia_source_id; + if (FAILED(line->get_sourceFileId(&dia_source_id))) { + fprintf(stderr, "failed to get line source file id\n"); + return false; + } + // duplicate file names are coalesced to share one ID + DWORD source_id = GetRealFileID(dia_source_id); + + DWORD line_num; + if (FAILED(line->get_lineNumber(&line_num))) { + fprintf(stderr, "failed to get line number\n"); + return false; + } + + fprintf(output_, "%x %x %d %d\n", rva, length, line_num, source_id); + line.Release(); + } + return true; +} + +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, + IDiaSymbol *block) { + // The function format is: + // FUNC
    + DWORD rva; + if (FAILED(block->get_relativeVirtualAddress(&rva))) { + fprintf(stderr, "couldn't get rva\n"); + return false; + } + + ULONGLONG length; + if (FAILED(block->get_length(&length))) { + fprintf(stderr, "failed to get function length\n"); + return false; + } + + if (length == 0) { + // Silently ignore zero-length functions, which can infrequently pop up. + return true; + } + + CComBSTR name; + int stack_param_size; + if (!GetSymbolFunctionName(function, &name, &stack_param_size)) { + return false; + } + + // If the decorated name didn't give the parameter size, try to + // calculate it. + if (stack_param_size < 0) { + stack_param_size = GetFunctionStackParamSize(function); + } + + fprintf(output_, "FUNC %x %" WIN_STRING_FORMAT_LL "x %x %ws\n", + rva, length, stack_param_size, name); + + CComPtr lines; + if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) { + return false; + } + + if (!PrintLines(lines)) { + return false; + } + return true; +} + +bool PDBSourceLineWriter::PrintSourceFiles() { + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed\n"); + return false; + } + + CComPtr compiland; + ULONG count; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr source_files; + if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) { + return false; + } + CComPtr file; + while (SUCCEEDED(source_files->Next(1, &file, &count)) && count == 1) { + DWORD file_id; + if (FAILED(file->get_uniqueId(&file_id))) { + return false; + } + + CComBSTR file_name; + if (FAILED(file->get_fileName(&file_name))) { + return false; + } + + wstring file_name_string(file_name); + if (!FileIDIsCached(file_name_string)) { + // this is a new file name, cache it and output a FILE line. + CacheFileID(file_name_string, file_id); + fwprintf(output_, L"FILE %d %s\n", file_id, file_name); + } else { + // this file name has already been seen, just save this + // ID for later lookup. + StoreDuplicateFileID(file_name_string, file_id); + } + file.Release(); + } + compiland.Release(); + } + return true; +} + +bool PDBSourceLineWriter::PrintFunctions() { + CComPtr symbols; + if (FAILED(session_->getSymbolsByAddr(&symbols))) { + fprintf(stderr, "failed to get symbol enumerator\n"); + return false; + } + + CComPtr symbol; + if (FAILED(symbols->symbolByAddr(1, 0, &symbol))) { + fprintf(stderr, "failed to enumerate symbols\n"); + return false; + } + + DWORD rva_last = 0; + if (FAILED(symbol->get_relativeVirtualAddress(&rva_last))) { + fprintf(stderr, "failed to get symbol rva\n"); + return false; + } + + ULONG count; + do { + DWORD tag; + if (FAILED(symbol->get_symTag(&tag))) { + fprintf(stderr, "failed to get symbol tag\n"); + return false; + } + + // For a given function, DIA seems to give either a symbol with + // SymTagFunction or SymTagPublicSymbol, but not both. This means + // that PDBSourceLineWriter will output either a FUNC or PUBLIC line, + // but not both. + if (tag == SymTagFunction) { + if (!PrintFunction(symbol, symbol)) { + return false; + } + } else if (tag == SymTagPublicSymbol) { + if (!PrintCodePublicSymbol(symbol)) { + return false; + } + } + symbol.Release(); + } while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1); + + // When building with PGO, the compiler can split functions into + // "hot" and "cold" blocks, and move the "cold" blocks out to separate + // pages, so the function can be noncontiguous. To find these blocks, + // we have to iterate over all the compilands, and then find blocks + // that are children of them. We can then find the lexical parents + // of those blocks and print out an extra FUNC line for blocks + // that are not contained in their parent functions. + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed on the global\n"); + return false; + } + + CComPtr compiland; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr blocks; + if (FAILED(compiland->findChildren(SymTagBlock, NULL, + nsNone, &blocks))) { + fprintf(stderr, "findChildren failed on a compiland\n"); + return false; + } + + CComPtr block; + while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { + // find this block's lexical parent function + CComPtr parent; + DWORD tag; + if (SUCCEEDED(block->get_lexicalParent(&parent)) && + SUCCEEDED(parent->get_symTag(&tag)) && + tag == SymTagFunction) { + // now get the block's offset and the function's offset and size, + // and determine if the block is outside of the function + DWORD func_rva, block_rva; + ULONGLONG func_length; + if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && + SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && + SUCCEEDED(parent->get_length(&func_length))) { + if (block_rva < func_rva || block_rva > (func_rva + func_length)) { + if (!PrintFunction(parent, block)) { + return false; + } + } + } + } + parent.Release(); + block.Release(); + } + blocks.Release(); + compiland.Release(); + } + + return true; +} + +bool PDBSourceLineWriter::PrintFrameData() { + // It would be nice if it were possible to output frame data alongside the + // associated function, as is done with line numbers, but the DIA API + // doesn't make it possible to get the frame data in that way. + + CComPtr tables; + if (FAILED(session_->getEnumTables(&tables))) + return false; + + // Pick up the first table that supports IDiaEnumFrameData. + CComPtr frame_data_enum; + CComPtr table; + ULONG count; + while (!frame_data_enum && + SUCCEEDED(tables->Next(1, &table, &count)) && + count == 1) { + table->QueryInterface(_uuidof(IDiaEnumFrameData), + reinterpret_cast(&frame_data_enum)); + table.Release(); + } + if (!frame_data_enum) + return false; + + DWORD last_type = -1; + DWORD last_rva = -1; + DWORD last_code_size = 0; + DWORD last_prolog_size = -1; + + CComPtr frame_data; + while (SUCCEEDED(frame_data_enum->Next(1, &frame_data, &count)) && + count == 1) { + DWORD type; + if (FAILED(frame_data->get_type(&type))) + return false; + + DWORD rva; + if (FAILED(frame_data->get_relativeVirtualAddress(&rva))) + return false; + + DWORD code_size; + if (FAILED(frame_data->get_lengthBlock(&code_size))) + return false; + + DWORD prolog_size; + if (FAILED(frame_data->get_lengthProlog(&prolog_size))) + return false; + + // epliog_size is always 0. + DWORD epilog_size = 0; + + // parameter_size is the size of parameters passed on the stack. If any + // parameters are not passed on the stack (such as in registers), their + // sizes will not be included in parameter_size. + DWORD parameter_size; + if (FAILED(frame_data->get_lengthParams(¶meter_size))) + return false; + + DWORD saved_register_size; + if (FAILED(frame_data->get_lengthSavedRegisters(&saved_register_size))) + return false; + + DWORD local_size; + if (FAILED(frame_data->get_lengthLocals(&local_size))) + return false; + + // get_maxStack can return S_FALSE, just use 0 in that case. + DWORD max_stack_size = 0; + if (FAILED(frame_data->get_maxStack(&max_stack_size))) + return false; + + // get_programString can return S_FALSE, indicating that there is no + // program string. In that case, check whether %ebp is used. + HRESULT program_string_result; + CComBSTR program_string; + if (FAILED(program_string_result = frame_data->get_program( + &program_string))) { + return false; + } + + // get_allocatesBasePointer can return S_FALSE, treat that as though + // %ebp is not used. + BOOL allocates_base_pointer = FALSE; + if (program_string_result != S_OK) { + if (FAILED(frame_data->get_allocatesBasePointer( + &allocates_base_pointer))) { + return false; + } + } + + // Only print out a line if type, rva, code_size, or prolog_size have + // changed from the last line. It is surprisingly common (especially in + // system library PDBs) for DIA to return a series of identical + // IDiaFrameData objects. For kernel32.pdb from Windows XP SP2 on x86, + // this check reduces the size of the dumped symbol file by a third. + if (type != last_type || rva != last_rva || code_size != last_code_size || + prolog_size != last_prolog_size) { + fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %d ", + type, rva, code_size, prolog_size, epilog_size, + parameter_size, saved_register_size, local_size, max_stack_size, + program_string_result == S_OK); + if (program_string_result == S_OK) { + fprintf(output_, "%ws\n", program_string); + } else { + fprintf(output_, "%d\n", allocates_base_pointer); + } + + last_type = type; + last_rva = rva; + last_code_size = code_size; + last_prolog_size = prolog_size; + } + + frame_data.Release(); + } + + return true; +} + +bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) { + BOOL is_code; + if (FAILED(symbol->get_code(&is_code))) { + return false; + } + if (!is_code) { + return true; + } + + DWORD rva; + if (FAILED(symbol->get_relativeVirtualAddress(&rva))) { + return false; + } + + CComBSTR name; + int stack_param_size; + if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) { + return false; + } + + fprintf(output_, "PUBLIC %x %x %ws\n", rva, + stack_param_size > 0 ? stack_param_size : 0, name); + return true; +} + +bool PDBSourceLineWriter::PrintPDBInfo() { + PDBModuleInfo info; + if (!GetModuleInfo(&info)) { + return false; + } + + // Hard-code "windows" for the OS because that's the only thing that makes + // sense for PDB files. (This might not be strictly correct for Windows CE + // support, but we don't care about that at the moment.) + fprintf(output_, "MODULE windows %ws %ws %ws\n", + info.cpu.c_str(), info.debug_identifier.c_str(), + info.debug_file.c_str()); + + return true; +} + +bool PDBSourceLineWriter::PrintPEInfo() { + PEModuleInfo info; + if (!GetPEInfo(&info)) { + return false; + } + + fprintf(output_, "INFO CODE_ID %ws %ws\n", + info.code_identifier.c_str(), + info.code_file.c_str()); + return true; +} + +// wcstol_positive_strict is sort of like wcstol, but much stricter. string +// should be a buffer pointing to a null-terminated string containing only +// decimal digits. If the entire string can be converted to an integer +// without overflowing, and there are no non-digit characters before the +// result is set to the value and this function returns true. Otherwise, +// this function returns false. This is an alternative to the strtol, atoi, +// and scanf families, which are not as strict about input and in some cases +// don't provide a good way for the caller to determine if a conversion was +// successful. +static bool wcstol_positive_strict(wchar_t *string, int *result) { + int value = 0; + for (wchar_t *c = string; *c != '\0'; ++c) { + int last_value = value; + value *= 10; + // Detect overflow. + if (value / 10 != last_value || value < 0) { + return false; + } + if (*c < '0' || *c > '9') { + return false; + } + unsigned int c_value = *c - '0'; + last_value = value; + value += c_value; + // Detect overflow. + if (value < last_value) { + return false; + } + // Forbid leading zeroes unless the string is just "0". + if (value == 0 && *(c+1) != '\0') { + return false; + } + } + *result = value; + return true; +} + +bool PDBSourceLineWriter::FindPEFile() { + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComBSTR symbols_file; + if (SUCCEEDED(global->get_symbolsFileName(&symbols_file))) { + wstring file(symbols_file); + + // Look for an EXE or DLL file. + const wchar_t *extensions[] = { L"exe", L"dll" }; + for (int i = 0; i < sizeof(extensions) / sizeof(extensions[0]); i++) { + size_t dot_pos = file.find_last_of(L"."); + if (dot_pos != wstring::npos) { + file.replace(dot_pos + 1, wstring::npos, extensions[i]); + // Check if this file exists. + if (GetFileAttributesW(file.c_str()) != INVALID_FILE_ATTRIBUTES) { + code_file_ = file; + return true; + } + } + } + } + + return false; +} + +// static +bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function, + BSTR *name, + int *stack_param_size) { + *stack_param_size = -1; + const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS | + UNDNAME_NO_FUNCTION_RETURNS | + UNDNAME_NO_ALLOCATION_MODEL | + UNDNAME_NO_ALLOCATION_LANGUAGE | + UNDNAME_NO_THISTYPE | + UNDNAME_NO_ACCESS_SPECIFIERS | + UNDNAME_NO_THROW_SIGNATURES | + UNDNAME_NO_MEMBER_TYPE | + UNDNAME_NO_RETURN_UDT_MODEL | + UNDNAME_NO_ECSU; + + // Use get_undecoratedNameEx to get readable C++ names with arguments. + if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) { + if (function->get_name(name) != S_OK) { + fprintf(stderr, "failed to get function name\n"); + return false; + } + // If a name comes from get_name because no undecorated form existed, + // it's already formatted properly to be used as output. Don't do any + // additional processing. + // + // MSVC7's DIA seems to not undecorate names in as many cases as MSVC8's. + // This will result in calling get_name for some C++ symbols, so + // all of the parameter and return type information may not be included in + // the name string. + } else { + // C++ uses a bogus "void" argument for functions and methods that don't + // take any parameters. Take it out of the undecorated name because it's + // ugly and unnecessary. + const wchar_t *replace_string = L"(void)"; + const size_t replace_length = wcslen(replace_string); + const wchar_t *replacement_string = L"()"; + size_t length = wcslen(*name); + if (length >= replace_length) { + wchar_t *name_end = *name + length - replace_length; + if (wcscmp(name_end, replace_string) == 0) { + WindowsStringUtils::safe_wcscpy(name_end, replace_length, + replacement_string); + length = wcslen(*name); + } + } + + // Undecorate names used for stdcall and fastcall. These names prefix + // the identifier with '_' (stdcall) or '@' (fastcall) and suffix it + // with '@' followed by the number of bytes of parameters, in decimal. + // If such a name is found, take note of the size and undecorate it. + // Only do this for names that aren't C++, which is determined based on + // whether the undecorated name contains any ':' or '(' characters. + if (!wcschr(*name, ':') && !wcschr(*name, '(') && + (*name[0] == '_' || *name[0] == '@')) { + wchar_t *last_at = wcsrchr(*name + 1, '@'); + if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) { + // If this function adheres to the fastcall convention, it accepts up + // to the first 8 bytes of parameters in registers (%ecx and %edx). + // We're only interested in the stack space used for parameters, so + // so subtract 8 and don't let the size go below 0. + if (*name[0] == '@') { + if (*stack_param_size > 8) { + *stack_param_size -= 8; + } else { + *stack_param_size = 0; + } + } + + // Undecorate the name by moving it one character to the left in its + // buffer, and terminating it where the last '@' had been. + WindowsStringUtils::safe_wcsncpy(*name, length, + *name + 1, last_at - *name - 1); + } else if (*name[0] == '_') { + // This symbol's name is encoded according to the cdecl rules. The + // name doesn't end in a '@' character followed by a decimal positive + // integer, so it's not a stdcall name. Strip off the leading + // underscore. + WindowsStringUtils::safe_wcsncpy(*name, length, *name + 1, length); + } + } + } + + return true; +} + +// static +int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) { + // This implementation is highly x86-specific. + + // Gather the symbols corresponding to data. + CComPtr data_children; + if (FAILED(function->findChildren(SymTagData, NULL, nsNone, + &data_children))) { + return 0; + } + + // lowest_base is the lowest %ebp-relative byte offset used for a parameter. + // highest_end is one greater than the highest offset (i.e. base + length). + // Stack parameters are assumed to be contiguous, because in reality, they + // are. + int lowest_base = INT_MAX; + int highest_end = INT_MIN; + + CComPtr child; + DWORD count; + while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) { + // If any operation fails at this point, just proceed to the next child. + // Use the next_child label instead of continue because child needs to + // be released before it's reused. Declare constructable/destructable + // types early to avoid gotos that cross initializations. + CComPtr child_type; + + // DataIsObjectPtr is only used for |this|. Because |this| can be passed + // as a stack parameter, look for it in addition to traditional + // parameters. + DWORD child_kind; + if (FAILED(child->get_dataKind(&child_kind)) || + (child_kind != DataIsParam && child_kind != DataIsObjectPtr)) { + goto next_child; + } + + // Only concentrate on register-relative parameters. Parameters may also + // be enregistered (passed directly in a register), but those don't + // consume any stack space, so they're not of interest. + DWORD child_location_type; + if (FAILED(child->get_locationType(&child_location_type)) || + child_location_type != LocIsRegRel) { + goto next_child; + } + + // Of register-relative parameters, the only ones that make any sense are + // %ebp- or %esp-relative. Note that MSVC's debugging information always + // gives parameters as %ebp-relative even when a function doesn't use a + // traditional frame pointer and stack parameters are accessed relative to + // %esp, so just look for %ebp-relative parameters. If you wanted to + // access parameters, you'd probably want to treat these %ebp-relative + // offsets as if they were relative to %esp before a function's prolog + // executed. + DWORD child_register; + if (FAILED(child->get_registerId(&child_register)) || + child_register != CV_REG_EBP) { + goto next_child; + } + + LONG child_register_offset; + if (FAILED(child->get_offset(&child_register_offset))) { + goto next_child; + } + + // IDiaSymbol::get_type can succeed but still pass back a NULL value. + if (FAILED(child->get_type(&child_type)) || !child_type) { + goto next_child; + } + + ULONGLONG child_length; + if (FAILED(child_type->get_length(&child_length))) { + goto next_child; + } + + int child_end = child_register_offset + static_cast(child_length); + if (child_register_offset < lowest_base) { + lowest_base = child_register_offset; + } + if (child_end > highest_end) { + highest_end = child_end; + } + +next_child: + child.Release(); + } + + int param_size = 0; + // Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest + // possible address to find a stack parameter before executing a function's + // prolog (see above). Some optimizations cause parameter offsets to be + // lower than 4, but we're not concerned with those because we're only + // looking for parameters contained in addresses higher than where the + // return address is stored. + if (lowest_base < 4) { + lowest_base = 4; + } + if (highest_end > lowest_base) { + // All stack parameters are pushed as at least 4-byte quantities. If the + // last type was narrower than 4 bytes, promote it. This assumes that all + // parameters' offsets are 4-byte-aligned, which is always the case. Only + // worry about the last type, because we're not summing the type sizes, + // just looking at the lowest and highest offsets. + int remainder = highest_end % 4; + if (remainder) { + highest_end += 4 - remainder; + } + + param_size = highest_end - lowest_base; + } + + return param_size; +} + +bool PDBSourceLineWriter::WriteMap(FILE *map_file) { + output_ = map_file; + + bool ret = PrintPDBInfo(); + // This is not a critical piece of the symbol file. + PrintPEInfo(); + ret = ret && + PrintSourceFiles() && + PrintFunctions() && + PrintFrameData(); + + output_ = NULL; + return ret; +} + +void PDBSourceLineWriter::Close() { + session_.Release(); +} + +bool PDBSourceLineWriter::GetModuleInfo(PDBModuleInfo *info) { + if (!info) { + return false; + } + + info->debug_file.clear(); + info->debug_identifier.clear(); + info->cpu.clear(); + + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + return false; + } + + DWORD machine_type; + // get_machineType can return S_FALSE. + if (global->get_machineType(&machine_type) == S_OK) { + // The documentation claims that get_machineType returns a value from + // the CV_CPU_TYPE_e enumeration, but that's not the case. + // Instead, it returns one of the IMAGE_FILE_MACHINE values as + // defined here: + // http://msdn.microsoft.com/en-us/library/ms680313%28VS.85%29.aspx + switch (machine_type) { + case IMAGE_FILE_MACHINE_I386: + info->cpu = L"x86"; + break; + case IMAGE_FILE_MACHINE_AMD64: + info->cpu = L"x86_64"; + break; + default: + info->cpu = L"unknown"; + break; + } + } else { + // Unexpected, but handle gracefully. + info->cpu = L"unknown"; + } + + // DWORD* and int* are not compatible. This is clean and avoids a cast. + DWORD age; + if (FAILED(global->get_age(&age))) { + return false; + } + + bool uses_guid; + if (!UsesGUID(&uses_guid)) { + return false; + } + + if (uses_guid) { + GUID guid; + if (FAILED(global->get_guid(&guid))) { + return false; + } + + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + wchar_t age_string[9]; + swprintf(age_string, sizeof(age_string) / sizeof(age_string[0]), + L"%x", age); + + // remove when VC++7.1 is no longer supported + age_string[sizeof(age_string) / sizeof(age_string[0]) - 1] = L'\0'; + + info->debug_identifier = GUIDString::GUIDToSymbolServerWString(&guid); + info->debug_identifier.append(age_string); + } else { + DWORD signature; + if (FAILED(global->get_signature(&signature))) { + return false; + } + + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + wchar_t identifier_string[17]; + swprintf(identifier_string, + sizeof(identifier_string) / sizeof(identifier_string[0]), + L"%08X%x", signature, age); + + // remove when VC++7.1 is no longer supported + identifier_string[sizeof(identifier_string) / + sizeof(identifier_string[0]) - 1] = L'\0'; + + info->debug_identifier = identifier_string; + } + + CComBSTR debug_file_string; + if (FAILED(global->get_symbolsFileName(&debug_file_string))) { + return false; + } + info->debug_file = + WindowsStringUtils::GetBaseName(wstring(debug_file_string)); + + return true; +} + +bool PDBSourceLineWriter::GetPEInfo(PEModuleInfo *info) { + if (!info) { + return false; + } + + if (code_file_.empty() && !FindPEFile()) { + fprintf(stderr, "Couldn't locate EXE or DLL file.\n"); + return false; + } + + // Convert wchar to native charset because ImageLoad only takes + // a PSTR as input. + string code_file; + if (!WindowsStringUtils::safe_wcstombs(code_file_, &code_file)) { + return false; + } + + AutoImage img(ImageLoad((PSTR)code_file.c_str(), NULL)); + if (!img) { + fprintf(stderr, "Failed to open PE file: %s\n", code_file.c_str()); + return false; + } + + info->code_file = WindowsStringUtils::GetBaseName(code_file_); + + // The date and time that the file was created by the linker. + DWORD TimeDateStamp = img->FileHeader->FileHeader.TimeDateStamp; + // The size of the file in bytes, including all headers. + DWORD SizeOfImage = 0; + PIMAGE_OPTIONAL_HEADER64 opt = + &((PIMAGE_NT_HEADERS64)img->FileHeader)->OptionalHeader; + if (opt->Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + // 64-bit PE file. + SizeOfImage = opt->SizeOfImage; + } + else { + // 32-bit PE file. + SizeOfImage = img->FileHeader->OptionalHeader.SizeOfImage; + } + wchar_t code_identifier[32]; + swprintf(code_identifier, + sizeof(code_identifier) / sizeof(code_identifier[0]), + L"%08X%X", TimeDateStamp, SizeOfImage); + info->code_identifier = code_identifier; + + return true; +} + +bool PDBSourceLineWriter::UsesGUID(bool *uses_guid) { + if (!uses_guid) + return false; + + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) + return false; + + GUID guid; + if (FAILED(global->get_guid(&guid))) + return false; + + DWORD signature; + if (FAILED(global->get_signature(&signature))) + return false; + + // There are two possibilities for guid: either it's a real 128-bit GUID + // as identified in a code module by a new-style CodeView record, or it's + // a 32-bit signature (timestamp) as identified by an old-style record. + // See MDCVInfoPDB70 and MDCVInfoPDB20 in minidump_format.h. + // + // Because DIA doesn't provide a way to directly determine whether a module + // uses a GUID or a 32-bit signature, this code checks whether the first 32 + // bits of guid are the same as the signature, and if the rest of guid is + // zero. If so, then with a pretty high degree of certainty, there's an + // old-style CodeView record in use. This method will only falsely find an + // an old-style CodeView record if a real 128-bit GUID has its first 32 + // bits set the same as the module's signature (timestamp) and the rest of + // the GUID is set to 0. This is highly unlikely. + + GUID signature_guid = {signature}; // 0-initializes other members + *uses_guid = !IsEqualGUID(guid, signature_guid); + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.h b/src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.h new file mode 100644 index 0000000..ef2a27d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/pdb_source_line_writer.h @@ -0,0 +1,238 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// PDBSourceLineWriter uses a pdb file produced by Visual C++ to output +// a line/address map for use with BasicSourceLineResolver. + +#ifndef _PDB_SOURCE_LINE_WRITER_H__ +#define _PDB_SOURCE_LINE_WRITER_H__ + +#include + +#include +#include + +struct IDiaEnumLineNumbers; +struct IDiaSession; +struct IDiaSymbol; + +namespace google_breakpad { + +using std::wstring; +using stdext::hash_map; + +// A structure that carries information that identifies a pdb file. +struct PDBModuleInfo { + public: + // The basename of the pdb file from which information was loaded. + wstring debug_file; + + // The pdb's identifier. For recent pdb files, the identifier consists + // of the pdb's guid, in uppercase hexadecimal form without any dashes + // or separators, followed immediately by the pdb's age, also in + // uppercase hexadecimal form. For older pdb files which have no guid, + // the identifier is the pdb's 32-bit signature value, in zero-padded + // hexadecimal form, followed immediately by the pdb's age, in lowercase + // hexadecimal form. + wstring debug_identifier; + + // A string identifying the cpu that the pdb is associated with. + // Currently, this may be "x86" or "unknown". + wstring cpu; +}; + +// A structure that carries information that identifies a PE file, +// either an EXE or a DLL. +struct PEModuleInfo { + // The basename of the PE file. + wstring code_file; + + // The PE file's code identifier, which consists of its timestamp + // and file size concatenated together into a single hex string. + // (The fields IMAGE_OPTIONAL_HEADER::SizeOfImage and + // IMAGE_FILE_HEADER::TimeDateStamp, as defined in the ImageHlp + // documentation.) This is not well documented, if it's documented + // at all, but it's what symstore does and what DbgHelp supports. + wstring code_identifier; +}; + +class PDBSourceLineWriter { + public: + enum FileFormat { + PDB_FILE, // a .pdb file containing debug symbols + EXE_FILE, // a .exe or .dll file + ANY_FILE // try PDB_FILE and then EXE_FILE + }; + + explicit PDBSourceLineWriter(); + ~PDBSourceLineWriter(); + + // Opens the given file. For executable files, the corresponding pdb + // file must be available; Open will be if it is not. + // If there is already a pdb file open, it is automatically closed. + // Returns true on success. + bool Open(const wstring &file, FileFormat format); + + // Locates the pdb file for the given executable (exe or dll) file, + // and opens it. If there is already a pdb file open, it is automatically + // closed. Returns true on success. + bool OpenExecutable(const wstring &exe_file); + + // Writes a map file from the current pdb file to the given file stream. + // Returns true on success. + bool WriteMap(FILE *map_file); + + // Closes the current pdb file and its associated resources. + void Close(); + + // Retrieves information about the module's debugging file. Returns + // true on success and false on failure. + bool GetModuleInfo(PDBModuleInfo *info); + + // Retrieves information about the module's PE file. Returns + // true on success and false on failure. + bool GetPEInfo(PEModuleInfo *info); + + // Sets uses_guid to true if the opened file uses a new-style CodeView + // record with a 128-bit GUID, or false if the opened file uses an old-style + // CodeView record. When no GUID is available, a 32-bit signature should be + // used to identify the module instead. If the information cannot be + // determined, this method returns false. + bool UsesGUID(bool *uses_guid); + + private: + // Outputs the line/address pairs for each line in the enumerator. + // Returns true on success. + bool PrintLines(IDiaEnumLineNumbers *lines); + + // Outputs a function address and name, followed by its source line list. + // block can be the same object as function, or it can be a reference + // to a code block that is lexically part of this function, but + // resides at a separate address. + // Returns true on success. + bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block); + + // Outputs all functions as described above. Returns true on success. + bool PrintFunctions(); + + // Outputs all of the source files in the session's pdb file. + // Returns true on success. + bool PrintSourceFiles(); + + // Outputs all of the frame information necessary to construct stack + // backtraces in the absence of frame pointers. Returns true on success. + bool PrintFrameData(); + + // Outputs a single public symbol address and name, if the symbol corresponds + // to a code address. Returns true on success. If symbol is does not + // correspond to code, returns true without outputting anything. + bool PrintCodePublicSymbol(IDiaSymbol *symbol); + + // Outputs a line identifying the PDB file that is being dumped, along with + // its uuid and age. + bool PrintPDBInfo(); + + // Outputs a line identifying the PE file corresponding to the PDB + // file that is being dumped, along with its code identifier, + // which consists of its timestamp and file size. + bool PrintPEInfo(); + + // Returns true if this filename has already been seen, + // and an ID is stored for it, or false if it has not. + bool FileIDIsCached(const wstring &file) { + return unique_files_.find(file) != unique_files_.end(); + }; + + // Cache this filename and ID for later reuse. + void CacheFileID(const wstring &file, DWORD id) { + unique_files_[file] = id; + }; + + // Store this ID in the cache as a duplicate for this filename. + void StoreDuplicateFileID(const wstring &file, DWORD id) { + hash_map::iterator iter = unique_files_.find(file); + if (iter != unique_files_.end()) { + // map this id to the previously seen one + file_ids_[id] = iter->second; + } + }; + + // Given a file's unique ID, return the ID that should be used to + // reference it. There may be multiple files with identical filenames + // but different unique IDs. The cache attempts to coalesce these into + // one ID per unique filename. + DWORD GetRealFileID(DWORD id) { + hash_map::iterator iter = file_ids_.find(id); + if (iter == file_ids_.end()) + return id; + return iter->second; + }; + + // Find the PE file corresponding to the loaded PDB file, and + // set the code_file_ member. Returns false on failure. + bool FindPEFile(); + + // Returns the function name for a symbol. If possible, the name is + // undecorated. If the symbol's decorated form indicates the size of + // parameters on the stack, this information is returned in stack_param_size. + // Returns true on success. If the symbol doesn't encode parameter size + // information, stack_param_size is set to -1. + static bool GetSymbolFunctionName(IDiaSymbol *function, BSTR *name, + int *stack_param_size); + + // Returns the number of bytes of stack space used for a function's + // parameters. function must have the tag SymTagFunction. In the event of + // a failure, returns 0, which is also a valid number of bytes. + static int GetFunctionStackParamSize(IDiaSymbol *function); + + // The filename of the PE file corresponding to the currently-open + // pdb file. + wstring code_file_; + + // The session for the currently-open pdb file. + CComPtr session_; + + // The current output file for this WriteMap invocation. + FILE *output_; + + // There may be many duplicate filenames with different IDs. + // This maps from the DIA "unique ID" to a single ID per unique + // filename. + hash_map file_ids_; + // This maps unique filenames to file IDs. + hash_map unique_files_; + + // Disallow copy ctor and operator= + PDBSourceLineWriter(const PDBSourceLineWriter&); + void operator=(const PDBSourceLineWriter&); +}; + +} // namespace google_breakpad + +#endif // _PDB_SOURCE_LINE_WRITER_H__ diff --git a/src/lib/crashdump/gbreakpad/common/windows/string_utils-inl.h b/src/lib/crashdump/gbreakpad/common/windows/string_utils-inl.h new file mode 100644 index 0000000..d281aaa --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/string_utils-inl.h @@ -0,0 +1,142 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// string_utils-inl.h: Safer string manipulation on Windows, supporting +// pre-MSVC8 environments. + +#ifndef COMMON_WINDOWS_STRING_UTILS_INL_H__ +#define COMMON_WINDOWS_STRING_UTILS_INL_H__ + +#include +#include + +#include + +// The "ll" printf format size specifier corresponding to |long long| was +// intrudced in MSVC8. Earlier versions did not provide this size specifier, +// but "I64" can be used to print 64-bit types. Don't use "I64" where "ll" +// is available, in the event of oddball systems where |long long| is not +// 64 bits wide. +#if _MSC_VER >= 1400 // MSVC 2005/8 +#define WIN_STRING_FORMAT_LL "ll" +#else // MSC_VER >= 1400 +#define WIN_STRING_FORMAT_LL "I64" +#endif // MSC_VER >= 1400 + +// A nonconforming version of swprintf, without the length argument, was +// included with the CRT prior to MSVC8. Although a conforming version was +// also available via an overload, it is not reliably chosen. _snwprintf +// behaves as a standards-confirming swprintf should, so force the use of +// _snwprintf when using older CRTs. +#if _MSC_VER < 1400 // MSVC 2005/8 +#define swprintf _snwprintf +#else +// For MSVC8 and newer, swprintf_s is the recommended method. Conveniently, +// it takes the same argument list as swprintf. +#define swprintf swprintf_s +#endif // MSC_VER < 1400 + +namespace google_breakpad { + +using std::string; +using std::wstring; + +class WindowsStringUtils { + public: + // Roughly equivalent to MSVC8's wcscpy_s, except pre-MSVC8, this does + // not fail if source is longer than destination_size. The destination + // buffer is always 0-terminated. + static void safe_wcscpy(wchar_t *destination, size_t destination_size, + const wchar_t *source); + + // Roughly equivalent to MSVC8's wcsncpy_s, except that _TRUNCATE cannot + // be passed directly, and pre-MSVC8, this will not fail if source or count + // are longer than destination_size. The destination buffer is always + // 0-terminated. + static void safe_wcsncpy(wchar_t *destination, size_t destination_size, + const wchar_t *source, size_t count); + + // Performs multi-byte to wide character conversion on C++ strings, using + // mbstowcs_s (MSVC8) or mbstowcs (pre-MSVC8). Returns false on failure, + // without setting wcs. + static bool safe_mbstowcs(const string &mbs, wstring *wcs); + + // The inverse of safe_mbstowcs. + static bool safe_wcstombs(const wstring &wcs, string *mbs); + + // Returns the base name of a file, e.g. strips off the path. + static wstring GetBaseName(const wstring &filename); + + private: + // Disallow instantiation and other object-based operations. + WindowsStringUtils(); + WindowsStringUtils(const WindowsStringUtils&); + ~WindowsStringUtils(); + void operator=(const WindowsStringUtils&); +}; + +// static +inline void WindowsStringUtils::safe_wcscpy(wchar_t *destination, + size_t destination_size, + const wchar_t *source) { +#if _MSC_VER >= 1400 // MSVC 2005/8 + wcscpy_s(destination, destination_size, source); +#else // _MSC_VER >= 1400 + // Pre-MSVC 2005/8 doesn't have wcscpy_s. Simulate it with wcsncpy. + // wcsncpy doesn't 0-terminate the destination buffer if the source string + // is longer than size. Ensure that the destination is 0-terminated. + wcsncpy(destination, source, destination_size); + if (destination && destination_size) + destination[destination_size - 1] = 0; +#endif // _MSC_VER >= 1400 +} + +// static +inline void WindowsStringUtils::safe_wcsncpy(wchar_t *destination, + size_t destination_size, + const wchar_t *source, + size_t count) { +#if _MSC_VER >= 1400 // MSVC 2005/8 + wcsncpy_s(destination, destination_size, source, count); +#else // _MSC_VER >= 1400 + // Pre-MSVC 2005/8 doesn't have wcsncpy_s. Simulate it with wcsncpy. + // wcsncpy doesn't 0-terminate the destination buffer if the source string + // is longer than size. Ensure that the destination is 0-terminated. + if (destination_size < count) + count = destination_size; + + wcsncpy(destination, source, count); + if (destination && count) + destination[count - 1] = 0; +#endif // _MSC_VER >= 1400 +} + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_STRING_UTILS_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/common/windows/string_utils.cc b/src/lib/crashdump/gbreakpad/common/windows/string_utils.cc new file mode 100644 index 0000000..e6ffa08 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/common/windows/string_utils.cc @@ -0,0 +1,133 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "common/windows/string_utils-inl.h" + +namespace google_breakpad { + +// static +wstring WindowsStringUtils::GetBaseName(const wstring &filename) { + wstring base_name(filename); + size_t slash_pos = base_name.find_last_of(L"/\\"); + if (slash_pos != wstring::npos) { + base_name.erase(0, slash_pos + 1); + } + return base_name; +} + +// static +bool WindowsStringUtils::safe_mbstowcs(const string &mbs, wstring *wcs) { + assert(wcs); + + // First, determine the length of the destination buffer. + size_t wcs_length; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + errno_t err; + if ((err = mbstowcs_s(&wcs_length, NULL, 0, mbs.c_str(), _TRUNCATE)) != 0) { + return false; + } + assert(wcs_length > 0); +#else // _MSC_VER >= 1400 + if ((wcs_length = mbstowcs(NULL, mbs.c_str(), mbs.length())) < 0) { + return false; + } + + // Leave space for the 0-terminator. + ++wcs_length; +#endif // _MSC_VER >= 1400 + + std::vector wcs_v(wcs_length); + + // Now, convert. +#if _MSC_VER >= 1400 // MSVC 2005/8 + if ((err = mbstowcs_s(NULL, &wcs_v[0], wcs_length, mbs.c_str(), + _TRUNCATE)) != 0) { + return false; + } +#else // _MSC_VER >= 1400 + if (mbstowcs(&wcs_v[0], mbs.c_str(), mbs.length()) < 0) { + return false; + } + + // Ensure presence of 0-terminator. + wcs_v[wcs_length - 1] = '\0'; +#endif // _MSC_VER >= 1400 + + *wcs = &wcs_v[0]; + return true; +} + +// static +bool WindowsStringUtils::safe_wcstombs(const wstring &wcs, string *mbs) { + assert(mbs); + + // First, determine the length of the destination buffer. + size_t mbs_length; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + errno_t err; + if ((err = wcstombs_s(&mbs_length, NULL, 0, wcs.c_str(), _TRUNCATE)) != 0) { + return false; + } + assert(mbs_length > 0); +#else // _MSC_VER >= 1400 + if ((mbs_length = wcstombs(NULL, wcs.c_str(), wcs.length())) < 0) { + return false; + } + + // Leave space for the 0-terminator. + ++mbs_length; +#endif // _MSC_VER >= 1400 + + std::vector mbs_v(mbs_length); + + // Now, convert. +#if _MSC_VER >= 1400 // MSVC 2005/8 + if ((err = wcstombs_s(NULL, &mbs_v[0], mbs_length, wcs.c_str(), + _TRUNCATE)) != 0) { + return false; + } +#else // _MSC_VER >= 1400 + if (wcstombs(&mbs_v[0], wcs.c_str(), wcs.length()) < 0) { + return false; + } + + // Ensure presence of 0-terminator. + mbs_v[mbs_length - 1] = '\0'; +#endif // _MSC_VER >= 1400 + + *mbs = &mbs_v[0]; + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/gbreakpad.pri b/src/lib/crashdump/gbreakpad/gbreakpad.pri new file mode 100644 index 0000000..013ee44 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/gbreakpad.pri @@ -0,0 +1,49 @@ +BREAKPAD_PATH = $$PWD +INCLUDEPATH += $$BREAKPAD_PATH + +# every *nix +unix { + SOURCES += $$BREAKPAD_PATH/client/minidump_file_writer.cc \ + $$BREAKPAD_PATH/common/string_conversion.cc \ + $$BREAKPAD_PATH/common/convert_UTF.c \ + $$BREAKPAD_PATH/common/md5.cc +} + +# mac os x +mac { + # hack to make minidump_generator.cc compile as it uses + # esp instead of __esp + # DEFINES += __DARWIN_UNIX03=0 -- looks like we doesn't need it anymore + + SOURCES += $$BREAKPAD_PATH/client/mac/handler/exception_handler.cc \ + $$BREAKPAD_PATH/client/mac/handler/minidump_generator.cc \ + $$BREAKPAD_PATH/client/mac/handler/dynamic_images.cc \ + $$BREAKPAD_PATH/common/mac/string_utilities.cc \ + $$BREAKPAD_PATH/common/mac/file_id.cc \ + $$BREAKPAD_PATH/common/mac/macho_id.cc \ + $$BREAKPAD_PATH/common/mac/macho_utilities.cc \ + $$BREAKPAD_PATH/common/mac/macho_walker.cc +} + +# other *nix +unix:!mac { + SOURCES += $$BREAKPAD_PATH/client/linux/handler/exception_handler.cc \ + #$$BREAKPAD_PATH/client/linux/handler/minidump_generator.cc \ + #$$BREAKPAD_PATH/client/linux/handler/linux_thread.cc \ + $$BREAKPAD_PATH/client/linux/log/log.cc \ + $$BREAKPAD_PATH/client/linux/crash_generation/crash_generation_client.cc \ + $$BREAKPAD_PATH/client/linux/minidump_writer/minidump_writer.cc \ + $$BREAKPAD_PATH/client/linux/minidump_writer/linux_core_dumper.cc \ + $$BREAKPAD_PATH/client/linux/minidump_writer/linux_dumper.cc \ + $$BREAKPAD_PATH/client/linux/minidump_writer/linux_ptrace_dumper.cc \ + $$BREAKPAD_PATH/common/linux/guid_creator.cc \ + $$BREAKPAD_PATH/common/linux/file_id.cc \ + $$BREAKPAD_PATH/common/linux/safe_readlink.cc \ + $$BREAKPAD_PATH/common/linux/memory_mapped_file.cc \ +} + +win32 { + SOURCES += $$BREAKPAD_PATH/client/windows/handler/exception_handler.cc \ + $$BREAKPAD_PATH/client/windows/crash_generation/crash_generation_client.cc \ + $$BREAKPAD_PATH/common/windows/guid_string.cc +} diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/breakpad_types.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/breakpad_types.h new file mode 100644 index 0000000..926b47f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/breakpad_types.h @@ -0,0 +1,83 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* breakpad_types.h: Precise-width types + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file ensures that types u_intN_t are defined for N = 8, 16, 32, and + * 64. Types of precise widths are crucial to the task of writing data + * structures on one platform and reading them on another. + * + * Author: Mark Mentovai */ + +#ifndef GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__ +#define GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__ + +#ifndef _WIN32 + +#include +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif /* __STDC_FORMAT_MACROS */ +#include + +#if defined(__SUNPRO_CC) || (defined(__GNUC__) && defined(__sun__)) +typedef uint8_t u_int8_t; +typedef uint16_t u_int16_t; +typedef uint32_t u_int32_t; +typedef uint64_t u_int64_t; +#endif + +#else /* !_WIN32 */ + +#include + +typedef unsigned __int8 u_int8_t; +typedef unsigned __int16 u_int16_t; +typedef unsigned __int32 u_int32_t; +typedef unsigned __int64 u_int64_t; + +#endif /* !_WIN32 */ + +typedef struct { + u_int64_t high; + u_int64_t low; +} u_int128_t; + +typedef u_int64_t breakpad_time_t; + +/* Try to get PRIx64 from inttypes.h, but if it's not defined, fall back to + * llx, which is the format string for "long long" - this is a 64-bit + * integral type on many systems. */ +#ifndef PRIx64 +#define PRIx64 "llx" +#endif /* !PRIx64 */ + +#endif /* GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_amd64.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_amd64.h new file mode 100644 index 0000000..fa6a996 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_amd64.h @@ -0,0 +1,235 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on amd64. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai + * Change to split into its own file: Neal Sidhwaney */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_AMD64_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_AMD64_H__ + + +/* + * AMD64 support, see WINNT.H + */ + +typedef struct { + u_int16_t control_word; + u_int16_t status_word; + u_int8_t tag_word; + u_int8_t reserved1; + u_int16_t error_opcode; + u_int32_t error_offset; + u_int16_t error_selector; + u_int16_t reserved2; + u_int32_t data_offset; + u_int16_t data_selector; + u_int16_t reserved3; + u_int32_t mx_csr; + u_int32_t mx_csr_mask; + u_int128_t float_registers[8]; + u_int128_t xmm_registers[16]; + u_int8_t reserved4[96]; +} MDXmmSaveArea32AMD64; /* XMM_SAVE_AREA32 */ + +#define MD_CONTEXT_AMD64_VR_COUNT 26 + +typedef struct { + /* + * Register parameter home addresses. + */ + u_int64_t p1_home; + u_int64_t p2_home; + u_int64_t p3_home; + u_int64_t p4_home; + u_int64_t p5_home; + u_int64_t p6_home; + + /* The next field determines the layout of the structure, and which parts + * of it are populated */ + u_int32_t context_flags; + u_int32_t mx_csr; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + u_int16_t cs; + + /* The next 4 registers are included with MD_CONTEXT_AMD64_SEGMENTS */ + u_int16_t ds; + u_int16_t es; + u_int16_t fs; + u_int16_t gs; + + /* The next 2 registers are included with MD_CONTEXT_AMD64_CONTROL */ + u_int16_t ss; + u_int32_t eflags; + + /* The next 6 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */ + u_int64_t dr0; + u_int64_t dr1; + u_int64_t dr2; + u_int64_t dr3; + u_int64_t dr6; + u_int64_t dr7; + + /* The next 4 registers are included with MD_CONTEXT_AMD64_INTEGER */ + u_int64_t rax; + u_int64_t rcx; + u_int64_t rdx; + u_int64_t rbx; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + u_int64_t rsp; + + /* The next 11 registers are included with MD_CONTEXT_AMD64_INTEGER */ + u_int64_t rbp; + u_int64_t rsi; + u_int64_t rdi; + u_int64_t r8; + u_int64_t r9; + u_int64_t r10; + u_int64_t r11; + u_int64_t r12; + u_int64_t r13; + u_int64_t r14; + u_int64_t r15; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + u_int64_t rip; + + /* The next set of registers are included with + * MD_CONTEXT_AMD64_FLOATING_POINT + */ + union { + MDXmmSaveArea32AMD64 flt_save; + struct { + u_int128_t header[2]; + u_int128_t legacy[8]; + u_int128_t xmm0; + u_int128_t xmm1; + u_int128_t xmm2; + u_int128_t xmm3; + u_int128_t xmm4; + u_int128_t xmm5; + u_int128_t xmm6; + u_int128_t xmm7; + u_int128_t xmm8; + u_int128_t xmm9; + u_int128_t xmm10; + u_int128_t xmm11; + u_int128_t xmm12; + u_int128_t xmm13; + u_int128_t xmm14; + u_int128_t xmm15; + } sse_registers; + }; + + u_int128_t vector_register[MD_CONTEXT_AMD64_VR_COUNT]; + u_int64_t vector_control; + + /* The next 5 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */ + u_int64_t debug_control; + u_int64_t last_branch_to_rip; + u_int64_t last_branch_from_rip; + u_int64_t last_exception_to_rip; + u_int64_t last_exception_from_rip; + +} MDRawContextAMD64; /* CONTEXT */ + +/* For (MDRawContextAMD64).context_flags. These values indicate the type of + * context stored in the structure. The high 24 bits identify the CPU, the + * low 8 bits identify the type of context saved. */ +#define MD_CONTEXT_AMD64 0x00100000 /* CONTEXT_AMD64 */ +#define MD_CONTEXT_AMD64_CONTROL (MD_CONTEXT_AMD64 | 0x00000001) + /* CONTEXT_CONTROL */ +#define MD_CONTEXT_AMD64_INTEGER (MD_CONTEXT_AMD64 | 0x00000002) + /* CONTEXT_INTEGER */ +#define MD_CONTEXT_AMD64_SEGMENTS (MD_CONTEXT_AMD64 | 0x00000004) + /* CONTEXT_SEGMENTS */ +#define MD_CONTEXT_AMD64_FLOATING_POINT (MD_CONTEXT_AMD64 | 0x00000008) + /* CONTEXT_FLOATING_POINT */ +#define MD_CONTEXT_AMD64_DEBUG_REGISTERS (MD_CONTEXT_AMD64 | 0x00000010) + /* CONTEXT_DEBUG_REGISTERS */ +#define MD_CONTEXT_AMD64_XSTATE (MD_CONTEXT_AMD64 | 0x00000040) + /* CONTEXT_XSTATE */ + +/* WinNT.h refers to CONTEXT_MMX_REGISTERS but doesn't appear to define it + * I think it really means CONTEXT_FLOATING_POINT. + */ + +#define MD_CONTEXT_AMD64_FULL (MD_CONTEXT_AMD64_CONTROL | \ + MD_CONTEXT_AMD64_INTEGER | \ + MD_CONTEXT_AMD64_FLOATING_POINT) + /* CONTEXT_FULL */ + +#define MD_CONTEXT_AMD64_ALL (MD_CONTEXT_AMD64_FULL | \ + MD_CONTEXT_AMD64_SEGMENTS | \ + MD_CONTEXT_X86_DEBUG_REGISTERS) + /* CONTEXT_ALL */ + + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_AMD64_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_arm.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_arm.h new file mode 100644 index 0000000..dd07129 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_arm.h @@ -0,0 +1,151 @@ +/* Copyright (c) 2009, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ARM. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by + * ensuring that all members are aligned on their natural boundaries. + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. + * + * Author: Julian Seward + */ + +/* + * ARM support + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ + +#define MD_FLOATINGSAVEAREA_ARM_FPR_COUNT 32 +#define MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT 8 + +/* + * Note that these structures *do not* map directly to the CONTEXT + * structure defined in WinNT.h in the Windows Mobile SDK. That structure + * does not accomodate VFPv3, and I'm unsure if it was ever used in the + * wild anyway, as Windows CE only seems to produce "cedumps" which + * are not exactly minidumps. + */ +typedef struct { + u_int64_t fpscr; /* FPU status register */ + + /* 32 64-bit floating point registers, d0 .. d31. */ + u_int64_t regs[MD_FLOATINGSAVEAREA_ARM_FPR_COUNT]; + + /* Miscellaneous control words */ + u_int32_t extra[MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT]; +} MDFloatingSaveAreaARM; + +#define MD_CONTEXT_ARM_GPR_COUNT 16 + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + u_int32_t context_flags; + + /* 16 32-bit integer registers, r0 .. r15 + * Note the following fixed uses: + * r13 is the stack pointer + * r14 is the link register + * r15 is the program counter + */ + u_int32_t iregs[MD_CONTEXT_ARM_GPR_COUNT]; + + /* CPSR (flags, basically): 32 bits: + bit 31 - N (negative) + bit 30 - Z (zero) + bit 29 - C (carry) + bit 28 - V (overflow) + bit 27 - Q (saturation flag, sticky) + All other fields -- ignore */ + u_int32_t cpsr; + + /* The next field is included with MD_CONTEXT_ARM_FLOATING_POINT */ + MDFloatingSaveAreaARM float_save; + +} MDRawContextARM; + +/* Indices into iregs for registers with a dedicated or conventional + * purpose. + */ +enum MDARMRegisterNumbers { + MD_CONTEXT_ARM_REG_IOS_FP = 7, + MD_CONTEXT_ARM_REG_FP = 11, + MD_CONTEXT_ARM_REG_SP = 13, + MD_CONTEXT_ARM_REG_LR = 14, + MD_CONTEXT_ARM_REG_PC = 15 +}; + +/* For (MDRawContextARM).context_flags. These values indicate the type of + * context stored in the structure. */ +/* CONTEXT_ARM from the Windows CE 5.0 SDK. This value isn't correct + * because this bit can be used for flags. Presumably this value was + * never actually used in minidumps, but only in "CEDumps" which + * are a whole parallel minidump file format for Windows CE. + * Therefore, Breakpad defines its own value for ARM CPUs. + */ +#define MD_CONTEXT_ARM_OLD 0x00000040 +/* This value was chosen to avoid likely conflicts with MD_CONTEXT_* + * for other CPUs. */ +#define MD_CONTEXT_ARM 0x40000000 +#define MD_CONTEXT_ARM_INTEGER (MD_CONTEXT_ARM | 0x00000002) +#define MD_CONTEXT_ARM_FLOATING_POINT (MD_CONTEXT_ARM | 0x00000004) + +#define MD_CONTEXT_ARM_FULL (MD_CONTEXT_ARM_INTEGER | \ + MD_CONTEXT_ARM_FLOATING_POINT) + +#define MD_CONTEXT_ARM_ALL (MD_CONTEXT_ARM_INTEGER | \ + MD_CONTEXT_ARM_FLOATING_POINT) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc.h new file mode 100644 index 0000000..038e921 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc.h @@ -0,0 +1,163 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ppc. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai + * Change to split into its own file: Neal Sidhwaney */ + +/* + * Breakpad minidump extension for PowerPC support. Based on Darwin/Mac OS X' + * mach/ppc/_types.h + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC_H__ + +#define MD_FLOATINGSAVEAREA_PPC_FPR_COUNT 32 + +typedef struct { + /* fpregs is a double[32] in mach/ppc/_types.h, but a u_int64_t is used + * here for precise sizing. */ + u_int64_t fpregs[MD_FLOATINGSAVEAREA_PPC_FPR_COUNT]; + u_int32_t fpscr_pad; + u_int32_t fpscr; /* Status/control */ +} MDFloatingSaveAreaPPC; /* Based on ppc_float_state */ + + +#define MD_VECTORSAVEAREA_PPC_VR_COUNT 32 + +typedef struct { + /* Vector registers (including vscr) are 128 bits, but mach/ppc/_types.h + * exposes them as four 32-bit quantities. */ + u_int128_t save_vr[MD_VECTORSAVEAREA_PPC_VR_COUNT]; + u_int128_t save_vscr; /* Status/control */ + u_int32_t save_pad5[4]; + u_int32_t save_vrvalid; /* Identifies which vector registers are saved */ + u_int32_t save_pad6[7]; +} MDVectorSaveAreaPPC; /* ppc_vector_state */ + + +#define MD_CONTEXT_PPC_GPR_COUNT 32 + +/* Use the same 32-bit alignment when accessing this structure from 64-bit code + * as is used natively in 32-bit code. #pragma pack is a MSVC extension + * supported by gcc. */ +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#pragma pack(4) +#else +#pragma pack(push, 4) +#endif + +typedef struct { + /* context_flags is not present in ppc_thread_state, but it aids + * identification of MDRawContextPPC among other raw context types, + * and it guarantees alignment when we get to float_save. */ + u_int32_t context_flags; + + u_int32_t srr0; /* Machine status save/restore: stores pc + * (instruction) */ + u_int32_t srr1; /* Machine status save/restore: stores msr + * (ps, program/machine state) */ + /* ppc_thread_state contains 32 fields, r0 .. r31. Here, an array is + * used for brevity. */ + u_int32_t gpr[MD_CONTEXT_PPC_GPR_COUNT]; + u_int32_t cr; /* Condition */ + u_int32_t xer; /* Integer (fiXed-point) exception */ + u_int32_t lr; /* Link */ + u_int32_t ctr; /* Count */ + u_int32_t mq; /* Multiply/Quotient (PPC 601, POWER only) */ + u_int32_t vrsave; /* Vector save */ + + /* float_save and vector_save aren't present in ppc_thread_state, but + * are represented in separate structures that still define a thread's + * context. */ + MDFloatingSaveAreaPPC float_save; + MDVectorSaveAreaPPC vector_save; +} MDRawContextPPC; /* Based on ppc_thread_state */ + +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#pragma pack(0) +#else +#pragma pack(pop) +#endif + +/* For (MDRawContextPPC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_PPC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_PPC 0x20000000 +#define MD_CONTEXT_PPC_BASE (MD_CONTEXT_PPC | 0x00000001) +#define MD_CONTEXT_PPC_FLOATING_POINT (MD_CONTEXT_PPC | 0x00000008) +#define MD_CONTEXT_PPC_VECTOR (MD_CONTEXT_PPC | 0x00000020) + +#define MD_CONTEXT_PPC_FULL MD_CONTEXT_PPC_BASE +#define MD_CONTEXT_PPC_ALL (MD_CONTEXT_PPC_FULL | \ + MD_CONTEXT_PPC_FLOATING_POINT | \ + MD_CONTEXT_PPC_VECTOR) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc64.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc64.h new file mode 100644 index 0000000..a788e5d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_ppc64.h @@ -0,0 +1,129 @@ +/* Copyright (c) 2008, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ppc64. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Neal Sidhwaney */ + + +/* + * Breakpad minidump extension for PPC64 support. Based on Darwin/Mac OS X' + * mach/ppc/_types.h + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC64_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC64_H__ + +#include "minidump_cpu_ppc.h" + +// these types are the same in ppc64 & ppc +typedef MDFloatingSaveAreaPPC MDFloatingSaveAreaPPC64; +typedef MDVectorSaveAreaPPC MDVectorSaveAreaPPC64; + +#define MD_CONTEXT_PPC64_GPR_COUNT MD_CONTEXT_PPC_GPR_COUNT + +typedef struct { + /* context_flags is not present in ppc_thread_state, but it aids + * identification of MDRawContextPPC among other raw context types, + * and it guarantees alignment when we get to float_save. */ + u_int64_t context_flags; + + u_int64_t srr0; /* Machine status save/restore: stores pc + * (instruction) */ + u_int64_t srr1; /* Machine status save/restore: stores msr + * (ps, program/machine state) */ + /* ppc_thread_state contains 32 fields, r0 .. r31. Here, an array is + * used for brevity. */ + u_int64_t gpr[MD_CONTEXT_PPC64_GPR_COUNT]; + u_int64_t cr; /* Condition */ + u_int64_t xer; /* Integer (fiXed-point) exception */ + u_int64_t lr; /* Link */ + u_int64_t ctr; /* Count */ + u_int64_t vrsave; /* Vector save */ + + /* float_save and vector_save aren't present in ppc_thread_state, but + * are represented in separate structures that still define a thread's + * context. */ + MDFloatingSaveAreaPPC float_save; + MDVectorSaveAreaPPC vector_save; +} MDRawContextPPC64; /* Based on ppc_thread_state */ + +/* For (MDRawContextPPC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_PPC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_PPC 0x20000000 +#define MD_CONTEXT_PPC_BASE (MD_CONTEXT_PPC | 0x00000001) +#define MD_CONTEXT_PPC_FLOATING_POINT (MD_CONTEXT_PPC | 0x00000008) +#define MD_CONTEXT_PPC_VECTOR (MD_CONTEXT_PPC | 0x00000020) + +#define MD_CONTEXT_PPC_FULL MD_CONTEXT_PPC_BASE +#define MD_CONTEXT_PPC_ALL (MD_CONTEXT_PPC_FULL | \ + MD_CONTEXT_PPC_FLOATING_POINT | \ + MD_CONTEXT_PPC_VECTOR) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC64_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_sparc.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_sparc.h new file mode 100644 index 0000000..ee95b64 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_sparc.h @@ -0,0 +1,158 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on sparc. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai + * Change to split into its own file: Neal Sidhwaney */ + +/* + * SPARC support, see (solaris)sys/procfs_isa.h also + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_SPARC_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_SPARC_H__ + +#define MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT 32 + +typedef struct { + + /* FPU floating point regs */ + u_int64_t regs[MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT]; + + u_int64_t filler; + u_int64_t fsr; /* FPU status register */ +} MDFloatingSaveAreaSPARC; /* FLOATING_SAVE_AREA */ + +#define MD_CONTEXT_SPARC_GPR_COUNT 32 + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + u_int32_t context_flags; + u_int32_t flag_pad; + /* + * General register access (SPARC). + * Don't confuse definitions here with definitions in . + * Registers are 32 bits for ILP32, 64 bits for LP64. + * SPARC V7/V8 is for 32bit, SPARC V9 is for 64bit + */ + + /* 32 Integer working registers */ + + /* g_r[0-7] global registers(g0-g7) + * g_r[8-15] out registers(o0-o7) + * g_r[16-23] local registers(l0-l7) + * g_r[24-31] in registers(i0-i7) + */ + u_int64_t g_r[MD_CONTEXT_SPARC_GPR_COUNT]; + + /* several control registers */ + + /* Processor State register(PSR) for SPARC V7/V8 + * Condition Code register (CCR) for SPARC V9 + */ + u_int64_t ccr; + + u_int64_t pc; /* Program Counter register (PC) */ + u_int64_t npc; /* Next Program Counter register (nPC) */ + u_int64_t y; /* Y register (Y) */ + + /* Address Space Identifier register (ASI) for SPARC V9 + * WIM for SPARC V7/V8 + */ + u_int64_t asi; + + /* Floating-Point Registers State register (FPRS) for SPARC V9 + * TBR for for SPARC V7/V8 + */ + u_int64_t fprs; + + /* The next field is included with MD_CONTEXT_SPARC_FLOATING_POINT */ + MDFloatingSaveAreaSPARC float_save; + +} MDRawContextSPARC; /* CONTEXT_SPARC */ + +/* For (MDRawContextSPARC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_SPARC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_SPARC 0x10000000 +#define MD_CONTEXT_SPARC_CONTROL (MD_CONTEXT_SPARC | 0x00000001) +#define MD_CONTEXT_SPARC_INTEGER (MD_CONTEXT_SPARC | 0x00000002) +#define MD_CONTEXT_SAPARC_FLOATING_POINT (MD_CONTEXT_SPARC | 0x00000004) +#define MD_CONTEXT_SAPARC_EXTRA (MD_CONTEXT_SPARC | 0x00000008) + +#define MD_CONTEXT_SPARC_FULL (MD_CONTEXT_SPARC_CONTROL | \ + MD_CONTEXT_SPARC_INTEGER) + +#define MD_CONTEXT_SPARC_ALL (MD_CONTEXT_SPARC_FULL | \ + MD_CONTEXT_SAPARC_FLOATING_POINT | \ + MD_CONTEXT_SAPARC_EXTRA) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_SPARC_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_x86.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_x86.h new file mode 100644 index 0000000..32aff8a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_cpu_x86.h @@ -0,0 +1,174 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on x86. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_X86_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_X86_H__ + +#define MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE 80 + /* SIZE_OF_80387_REGISTERS */ + +typedef struct { + u_int32_t control_word; + u_int32_t status_word; + u_int32_t tag_word; + u_int32_t error_offset; + u_int32_t error_selector; + u_int32_t data_offset; + u_int32_t data_selector; + + /* register_area contains eight 80-bit (x87 "long double") quantities for + * floating-point registers %st0 (%mm0) through %st7 (%mm7). */ + u_int8_t register_area[MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE]; + u_int32_t cr0_npx_state; +} MDFloatingSaveAreaX86; /* FLOATING_SAVE_AREA */ + + +#define MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE 512 + /* MAXIMUM_SUPPORTED_EXTENSION */ + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated */ + u_int32_t context_flags; + + /* The next 6 registers are included with MD_CONTEXT_X86_DEBUG_REGISTERS */ + u_int32_t dr0; + u_int32_t dr1; + u_int32_t dr2; + u_int32_t dr3; + u_int32_t dr6; + u_int32_t dr7; + + /* The next field is included with MD_CONTEXT_X86_FLOATING_POINT */ + MDFloatingSaveAreaX86 float_save; + + /* The next 4 registers are included with MD_CONTEXT_X86_SEGMENTS */ + u_int32_t gs; + u_int32_t fs; + u_int32_t es; + u_int32_t ds; + /* The next 6 registers are included with MD_CONTEXT_X86_INTEGER */ + u_int32_t edi; + u_int32_t esi; + u_int32_t ebx; + u_int32_t edx; + u_int32_t ecx; + u_int32_t eax; + + /* The next 6 registers are included with MD_CONTEXT_X86_CONTROL */ + u_int32_t ebp; + u_int32_t eip; + u_int32_t cs; /* WinNT.h says "must be sanitized" */ + u_int32_t eflags; /* WinNT.h says "must be sanitized" */ + u_int32_t esp; + u_int32_t ss; + + /* The next field is included with MD_CONTEXT_X86_EXTENDED_REGISTERS. + * It contains vector (MMX/SSE) registers. It it laid out in the + * format used by the fxsave and fsrstor instructions, so it includes + * a copy of the x87 floating-point registers as well. See FXSAVE in + * "Intel Architecture Software Developer's Manual, Volume 2." */ + u_int8_t extended_registers[ + MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE]; +} MDRawContextX86; /* CONTEXT */ + +/* For (MDRawContextX86).context_flags. These values indicate the type of + * context stored in the structure. The high 24 bits identify the CPU, the + * low 8 bits identify the type of context saved. */ +#define MD_CONTEXT_X86 0x00010000 + /* CONTEXT_i386, CONTEXT_i486: identifies CPU */ +#define MD_CONTEXT_X86_CONTROL (MD_CONTEXT_X86 | 0x00000001) + /* CONTEXT_CONTROL */ +#define MD_CONTEXT_X86_INTEGER (MD_CONTEXT_X86 | 0x00000002) + /* CONTEXT_INTEGER */ +#define MD_CONTEXT_X86_SEGMENTS (MD_CONTEXT_X86 | 0x00000004) + /* CONTEXT_SEGMENTS */ +#define MD_CONTEXT_X86_FLOATING_POINT (MD_CONTEXT_X86 | 0x00000008) + /* CONTEXT_FLOATING_POINT */ +#define MD_CONTEXT_X86_DEBUG_REGISTERS (MD_CONTEXT_X86 | 0x00000010) + /* CONTEXT_DEBUG_REGISTERS */ +#define MD_CONTEXT_X86_EXTENDED_REGISTERS (MD_CONTEXT_X86 | 0x00000020) + /* CONTEXT_EXTENDED_REGISTERS */ +#define MD_CONTEXT_X86_XSTATE (MD_CONTEXT_X86 | 0x00000040) + /* CONTEXT_XSTATE */ + +#define MD_CONTEXT_X86_FULL (MD_CONTEXT_X86_CONTROL | \ + MD_CONTEXT_X86_INTEGER | \ + MD_CONTEXT_X86_SEGMENTS) + /* CONTEXT_FULL */ + +#define MD_CONTEXT_X86_ALL (MD_CONTEXT_X86_FULL | \ + MD_CONTEXT_X86_FLOATING_POINT | \ + MD_CONTEXT_X86_DEBUG_REGISTERS | \ + MD_CONTEXT_X86_EXTENDED_REGISTERS) + /* CONTEXT_ALL */ + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_X86_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_linux.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_linux.h new file mode 100644 index 0000000..d52c751 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_linux.h @@ -0,0 +1,85 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_exception_linux.h: A definition of exception codes for + * Linux + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + + +/* For (MDException).exception_code. These values come from bits/signum.h. + */ +typedef enum { + MD_EXCEPTION_CODE_LIN_SIGHUP = 1, /* Hangup (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGINT = 2, /* Interrupt (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGQUIT = 3, /* Quit (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGILL = 4, /* Illegal instruction (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGTRAP = 5, /* Trace trap (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGABRT = 6, /* Abort (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGBUS = 7, /* BUS error (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGFPE = 8, /* Floating-point exception (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGKILL = 9, /* Kill, unblockable (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGUSR1 = 10, /* User-defined signal 1 (POSIX). */ + MD_EXCEPTION_CODE_LIN_SIGSEGV = 11, /* Segmentation violation (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGUSR2 = 12, /* User-defined signal 2 (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGPIPE = 13, /* Broken pipe (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGALRM = 14, /* Alarm clock (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTERM = 15, /* Termination (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGSTKFLT = 16, /* Stack faultd */ + MD_EXCEPTION_CODE_LIN_SIGCHLD = 17, /* Child status has changed (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGCONT = 18, /* Continue (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGSTOP = 19, /* Stop, unblockable (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTSTP = 20, /* Keyboard stop (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTTIN = 21, /* Background read from tty (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTTOU = 22, /* Background write to tty (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGURG = 23, + /* Urgent condition on socket (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGXCPU = 24, /* CPU limit exceeded (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGXFSZ = 25, + /* File size limit exceeded (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGVTALRM = 26, /* Virtual alarm clock (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGPROF = 27, /* Profiling alarm clock (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGWINCH = 28, /* Window size change (4.3 BSD, Sun) */ + MD_EXCEPTION_CODE_LIN_SIGIO = 29, /* I/O now possible (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGPWR = 30, /* Power failure restart (System V) */ + MD_EXCEPTION_CODE_LIN_SIGSYS = 31 /* Bad system call */ +} MDExceptionCodeLinux; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_mac.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_mac.h new file mode 100644 index 0000000..01f8feb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_mac.h @@ -0,0 +1,195 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_exception_mac.h: A definition of exception codes for Mac + * OS X + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +/* For (MDException).exception_code. Breakpad minidump extension for Mac OS X + * support. Based on Darwin/Mac OS X' mach/exception_types.h. This is + * what Mac OS X calls an "exception", not a "code". */ +typedef enum { + /* Exception code. The high 16 bits of exception_code contains one of + * these values. */ + MD_EXCEPTION_MAC_BAD_ACCESS = 1, /* code can be a kern_return_t */ + /* EXC_BAD_ACCESS */ + MD_EXCEPTION_MAC_BAD_INSTRUCTION = 2, /* code is CPU-specific */ + /* EXC_BAD_INSTRUCTION */ + MD_EXCEPTION_MAC_ARITHMETIC = 3, /* code is CPU-specific */ + /* EXC_ARITHMETIC */ + MD_EXCEPTION_MAC_EMULATION = 4, /* code is CPU-specific */ + /* EXC_EMULATION */ + MD_EXCEPTION_MAC_SOFTWARE = 5, + /* EXC_SOFTWARE */ + MD_EXCEPTION_MAC_BREAKPOINT = 6, /* code is CPU-specific */ + /* EXC_BREAKPOINT */ + MD_EXCEPTION_MAC_SYSCALL = 7, + /* EXC_SYSCALL */ + MD_EXCEPTION_MAC_MACH_SYSCALL = 8, + /* EXC_MACH_SYSCALL */ + MD_EXCEPTION_MAC_RPC_ALERT = 9 + /* EXC_RPC_ALERT */ +} MDExceptionMac; + +/* For (MDException).exception_flags. Breakpad minidump extension for Mac OS X + * support. Based on Darwin/Mac OS X' mach/ppc/exception.h and + * mach/i386/exception.h. This is what Mac OS X calls a "code". */ +typedef enum { + /* With MD_EXCEPTION_BAD_ACCESS. These are relevant kern_return_t values + * from mach/kern_return.h. */ + MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS = 1, + /* KERN_INVALID_ADDRESS */ + MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE = 2, + /* KERN_PROTECTION_FAILURE */ + MD_EXCEPTION_CODE_MAC_NO_ACCESS = 8, + /* KERN_NO_ACCESS */ + MD_EXCEPTION_CODE_MAC_MEMORY_FAILURE = 9, + /* KERN_MEMORY_FAILURE */ + MD_EXCEPTION_CODE_MAC_MEMORY_ERROR = 10, + /* KERN_MEMORY_ERROR */ + + /* With MD_EXCEPTION_SOFTWARE */ + MD_EXCEPTION_CODE_MAC_BAD_SYSCALL = 0x00010000, /* Mach SIGSYS */ + MD_EXCEPTION_CODE_MAC_BAD_PIPE = 0x00010001, /* Mach SIGPIPE */ + MD_EXCEPTION_CODE_MAC_ABORT = 0x00010002, /* Mach SIGABRT */ + /* Custom values */ + MD_EXCEPTION_CODE_MAC_NS_EXCEPTION = 0xDEADC0DE, /* uncaught NSException */ + + /* With MD_EXCEPTION_MAC_BAD_ACCESS on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_VM_PROT_READ = 0x0101, + /* EXC_PPC_VM_PROT_READ */ + MD_EXCEPTION_CODE_MAC_PPC_BADSPACE = 0x0102, + /* EXC_PPC_BADSPACE */ + MD_EXCEPTION_CODE_MAC_PPC_UNALIGNED = 0x0103, + /* EXC_PPC_UNALIGNED */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_INVALID_SYSCALL = 1, + /* EXC_PPC_INVALID_SYSCALL */ + MD_EXCEPTION_CODE_MAC_PPC_UNIMPLEMENTED_INSTRUCTION = 2, + /* EXC_PPC_UNIPL_INST */ + MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_INSTRUCTION = 3, + /* EXC_PPC_PRIVINST */ + MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_REGISTER = 4, + /* EXC_PPC_PRIVREG */ + MD_EXCEPTION_CODE_MAC_PPC_TRACE = 5, + /* EXC_PPC_TRACE */ + MD_EXCEPTION_CODE_MAC_PPC_PERFORMANCE_MONITOR = 6, + /* EXC_PPC_PERFMON */ + + /* With MD_EXCEPTION_MAC_ARITHMETIC on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_OVERFLOW = 1, + /* EXC_PPC_OVERFLOW */ + MD_EXCEPTION_CODE_MAC_PPC_ZERO_DIVIDE = 2, + /* EXC_PPC_ZERO_DIVIDE */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_INEXACT = 3, + /* EXC_FLT_INEXACT */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_ZERO_DIVIDE = 4, + /* EXC_PPC_FLT_ZERO_DIVIDE */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_UNDERFLOW = 5, + /* EXC_PPC_FLT_UNDERFLOW */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_OVERFLOW = 6, + /* EXC_PPC_FLT_OVERFLOW */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_NOT_A_NUMBER = 7, + /* EXC_PPC_FLT_NOT_A_NUMBER */ + + /* With MD_EXCEPTION_MAC_EMULATION on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_NO_EMULATION = 8, + /* EXC_PPC_NOEMULATION */ + MD_EXCEPTION_CODE_MAC_PPC_ALTIVEC_ASSIST = 9, + /* EXC_PPC_ALTIVECASSIST */ + + /* With MD_EXCEPTION_MAC_SOFTWARE on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_TRAP = 0x00000001, /* EXC_PPC_TRAP */ + MD_EXCEPTION_CODE_MAC_PPC_MIGRATE = 0x00010100, /* EXC_PPC_MIGRATE */ + + /* With MD_EXCEPTION_MAC_BREAKPOINT on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_BREAKPOINT = 1, /* EXC_PPC_BREAKPOINT */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on x86, see also x86 interrupt + * values below. */ + MD_EXCEPTION_CODE_MAC_X86_INVALID_OPERATION = 1, /* EXC_I386_INVOP */ + + /* With MD_EXCEPTION_MAC_ARITHMETIC on x86 */ + MD_EXCEPTION_CODE_MAC_X86_DIV = 1, /* EXC_I386_DIV */ + MD_EXCEPTION_CODE_MAC_X86_INTO = 2, /* EXC_I386_INTO */ + MD_EXCEPTION_CODE_MAC_X86_NOEXT = 3, /* EXC_I386_NOEXT */ + MD_EXCEPTION_CODE_MAC_X86_EXTOVR = 4, /* EXC_I386_EXTOVR */ + MD_EXCEPTION_CODE_MAC_X86_EXTERR = 5, /* EXC_I386_EXTERR */ + MD_EXCEPTION_CODE_MAC_X86_EMERR = 6, /* EXC_I386_EMERR */ + MD_EXCEPTION_CODE_MAC_X86_BOUND = 7, /* EXC_I386_BOUND */ + MD_EXCEPTION_CODE_MAC_X86_SSEEXTERR = 8, /* EXC_I386_SSEEXTERR */ + + /* With MD_EXCEPTION_MAC_BREAKPOINT on x86 */ + MD_EXCEPTION_CODE_MAC_X86_SGL = 1, /* EXC_I386_SGL */ + MD_EXCEPTION_CODE_MAC_X86_BPT = 2, /* EXC_I386_BPT */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on x86. These are the raw + * x86 interrupt codes. Most of these are mapped to other Mach + * exceptions and codes, are handled, or should not occur in user space. + * A few of these will do occur with MD_EXCEPTION_MAC_BAD_INSTRUCTION. */ + /* EXC_I386_DIVERR = 0: mapped to EXC_ARITHMETIC/EXC_I386_DIV */ + /* EXC_I386_SGLSTP = 1: mapped to EXC_BREAKPOINT/EXC_I386_SGL */ + /* EXC_I386_NMIFLT = 2: should not occur in user space */ + /* EXC_I386_BPTFLT = 3: mapped to EXC_BREAKPOINT/EXC_I386_BPT */ + /* EXC_I386_INTOFLT = 4: mapped to EXC_ARITHMETIC/EXC_I386_INTO */ + /* EXC_I386_BOUNDFLT = 5: mapped to EXC_ARITHMETIC/EXC_I386_BOUND */ + /* EXC_I386_INVOPFLT = 6: mapped to EXC_BAD_INSTRUCTION/EXC_I386_INVOP */ + /* EXC_I386_NOEXTFLT = 7: should be handled by the kernel */ + /* EXC_I386_DBLFLT = 8: should be handled (if possible) by the kernel */ + /* EXC_I386_EXTOVRFLT = 9: mapped to EXC_BAD_ACCESS/(PROT_READ|PROT_EXEC) */ + MD_EXCEPTION_CODE_MAC_X86_INVALID_TASK_STATE_SEGMENT = 10, + /* EXC_INVTSSFLT */ + MD_EXCEPTION_CODE_MAC_X86_SEGMENT_NOT_PRESENT = 11, + /* EXC_SEGNPFLT */ + MD_EXCEPTION_CODE_MAC_X86_STACK_FAULT = 12, + /* EXC_STKFLT */ + MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT = 13, + /* EXC_GPFLT */ + /* EXC_I386_PGFLT = 14: should not occur in user space */ + /* EXC_I386_EXTERRFLT = 16: mapped to EXC_ARITHMETIC/EXC_I386_EXTERR */ + MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT = 17 + /* EXC_ALIGNFLT (for vector operations) */ + /* EXC_I386_ENOEXTFLT = 32: should be handled by the kernel */ + /* EXC_I386_ENDPERR = 33: should not occur */ +} MDExceptionCodeMac; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_OSX_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_solaris.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_solaris.h new file mode 100644 index 0000000..f18ddf4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_solaris.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_exception_solaris.h: A definition of exception codes for + * Solaris + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +/* For (MDException).exception_code. These values come from sys/iso/signal_iso.h + */ +typedef enum { + MD_EXCEPTION_CODE_SOL_SIGHUP = 1, /* Hangup */ + MD_EXCEPTION_CODE_SOL_SIGINT = 2, /* interrupt (rubout) */ + MD_EXCEPTION_CODE_SOL_SIGQUIT = 3, /* quit (ASCII FS) */ + MD_EXCEPTION_CODE_SOL_SIGILL = 4, /* illegal instruction (not reset when caught) */ + MD_EXCEPTION_CODE_SOL_SIGTRAP = 5, /* trace trap (not reset when caught) */ + MD_EXCEPTION_CODE_SOL_SIGIOT = 6, /* IOT instruction */ + MD_EXCEPTION_CODE_SOL_SIGABRT = 6, /* used by abort, replace SIGIOT in the future */ + MD_EXCEPTION_CODE_SOL_SIGEMT = 7, /* EMT instruction */ + MD_EXCEPTION_CODE_SOL_SIGFPE = 8, /* floating point exception */ + MD_EXCEPTION_CODE_SOL_SIGKILL = 9, /* kill (cannot be caught or ignored) */ + MD_EXCEPTION_CODE_SOL_SIGBUS = 10, /* bus error */ + MD_EXCEPTION_CODE_SOL_SIGSEGV = 11, /* segmentation violation */ + MD_EXCEPTION_CODE_SOL_SIGSYS = 12, /* bad argument to system call */ + MD_EXCEPTION_CODE_SOL_SIGPIPE = 13, /* write on a pipe with no one to read it */ + MD_EXCEPTION_CODE_SOL_SIGALRM = 14, /* alarm clock */ + MD_EXCEPTION_CODE_SOL_SIGTERM = 15, /* software termination signal from kill */ + MD_EXCEPTION_CODE_SOL_SIGUSR1 = 16, /* user defined signal 1 */ + MD_EXCEPTION_CODE_SOL_SIGUSR2 = 17, /* user defined signal 2 */ + MD_EXCEPTION_CODE_SOL_SIGCLD = 18, /* child status change */ + MD_EXCEPTION_CODE_SOL_SIGCHLD = 18, /* child status change alias (POSIX) */ + MD_EXCEPTION_CODE_SOL_SIGPWR = 19, /* power-fail restart */ + MD_EXCEPTION_CODE_SOL_SIGWINCH = 20, /* window size change */ + MD_EXCEPTION_CODE_SOL_SIGURG = 21, /* urgent socket condition */ + MD_EXCEPTION_CODE_SOL_SIGPOLL = 22, /* pollable event occurred */ + MD_EXCEPTION_CODE_SOL_SIGIO = 22, /* socket I/O possible (SIGPOLL alias) */ + MD_EXCEPTION_CODE_SOL_SIGSTOP = 23, /* stop (cannot be caught or ignored) */ + MD_EXCEPTION_CODE_SOL_SIGTSTP = 24, /* user stop requested from tty */ + MD_EXCEPTION_CODE_SOL_SIGCONT = 25, /* stopped process has been continued */ + MD_EXCEPTION_CODE_SOL_SIGTTIN = 26, /* background tty read attempted */ + MD_EXCEPTION_CODE_SOL_SIGTTOU = 27, /* background tty write attempted */ + MD_EXCEPTION_CODE_SOL_SIGVTALRM = 28, /* virtual timer expired */ + MD_EXCEPTION_CODE_SOL_SIGPROF = 29, /* profiling timer expired */ + MD_EXCEPTION_CODE_SOL_SIGXCPU = 30, /* exceeded cpu limit */ + MD_EXCEPTION_CODE_SOL_SIGXFSZ = 31, /* exceeded file size limit */ + MD_EXCEPTION_CODE_SOL_SIGWAITING = 32, /* reserved signal no longer used by threading code */ + MD_EXCEPTION_CODE_SOL_SIGLWP = 33, /* reserved signal no longer used by threading code */ + MD_EXCEPTION_CODE_SOL_SIGFREEZE = 34, /* special signal used by CPR */ + MD_EXCEPTION_CODE_SOL_SIGTHAW = 35, /* special signal used by CPR */ + MD_EXCEPTION_CODE_SOL_SIGCANCEL = 36, /* reserved signal for thread cancellation */ + MD_EXCEPTION_CODE_SOL_SIGLOST = 37, /* resource lost (eg, record-lock lost) */ + MD_EXCEPTION_CODE_SOL_SIGXRES = 38, /* resource control exceeded */ + MD_EXCEPTION_CODE_SOL_SIGJVM1 = 39, /* reserved signal for Java Virtual Machine */ + MD_EXCEPTION_CODE_SOL_SIGJVM2 = 40 /* reserved signal for Java Virtual Machine */ +} MDExceptionCodeSolaris; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_win32.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_win32.h new file mode 100644 index 0000000..458a705 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_exception_win32.h @@ -0,0 +1,116 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_exception_win32.h: Definitions of exception codes for + * Win32 platform + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + + +/* For (MDException).exception_code. These values come from WinBase.h + * and WinNT.h (names beginning with EXCEPTION_ are in WinBase.h, + * they are STATUS_ in WinNT.h). */ +typedef enum { + MD_EXCEPTION_CODE_WIN_CONTROL_C = 0x40010005, + /* DBG_CONTROL_C */ + MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION = 0x80000001, + /* EXCEPTION_GUARD_PAGE */ + MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT = 0x80000002, + /* EXCEPTION_DATATYPE_MISALIGNMENT */ + MD_EXCEPTION_CODE_WIN_BREAKPOINT = 0x80000003, + /* EXCEPTION_BREAKPOINT */ + MD_EXCEPTION_CODE_WIN_SINGLE_STEP = 0x80000004, + /* EXCEPTION_SINGLE_STEP */ + MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION = 0xc0000005, + /* EXCEPTION_ACCESS_VIOLATION */ + MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR = 0xc0000006, + /* EXCEPTION_IN_PAGE_ERROR */ + MD_EXCEPTION_CODE_WIN_INVALID_HANDLE = 0xc0000008, + /* EXCEPTION_INVALID_HANDLE */ + MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION = 0xc000001d, + /* EXCEPTION_ILLEGAL_INSTRUCTION */ + MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION = 0xc0000025, + /* EXCEPTION_NONCONTINUABLE_EXCEPTION */ + MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION = 0xc0000026, + /* EXCEPTION_INVALID_DISPOSITION */ + MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED = 0xc000008c, + /* EXCEPTION_BOUNDS_EXCEEDED */ + MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND = 0xc000008d, + /* EXCEPTION_FLT_DENORMAL_OPERAND */ + MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO = 0xc000008e, + /* EXCEPTION_FLT_DIVIDE_BY_ZERO */ + MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT = 0xc000008f, + /* EXCEPTION_FLT_INEXACT_RESULT */ + MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION = 0xc0000090, + /* EXCEPTION_FLT_INVALID_OPERATION */ + MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW = 0xc0000091, + /* EXCEPTION_FLT_OVERFLOW */ + MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK = 0xc0000092, + /* EXCEPTION_FLT_STACK_CHECK */ + MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW = 0xc0000093, + /* EXCEPTION_FLT_UNDERFLOW */ + MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO = 0xc0000094, + /* EXCEPTION_INT_DIVIDE_BY_ZERO */ + MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW = 0xc0000095, + /* EXCEPTION_INT_OVERFLOW */ + MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION = 0xc0000096, + /* EXCEPTION_PRIV_INSTRUCTION */ + MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW = 0xc00000fd, + /* EXCEPTION_STACK_OVERFLOW */ + MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194, + /* EXCEPTION_POSSIBLE_DEADLOCK */ + MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN = 0xc0000409, + /* STATUS_STACK_BUFFER_OVERRUN */ + MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION = 0xc0000374, + /* STATUS_HEAP_CORRUPTION */ + MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363 + /* Per http://support.microsoft.com/kb/185294, + generated by Visual C++ compiler */ +} MDExceptionCodeWin; + +// These constants are defined in the MSDN documentation of +// the EXCEPTION_RECORD structure. +typedef enum { + MD_ACCESS_VIOLATION_WIN_READ = 0, + MD_ACCESS_VIOLATION_WIN_WRITE = 1, + MD_ACCESS_VIOLATION_WIN_EXEC = 8 +} MDAccessViolationTypeWin; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_format.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_format.h new file mode 100644 index 0000000..a92007a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_format.h @@ -0,0 +1,794 @@ +/* Copyright (c) 2006, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_FORMAT_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_FORMAT_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + + +#if defined(_MSC_VER) +/* Disable "zero-sized array in struct/union" warnings when compiling in + * MSVC. DbgHelp.h does this too. */ +#pragma warning(push) +#pragma warning(disable:4200) +#endif /* _MSC_VER */ + + +/* + * guiddef.h + */ + +typedef struct { + u_int32_t data1; + u_int16_t data2; + u_int16_t data3; + u_int8_t data4[8]; +} MDGUID; /* GUID */ + + +/* + * WinNT.h + */ + +/* Non-x86 CPU identifiers found in the high 24 bits of + * (MDRawContext*).context_flags. These aren't used by Breakpad, but are + * defined here for reference, to avoid assigning values that conflict + * (although some values already conflict). */ +#define MD_CONTEXT_IA64 0x00080000 /* CONTEXT_IA64 */ +/* Additional values from winnt.h in the Windows CE 5.0 SDK: */ +#define MD_CONTEXT_SHX 0x000000c0 /* CONTEXT_SH4 (Super-H, includes SH3) */ +#define MD_CONTEXT_MIPS 0x00010000 /* CONTEXT_R4000 (same value as x86?) */ +#define MD_CONTEXT_ALPHA 0x00020000 /* CONTEXT_ALPHA */ + +/* As of Windows 7 SP1, the number of flag bits has increased to + * include 0x40 (CONTEXT_XSTATE): + * http://msdn.microsoft.com/en-us/library/hh134238%28v=vs.85%29.aspx */ +#define MD_CONTEXT_CPU_MASK 0xffffff00 + + +/* This is a base type for MDRawContextX86 and MDRawContextPPC. This + * structure should never be allocated directly. The actual structure type + * can be determined by examining the context_flags field. */ +typedef struct { + u_int32_t context_flags; +} MDRawContextBase; + +#include "minidump_cpu_amd64.h" +#include "minidump_cpu_arm.h" +#include "minidump_cpu_ppc.h" +#include "minidump_cpu_ppc64.h" +#include "minidump_cpu_sparc.h" +#include "minidump_cpu_x86.h" + +/* + * WinVer.h + */ + + +typedef struct { + u_int32_t signature; + u_int32_t struct_version; + u_int32_t file_version_hi; + u_int32_t file_version_lo; + u_int32_t product_version_hi; + u_int32_t product_version_lo; + u_int32_t file_flags_mask; /* Identifies valid bits in fileFlags */ + u_int32_t file_flags; + u_int32_t file_os; + u_int32_t file_type; + u_int32_t file_subtype; + u_int32_t file_date_hi; + u_int32_t file_date_lo; +} MDVSFixedFileInfo; /* VS_FIXEDFILEINFO */ + +/* For (MDVSFixedFileInfo).signature */ +#define MD_VSFIXEDFILEINFO_SIGNATURE 0xfeef04bd + /* VS_FFI_SIGNATURE */ + +/* For (MDVSFixedFileInfo).version */ +#define MD_VSFIXEDFILEINFO_VERSION 0x00010000 + /* VS_FFI_STRUCVERSION */ + +/* For (MDVSFixedFileInfo).file_flags_mask and + * (MDVSFixedFileInfo).file_flags */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG 0x00000001 + /* VS_FF_DEBUG */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_PRERELEASE 0x00000002 + /* VS_FF_PRERELEASE */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_PATCHED 0x00000004 + /* VS_FF_PATCHED */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_PRIVATEBUILD 0x00000008 + /* VS_FF_PRIVATEBUILD */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_INFOINFERRED 0x00000010 + /* VS_FF_INFOINFERRED */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_SPECIALBUILD 0x00000020 + /* VS_FF_SPECIALBUILD */ + +/* For (MDVSFixedFileInfo).file_os: high 16 bits */ +#define MD_VSFIXEDFILEINFO_FILE_OS_UNKNOWN 0 /* VOS_UNKNOWN */ +#define MD_VSFIXEDFILEINFO_FILE_OS_DOS (1 << 16) /* VOS_DOS */ +#define MD_VSFIXEDFILEINFO_FILE_OS_OS216 (2 << 16) /* VOS_OS216 */ +#define MD_VSFIXEDFILEINFO_FILE_OS_OS232 (3 << 16) /* VOS_OS232 */ +#define MD_VSFIXEDFILEINFO_FILE_OS_NT (4 << 16) /* VOS_NT */ +#define MD_VSFIXEDFILEINFO_FILE_OS_WINCE (5 << 16) /* VOS_WINCE */ +/* Low 16 bits */ +#define MD_VSFIXEDFILEINFO_FILE_OS__BASE 0 /* VOS__BASE */ +#define MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS16 1 /* VOS__WINDOWS16 */ +#define MD_VSFIXEDFILEINFO_FILE_OS__PM16 2 /* VOS__PM16 */ +#define MD_VSFIXEDFILEINFO_FILE_OS__PM32 3 /* VOS__PM32 */ +#define MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS32 4 /* VOS__WINDOWS32 */ + +/* For (MDVSFixedFileInfo).file_type */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_UNKNOWN 0 /* VFT_UNKNOWN */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_APP 1 /* VFT_APP */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_DLL 2 /* VFT_DLL */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_DRV 3 /* VFT_DLL */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_FONT 4 /* VFT_FONT */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_VXD 5 /* VFT_VXD */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_STATIC_LIB 7 /* VFT_STATIC_LIB */ + +/* For (MDVSFixedFileInfo).file_subtype */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_UNKNOWN 0 + /* VFT2_UNKNOWN */ +/* with file_type = MD_VSFIXEDFILEINFO_FILETYPE_DRV */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_PRINTER 1 + /* VFT2_DRV_PRINTER */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_KEYBOARD 2 + /* VFT2_DRV_KEYBOARD */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_LANGUAGE 3 + /* VFT2_DRV_LANGUAGE */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_DISPLAY 4 + /* VFT2_DRV_DISPLAY */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_MOUSE 5 + /* VFT2_DRV_MOUSE */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_NETWORK 6 + /* VFT2_DRV_NETWORK */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_SYSTEM 7 + /* VFT2_DRV_SYSTEM */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_INSTALLABLE 8 + /* VFT2_DRV_INSTALLABLE */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_SOUND 9 + /* VFT2_DRV_SOUND */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_COMM 10 + /* VFT2_DRV_COMM */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_INPUTMETHOD 11 + /* VFT2_DRV_INPUTMETHOD */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_VERSIONED_PRINTER 12 + /* VFT2_DRV_VERSIONED_PRINTER */ +/* with file_type = MD_VSFIXEDFILEINFO_FILETYPE_FONT */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_FONT_RASTER 1 + /* VFT2_FONT_RASTER */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_FONT_VECTOR 2 + /* VFT2_FONT_VECTOR */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_FONT_TRUETYPE 3 + /* VFT2_FONT_TRUETYPE */ + + +/* + * DbgHelp.h + */ + + +/* An MDRVA is an offset into the minidump file. The beginning of the + * MDRawHeader is at offset 0. */ +typedef u_int32_t MDRVA; /* RVA */ + +typedef struct { + u_int32_t data_size; + MDRVA rva; +} MDLocationDescriptor; /* MINIDUMP_LOCATION_DESCRIPTOR */ + + +typedef struct { + /* The base address of the memory range on the host that produced the + * minidump. */ + u_int64_t start_of_memory_range; + + MDLocationDescriptor memory; +} MDMemoryDescriptor; /* MINIDUMP_MEMORY_DESCRIPTOR */ + + +typedef struct { + u_int32_t signature; + u_int32_t version; + u_int32_t stream_count; + MDRVA stream_directory_rva; /* A |stream_count|-sized array of + * MDRawDirectory structures. */ + u_int32_t checksum; /* Can be 0. In fact, that's all that's + * been found in minidump files. */ + u_int32_t time_date_stamp; /* time_t */ + u_int64_t flags; +} MDRawHeader; /* MINIDUMP_HEADER */ + +/* For (MDRawHeader).signature and (MDRawHeader).version. Note that only the + * low 16 bits of (MDRawHeader).version are MD_HEADER_VERSION. Per the + * documentation, the high 16 bits are implementation-specific. */ +#define MD_HEADER_SIGNATURE 0x504d444d /* 'PMDM' */ + /* MINIDUMP_SIGNATURE */ +#define MD_HEADER_VERSION 0x0000a793 /* 42899 */ + /* MINIDUMP_VERSION */ + +/* For (MDRawHeader).flags: */ +typedef enum { + /* MD_NORMAL is the standard type of minidump. It includes full + * streams for the thread list, module list, exception, system info, + * and miscellaneous info. A memory list stream is also present, + * pointing to the same stack memory contained in the thread list, + * as well as a 256-byte region around the instruction address that + * was executing when the exception occurred. Stack memory is from + * 4 bytes below a thread's stack pointer up to the top of the + * memory region encompassing the stack. */ + MD_NORMAL = 0x00000000, + MD_WITH_DATA_SEGS = 0x00000001, + MD_WITH_FULL_MEMORY = 0x00000002, + MD_WITH_HANDLE_DATA = 0x00000004, + MD_FILTER_MEMORY = 0x00000008, + MD_SCAN_MEMORY = 0x00000010, + MD_WITH_UNLOADED_MODULES = 0x00000020, + MD_WITH_INDIRECTLY_REFERENCED_MEMORY = 0x00000040, + MD_FILTER_MODULE_PATHS = 0x00000080, + MD_WITH_PROCESS_THREAD_DATA = 0x00000100, + MD_WITH_PRIVATE_READ_WRITE_MEMORY = 0x00000200, + MD_WITHOUT_OPTIONAL_DATA = 0x00000400, + MD_WITH_FULL_MEMORY_INFO = 0x00000800, + MD_WITH_THREAD_INFO = 0x00001000, + MD_WITH_CODE_SEGS = 0x00002000, + MD_WITHOUT_AUXILLIARY_SEGS = 0x00004000, + MD_WITH_FULL_AUXILLIARY_STATE = 0x00008000, + MD_WITH_PRIVATE_WRITE_COPY_MEMORY = 0x00010000, + MD_IGNORE_INACCESSIBLE_MEMORY = 0x00020000, + MD_WITH_TOKEN_INFORMATION = 0x00040000 +} MDType; /* MINIDUMP_TYPE */ + + +typedef struct { + u_int32_t stream_type; + MDLocationDescriptor location; +} MDRawDirectory; /* MINIDUMP_DIRECTORY */ + +/* For (MDRawDirectory).stream_type */ +typedef enum { + MD_UNUSED_STREAM = 0, + MD_RESERVED_STREAM_0 = 1, + MD_RESERVED_STREAM_1 = 2, + MD_THREAD_LIST_STREAM = 3, /* MDRawThreadList */ + MD_MODULE_LIST_STREAM = 4, /* MDRawModuleList */ + MD_MEMORY_LIST_STREAM = 5, /* MDRawMemoryList */ + MD_EXCEPTION_STREAM = 6, /* MDRawExceptionStream */ + MD_SYSTEM_INFO_STREAM = 7, /* MDRawSystemInfo */ + MD_THREAD_EX_LIST_STREAM = 8, + MD_MEMORY_64_LIST_STREAM = 9, + MD_COMMENT_STREAM_A = 10, + MD_COMMENT_STREAM_W = 11, + MD_HANDLE_DATA_STREAM = 12, + MD_FUNCTION_TABLE_STREAM = 13, + MD_UNLOADED_MODULE_LIST_STREAM = 14, + MD_MISC_INFO_STREAM = 15, /* MDRawMiscInfo */ + MD_MEMORY_INFO_LIST_STREAM = 16, /* MDRawMemoryInfoList */ + MD_THREAD_INFO_LIST_STREAM = 17, + MD_HANDLE_OPERATION_LIST_STREAM = 18, + MD_LAST_RESERVED_STREAM = 0x0000ffff, + + /* Breakpad extension types. 0x4767 = "Gg" */ + MD_BREAKPAD_INFO_STREAM = 0x47670001, /* MDRawBreakpadInfo */ + MD_ASSERTION_INFO_STREAM = 0x47670002 /* MDRawAssertionInfo */ +} MDStreamType; /* MINIDUMP_STREAM_TYPE */ + + +typedef struct { + u_int32_t length; /* Length of buffer in bytes (not characters), + * excluding 0-terminator */ + u_int16_t buffer[1]; /* UTF-16-encoded, 0-terminated */ +} MDString; /* MINIDUMP_STRING */ + +static const size_t MDString_minsize = offsetof(MDString, buffer[0]); + + +typedef struct { + u_int32_t thread_id; + u_int32_t suspend_count; + u_int32_t priority_class; + u_int32_t priority; + u_int64_t teb; /* Thread environment block */ + MDMemoryDescriptor stack; + MDLocationDescriptor thread_context; /* MDRawContext[CPU] */ +} MDRawThread; /* MINIDUMP_THREAD */ + + +typedef struct { + u_int32_t number_of_threads; + MDRawThread threads[1]; +} MDRawThreadList; /* MINIDUMP_THREAD_LIST */ + +static const size_t MDRawThreadList_minsize = offsetof(MDRawThreadList, + threads[0]); + + +typedef struct { + u_int64_t base_of_image; + u_int32_t size_of_image; + u_int32_t checksum; /* 0 if unknown */ + u_int32_t time_date_stamp; /* time_t */ + MDRVA module_name_rva; /* MDString, pathname or filename */ + MDVSFixedFileInfo version_info; + + /* The next field stores a CodeView record and is populated when a module's + * debug information resides in a PDB file. It identifies the PDB file. */ + MDLocationDescriptor cv_record; + + /* The next field is populated when a module's debug information resides + * in a DBG file. It identifies the DBG file. This field is effectively + * obsolete with modules built by recent toolchains. */ + MDLocationDescriptor misc_record; + + /* Alignment problem: reserved0 and reserved1 are defined by the platform + * SDK as 64-bit quantities. However, that results in a structure whose + * alignment is unpredictable on different CPUs and ABIs. If the ABI + * specifies full alignment of 64-bit quantities in structures (as ppc + * does), there will be padding between miscRecord and reserved0. If + * 64-bit quantities can be aligned on 32-bit boundaries (as on x86), + * this padding will not exist. (Note that the structure up to this point + * contains 1 64-bit member followed by 21 32-bit members.) + * As a workaround, reserved0 and reserved1 are instead defined here as + * four 32-bit quantities. This should be harmless, as there are + * currently no known uses for these fields. */ + u_int32_t reserved0[2]; + u_int32_t reserved1[2]; +} MDRawModule; /* MINIDUMP_MODULE */ + +/* The inclusion of a 64-bit type in MINIDUMP_MODULE forces the struct to + * be tail-padded out to a multiple of 64 bits under some ABIs (such as PPC). + * This doesn't occur on systems that don't tail-pad in this manner. Define + * this macro to be the usable size of the MDRawModule struct, and use it in + * place of sizeof(MDRawModule). */ +#define MD_MODULE_SIZE 108 + + +/* (MDRawModule).cv_record can reference MDCVInfoPDB20 or MDCVInfoPDB70. + * Ref.: http://www.debuginfo.com/articles/debuginfomatch.html + * MDCVInfoPDB70 is the expected structure type with recent toolchains. */ + +typedef struct { + u_int32_t signature; + u_int32_t offset; /* Offset to debug data (expect 0 in minidump) */ +} MDCVHeader; + +typedef struct { + MDCVHeader cv_header; + u_int32_t signature; /* time_t debug information created */ + u_int32_t age; /* revision of PDB file */ + u_int8_t pdb_file_name[1]; /* Pathname or filename of PDB file */ +} MDCVInfoPDB20; + +static const size_t MDCVInfoPDB20_minsize = offsetof(MDCVInfoPDB20, + pdb_file_name[0]); + +#define MD_CVINFOPDB20_SIGNATURE 0x3031424e /* cvHeader.signature = '01BN' */ + +typedef struct { + u_int32_t cv_signature; + MDGUID signature; /* GUID, identifies PDB file */ + u_int32_t age; /* Identifies incremental changes to PDB file */ + u_int8_t pdb_file_name[1]; /* Pathname or filename of PDB file, + * 0-terminated 8-bit character data (UTF-8?) */ +} MDCVInfoPDB70; + +static const size_t MDCVInfoPDB70_minsize = offsetof(MDCVInfoPDB70, + pdb_file_name[0]); + +#define MD_CVINFOPDB70_SIGNATURE 0x53445352 /* cvSignature = 'SDSR' */ + +typedef struct { + u_int32_t data1[2]; + u_int32_t data2; + u_int32_t data3; + u_int32_t data4; + u_int32_t data5[3]; + u_int8_t extra[2]; +} MDCVInfoELF; + +/* In addition to the two CodeView record formats above, used for linking + * to external pdb files, it is possible for debugging data to be carried + * directly in the CodeView record itself. These signature values will + * be found in the first 4 bytes of the CodeView record. Additional values + * not commonly experienced in the wild are given by "Microsoft Symbol and + * Type Information", http://www.x86.org/ftp/manuals/tools/sym.pdf, section + * 7.2. An in-depth description of the CodeView 4.1 format is given by + * "Undocumented Windows 2000 Secrets", Windows 2000 Debugging Support/ + * Microsoft Symbol File Internals/CodeView Subsections, + * http://www.rawol.com/features/undocumented/sbs-w2k-1-windows-2000-debugging-support.pdf + */ +#define MD_CVINFOCV41_SIGNATURE 0x3930424e /* '90BN', CodeView 4.10. */ +#define MD_CVINFOCV50_SIGNATURE 0x3131424e /* '11BN', CodeView 5.0, + * MS C7-format (/Z7). */ + +#define MD_CVINFOUNKNOWN_SIGNATURE 0xffffffff /* An unlikely value. */ + +/* (MDRawModule).miscRecord can reference MDImageDebugMisc. The Windows + * structure is actually defined in WinNT.h. This structure is effectively + * obsolete with modules built by recent toolchains. */ + +typedef struct { + u_int32_t data_type; /* IMAGE_DEBUG_TYPE_*, not defined here because + * this debug record type is mostly obsolete. */ + u_int32_t length; /* Length of entire MDImageDebugMisc structure */ + u_int8_t unicode; /* True if data is multibyte */ + u_int8_t reserved[3]; + u_int8_t data[1]; +} MDImageDebugMisc; /* IMAGE_DEBUG_MISC */ + +static const size_t MDImageDebugMisc_minsize = offsetof(MDImageDebugMisc, + data[0]); + + +typedef struct { + u_int32_t number_of_modules; + MDRawModule modules[1]; +} MDRawModuleList; /* MINIDUMP_MODULE_LIST */ + +static const size_t MDRawModuleList_minsize = offsetof(MDRawModuleList, + modules[0]); + + +typedef struct { + u_int32_t number_of_memory_ranges; + MDMemoryDescriptor memory_ranges[1]; +} MDRawMemoryList; /* MINIDUMP_MEMORY_LIST */ + +static const size_t MDRawMemoryList_minsize = offsetof(MDRawMemoryList, + memory_ranges[0]); + + +#define MD_EXCEPTION_MAXIMUM_PARAMETERS 15 + +typedef struct { + u_int32_t exception_code; /* Windows: MDExceptionCodeWin, + * Mac OS X: MDExceptionMac, + * Linux: MDExceptionCodeLinux. */ + u_int32_t exception_flags; /* Windows: 1 if noncontinuable, + Mac OS X: MDExceptionCodeMac. */ + u_int64_t exception_record; /* Address (in the minidump-producing host's + * memory) of another MDException, for + * nested exceptions. */ + u_int64_t exception_address; /* The address that caused the exception. + * Mac OS X: exception subcode (which is + * typically the address). */ + u_int32_t number_parameters; /* Number of valid elements in + * exception_information. */ + u_int32_t __align; + u_int64_t exception_information[MD_EXCEPTION_MAXIMUM_PARAMETERS]; +} MDException; /* MINIDUMP_EXCEPTION */ + +#include "minidump_exception_win32.h" +#include "minidump_exception_mac.h" +#include "minidump_exception_linux.h" +#include "minidump_exception_solaris.h" + +typedef struct { + u_int32_t thread_id; /* Thread in which the exception + * occurred. Corresponds to + * (MDRawThread).thread_id. */ + u_int32_t __align; + MDException exception_record; + MDLocationDescriptor thread_context; /* MDRawContext[CPU] */ +} MDRawExceptionStream; /* MINIDUMP_EXCEPTION_STREAM */ + + +typedef union { + struct { + u_int32_t vendor_id[3]; /* cpuid 0: ebx, edx, ecx */ + u_int32_t version_information; /* cpuid 1: eax */ + u_int32_t feature_information; /* cpuid 1: edx */ + u_int32_t amd_extended_cpu_features; /* cpuid 0x80000001, ebx */ + } x86_cpu_info; + struct { + u_int64_t processor_features[2]; + } other_cpu_info; +} MDCPUInformation; /* CPU_INFORMATION */ + + +typedef struct { + /* The next 3 fields and numberOfProcessors are from the SYSTEM_INFO + * structure as returned by GetSystemInfo */ + u_int16_t processor_architecture; + u_int16_t processor_level; /* x86: 5 = 586, 6 = 686, ... */ + u_int16_t processor_revision; /* x86: 0xMMSS, where MM=model, + * SS=stepping */ + + u_int8_t number_of_processors; + u_int8_t product_type; /* Windows: VER_NT_* from WinNT.h */ + + /* The next 5 fields are from the OSVERSIONINFO structure as returned + * by GetVersionEx */ + u_int32_t major_version; + u_int32_t minor_version; + u_int32_t build_number; + u_int32_t platform_id; + MDRVA csd_version_rva; /* MDString further identifying the + * host OS. + * Windows: name of the installed OS + * service pack. + * Mac OS X: the Apple OS build number + * (sw_vers -buildVersion). + * Linux: uname -srvmo */ + + u_int16_t suite_mask; /* Windows: VER_SUITE_* from WinNT.h */ + u_int16_t reserved2; + + MDCPUInformation cpu; +} MDRawSystemInfo; /* MINIDUMP_SYSTEM_INFO */ + +/* For (MDRawSystemInfo).processor_architecture: */ +typedef enum { + MD_CPU_ARCHITECTURE_X86 = 0, /* PROCESSOR_ARCHITECTURE_INTEL */ + MD_CPU_ARCHITECTURE_MIPS = 1, /* PROCESSOR_ARCHITECTURE_MIPS */ + MD_CPU_ARCHITECTURE_ALPHA = 2, /* PROCESSOR_ARCHITECTURE_ALPHA */ + MD_CPU_ARCHITECTURE_PPC = 3, /* PROCESSOR_ARCHITECTURE_PPC */ + MD_CPU_ARCHITECTURE_SHX = 4, /* PROCESSOR_ARCHITECTURE_SHX + * (Super-H) */ + MD_CPU_ARCHITECTURE_ARM = 5, /* PROCESSOR_ARCHITECTURE_ARM */ + MD_CPU_ARCHITECTURE_IA64 = 6, /* PROCESSOR_ARCHITECTURE_IA64 */ + MD_CPU_ARCHITECTURE_ALPHA64 = 7, /* PROCESSOR_ARCHITECTURE_ALPHA64 */ + MD_CPU_ARCHITECTURE_MSIL = 8, /* PROCESSOR_ARCHITECTURE_MSIL + * (Microsoft Intermediate Language) */ + MD_CPU_ARCHITECTURE_AMD64 = 9, /* PROCESSOR_ARCHITECTURE_AMD64 */ + MD_CPU_ARCHITECTURE_X86_WIN64 = 10, + /* PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 (WoW64) */ + MD_CPU_ARCHITECTURE_SPARC = 0x8001, /* Breakpad-defined value for SPARC */ + MD_CPU_ARCHITECTURE_UNKNOWN = 0xffff /* PROCESSOR_ARCHITECTURE_UNKNOWN */ +} MDCPUArchitecture; + +/* For (MDRawSystemInfo).platform_id: */ +typedef enum { + MD_OS_WIN32S = 0, /* VER_PLATFORM_WIN32s (Windows 3.1) */ + MD_OS_WIN32_WINDOWS = 1, /* VER_PLATFORM_WIN32_WINDOWS (Windows 95-98-Me) */ + MD_OS_WIN32_NT = 2, /* VER_PLATFORM_WIN32_NT (Windows NT, 2000+) */ + MD_OS_WIN32_CE = 3, /* VER_PLATFORM_WIN32_CE, VER_PLATFORM_WIN32_HH + * (Windows CE, Windows Mobile, "Handheld") */ + + /* The following values are Breakpad-defined. */ + MD_OS_UNIX = 0x8000, /* Generic Unix-ish */ + MD_OS_MAC_OS_X = 0x8101, /* Mac OS X/Darwin */ + MD_OS_IOS = 0x8102, /* iOS */ + MD_OS_LINUX = 0x8201, /* Linux */ + MD_OS_SOLARIS = 0x8202 /* Solaris */ +} MDOSPlatform; + + +typedef struct { + u_int32_t size_of_info; /* Length of entire MDRawMiscInfo structure. */ + u_int32_t flags1; + + /* The next field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_ID. */ + u_int32_t process_id; + + /* The next 3 fields are only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_TIMES. */ + u_int32_t process_create_time; /* time_t process started */ + u_int32_t process_user_time; /* seconds of user CPU time */ + u_int32_t process_kernel_time; /* seconds of kernel CPU time */ + + /* The following fields are not present in MINIDUMP_MISC_INFO but are + * in MINIDUMP_MISC_INFO_2. When this struct is populated, these values + * may not be set. Use flags1 or sizeOfInfo to determine whether these + * values are present. These are only valid when flags1 contains + * MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO. */ + u_int32_t processor_max_mhz; + u_int32_t processor_current_mhz; + u_int32_t processor_mhz_limit; + u_int32_t processor_max_idle_state; + u_int32_t processor_current_idle_state; +} MDRawMiscInfo; /* MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO2 */ + +#define MD_MISCINFO_SIZE 24 +#define MD_MISCINFO2_SIZE 44 + +/* For (MDRawMiscInfo).flags1. These values indicate which fields in the + * MDRawMiscInfoStructure are valid. */ +typedef enum { + MD_MISCINFO_FLAGS1_PROCESS_ID = 0x00000001, + /* MINIDUMP_MISC1_PROCESS_ID */ + MD_MISCINFO_FLAGS1_PROCESS_TIMES = 0x00000002, + /* MINIDUMP_MISC1_PROCESS_TIMES */ + MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO = 0x00000004 + /* MINIDUMP_MISC1_PROCESSOR_POWER_INFO */ +} MDMiscInfoFlags1; + +/* + * Around DbgHelp version 6.0, the style of new LIST structures changed + * from including an array of length 1 at the end of the struct to + * represent the variable-length data to including explicit + * "size of header", "size of entry" and "number of entries" fields + * in the header, presumably to allow backwards-compatibly-extending + * the structures in the future. The actual list entries follow the + * header data directly in this case. + */ + +typedef struct { + u_int32_t size_of_header; /* sizeof(MDRawMemoryInfoList) */ + u_int32_t size_of_entry; /* sizeof(MDRawMemoryInfo) */ + u_int64_t number_of_entries; +} MDRawMemoryInfoList; /* MINIDUMP_MEMORY_INFO_LIST */ + +typedef struct { + u_int64_t base_address; /* Base address of a region of pages */ + u_int64_t allocation_base; /* Base address of a range of pages + * within this region. */ + u_int32_t allocation_protection; /* Memory protection when this region + * was originally allocated: + * MDMemoryProtection */ + u_int32_t __alignment1; + u_int64_t region_size; + u_int32_t state; /* MDMemoryState */ + u_int32_t protection; /* MDMemoryProtection */ + u_int32_t type; /* MDMemoryType */ + u_int32_t __alignment2; +} MDRawMemoryInfo; /* MINIDUMP_MEMORY_INFO */ + +/* For (MDRawMemoryInfo).state */ +typedef enum { + MD_MEMORY_STATE_COMMIT = 0x1000, /* physical storage has been allocated */ + MD_MEMORY_STATE_RESERVE = 0x2000, /* reserved, but no physical storage */ + MD_MEMORY_STATE_FREE = 0x10000 /* available to be allocated */ +} MDMemoryState; + +/* For (MDRawMemoryInfo).allocation_protection and .protection */ +typedef enum { + MD_MEMORY_PROTECT_NOACCESS = 0x01, /* PAGE_NOACCESS */ + MD_MEMORY_PROTECT_READONLY = 0x02, /* PAGE_READONLY */ + MD_MEMORY_PROTECT_READWRITE = 0x04, /* PAGE_READWRITE */ + MD_MEMORY_PROTECT_WRITECOPY = 0x08, /* PAGE_WRITECOPY */ + MD_MEMORY_PROTECT_EXECUTE = 0x10, /* PAGE_EXECUTE */ + MD_MEMORY_PROTECT_EXECUTE_READ = 0x20, /* PAGE_EXECUTE_READ */ + MD_MEMORY_PROTECT_EXECUTE_READWRITE = 0x40, /* PAGE_EXECUTE_READWRITE */ + MD_MEMORY_PROTECT_EXECUTE_WRITECOPY = 0x80, /* PAGE_EXECUTE_WRITECOPY */ + /* These options can be combined with the previous flags. */ + MD_MEMORY_PROTECT_GUARD = 0x100, /* PAGE_GUARD */ + MD_MEMORY_PROTECT_NOCACHE = 0x200, /* PAGE_NOCACHE */ + MD_MEMORY_PROTECT_WRITECOMBINE = 0x400, /* PAGE_WRITECOMBINE */ +} MDMemoryProtection; + +/* Used to mask the mutually exclusive options from the combinable flags. */ +const u_int32_t MD_MEMORY_PROTECTION_ACCESS_MASK = 0xFF; + +/* For (MDRawMemoryInfo).type */ +typedef enum { + MD_MEMORY_TYPE_PRIVATE = 0x20000, /* not shared by other processes */ + MD_MEMORY_TYPE_MAPPED = 0x40000, /* mapped into the view of a section */ + MD_MEMORY_TYPE_IMAGE = 0x1000000 /* mapped into the view of an image */ +} MDMemoryType; + +/* + * Breakpad extension types + */ + + +typedef struct { + /* validity is a bitmask with values from MDBreakpadInfoValidity, indicating + * which of the other fields in the structure are valid. */ + u_int32_t validity; + + /* Thread ID of the handler thread. dump_thread_id should correspond to + * the thread_id of an MDRawThread in the minidump's MDRawThreadList if + * a dedicated thread in that list was used to produce the minidump. If + * the MDRawThreadList does not contain a dedicated thread used to produce + * the minidump, this field should be set to 0 and the validity field + * must not contain MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID. */ + u_int32_t dump_thread_id; + + /* Thread ID of the thread that requested the minidump be produced. As + * with dump_thread_id, requesting_thread_id should correspond to the + * thread_id of an MDRawThread in the minidump's MDRawThreadList. For + * minidumps produced as a result of an exception, requesting_thread_id + * will be the same as the MDRawExceptionStream's thread_id field. For + * minidumps produced "manually" at the program's request, + * requesting_thread_id will indicate which thread caused the dump to be + * written. If the minidump was produced at the request of something + * other than a thread in the MDRawThreadList, this field should be set + * to 0 and the validity field must not contain + * MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID. */ + u_int32_t requesting_thread_id; +} MDRawBreakpadInfo; + +/* For (MDRawBreakpadInfo).validity: */ +typedef enum { + /* When set, the dump_thread_id field is valid. */ + MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID = 1 << 0, + + /* When set, the requesting_thread_id field is valid. */ + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID = 1 << 1 +} MDBreakpadInfoValidity; + +typedef struct { + /* expression, function, and file are 0-terminated UTF-16 strings. They + * may be truncated if necessary, but should always be 0-terminated when + * written to a file. + * Fixed-length strings are used because MiniDumpWriteDump doesn't offer + * a way for user streams to point to arbitrary RVAs for strings. */ + u_int16_t expression[128]; /* Assertion that failed... */ + u_int16_t function[128]; /* ...within this function... */ + u_int16_t file[128]; /* ...in this file... */ + u_int32_t line; /* ...at this line. */ + u_int32_t type; +} MDRawAssertionInfo; + +/* For (MDRawAssertionInfo).type: */ +typedef enum { + MD_ASSERTION_INFO_TYPE_UNKNOWN = 0, + + /* Used for assertions that would be raised by the MSVC CRT but are + * directed to an invalid parameter handler instead. */ + MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER, + + /* Used for assertions that would be raised by the MSVC CRT but are + * directed to a pure virtual call handler instead. */ + MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL +} MDAssertionInfoData; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif /* _MSC_VER */ + + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_FORMAT_H__ */ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_size.h b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_size.h new file mode 100644 index 0000000..918544b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/common/minidump_size.h @@ -0,0 +1,107 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. */ + +// minidump_size.h: Provides a C++ template for programmatic access to +// the sizes of various types defined in minidump_format.h. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__ + +#include + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +template +class minidump_size { + public: + static size_t size() { return sizeof(T); } +}; + +// Explicit specializations for variable-length types. The size returned +// for these should be the size for an object without its variable-length +// section. + +template<> +class minidump_size { + public: + static size_t size() { return MDString_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDRawThreadList_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDCVInfoPDB20_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDCVInfoPDB70_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDImageDebugMisc_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDRawModuleList_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDRawMemoryList_minsize; } +}; + +// Explicit specialization for MDRawModule, for which sizeof may include +// tail-padding on some architectures but not others. + +template<> +class minidump_size { + public: + static size_t size() { return MD_MODULE_SIZE; } +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/basic_source_line_resolver.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/basic_source_line_resolver.h new file mode 100644 index 0000000..f77b3bb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/basic_source_line_resolver.h @@ -0,0 +1,84 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// basic_source_line_resolver.h: BasicSourceLineResolver is derived from +// SourceLineResolverBase, and is a concrete implementation of +// SourceLineResolverInterface, using address map files produced by a +// compatible writer, e.g. PDBSourceLineWriter. +// +// see "processor/source_line_resolver_base.h" +// and "source_line_resolver_interface.h" for more documentation. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ + +#include + +#include "google_breakpad/processor/source_line_resolver_base.h" + +namespace google_breakpad { + +using std::string; +using std::map; + +class BasicSourceLineResolver : public SourceLineResolverBase { + public: + BasicSourceLineResolver(); + virtual ~BasicSourceLineResolver() { } + + using SourceLineResolverBase::LoadModule; + using SourceLineResolverBase::LoadModuleUsingMapBuffer; + using SourceLineResolverBase::LoadModuleUsingMemoryBuffer; + using SourceLineResolverBase::ShouldDeleteMemoryBufferAfterLoadModule; + using SourceLineResolverBase::UnloadModule; + using SourceLineResolverBase::HasModule; + using SourceLineResolverBase::FillSourceLineInfo; + using SourceLineResolverBase::FindWindowsFrameInfo; + using SourceLineResolverBase::FindCFIFrameInfo; + + private: + // friend declarations: + friend class BasicModuleFactory; + friend class ModuleComparer; + friend class ModuleSerializer; + template friend class SimpleSerializer; + + // Function derives from SourceLineResolverBase::Function. + struct Function; + // Module implements SourceLineResolverBase::Module interface. + class Module; + + // Disallow unwanted copy ctor and assignment operator + BasicSourceLineResolver(const BasicSourceLineResolver&); + void operator=(const BasicSourceLineResolver&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/call_stack.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/call_stack.h new file mode 100644 index 0000000..21f595e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/call_stack.h @@ -0,0 +1,77 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// call_stack.h: A call stack comprised of stack frames. +// +// This class manages a vector of stack frames. It is used instead of +// exposing the vector directly to allow the CallStack to own StackFrame +// pointers without having to publicly export the linked_ptr class. A +// CallStack must be composed of pointers instead of objects to allow for +// CPU-specific StackFrame subclasses. +// +// By convention, the stack frame at index 0 is the innermost callee frame, +// and the frame at the highest index in a call stack is the outermost +// caller. CallStack only allows stacks to be built by pushing frames, +// beginning with the innermost callee frame. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__ + +#include + +namespace google_breakpad { + +using std::vector; + +struct StackFrame; +template class linked_ptr; + +class CallStack { + public: + CallStack() { Clear(); } + ~CallStack(); + + // Resets the CallStack to its initial empty state + void Clear(); + + const vector* frames() const { return &frames_; } + + private: + // Stackwalker is responsible for building the frames_ vector. + friend class Stackwalker; + + // Storage for pushed frames. + vector frames_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCSSOR_CALL_STACK_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/code_module.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/code_module.h new file mode 100644 index 0000000..38ee956 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/code_module.h @@ -0,0 +1,94 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// code_module.h: Carries information about code modules that are loaded +// into a process. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__ + +#include +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::string; + +class CodeModule { + public: + virtual ~CodeModule() {} + + // The base address of this code module as it was loaded by the process. + // (u_int64_t)-1 on error. + virtual u_int64_t base_address() const = 0; + + // The size of the code module. 0 on error. + virtual u_int64_t size() const = 0; + + // The path or file name that the code module was loaded from. Empty on + // error. + virtual string code_file() const = 0; + + // An identifying string used to discriminate between multiple versions and + // builds of the same code module. This may contain a uuid, timestamp, + // version number, or any combination of this or other information, in an + // implementation-defined format. Empty on error. + virtual string code_identifier() const = 0; + + // The filename containing debugging information associated with the code + // module. If debugging information is stored in a file separate from the + // code module itself (as is the case when .pdb or .dSYM files are used), + // this will be different from code_file. If debugging information is + // stored in the code module itself (possibly prior to stripping), this + // will be the same as code_file. Empty on error. + virtual string debug_file() const = 0; + + // An identifying string similar to code_identifier, but identifies a + // specific version and build of the associated debug file. This may be + // the same as code_identifier when the debug_file and code_file are + // identical or when the same identifier is used to identify distinct + // debug and code files. + virtual string debug_identifier() const = 0; + + // A human-readable representation of the code module's version. Empty on + // error. + virtual string version() const = 0; + + // Creates a new copy of this CodeModule object, which the caller takes + // ownership of. The new CodeModule may be of a different concrete class + // than the CodeModule being copied, but will behave identically to the + // copied CodeModule as far as the CodeModule interface is concerned. + virtual const CodeModule* Copy() const = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/code_modules.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/code_modules.h new file mode 100644 index 0000000..29c55d4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/code_modules.h @@ -0,0 +1,98 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// code_modules.h: Contains all of the CodeModule objects that were loaded +// into a single process. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__ + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +class CodeModule; + +class CodeModules { + public: + virtual ~CodeModules() {} + + // The number of contained CodeModule objects. + virtual unsigned int module_count() const = 0; + + // Random access to modules. Returns the module whose code is present + // at the address indicated by |address|. If no module is present at this + // address, returns NULL. Ownership of the returned CodeModule is retained + // by the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleForAddress(u_int64_t address) const = 0; + + // Returns the module corresponding to the main executable. If there is + // no main executable, returns NULL. Ownership of the returned CodeModule + // is retained by the CodeModules object; pointers returned by this method + // are valid for comparison with pointers returned by the other Get + // methods. + virtual const CodeModule* GetMainModule() const = 0; + + // Sequential access to modules. A sequence number of 0 corresponds to the + // module residing lowest in memory. If the sequence number is out of + // range, returns NULL. Ownership of the returned CodeModule is retained + // by the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleAtSequence( + unsigned int sequence) const = 0; + + // Sequential access to modules. This is similar to GetModuleAtSequence, + // except no ordering requirement is enforced. A CodeModules implementation + // may return CodeModule objects from GetModuleAtIndex in any order it + // wishes, provided that the order remain the same throughout the life of + // the CodeModules object. Typically, GetModuleAtIndex would be used by + // a caller to enumerate all CodeModule objects quickly when the enumeration + // does not require any ordering. If the index argument is out of range, + // returns NULL. Ownership of the returned CodeModule is retained by + // the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleAtIndex(unsigned int index) const = 0; + + // Creates a new copy of this CodeModules object, which the caller takes + // ownership of. The new object will also contain copies of the existing + // object's child CodeModule objects. The new CodeModules object may be of + // a different concrete class than the object being copied, but will behave + // identically to the copied object as far as the CodeModules and CodeModule + // interfaces are concerned, except that the order that GetModuleAtIndex + // returns objects in may differ between a copy and the original CodeModules + // object. + virtual const CodeModules* Copy() const = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/exploitability.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/exploitability.h new file mode 100644 index 0000000..225206d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/exploitability.h @@ -0,0 +1,73 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// exploitability_engine.h: Generic exploitability engine. +// +// The Exploitability class is an abstract base class providing common +// generic methods that apply to exploitability engines for specific platforms. +// Specific implementations will extend this class by providing run +// methods to fill in the exploitability_ enumeration of the ProcessState +// for a crash. +// +// Author: Cris Neckar + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_ + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/process_state.h" + +namespace google_breakpad { + +class Exploitability { + public: + virtual ~Exploitability() {} + + static Exploitability *ExploitabilityForPlatform(Minidump *dump, + ProcessState *process_state); + + ExploitabilityRating CheckExploitability(); + bool AddressIsAscii(u_int64_t); + + protected: + Exploitability(Minidump *dump, + ProcessState *process_state); + + Minidump *dump_; + ProcessState *process_state_; + SystemInfo *system_info_; + + private: + virtual ExploitabilityRating CheckPlatformExploitability() = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/fast_source_line_resolver.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/fast_source_line_resolver.h new file mode 100644 index 0000000..60f6dfc --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/fast_source_line_resolver.h @@ -0,0 +1,99 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// fast_source_line_resolver.h: FastSourceLineResolver is derived from +// SourceLineResolverBase, and is a concrete implementation of +// SourceLineResolverInterface. +// +// FastSourceLineResolver is a sibling class of BasicSourceLineResolver. The +// difference is FastSourceLineResolver loads a serialized memory chunk of data +// which can be used directly a Module without parsing or copying of underlying +// data. Therefore loading a symbol in FastSourceLineResolver is much faster +// and more memory-efficient than BasicSourceLineResolver. +// +// See "source_line_resolver_base.h" and +// "google_breakpad/source_line_resolver_interface.h" for more reference. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__ + +#include +#include + +#include "google_breakpad/processor/source_line_resolver_base.h" + +namespace google_breakpad { + +using std::map; + +class FastSourceLineResolver : public SourceLineResolverBase { + public: + FastSourceLineResolver(); + virtual ~FastSourceLineResolver() { } + + using SourceLineResolverBase::FillSourceLineInfo; + using SourceLineResolverBase::FindCFIFrameInfo; + using SourceLineResolverBase::FindWindowsFrameInfo; + using SourceLineResolverBase::HasModule; + using SourceLineResolverBase::LoadModule; + using SourceLineResolverBase::LoadModuleUsingMapBuffer; + using SourceLineResolverBase::LoadModuleUsingMemoryBuffer; + using SourceLineResolverBase::UnloadModule; + + private: + // Friend declarations. + friend class ModuleComparer; + friend class ModuleSerializer; + friend class FastModuleFactory; + + // Nested types that will derive from corresponding nested types defined in + // SourceLineResolverBase. + struct Line; + struct Function; + struct PublicSymbol; + class Module; + + // Deserialize raw memory data to construct a WindowsFrameInfo object. + static WindowsFrameInfo CopyWFI(const char *raw_memory); + + // FastSourceLineResolver requires the memory buffer stays alive during the + // lifetime of a corresponding module, therefore it needs to redefine this + // virtual method. + virtual bool ShouldDeleteMemoryBufferAfterLoadModule(); + + // Disallow unwanted copy ctor and assignment operator + FastSourceLineResolver(const FastSourceLineResolver&); + void operator=(const FastSourceLineResolver&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/memory_region.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/memory_region.h new file mode 100644 index 0000000..15e23dd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/memory_region.h @@ -0,0 +1,76 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// memory_region.h: Access to memory regions. +// +// A MemoryRegion provides virtual access to a range of memory. It is an +// abstraction allowing the actual source of memory to be independent of +// methods which need to access a virtual memory space. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__ + + +#include "google_breakpad/common/breakpad_types.h" + + +namespace google_breakpad { + + +class MemoryRegion { + public: + virtual ~MemoryRegion() {} + + // The base address of this memory region. + virtual u_int64_t GetBase() const = 0; + + // The size of this memory region. + virtual u_int32_t GetSize() const = 0; + + // Access to data of various sizes within the memory region. address + // is a pointer to read, and it must lie within the memory region as + // defined by its base address and size. The location pointed to by + // value is set to the value at address. Byte-swapping is performed + // if necessary so that the value is appropriate for the running + // program. Returns true on success. Fails and returns false if address + // is out of the region's bounds (after considering the width of value), + // or for other types of errors. + virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const =0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const =0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const =0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const =0; +}; + + +} // namespace google_breakpad + + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump.h new file mode 100644 index 0000000..012a813 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump.h @@ -0,0 +1,1032 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump.h: A minidump reader. +// +// The basic structure of this module tracks the structure of the minidump +// file itself. At the top level, a minidump file is represented by a +// Minidump object. Like most other classes in this module, Minidump +// provides a Read method that initializes the object with information from +// the file. Most of the classes in this file are wrappers around the +// "raw" structures found in the minidump file itself, and defined in +// minidump_format.h. For example, each thread is represented by a +// MinidumpThread object, whose parameters are specified in an MDRawThread +// structure. A properly byte-swapped MDRawThread can be obtained from a +// MinidumpThread easily by calling its thread() method. +// +// Most of the module lazily reads only the portion of the minidump file +// necessary to fulfill the user's request. Calling Minidump::Read +// only reads the minidump's directory. The thread list is not read until +// it is needed, and even once it's read, the memory regions for each +// thread's stack aren't read until they're needed. This strategy avoids +// unnecessary file input, and allocating memory for data in which the user +// has no interest. Note that although memory allocations for a typical +// minidump file are not particularly large, it is possible for legitimate +// minidumps to be sizable. A full-memory minidump, for example, contains +// a snapshot of the entire mapped memory space. Even a normal minidump, +// with stack memory only, can be large if, for example, the dump was +// generated in response to a crash that occurred due to an infinite- +// recursion bug that caused the stack's limits to be exceeded. Finally, +// some users of this library will unfortunately find themselves in the +// position of having to process potentially-hostile minidumps that might +// attempt to cause problems by forcing the minidump processor to over- +// allocate memory. +// +// Memory management in this module is based on a strict +// you-don't-own-anything policy. The only object owned by the user is +// the top-level Minidump object, the creation and destruction of which +// must be the user's own responsibility. All other objects obtained +// through interaction with this module are ultimately owned by the +// Minidump object, and will be freed upon the Minidump object's destruction. +// Because memory regions can potentially involve large allocations, a +// FreeMemory method is provided by MinidumpMemoryRegion, allowing the user +// to release data when it is no longer needed. Use of this method is +// optional but recommended. If freed data is later required, it will +// be read back in from the minidump file again. +// +// There is one exception to this memory management policy: +// Minidump::ReadString will return a string object to the user, and the user +// is responsible for its deletion. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ + +#ifndef _WIN32 +#include +#endif + +#include +#include +#include +#include + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" + + +namespace google_breakpad { + + +using std::map; +using std::string; +using std::vector; + + +class Minidump; +template class RangeMap; + + +// MinidumpObject is the base of all Minidump* objects except for Minidump +// itself. +class MinidumpObject { + public: + virtual ~MinidumpObject() {} + + bool valid() const { return valid_; } + + protected: + explicit MinidumpObject(Minidump* minidump); + + // Refers to the Minidump object that is the ultimate parent of this + // Some MinidumpObjects are owned by other MinidumpObjects, but at the + // root of the ownership tree is always a Minidump. The Minidump object + // is kept here for access to its seeking and reading facilities, and + // for access to data about the minidump file itself, such as whether + // it should be byte-swapped. + Minidump* minidump_; + + // MinidumpObjects are not valid when created. When a subclass populates + // its own fields, it can set valid_ to true. Accessors and mutators may + // wish to consider or alter the valid_ state as they interact with + // objects. + bool valid_; +}; + + +// This class exists primarily to provide a virtual destructor in a base +// class common to all objects that might be stored in +// Minidump::mStreamObjects. Some object types (MinidumpContext) will +// never be stored in Minidump::mStreamObjects, but are represented as +// streams and adhere to the same interface, and may be derived from +// this class. +class MinidumpStream : public MinidumpObject { + public: + virtual ~MinidumpStream() {} + + protected: + explicit MinidumpStream(Minidump* minidump); + + private: + // Populate (and validate) the MinidumpStream. minidump_ is expected + // to be positioned at the beginning of the stream, so that the next + // read from the minidump will be at the beginning of the stream. + // expected_size should be set to the stream's length as contained in + // the MDRawDirectory record or other identifying record. A class + // that implements MinidumpStream can compare expected_size to a + // known size as an integrity check. + virtual bool Read(u_int32_t expected_size) = 0; +}; + + +// MinidumpContext carries a CPU-specific MDRawContext structure, which +// contains CPU context such as register states. Each thread has its +// own context, and the exception record, if present, also has its own +// context. Note that if the exception record is present, the context it +// refers to is probably what the user wants to use for the exception +// thread, instead of that thread's own context. The exception thread's +// context (as opposed to the exception record's context) will contain +// context for the exception handler (which performs minidump generation), +// and not the context that caused the exception (which is probably what the +// user wants). +class MinidumpContext : public MinidumpStream { + public: + virtual ~MinidumpContext(); + + // Returns an MD_CONTEXT_* value such as MD_CONTEXT_X86 or MD_CONTEXT_PPC + // identifying the CPU type that the context was collected from. The + // returned value will identify the CPU only, and will have any other + // MD_CONTEXT_* bits masked out. Returns 0 on failure. + u_int32_t GetContextCPU() const; + + // Returns raw CPU-specific context data for the named CPU type. If the + // context data does not match the CPU type or does not exist, returns + // NULL. + const MDRawContextAMD64* GetContextAMD64() const; + const MDRawContextARM* GetContextARM() const; + const MDRawContextPPC* GetContextPPC() const; + const MDRawContextSPARC* GetContextSPARC() const; + const MDRawContextX86* GetContextX86() const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class MinidumpThread; + friend class MinidumpException; + + explicit MinidumpContext(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + // Free the CPU-specific context structure. + void FreeContext(); + + // If the minidump contains a SYSTEM_INFO_STREAM, makes sure that the + // system info stream gives an appropriate CPU type matching the context + // CPU type in context_cpu_type. Returns false if the CPU type does not + // match. Returns true if the CPU type matches or if the minidump does + // not contain a system info stream. + bool CheckAgainstSystemInfo(u_int32_t context_cpu_type); + + // Store this separately because of the weirdo AMD64 context + u_int32_t context_flags_; + + // The CPU-specific context structure. + union { + MDRawContextBase* base; + MDRawContextX86* x86; + MDRawContextPPC* ppc; + MDRawContextAMD64* amd64; + // on Solaris SPARC, sparc is defined as a numeric constant, + // so variables can NOT be named as sparc + MDRawContextSPARC* ctx_sparc; + MDRawContextARM* arm; + } context_; +}; + + +// MinidumpMemoryRegion does not wrap any MDRaw structure, and only contains +// a reference to an MDMemoryDescriptor. This object is intended to wrap +// portions of a minidump file that contain memory dumps. In normal +// minidumps, each MinidumpThread owns a MinidumpMemoryRegion corresponding +// to the thread's stack memory. MinidumpMemoryList also gives access to +// memory regions in its list as MinidumpMemoryRegions. This class +// adheres to MemoryRegion so that it may be used as a data provider to +// the Stackwalker family of classes. +class MinidumpMemoryRegion : public MinidumpObject, + public MemoryRegion { + public: + virtual ~MinidumpMemoryRegion(); + + static void set_max_bytes(u_int32_t max_bytes) { max_bytes_ = max_bytes; } + static u_int32_t max_bytes() { return max_bytes_; } + + // Returns a pointer to the base of the memory region. Returns the + // cached value if available, otherwise, reads the minidump file and + // caches the memory region. + const u_int8_t* GetMemory() const; + + // The address of the base of the memory region. + u_int64_t GetBase() const; + + // The size, in bytes, of the memory region. + u_int32_t GetSize() const; + + // Frees the cached memory region, if cached. + void FreeMemory(); + + // Obtains the value of memory at the pointer specified by address. + bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const; + bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const; + bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const; + bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class MinidumpThread; + friend class MinidumpMemoryList; + + explicit MinidumpMemoryRegion(Minidump* minidump); + + // Identify the base address and size of the memory region, and the + // location it may be found in the minidump file. + void SetDescriptor(MDMemoryDescriptor* descriptor); + + // Implementation for GetMemoryAtAddress + template bool GetMemoryAtAddressInternal(u_int64_t address, + T* value) const; + + // The largest memory region that will be read from a minidump. The + // default is 1MB. + static u_int32_t max_bytes_; + + // Base address and size of the memory region, and its position in the + // minidump file. + MDMemoryDescriptor* descriptor_; + + // Cached memory. + mutable vector* memory_; +}; + + +// MinidumpThread contains information about a thread of execution, +// including a snapshot of the thread's stack and CPU context. For +// the thread that caused an exception, the context carried by +// MinidumpException is probably desired instead of the CPU context +// provided here. +class MinidumpThread : public MinidumpObject { + public: + virtual ~MinidumpThread(); + + const MDRawThread* thread() const { return valid_ ? &thread_ : NULL; } + MinidumpMemoryRegion* GetMemory(); + MinidumpContext* GetContext(); + + // The thread ID is used to determine if a thread is the exception thread, + // so a special getter is provided to retrieve this data from the + // MDRawThread structure. Returns false if the thread ID cannot be + // determined. + bool GetThreadID(u_int32_t *thread_id) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + // These objects are managed by MinidumpThreadList. + friend class MinidumpThreadList; + + explicit MinidumpThread(Minidump* minidump); + + // This works like MinidumpStream::Read, but is driven by + // MinidumpThreadList. No size checking is done, because + // MinidumpThreadList handles that directly. + bool Read(); + + MDRawThread thread_; + MinidumpMemoryRegion* memory_; + MinidumpContext* context_; +}; + + +// MinidumpThreadList contains all of the threads (as MinidumpThreads) in +// a process. +class MinidumpThreadList : public MinidumpStream { + public: + virtual ~MinidumpThreadList(); + + static void set_max_threads(u_int32_t max_threads) { + max_threads_ = max_threads; + } + static u_int32_t max_threads() { return max_threads_; } + + unsigned int thread_count() const { + return valid_ ? thread_count_ : 0; + } + + // Sequential access to threads. + MinidumpThread* GetThreadAtIndex(unsigned int index) const; + + // Random access to threads. + MinidumpThread* GetThreadByID(u_int32_t thread_id); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + typedef map IDToThreadMap; + typedef vector MinidumpThreads; + + static const u_int32_t kStreamType = MD_THREAD_LIST_STREAM; + + explicit MinidumpThreadList(Minidump* aMinidump); + + bool Read(u_int32_t aExpectedSize); + + // The largest number of threads that will be read from a minidump. The + // default is 256. + static u_int32_t max_threads_; + + // Access to threads using the thread ID as the key. + IDToThreadMap id_to_thread_map_; + + // The list of threads. + MinidumpThreads* threads_; + u_int32_t thread_count_; +}; + + +// MinidumpModule wraps MDRawModule, which contains information about loaded +// code modules. Access is provided to various data referenced indirectly +// by MDRawModule, such as the module's name and a specification for where +// to locate debugging information for the module. +class MinidumpModule : public MinidumpObject, + public CodeModule { + public: + virtual ~MinidumpModule(); + + static void set_max_cv_bytes(u_int32_t max_cv_bytes) { + max_cv_bytes_ = max_cv_bytes; + } + static u_int32_t max_cv_bytes() { return max_cv_bytes_; } + + static void set_max_misc_bytes(u_int32_t max_misc_bytes) { + max_misc_bytes_ = max_misc_bytes; + } + static u_int32_t max_misc_bytes() { return max_misc_bytes_; } + + const MDRawModule* module() const { return valid_ ? &module_ : NULL; } + + // CodeModule implementation + virtual u_int64_t base_address() const { + return valid_ ? module_.base_of_image : static_cast(-1); + } + virtual u_int64_t size() const { return valid_ ? module_.size_of_image : 0; } + virtual string code_file() const; + virtual string code_identifier() const; + virtual string debug_file() const; + virtual string debug_identifier() const; + virtual string version() const; + virtual const CodeModule* Copy() const; + + // The CodeView record, which contains information to locate the module's + // debugging information (pdb). This is returned as u_int8_t* because + // the data can be of types MDCVInfoPDB20* or MDCVInfoPDB70*, or it may be + // of a type unknown to Breakpad, in which case the raw data will still be + // returned but no byte-swapping will have been performed. Check the + // record's signature in the first four bytes to differentiate between + // the various types. Current toolchains generate modules which carry + // MDCVInfoPDB70 by default. Returns a pointer to the CodeView record on + // success, and NULL on failure. On success, the optional |size| argument + // is set to the size of the CodeView record. + const u_int8_t* GetCVRecord(u_int32_t* size); + + // The miscellaneous debug record, which is obsolete. Current toolchains + // do not generate this type of debugging information (dbg), and this + // field is not expected to be present. Returns a pointer to the debugging + // record on success, and NULL on failure. On success, the optional |size| + // argument is set to the size of the debugging record. + const MDImageDebugMisc* GetMiscRecord(u_int32_t* size); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + // These objects are managed by MinidumpModuleList. + friend class MinidumpModuleList; + + explicit MinidumpModule(Minidump* minidump); + + // This works like MinidumpStream::Read, but is driven by + // MinidumpModuleList. No size checking is done, because + // MinidumpModuleList handles that directly. + bool Read(); + + // Reads indirectly-referenced data, including the module name, CodeView + // record, and miscellaneous debugging record. This is necessary to allow + // MinidumpModuleList to fully construct MinidumpModule objects without + // requiring seeks to read a contiguous set of MinidumpModule objects. + // All auxiliary data should be available when Read is called, in order to + // allow the CodeModule getters to be const methods. + bool ReadAuxiliaryData(); + + // The largest number of bytes that will be read from a minidump for a + // CodeView record or miscellaneous debugging record, respectively. The + // default for each is 1024. + static u_int32_t max_cv_bytes_; + static u_int32_t max_misc_bytes_; + + // True after a successful Read. This is different from valid_, which is + // not set true until ReadAuxiliaryData also completes successfully. + // module_valid_ is only used by ReadAuxiliaryData and the functions it + // calls to determine whether the object is ready for auxiliary data to + // be read. + bool module_valid_; + + // True if debug info was read from the module. Certain modules + // may contain debug records in formats we don't support, + // so we can just set this to false to ignore them. + bool has_debug_info_; + + MDRawModule module_; + + // Cached module name. + const string* name_; + + // Cached CodeView record - this is MDCVInfoPDB20 or (likely) + // MDCVInfoPDB70, or possibly something else entirely. Stored as a u_int8_t + // because the structure contains a variable-sized string and its exact + // size cannot be known until it is processed. + vector* cv_record_; + + // If cv_record_ is present, cv_record_signature_ contains a copy of the + // CodeView record's first four bytes, for ease of determinining the + // type of structure that cv_record_ contains. + u_int32_t cv_record_signature_; + + // Cached MDImageDebugMisc (usually not present), stored as u_int8_t + // because the structure contains a variable-sized string and its exact + // size cannot be known until it is processed. + vector* misc_record_; +}; + + +// MinidumpModuleList contains all of the loaded code modules for a process +// in the form of MinidumpModules. It maintains a map of these modules +// so that it may easily provide a code module corresponding to a specific +// address. +class MinidumpModuleList : public MinidumpStream, + public CodeModules { + public: + virtual ~MinidumpModuleList(); + + static void set_max_modules(u_int32_t max_modules) { + max_modules_ = max_modules; + } + static u_int32_t max_modules() { return max_modules_; } + + // CodeModules implementation. + virtual unsigned int module_count() const { + return valid_ ? module_count_ : 0; + } + virtual const MinidumpModule* GetModuleForAddress(u_int64_t address) const; + virtual const MinidumpModule* GetMainModule() const; + virtual const MinidumpModule* GetModuleAtSequence( + unsigned int sequence) const; + virtual const MinidumpModule* GetModuleAtIndex(unsigned int index) const; + virtual const CodeModules* Copy() const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + typedef vector MinidumpModules; + + static const u_int32_t kStreamType = MD_MODULE_LIST_STREAM; + + explicit MinidumpModuleList(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + // The largest number of modules that will be read from a minidump. The + // default is 1024. + static u_int32_t max_modules_; + + // Access to modules using addresses as the key. + RangeMap *range_map_; + + MinidumpModules *modules_; + u_int32_t module_count_; +}; + + +// MinidumpMemoryList corresponds to a minidump's MEMORY_LIST_STREAM stream, +// which references the snapshots of all of the memory regions contained +// within the minidump. For a normal minidump, this includes stack memory +// (also referenced by each MinidumpThread, in fact, the MDMemoryDescriptors +// here and in MDRawThread both point to exactly the same data in a +// minidump file, conserving space), as well as a 256-byte snapshot of memory +// surrounding the instruction pointer in the case of an exception. Other +// types of minidumps may contain significantly more memory regions. Full- +// memory minidumps contain all of a process' mapped memory. +class MinidumpMemoryList : public MinidumpStream { + public: + virtual ~MinidumpMemoryList(); + + static void set_max_regions(u_int32_t max_regions) { + max_regions_ = max_regions; + } + static u_int32_t max_regions() { return max_regions_; } + + unsigned int region_count() const { return valid_ ? region_count_ : 0; } + + // Sequential access to memory regions. + MinidumpMemoryRegion* GetMemoryRegionAtIndex(unsigned int index); + + // Random access to memory regions. Returns the region encompassing + // the address identified by address. + MinidumpMemoryRegion* GetMemoryRegionForAddress(u_int64_t address); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + typedef vector MemoryDescriptors; + typedef vector MemoryRegions; + + static const u_int32_t kStreamType = MD_MEMORY_LIST_STREAM; + + explicit MinidumpMemoryList(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + // The largest number of memory regions that will be read from a minidump. + // The default is 256. + static u_int32_t max_regions_; + + // Access to memory regions using addresses as the key. + RangeMap *range_map_; + + // The list of descriptors. This is maintained separately from the list + // of regions, because MemoryRegion doesn't own its MemoryDescriptor, it + // maintains a pointer to it. descriptors_ provides the storage for this + // purpose. + MemoryDescriptors *descriptors_; + + // The list of regions. + MemoryRegions *regions_; + u_int32_t region_count_; +}; + + +// MinidumpException wraps MDRawExceptionStream, which contains information +// about the exception that caused the minidump to be generated, if the +// minidump was generated in an exception handler called as a result of +// an exception. It also provides access to a MinidumpContext object, +// which contains the CPU context for the exception thread at the time +// the exception occurred. +class MinidumpException : public MinidumpStream { + public: + virtual ~MinidumpException(); + + const MDRawExceptionStream* exception() const { + return valid_ ? &exception_ : NULL; + } + + // The thread ID is used to determine if a thread is the exception thread, + // so a special getter is provided to retrieve this data from the + // MDRawExceptionStream structure. Returns false if the thread ID cannot + // be determined. + bool GetThreadID(u_int32_t *thread_id) const; + + MinidumpContext* GetContext(); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_EXCEPTION_STREAM; + + explicit MinidumpException(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + MDRawExceptionStream exception_; + MinidumpContext* context_; +}; + +// MinidumpAssertion wraps MDRawAssertionInfo, which contains information +// about an assertion that caused the minidump to be generated. +class MinidumpAssertion : public MinidumpStream { + public: + virtual ~MinidumpAssertion(); + + const MDRawAssertionInfo* assertion() const { + return valid_ ? &assertion_ : NULL; + } + + string expression() const { + return valid_ ? expression_ : ""; + } + + string function() const { + return valid_ ? function_ : ""; + } + + string file() const { + return valid_ ? file_ : ""; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_ASSERTION_INFO_STREAM; + + explicit MinidumpAssertion(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + MDRawAssertionInfo assertion_; + string expression_; + string function_; + string file_; +}; + + +// MinidumpSystemInfo wraps MDRawSystemInfo and provides information about +// the system on which the minidump was generated. See also MinidumpMiscInfo. +class MinidumpSystemInfo : public MinidumpStream { + public: + virtual ~MinidumpSystemInfo(); + + const MDRawSystemInfo* system_info() const { + return valid_ ? &system_info_ : NULL; + } + + // GetOS and GetCPU return textual representations of the operating system + // and CPU that produced the minidump. Unlike most other Minidump* methods, + // they return string objects, not weak pointers. Defined values for + // GetOS() are "mac", "windows", and "linux". Defined values for GetCPU + // are "x86" and "ppc". These methods return an empty string when their + // values are unknown. + string GetOS(); + string GetCPU(); + + // I don't know what CSD stands for, but this field is documented as + // returning a textual representation of the OS service pack. On other + // platforms, this provides additional information about an OS version + // level beyond major.minor.micro. Returns NULL if unknown. + const string* GetCSDVersion(); + + // If a CPU vendor string can be determined, returns a pointer to it, + // otherwise, returns NULL. CPU vendor strings can be determined from + // x86 CPUs with CPUID 0. + const string* GetCPUVendor(); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_SYSTEM_INFO_STREAM; + + explicit MinidumpSystemInfo(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + MDRawSystemInfo system_info_; + + // Textual representation of the OS service pack, for minidumps produced + // by MiniDumpWriteDump on Windows. + const string* csd_version_; + + // A string identifying the CPU vendor, if known. + const string* cpu_vendor_; +}; + + +// MinidumpMiscInfo wraps MDRawMiscInfo and provides information about +// the process that generated the minidump, and optionally additional system +// information. See also MinidumpSystemInfo. +class MinidumpMiscInfo : public MinidumpStream { + public: + const MDRawMiscInfo* misc_info() const { + return valid_ ? &misc_info_ : NULL; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_MISC_INFO_STREAM; + + explicit MinidumpMiscInfo(Minidump* minidump_); + + bool Read(u_int32_t expected_size_); + + MDRawMiscInfo misc_info_; +}; + + +// MinidumpBreakpadInfo wraps MDRawBreakpadInfo, which is an optional stream in +// a minidump that provides additional information about the process state +// at the time the minidump was generated. +class MinidumpBreakpadInfo : public MinidumpStream { + public: + const MDRawBreakpadInfo* breakpad_info() const { + return valid_ ? &breakpad_info_ : NULL; + } + + // These thread IDs are used to determine if threads deserve special + // treatment, so special getters are provided to retrieve this data from + // the MDRawBreakpadInfo structure. The getters return false if the thread + // IDs cannot be determined. + bool GetDumpThreadID(u_int32_t *thread_id) const; + bool GetRequestingThreadID(u_int32_t *thread_id) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_BREAKPAD_INFO_STREAM; + + explicit MinidumpBreakpadInfo(Minidump* minidump_); + + bool Read(u_int32_t expected_size_); + + MDRawBreakpadInfo breakpad_info_; +}; + +// MinidumpMemoryInfo wraps MDRawMemoryInfo, which provides information +// about mapped memory regions in a process, including their ranges +// and protection. +class MinidumpMemoryInfo : public MinidumpObject { + public: + const MDRawMemoryInfo* info() const { return valid_ ? &memory_info_ : NULL; } + + // The address of the base of the memory region. + u_int64_t GetBase() const { return valid_ ? memory_info_.base_address : 0; } + + // The size, in bytes, of the memory region. + u_int32_t GetSize() const { return valid_ ? memory_info_.region_size : 0; } + + // Return true if the memory protection allows execution. + bool IsExecutable() const; + + // Return true if the memory protection allows writing. + bool IsWritable() const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + // These objects are managed by MinidumpMemoryInfoList. + friend class MinidumpMemoryInfoList; + + explicit MinidumpMemoryInfo(Minidump* minidump); + + // This works like MinidumpStream::Read, but is driven by + // MinidumpMemoryInfoList. No size checking is done, because + // MinidumpMemoryInfoList handles that directly. + bool Read(); + + MDRawMemoryInfo memory_info_; +}; + +// MinidumpMemoryInfoList contains a list of information about +// mapped memory regions for a process in the form of MDRawMemoryInfo. +// It maintains a map of these structures so that it may easily provide +// info corresponding to a specific address. +class MinidumpMemoryInfoList : public MinidumpStream { + public: + virtual ~MinidumpMemoryInfoList(); + + unsigned int info_count() const { return valid_ ? info_count_ : 0; } + + const MinidumpMemoryInfo* GetMemoryInfoForAddress(u_int64_t address) const; + const MinidumpMemoryInfo* GetMemoryInfoAtIndex(unsigned int index) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + typedef vector MinidumpMemoryInfos; + + static const u_int32_t kStreamType = MD_MEMORY_INFO_LIST_STREAM; + + explicit MinidumpMemoryInfoList(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + // Access to memory info using addresses as the key. + RangeMap *range_map_; + + MinidumpMemoryInfos* infos_; + u_int32_t info_count_; +}; + + +// Minidump is the user's interface to a minidump file. It wraps MDRawHeader +// and provides access to the minidump's top-level stream directory. +class Minidump { + public: + // path is the pathname of a file containing the minidump. + explicit Minidump(const string& path); + // input is an istream wrapping minidump data. Minidump holds a + // weak pointer to input, and the caller must ensure that the stream + // is valid as long as the Minidump object is. + explicit Minidump(std::istream& input); + + virtual ~Minidump(); + + // path may be empty if the minidump was not opened from a file + virtual string path() const { + return path_; + } + static void set_max_streams(u_int32_t max_streams) { + max_streams_ = max_streams; + } + static u_int32_t max_streams() { return max_streams_; } + + static void set_max_string_length(u_int32_t max_string_length) { + max_string_length_ = max_string_length; + } + static u_int32_t max_string_length() { return max_string_length_; } + + virtual const MDRawHeader* header() const { return valid_ ? &header_ : NULL; } + + // Reads the minidump file's header and top-level stream directory. + // The minidump is expected to be positioned at the beginning of the + // header. Read() sets up the stream list and map, and validates the + // Minidump object. + virtual bool Read(); + + // The next set of methods are stubs that call GetStream. They exist to + // force code generation of the templatized API within the module, and + // to avoid exposing an ugly API (GetStream needs to accept a garbage + // parameter). + virtual MinidumpThreadList* GetThreadList(); + MinidumpModuleList* GetModuleList(); + MinidumpMemoryList* GetMemoryList(); + MinidumpException* GetException(); + MinidumpAssertion* GetAssertion(); + MinidumpSystemInfo* GetSystemInfo(); + MinidumpMiscInfo* GetMiscInfo(); + MinidumpBreakpadInfo* GetBreakpadInfo(); + MinidumpMemoryInfoList* GetMemoryInfoList(); + + // The next set of methods are provided for users who wish to access + // data in minidump files directly, while leveraging the rest of + // this class and related classes to handle the basic minidump + // structure and known stream types. + + unsigned int GetDirectoryEntryCount() const { + return valid_ ? header_.stream_count : 0; + } + const MDRawDirectory* GetDirectoryEntryAtIndex(unsigned int index) const; + + // The next 2 methods are lower-level I/O routines. They use fd_. + + // Reads count bytes from the minidump at the current position into + // the storage area pointed to by bytes. bytes must be of sufficient + // size. After the read, the file position is advanced by count. + bool ReadBytes(void* bytes, size_t count); + + // Sets the position of the minidump file to offset. + bool SeekSet(off_t offset); + + // Returns the current position of the minidump file. + off_t Tell(); + + // The next 2 methods are medium-level I/O routines. + + // ReadString returns a string which is owned by the caller! offset + // specifies the offset that a length-encoded string is stored at in the + // minidump file. + string* ReadString(off_t offset); + + // SeekToStreamType positions the file at the beginning of a stream + // identified by stream_type, and informs the caller of the stream's + // length by setting *stream_length. Because stream_map maps each stream + // type to only one stream in the file, this might mislead the user into + // thinking that the stream that this seeks to is the only stream with + // type stream_type. That can't happen for streams that these classes + // deal with directly, because they're only supposed to be present in the + // file singly, and that's verified when stream_map_ is built. Users who + // are looking for other stream types should be aware of this + // possibility, and consider using GetDirectoryEntryAtIndex (possibly + // with GetDirectoryEntryCount) if expecting multiple streams of the same + // type in a single minidump file. + bool SeekToStreamType(u_int32_t stream_type, u_int32_t* stream_length); + + bool swap() const { return valid_ ? swap_ : false; } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + // MinidumpStreamInfo is used in the MinidumpStreamMap. It lets + // the Minidump object locate interesting streams quickly, and + // provides a convenient place to stash MinidumpStream objects. + struct MinidumpStreamInfo { + MinidumpStreamInfo() : stream_index(0), stream(NULL) {} + ~MinidumpStreamInfo() { delete stream; } + + // Index into the MinidumpDirectoryEntries vector + unsigned int stream_index; + + // Pointer to the stream if cached, or NULL if not yet populated + MinidumpStream* stream; + }; + + typedef vector MinidumpDirectoryEntries; + typedef map MinidumpStreamMap; + + template T* GetStream(T** stream); + + // Opens the minidump file, or if already open, seeks to the beginning. + bool Open(); + + // The largest number of top-level streams that will be read from a minidump. + // Note that streams are only read (and only consume memory) as needed, + // when directed by the caller. The default is 128. + static u_int32_t max_streams_; + + // The maximum length of a UTF-16 string that will be read from a minidump + // in 16-bit words. The default is 1024. UTF-16 strings are converted + // to UTF-8 when stored in memory, and each UTF-16 word will be represented + // by as many as 3 bytes in UTF-8. + static unsigned int max_string_length_; + + MDRawHeader header_; + + // The list of streams. + MinidumpDirectoryEntries* directory_; + + // Access to streams using the stream type as the key. + MinidumpStreamMap* stream_map_; + + // The pathname of the minidump file to process, set in the constructor. + // This may be empty if the minidump was opened directly from a stream. + const string path_; + + // The stream for all file I/O. Used by ReadBytes and SeekSet. + // Set based on the path in Open, or directly in the constructor. + std::istream* stream_; + + // swap_ is true if the minidump file should be byte-swapped. If the + // minidump was produced by a CPU that is other-endian than the CPU + // processing the minidump, this will be true. If the two CPUs are + // same-endian, this will be false. + bool swap_; + + // Validity of the Minidump structure, false immediately after + // construction or after a failed Read(); true following a successful + // Read(). + bool valid_; +}; + + +} // namespace google_breakpad + + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump_processor.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump_processor.h new file mode 100644 index 0000000..3ed0ebf --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/minidump_processor.h @@ -0,0 +1,169 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ + +#include +#include +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::string; + +class Minidump; +class ProcessState; +class SourceLineResolverInterface; +class SymbolSupplier; +struct SystemInfo; +// Return type for Process() +enum ProcessResult { + PROCESS_OK, // The minidump was + // processed + // successfully. + + PROCESS_ERROR_MINIDUMP_NOT_FOUND, // The minidump file + // was not found. + + PROCESS_ERROR_NO_MINIDUMP_HEADER, // The minidump file + // had no header + + PROCESS_ERROR_NO_THREAD_LIST, // The minidump file + // had no thread list. + + PROCESS_ERROR_GETTING_THREAD, // There was an error + // getting one + // thread's data from + // the minidump. + + PROCESS_ERROR_GETTING_THREAD_ID, // There was an error + // getting a thread id + // from the thread's + // data. + + PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than + // one requesting + // thread. + + PROCESS_ERROR_NO_MEMORY_FOR_THREAD, // A thread had no + // memory region. + + PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD, // We couldn't + // determine the + // StackWalker to walk + // the minidump's + // threads. + + PROCESS_SYMBOL_SUPPLIER_INTERRUPTED // The minidump + // processing was + // interrupted by the + // SymbolSupplier(not + // fatal) +}; + +class MinidumpProcessor { + public: + // Initializes this MinidumpProcessor. supplier should be an + // implementation of the SymbolSupplier abstract base class. + MinidumpProcessor(SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + // Initializes the MinidumpProcessor with the option of + // enabling the exploitability framework to analyze dumps + // for probable security relevance. + MinidumpProcessor(SymbolSupplier *supplier, + SourceLineResolverInterface *resolver, + bool enable_exploitability); + + ~MinidumpProcessor(); + + // Processes the minidump file and fills process_state with the result. + ProcessResult Process(const string &minidump_file, + ProcessState *process_state); + + // Processes the minidump structure and fills process_state with the + // result. + ProcessResult Process(Minidump *minidump, + ProcessState *process_state); + // Populates the cpu_* fields of the |info| parameter with textual + // representations of the CPU type that the minidump in |dump| was + // produced on. Returns false if this information is not available in + // the minidump. + static bool GetCPUInfo(Minidump *dump, SystemInfo *info); + + // Populates the os_* fields of the |info| parameter with textual + // representations of the operating system that the minidump in |dump| + // was produced on. Returns false if this information is not available in + // the minidump. + static bool GetOSInfo(Minidump *dump, SystemInfo *info); + + // Returns a textual representation of the reason that a crash occurred, + // if the minidump in dump was produced as a result of a crash. Returns + // an empty string if this information cannot be determined. If address + // is non-NULL, it will be set to contain the address that caused the + // exception, if this information is available. This will be a code + // address when the crash was caused by problems such as illegal + // instructions or divisions by zero, or a data address when the crash + // was caused by a memory access violation. + static string GetCrashReason(Minidump *dump, u_int64_t *address); + + // This function returns true if the passed-in error code is + // something unrecoverable(i.e. retry should not happen). For + // instance, if the minidump is corrupt, then it makes no sense to + // retry as we won't be able to glean additional information. + // However, as an example of the other case, the symbol supplier can + // return an error code indicating it was 'interrupted', which can + // happen of the symbols are fetched from a remote store, and a + // retry might be successful later on. + // You should not call this method with PROCESS_OK! Test for + // that separately before calling this. + static bool IsErrorUnrecoverable(ProcessResult p) { + assert(p != PROCESS_OK); + return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED); + } + + // Returns a textual representation of an assertion included + // in the minidump. Returns an empty string if this information + // does not exist or cannot be determined. + static string GetAssertion(Minidump *dump); + + private: + SymbolSupplier *supplier_; + SourceLineResolverInterface *resolver_; + + // This flag enables the exploitability scanner which attempts to + // guess how likely it is that the crash represents an exploitable + // memory corruption issue. + bool enable_exploitability_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/process_state.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/process_state.h new file mode 100644 index 0000000..f3f2ec4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/process_state.h @@ -0,0 +1,168 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// process_state.h: A snapshot of a process, in a fully-digested state. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__ + +#include +#include +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/system_info.h" +#include "google_breakpad/processor/minidump.h" + +namespace google_breakpad { + +using std::string; +using std::vector; + +class CallStack; +class CodeModules; + +enum ExploitabilityRating { + EXPLOITABILITY_HIGH, // The crash likely represents + // a exploitable memory corruption + // vulnerability. + + EXPLOITABLITY_MEDIUM, // The crash appears to corrupt + // memory in a way which may be + // exploitable in some situations. + + EXPLOITABILITY_LOW, // The crash either does not corrupt + // memory directly or control over + // the effected data is limited. The + // issue may still be exploitable + // on certain platforms or situations. + + EXPLOITABILITY_INTERESTING, // The crash does not appear to be + // directly exploitable. However it + // represents a condition which should + // be furthur analyzed. + + EXPLOITABILITY_NONE, // The crash does not appear to represent + // an exploitable condition. + + EXPLOITABILITY_NOT_ANALYZED, // The crash was not analyzed for + // exploitability because the engine + // was disabled. + + EXPLOITABILITY_ERR_NOENGINE, // The supplied minidump's platform does + // not have a exploitability engine + // associated with it. + + EXPLOITABILITY_ERR_PROCESSING // An error occured within the + // exploitability engine and no rating + // was calculated. +}; + +class ProcessState { + public: + ProcessState() : modules_(NULL) { Clear(); } + ~ProcessState(); + + // Resets the ProcessState to its default values + void Clear(); + + // Accessors. See the data declarations below. + u_int32_t time_date_stamp() const { return time_date_stamp_; } + bool crashed() const { return crashed_; } + string crash_reason() const { return crash_reason_; } + u_int64_t crash_address() const { return crash_address_; } + string assertion() const { return assertion_; } + int requesting_thread() const { return requesting_thread_; } + const vector* threads() const { return &threads_; } + const vector* thread_memory_regions() const { + return &thread_memory_regions_; + } + const SystemInfo* system_info() const { return &system_info_; } + const CodeModules* modules() const { return modules_; } + ExploitabilityRating exploitability() const { return exploitability_; } + + private: + // MinidumpProcessor is responsible for building ProcessState objects. + friend class MinidumpProcessor; + + // The time-date stamp of the minidump (time_t format) + u_int32_t time_date_stamp_; + + // True if the process crashed, false if the dump was produced outside + // of an exception handler. + bool crashed_; + + // If the process crashed, the type of crash. OS- and possibly CPU- + // specific. For example, "EXCEPTION_ACCESS_VIOLATION" (Windows), + // "EXC_BAD_ACCESS / KERN_INVALID_ADDRESS" (Mac OS X), "SIGSEGV" + // (other Unix). + string crash_reason_; + + // If the process crashed, and if crash_reason implicates memory, + // the memory address that caused the crash. For data access errors, + // this will be the data address that caused the fault. For code errors, + // this will be the address of the instruction that caused the fault. + u_int64_t crash_address_; + + // If there was an assertion that was hit, a textual representation + // of that assertion, possibly including the file and line at which + // it occurred. + string assertion_; + + // The index of the thread that requested a dump be written in the + // threads vector. If a dump was produced as a result of a crash, this + // will point to the thread that crashed. If the dump was produced as + // by user code without crashing, and the dump contains extended Breakpad + // information, this will point to the thread that requested the dump. + // If the dump was not produced as a result of an exception and no + // extended Breakpad information is present, this field will be set to -1, + // indicating that the dump thread is not available. + int requesting_thread_; + + // Stacks for each thread (except possibly the exception handler + // thread) at the time of the crash. + vector threads_; + vector thread_memory_regions_; + + // OS and CPU information. + SystemInfo system_info_; + + // The modules that were loaded into the process represented by the + // ProcessState. + const CodeModules *modules_; + + // The exploitability rating as determined by the exploitability + // engine. When the exploitability engine is not enabled this + // defaults to EXPLOITABILITY_NONE. + ExploitabilityRating exploitability_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_base.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_base.h new file mode 100644 index 0000000..efa76e7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_base.h @@ -0,0 +1,118 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// source_line_resolver_base.h: SourceLineResolverBase, an (incomplete) +// implementation of SourceLineResolverInterface. It serves as a common base +// class for concrete implementations: FastSourceLineResolver and +// BasicSourceLineResolver. It is designed for refactoring that removes +// code redundancy in the two concrete source line resolver classes. +// +// See "google_breakpad/processor/source_line_resolver_interface.h" for more +// documentation. + +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ + +#include +#include + +#include "google_breakpad/processor/source_line_resolver_interface.h" + +namespace google_breakpad { + +using std::map; + +// Forward declaration. +// ModuleFactory is a simple factory interface for creating a Module instance +// at run-time. +class ModuleFactory; + +class SourceLineResolverBase : public SourceLineResolverInterface { + public: + // Read the symbol_data from a file with given file_name. + // The part of code was originally in BasicSourceLineResolver::Module's + // LoadMap() method. + // Place dynamically allocated heap buffer in symbol_data. Caller has the + // ownership of the buffer, and should call delete [] to free the buffer. + static bool ReadSymbolFile(char **symbol_data, const string &file_name); + + protected: + // Users are not allowed create SourceLineResolverBase instance directly. + SourceLineResolverBase(ModuleFactory *module_factory); + virtual ~SourceLineResolverBase(); + + // Virtual methods inherited from SourceLineResolverInterface. + virtual bool LoadModule(const CodeModule *module, const string &map_file); + virtual bool LoadModuleUsingMapBuffer(const CodeModule *module, + const string &map_buffer); + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module, + char *memory_buffer); + virtual bool ShouldDeleteMemoryBufferAfterLoadModule(); + virtual void UnloadModule(const CodeModule *module); + virtual bool HasModule(const CodeModule *module); + virtual void FillSourceLineInfo(StackFrame *frame); + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame); + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame); + + // Nested structs and classes. + struct Line; + struct Function; + struct PublicSymbol; + struct CompareString { + bool operator()(const string &s1, const string &s2) const; + }; + // Module is an interface for an in-memory symbol file. + class Module; + class AutoFileCloser; + + // All of the modules that are loaded. + typedef map ModuleMap; + ModuleMap *modules_; + + // All of heap-allocated buffers that are owned locally by resolver. + typedef std::map MemoryMap; + MemoryMap *memory_buffers_; + + // Creates a concrete module at run-time. + ModuleFactory *module_factory_; + + private: + // ModuleFactory needs to have access to protected type Module. + friend class ModuleFactory; + + // Disallow unwanted copy ctor and assignment operator + SourceLineResolverBase(const SourceLineResolverBase&); + void operator=(const SourceLineResolverBase&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_interface.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_interface.h new file mode 100644 index 0000000..9fe7ef4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/source_line_resolver_interface.h @@ -0,0 +1,111 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Abstract interface to return function/file/line info for a memory address. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ + +#include +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_module.h" + +namespace google_breakpad { + +using std::string; + +struct StackFrame; +struct WindowsFrameInfo; +class CFIFrameInfo; + +class SourceLineResolverInterface { + public: + typedef u_int64_t MemAddr; + + virtual ~SourceLineResolverInterface() {} + + // Adds a module to this resolver, returning true on success. + // + // module should have at least the code_file, debug_file, + // and debug_identifier members populated. + // + // map_file should contain line/address mappings for this module. + virtual bool LoadModule(const CodeModule *module, + const string &map_file) = 0; + // Same as above, but takes the contents of a pre-read map buffer + virtual bool LoadModuleUsingMapBuffer(const CodeModule *module, + const string &map_buffer) = 0; + + // Add an interface to load symbol using C-String data insteading string. + // This is useful in the optimization design for avoiding unnecessary copying + // of symbol data, in order to improve memory efficiency. + // LoadModuleUsingMemoryBuffer() does NOT take ownership of memory_buffer. + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule *module, + char *memory_buffer) = 0; + + // Return true if the memory buffer should be deleted immediately after + // LoadModuleUsingMemoryBuffer(). Return false if the memory buffer has to be + // alive during the lifetime of the corresponding Module. + virtual bool ShouldDeleteMemoryBufferAfterLoadModule() = 0; + + // Request that the specified module be unloaded from this resolver. + // A resolver may choose to ignore such a request. + virtual void UnloadModule(const CodeModule *module) = 0; + + // Returns true if the module has been loaded. + virtual bool HasModule(const CodeModule *module) = 0; + + // Fills in the function_base, function_name, source_file_name, + // and source_line fields of the StackFrame. The instruction and + // module_name fields must already be filled in. + virtual void FillSourceLineInfo(StackFrame *frame) = 0; + + // If Windows stack walking information is available covering + // FRAME's instruction address, return a WindowsFrameInfo structure + // describing it. If the information is not available, returns NULL. + // A NULL return value does not indicate an error. The caller takes + // ownership of any returned WindowsFrameInfo object. + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) = 0; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) = 0; + + protected: + // SourceLineResolverInterface cannot be instantiated except by subclasses + SourceLineResolverInterface() {} +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame.h new file mode 100644 index 0000000..c444993 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame.h @@ -0,0 +1,121 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__ + +#include +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +class CodeModule; + +using std::string; + +struct StackFrame { + // Indicates how well the instruction pointer derived during + // stack walking is trusted. Since the stack walker can resort to + // stack scanning, it can wind up with dubious frames. + // In rough order of "trust metric". + enum FrameTrust { + FRAME_TRUST_NONE, // Unknown + FRAME_TRUST_SCAN, // Scanned the stack, found this + FRAME_TRUST_CFI_SCAN, // Scanned the stack using call frame info, found this + FRAME_TRUST_FP, // Derived from frame pointer + FRAME_TRUST_CFI, // Derived from call frame info + FRAME_TRUST_CONTEXT // Given as instruction pointer in a context + }; + + StackFrame() + : instruction(), + module(NULL), + function_name(), + function_base(), + source_file_name(), + source_line(), + source_line_base(), + trust(FRAME_TRUST_NONE) {} + virtual ~StackFrame() {} + + // Return a string describing how this stack frame was found + // by the stackwalker. + string trust_description() const { + switch (trust) { + case StackFrame::FRAME_TRUST_CONTEXT: + return "given as instruction pointer in context"; + case StackFrame::FRAME_TRUST_CFI: + return "call frame info"; + case StackFrame::FRAME_TRUST_CFI_SCAN: + return "call frame info with scanning"; + case StackFrame::FRAME_TRUST_FP: + return "previous frame's frame pointer"; + case StackFrame::FRAME_TRUST_SCAN: + return "stack scanning"; + default: + return "unknown"; + } + }; + + // The program counter location as an absolute virtual address. For the + // innermost called frame in a stack, this will be an exact program counter + // or instruction pointer value. For all other frames, this will be within + // the instruction that caused execution to branch to a called function, + // but may not necessarily point to the exact beginning of that instruction. + u_int64_t instruction; + + // The module in which the instruction resides. + const CodeModule *module; + + // The function name, may be omitted if debug symbols are not available. + string function_name; + + // The start address of the function, may be omitted if debug symbols + // are not available. + u_int64_t function_base; + + // The source file name, may be omitted if debug symbols are not available. + string source_file_name; + + // The (1-based) source line number, may be omitted if debug symbols are + // not available. + int source_line; + + // The start address of the source line, may be omitted if debug symbols + // are not available. + u_int64_t source_line_base; + + // Amount of trust the stack walker has in the instruction pointer + // of this frame. + FrameTrust trust; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame_cpu.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame_cpu.h new file mode 100644 index 0000000..cc6b014 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/stack_frame_cpu.h @@ -0,0 +1,243 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stack_frame_cpu.h: CPU-specific StackFrame extensions. +// +// These types extend the StackFrame structure to carry CPU-specific register +// state. They are defined in this header instead of stack_frame.h to +// avoid the need to include minidump_format.h when only the generic +// StackFrame type is needed. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stack_frame.h" + +namespace google_breakpad { + +struct WindowsFrameInfo; +class CFIFrameInfo; + +struct StackFrameX86 : public StackFrame { + // ContextValidity has one entry for each relevant hardware pointer + // register (%eip and %esp) and one entry for each general-purpose + // register. It's worthwhile having validity flags for caller-saves + // registers: they are valid in the youngest frame, and such a frame + // might save a callee-saves register in a caller-saves register, but + // SimpleCFIWalker won't touch registers unless they're marked as valid. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_EIP = 1 << 0, + CONTEXT_VALID_ESP = 1 << 1, + CONTEXT_VALID_EBP = 1 << 2, + CONTEXT_VALID_EAX = 1 << 3, + CONTEXT_VALID_EBX = 1 << 4, + CONTEXT_VALID_ECX = 1 << 5, + CONTEXT_VALID_EDX = 1 << 6, + CONTEXT_VALID_ESI = 1 << 7, + CONTEXT_VALID_EDI = 1 << 8, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameX86() + : context(), + context_validity(CONTEXT_VALID_NONE), + windows_frame_info(NULL), + cfi_frame_info(NULL) {} + ~StackFrameX86(); + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextX86 context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; + + // Any stack walking information we found describing this.instruction. + // These may be NULL if there is no such information for that address. + WindowsFrameInfo *windows_frame_info; + CFIFrameInfo *cfi_frame_info; +}; + +struct StackFramePPC : public StackFrame { + // ContextValidity should eventually contain entries for the validity of + // other nonvolatile (callee-save) registers as in + // StackFrameX86::ContextValidity, but the ppc stackwalker doesn't currently + // locate registers other than the ones listed here. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_SRR0 = 1 << 0, + CONTEXT_VALID_GPR1 = 1 << 1, + CONTEXT_VALID_ALL = -1 + }; + + StackFramePPC() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextPPC context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + +struct StackFrameAMD64 : public StackFrame { + // ContextValidity has one entry for each register that we might be able + // to recover. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_RAX = 1 << 0, + CONTEXT_VALID_RDX = 1 << 1, + CONTEXT_VALID_RCX = 1 << 2, + CONTEXT_VALID_RBX = 1 << 3, + CONTEXT_VALID_RSI = 1 << 4, + CONTEXT_VALID_RDI = 1 << 5, + CONTEXT_VALID_RBP = 1 << 6, + CONTEXT_VALID_RSP = 1 << 7, + CONTEXT_VALID_R8 = 1 << 8, + CONTEXT_VALID_R9 = 1 << 9, + CONTEXT_VALID_R10 = 1 << 10, + CONTEXT_VALID_R11 = 1 << 11, + CONTEXT_VALID_R12 = 1 << 12, + CONTEXT_VALID_R13 = 1 << 13, + CONTEXT_VALID_R14 = 1 << 14, + CONTEXT_VALID_R15 = 1 << 15, + CONTEXT_VALID_RIP = 1 << 16, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, which registers are present depends on what + // debugging information we had available. Refer to context_validity. + MDRawContextAMD64 context; + + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + // + // context_validity's type should actually be ContextValidity, but + // we use int instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. + int context_validity; +}; + +struct StackFrameSPARC : public StackFrame { + // to be confirmed + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_PC = 1 << 0, + CONTEXT_VALID_SP = 1 << 1, + CONTEXT_VALID_FP = 1 << 2, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameSPARC() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextSPARC context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + +struct StackFrameARM : public StackFrame { + // A flag for each register we might know. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_R0 = 1 << 0, + CONTEXT_VALID_R1 = 1 << 1, + CONTEXT_VALID_R2 = 1 << 2, + CONTEXT_VALID_R3 = 1 << 3, + CONTEXT_VALID_R4 = 1 << 4, + CONTEXT_VALID_R5 = 1 << 5, + CONTEXT_VALID_R6 = 1 << 6, + CONTEXT_VALID_R7 = 1 << 7, + CONTEXT_VALID_R8 = 1 << 8, + CONTEXT_VALID_R9 = 1 << 9, + CONTEXT_VALID_R10 = 1 << 10, + CONTEXT_VALID_R11 = 1 << 11, + CONTEXT_VALID_R12 = 1 << 12, + CONTEXT_VALID_R13 = 1 << 13, + CONTEXT_VALID_R14 = 1 << 14, + CONTEXT_VALID_R15 = 1 << 15, + CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE, + + // Aliases for registers with dedicated or conventional roles. + CONTEXT_VALID_FP = CONTEXT_VALID_R11, + CONTEXT_VALID_SP = CONTEXT_VALID_R13, + CONTEXT_VALID_LR = CONTEXT_VALID_R14, + CONTEXT_VALID_PC = CONTEXT_VALID_R15 + }; + + StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Return the ContextValidity flag for register rN. + static ContextValidity RegisterValidFlag(int n) { + return ContextValidity(1 << n); + } + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextARM context; + + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + // + // context_validity's type should actually be ContextValidity, but + // we use int instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. + int context_validity; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/stackwalker.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/stackwalker.h new file mode 100644 index 0000000..b184d02 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/stackwalker.h @@ -0,0 +1,203 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker.h: Generic stackwalker. +// +// The Stackwalker class is an abstract base class providing common generic +// methods that apply to stacks from all systems. Specific implementations +// will extend this class by providing GetContextFrame and GetCallerFrame +// methods to fill in system-specific data in a StackFrame structure. +// Stackwalker assembles these StackFrame strucutres into a CallStack. +// +// Author: Mark Mentovai + + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ + +#include +#include +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" + +namespace google_breakpad { + +class CallStack; +class MinidumpContext; +class SourceLineResolverInterface; +struct StackFrame; +class SymbolSupplier; +struct SystemInfo; + +using std::set; + + +class Stackwalker { + public: + virtual ~Stackwalker() {} + + // Populates the given CallStack by calling GetContextFrame and + // GetCallerFrame. The frames are further processed to fill all available + // data. Returns true if the stackwalk completed, or false if it was + // interrupted by SymbolSupplier::GetSymbolFile(). + bool Walk(CallStack *stack); + + // Returns a new concrete subclass suitable for the CPU that a stack was + // generated on, according to the CPU type indicated by the context + // argument. If no suitable concrete subclass exists, returns NULL. + static Stackwalker* StackwalkerForCPU(const SystemInfo *system_info, + MinidumpContext *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + static void set_max_frames(u_int32_t max_frames) { max_frames_ = max_frames; } + static u_int32_t max_frames() { return max_frames_; } + + protected: + // system_info identifies the operating system, NULL or empty if unknown. + // memory identifies a MemoryRegion that provides the stack memory + // for the stack to walk. modules, if non-NULL, is a CodeModules + // object that is used to look up which code module each stack frame is + // associated with. supplier is an optional caller-supplied SymbolSupplier + // implementation. If supplier is NULL, source line info will not be + // resolved. resolver is an instance of SourceLineResolverInterface + // (see source_line_resolver_interface.h and basic_source_line_resolver.h). + // If resolver is NULL, source line info will not be resolved. + Stackwalker(const SystemInfo *system_info, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + // This can be used to filter out potential return addresses when + // the stack walker resorts to stack scanning. + // Returns true if any of: + // * This address is within a loaded module, but we don't have symbols + // for that module. + // * This address is within a loaded module for which we have symbols, + // and falls inside a function in that module. + // Returns false otherwise. + bool InstructionAddressSeemsValid(u_int64_t address); + + template + bool ScanForReturnAddress(InstructionType location_start, + InstructionType *location_found, + InstructionType *ip_found) { + const int kRASearchWords = 30; + return ScanForReturnAddress(location_start, location_found, ip_found, + kRASearchWords); + } + + // Scan the stack starting at location_start, looking for an address + // that looks like a valid instruction pointer. Addresses must + // 1) be contained in the current stack memory + // 2) pass the checks in InstructionAddressSeemsValid + // + // Returns true if a valid-looking instruction pointer was found. + // When returning true, sets location_found to the address at which + // the value was found, and ip_found to the value contained at that + // location in memory. + template + bool ScanForReturnAddress(InstructionType location_start, + InstructionType *location_found, + InstructionType *ip_found, + int searchwords) { + for (InstructionType location = location_start; + location <= location_start + searchwords * sizeof(InstructionType); + location += sizeof(InstructionType)) { + InstructionType ip; + if (!memory_->GetMemoryAtAddress(location, &ip)) + break; + + if (modules_ && modules_->GetModuleForAddress(ip) && + InstructionAddressSeemsValid(ip)) { + + *ip_found = ip; + *location_found = location; + return true; + } + } + // nothing found + return false; + } + + // Information about the system that produced the minidump. Subclasses + // and the SymbolSupplier may find this information useful. + const SystemInfo *system_info_; + + // The stack memory to walk. Subclasses will require this region to + // get information from the stack. + MemoryRegion *memory_; + + // A list of modules, for populating each StackFrame's module information. + // This field is optional and may be NULL. + const CodeModules *modules_; + + protected: + // The SourceLineResolver implementation. + SourceLineResolverInterface *resolver_; + + private: + // Obtains the context frame, the innermost called procedure in a stack + // trace. Returns NULL on failure. GetContextFrame allocates a new + // StackFrame (or StackFrame subclass), ownership of which is taken by + // the caller. + virtual StackFrame* GetContextFrame() = 0; + + // Obtains a caller frame. Each call to GetCallerFrame should return the + // frame that called the last frame returned by GetContextFrame or + // GetCallerFrame. To aid this purpose, stack contains the CallStack + // made of frames that have already been walked. GetCallerFrame should + // return NULL on failure or when there are no more caller frames (when + // the end of the stack has been reached). GetCallerFrame allocates a new + // StackFrame (or StackFrame subclass), ownership of which is taken by + // the caller. + virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0; + + // The optional SymbolSupplier for resolving source line info. + SymbolSupplier *supplier_; + + // A list of modules that we haven't found symbols for. We track + // this in order to avoid repeatedly looking them up again within + // one minidump. + set no_symbol_modules_; + + // The maximum number of frames Stackwalker will walk through. + // This defaults to 1024 to prevent infinite loops. + static u_int32_t max_frames_; +}; + + +} // namespace google_breakpad + + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/symbol_supplier.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/symbol_supplier.h new file mode 100644 index 0000000..8c4d7f7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/symbol_supplier.h @@ -0,0 +1,98 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// The caller may implement the SymbolSupplier abstract base class +// to provide symbols for a given module. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__ + +#include + +namespace google_breakpad { + +using std::string; +class CodeModule; +struct SystemInfo; + +class SymbolSupplier { + public: + // Result type for GetSymbolFile + enum SymbolResult { + // no symbols were found, but continue processing + NOT_FOUND, + + // symbols were found, and the path has been placed in symbol_file + FOUND, + + // stops processing the minidump immediately + INTERRUPT + }; + + virtual ~SymbolSupplier() {} + + // Retrieves the symbol file for the given CodeModule, placing the + // path in symbol_file if successful. system_info contains strings + // identifying the operating system and CPU; SymbolSupplier may use + // to help locate the symbol file. system_info may be NULL or its + // fields may be empty if these values are unknown. symbol_file + // must be a pointer to a valid string + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file) = 0; + // Same as above, except also places symbol data into symbol_data. + // If symbol_data is NULL, the data is not returned. + // TODO(nealsid) Once we have symbol data caching behavior implemented + // investigate making all symbol suppliers implement all methods, + // and make this pure virtual + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) = 0; + + // Same as above, except allocates data buffer on heap and then places the + // symbol data into the buffer as C-string. + // SymbolSupplier is responsible for deleting the data buffer. After the call + // to GetCStringSymbolData(), the caller should call FreeSymbolData(const + // Module *module) once the data buffer is no longer needed. + // If symbol_data is not NULL, symbol supplier won't return FOUND unless it + // returns a valid buffer in symbol_data, e.g., returns INTERRUPT on memory + // allocation failure. + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) = 0; + + // Frees the data buffer allocated for the module in GetCStringSymbolData. + virtual void FreeSymbolData(const CodeModule *module) = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__ diff --git a/src/lib/crashdump/gbreakpad/google_breakpad/processor/system_info.h b/src/lib/crashdump/gbreakpad/google_breakpad/processor/system_info.h new file mode 100644 index 0000000..fdbdbfd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/google_breakpad/processor/system_info.h @@ -0,0 +1,97 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// system_info.h: Information about the system that was running a program +// when a crash report was produced. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__ + +#include + +namespace google_breakpad { + +using std::string; + +struct SystemInfo { + public: + SystemInfo() : os(), os_short(), os_version(), cpu(), cpu_info(), + cpu_count(0) {} + + // Resets the SystemInfo object to its default values. + void Clear() { + os.clear(); + os_short.clear(); + os_version.clear(); + cpu.clear(); + cpu_info.clear(); + cpu_count = 0; + } + + // A string identifying the operating system, such as "Windows NT", + // "Mac OS X", or "Linux". If the information is present in the dump but + // its value is unknown, this field will contain a numeric value. If + // the information is not present in the dump, this field will be empty. + string os; + + // A short form of the os string, using lowercase letters and no spaces, + // suitable for use in a filesystem. Possible values are "windows", + // "mac", and "linux". Empty if the information is not present in the dump + // or if the OS given by the dump is unknown. The values stored in this + // field should match those used by MinidumpSystemInfo::GetOS. + string os_short; + + // A string identifying the version of the operating system, such as + // "5.1.2600 Service Pack 2" or "10.4.8 8L2127". If the dump does not + // contain this information, this field will be empty. + string os_version; + + // A string identifying the basic CPU family, such as "x86" or "ppc". + // If this information is present in the dump but its value is unknown, + // this field will contain a numeric value. If the information is not + // present in the dump, this field will be empty. The values stored in + // this field should match those used by MinidumpSystemInfo::GetCPU. + string cpu; + + // A string further identifying the specific CPU, such as + // "GenuineIntel level 6 model 13 stepping 8". If the information is not + // present in the dump, or additional identifying information is not + // defined for the CPU family, this field will be empty. + string cpu_info; + + // The number of processors in the system. Will be greater than one for + // multi-core systems. + int cpu_count; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/address_map-inl.h b/src/lib/crashdump/gbreakpad/processor/address_map-inl.h new file mode 100644 index 0000000..251c447 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/address_map-inl.h @@ -0,0 +1,93 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// address_map-inl.h: Address map implementation. +// +// See address_map.h for documentation. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_ADDRESS_MAP_INL_H__ +#define PROCESSOR_ADDRESS_MAP_INL_H__ + +#include "processor/address_map.h" + +#include + +#include "processor/logging.h" + +namespace google_breakpad { + +template +bool AddressMap::Store(const AddressType &address, + const EntryType &entry) { + // Ensure that the specified address doesn't conflict with something already + // in the map. + if (map_.find(address) != map_.end()) { + BPLOG(INFO) << "Store failed, address " << HexString(address) << + " is already present"; + return false; + } + + map_.insert(MapValue(address, entry)); + return true; +} + +template +bool AddressMap::Retrieve( + const AddressType &address, + EntryType *entry, AddressType *entry_address) const { + BPLOG_IF(ERROR, !entry) << "AddressMap::Retrieve requires |entry|"; + assert(entry); + + // upper_bound gives the first element whose key is greater than address, + // but we want the first element whose key is less than or equal to address. + // Decrement the iterator to get there, but not if the upper_bound already + // points to the beginning of the map - in that case, address is lower than + // the lowest stored key, so return false. + MapConstIterator iterator = map_.upper_bound(address); + if (iterator == map_.begin()) + return false; + --iterator; + + *entry = iterator->second; + if (entry_address) + *entry_address = iterator->first; + + return true; +} + +template +void AddressMap::Clear() { + map_.clear(); +} + +} // namespace google_breakpad + +#endif // PROCESSOR_ADDRESS_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/address_map.h b/src/lib/crashdump/gbreakpad/processor/address_map.h new file mode 100644 index 0000000..2972cbb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/address_map.h @@ -0,0 +1,85 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// address_map.h: Address maps. +// +// An address map contains a set of objects keyed by address. Objects are +// retrieved from the map by returning the object with the highest key less +// than or equal to the lookup key. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_ADDRESS_MAP_H__ +#define PROCESSOR_ADDRESS_MAP_H__ + +#include + +namespace google_breakpad { + +// Forward declarations (for later friend declarations). +template class AddressMapSerializer; + +template +class AddressMap { + public: + AddressMap() : map_() {} + + // Inserts an entry into the map. Returns false without storing the entry + // if an entry is already stored in the map at the same address as specified + // by the address argument. + bool Store(const AddressType &address, const EntryType &entry); + + // Locates the entry stored at the highest address less than or equal to + // the address argument. If there is no such range, returns false. The + // entry is returned in entry, which is a required argument. If + // entry_address is not NULL, it will be set to the address that the entry + // was stored at. + bool Retrieve(const AddressType &address, + EntryType *entry, AddressType *entry_address) const; + + // Empties the address map, restoring it to the same state as when it was + // initially created. + void Clear(); + + private: + friend class AddressMapSerializer; + friend class ModuleComparer; + + // Convenience types. + typedef std::map AddressToEntryMap; + typedef typename AddressToEntryMap::const_iterator MapConstIterator; + typedef typename AddressToEntryMap::value_type MapValue; + + // Maps the address of each entry to an EntryType. + AddressToEntryMap map_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_ADDRESS_MAP_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/address_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/address_map_unittest.cc new file mode 100644 index 0000000..9b4095b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/address_map_unittest.cc @@ -0,0 +1,196 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// address_map_unittest.cc: Unit tests for AddressMap. +// +// Author: Mark Mentovai + +#include +#include + +#include "processor/address_map-inl.h" +#include "processor/linked_ptr.h" +#include "processor/logging.h" + +#define ASSERT_TRUE(condition) \ + if (!(condition)) { \ + fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \ + return false; \ + } + +#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition)) + +#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) + +namespace { + +using google_breakpad::AddressMap; +using google_breakpad::linked_ptr; + +// A CountedObject holds an int. A global (not thread safe!) count of +// allocated CountedObjects is maintained to help test memory management. +class CountedObject { + public: + explicit CountedObject(int id) : id_(id) { ++count_; } + ~CountedObject() { --count_; } + + static int count() { return count_; } + int id() const { return id_; } + + private: + static int count_; + int id_; +}; + +int CountedObject::count_; + +typedef int AddressType; +typedef AddressMap< AddressType, linked_ptr > TestMap; + +static bool DoAddressMapTest() { + ASSERT_EQ(CountedObject::count(), 0); + + TestMap test_map; + linked_ptr entry; + AddressType address; + + // Check that a new map is truly empty. + ASSERT_FALSE(test_map.Retrieve(0, &entry, &address)); + ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address)); + ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address)); + + // Check that Clear clears the map without leaking. + ASSERT_EQ(CountedObject::count(), 0); + ASSERT_TRUE(test_map.Store(1, + linked_ptr(new CountedObject(0)))); + ASSERT_TRUE(test_map.Retrieve(1, &entry, &address)); + ASSERT_EQ(CountedObject::count(), 1); + test_map.Clear(); + ASSERT_EQ(CountedObject::count(), 1); // still holding entry in this scope + + // Check that a cleared map is truly empty. + ASSERT_FALSE(test_map.Retrieve(0, &entry, &address)); + ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address)); + ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address)); + + // Check a single-element map. + ASSERT_TRUE(test_map.Store(10, + linked_ptr(new CountedObject(1)))); + ASSERT_FALSE(test_map.Retrieve(9, &entry, &address)); + ASSERT_TRUE(test_map.Retrieve(10, &entry, &address)); + ASSERT_EQ(CountedObject::count(), 1); + ASSERT_EQ(entry->id(), 1); + ASSERT_EQ(address, 10); + ASSERT_TRUE(test_map.Retrieve(11, &entry, &address)); + ASSERT_TRUE(test_map.Retrieve(11, &entry, NULL)); // NULL ok here + + // Add some more elements. + ASSERT_TRUE(test_map.Store(5, + linked_ptr(new CountedObject(2)))); + ASSERT_EQ(CountedObject::count(), 2); + ASSERT_TRUE(test_map.Store(20, + linked_ptr(new CountedObject(3)))); + ASSERT_TRUE(test_map.Store(15, + linked_ptr(new CountedObject(4)))); + ASSERT_FALSE(test_map.Store(10, + linked_ptr(new CountedObject(5)))); // already in map + ASSERT_TRUE(test_map.Store(16, + linked_ptr(new CountedObject(6)))); + ASSERT_TRUE(test_map.Store(14, + linked_ptr(new CountedObject(7)))); + + // Nothing was stored with a key under 5. Don't use ASSERT inside loops + // because it won't show exactly which key/entry/address failed. + for (AddressType key = 0; key < 5; ++key) { + if (test_map.Retrieve(key, &entry, &address)) { + fprintf(stderr, + "FAIL: retrieve %d expected false observed true @ %s:%d\n", + key, __FILE__, __LINE__); + return false; + } + } + + // Check everything that was stored. + const int id_verify[] = { 0, 0, 0, 0, 0, // unused + 2, 2, 2, 2, 2, // 5 - 9 + 1, 1, 1, 1, 7, // 10 - 14 + 4, 6, 6, 6, 6, // 15 - 19 + 3, 3, 3, 3, 3, // 20 - 24 + 3, 3, 3, 3, 3 }; // 25 - 29 + const AddressType address_verify[] = { 0, 0, 0, 0, 0, // unused + 5, 5, 5, 5, 5, // 5 - 9 + 10, 10, 10, 10, 14, // 10 - 14 + 15, 16, 16, 16, 16, // 15 - 19 + 20, 20, 20, 20, 20, // 20 - 24 + 20, 20, 20, 20, 20 }; // 25 - 29 + + for (AddressType key = 5; key < 30; ++key) { + if (!test_map.Retrieve(key, &entry, &address)) { + fprintf(stderr, + "FAIL: retrieve %d expected true observed false @ %s:%d\n", + key, __FILE__, __LINE__); + return false; + } + if (entry->id() != id_verify[key]) { + fprintf(stderr, + "FAIL: retrieve %d expected entry %d observed %d @ %s:%d\n", + key, id_verify[key], entry->id(), __FILE__, __LINE__); + return false; + } + if (address != address_verify[key]) { + fprintf(stderr, + "FAIL: retrieve %d expected address %d observed %d @ %s:%d\n", + key, address_verify[key], address, __FILE__, __LINE__); + return false; + } + } + + // The stored objects should still be in the map. + ASSERT_EQ(CountedObject::count(), 6); + + return true; +} + +static bool RunTests() { + if (!DoAddressMapTest()) + return false; + + // Leak check. + ASSERT_EQ(CountedObject::count(), 0); + + return true; +} + +} // namespace + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + return RunTests() ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/basic_code_module.h b/src/lib/crashdump/gbreakpad/processor/basic_code_module.h new file mode 100644 index 0000000..e3955d2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/basic_code_module.h @@ -0,0 +1,110 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// basic_code_module.h: Carries information about code modules that are loaded +// into a process. +// +// This is a basic concrete implementation of CodeModule. It cannot be +// instantiated directly, only based on other objects that implement +// the CodeModule interface. It exists to provide a CodeModule implementation +// a place to store information when the life of the original object (such as +// a MinidumpModule) cannot be guaranteed. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_BASIC_CODE_MODULE_H__ +#define PROCESSOR_BASIC_CODE_MODULE_H__ + +#include + +#include "google_breakpad/processor/code_module.h" + +namespace google_breakpad { + +using std::string; + +class BasicCodeModule : public CodeModule { + public: + // Creates a new BasicCodeModule given any existing CodeModule + // implementation. This is useful to make a copy of the data relevant to + // the CodeModule interface without requiring all of the resources that + // other CodeModule implementations may require. + explicit BasicCodeModule(const CodeModule *that) + : base_address_(that->base_address()), + size_(that->size()), + code_file_(that->code_file()), + code_identifier_(that->code_identifier()), + debug_file_(that->debug_file()), + debug_identifier_(that->debug_identifier()), + version_(that->version()) {} + + BasicCodeModule(u_int64_t base_address, u_int64_t size, + const string &code_file, + const string &code_identifier, + const string &debug_file, + const string &debug_identifier, + const string &version) + : base_address_(base_address), + size_(size), + code_file_(code_file), + code_identifier_(code_identifier), + debug_file_(debug_file), + debug_identifier_(debug_identifier), + version_(version) + {} + virtual ~BasicCodeModule() {} + + // See code_module.h for descriptions of these methods and the associated + // members. + virtual u_int64_t base_address() const { return base_address_; } + virtual u_int64_t size() const { return size_; } + virtual string code_file() const { return code_file_; } + virtual string code_identifier() const { return code_identifier_; } + virtual string debug_file() const { return debug_file_; } + virtual string debug_identifier() const { return debug_identifier_; } + virtual string version() const { return version_; } + virtual const CodeModule* Copy() const { return new BasicCodeModule(this); } + + private: + u_int64_t base_address_; + u_int64_t size_; + string code_file_; + string code_identifier_; + string debug_file_; + string debug_identifier_; + string version_; + + // Disallow copy constructor and assignment operator. + BasicCodeModule(const BasicCodeModule &that); + void operator=(const BasicCodeModule &that); +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_BASIC_CODE_MODULE_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/basic_code_modules.cc b/src/lib/crashdump/gbreakpad/processor/basic_code_modules.cc new file mode 100644 index 0000000..63da899 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/basic_code_modules.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// basic_code_modules.cc: Contains all of the CodeModule objects that +// were loaded into a single process. +// +// See basic_code_modules.h for documentation. +// +// Author: Mark Mentovai + +#include "processor/basic_code_modules.h" + +#include + +#include "google_breakpad/processor/code_module.h" +#include "processor/linked_ptr.h" +#include "processor/logging.h" +#include "processor/range_map-inl.h" + +namespace google_breakpad { + +BasicCodeModules::BasicCodeModules(const CodeModules *that) + : main_address_(0), + map_(new RangeMap >()) { + BPLOG_IF(ERROR, !that) << "BasicCodeModules::BasicCodeModules requires " + "|that|"; + assert(that); + + const CodeModule *main_module = that->GetMainModule(); + if (main_module) + main_address_ = main_module->base_address(); + + unsigned int count = that->module_count(); + for (unsigned int module_sequence = 0; + module_sequence < count; + ++module_sequence) { + // Make a copy of the module and insert it into the map. Use + // GetModuleAtIndex because ordering is unimportant when slurping the + // entire list, and GetModuleAtIndex may be faster than + // GetModuleAtSequence. + const CodeModule *module = that->GetModuleAtIndex(module_sequence)->Copy(); + if (!map_->StoreRange(module->base_address(), module->size(), + linked_ptr(module))) { + BPLOG(ERROR) << "Module " << module->code_file() << + " could not be stored"; + } + } +} + +BasicCodeModules::~BasicCodeModules() { + delete map_; +} + +unsigned int BasicCodeModules::module_count() const { + return map_->GetCount(); +} + +const CodeModule* BasicCodeModules::GetModuleForAddress( + u_int64_t address) const { + linked_ptr module; + if (!map_->RetrieveRange(address, &module, NULL, NULL)) { + BPLOG(INFO) << "No module at " << HexString(address); + return NULL; + } + + return module.get(); +} + +const CodeModule* BasicCodeModules::GetMainModule() const { + return GetModuleForAddress(main_address_); +} + +const CodeModule* BasicCodeModules::GetModuleAtSequence( + unsigned int sequence) const { + linked_ptr module; + if (!map_->RetrieveRangeAtIndex(sequence, &module, NULL, NULL)) { + BPLOG(ERROR) << "RetrieveRangeAtIndex failed for sequence " << sequence; + return NULL; + } + + return module.get(); +} + +const CodeModule* BasicCodeModules::GetModuleAtIndex( + unsigned int index) const { + // This class stores everything in a RangeMap, without any more-efficient + // way to walk the list of CodeModule objects. Implement GetModuleAtIndex + // using GetModuleAtSequence, which meets all of the requirements, and + // in addition, guarantees ordering. + return GetModuleAtSequence(index); +} + +const CodeModules* BasicCodeModules::Copy() const { + return new BasicCodeModules(this); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/basic_code_modules.h b/src/lib/crashdump/gbreakpad/processor/basic_code_modules.h new file mode 100644 index 0000000..df87fc4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/basic_code_modules.h @@ -0,0 +1,85 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// basic_code_modules.h: Contains all of the CodeModule objects that +// were loaded into a single process. +// +// This is a basic concrete implementation of CodeModules. It cannot be +// instantiated directly, only based on other objects that implement +// the CodeModules interface. It exists to provide a CodeModules +// implementation a place to store information when the life of the original +// object (such as a MinidumpModuleList) cannot be guaranteed. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_BASIC_CODE_MODULES_H__ +#define PROCESSOR_BASIC_CODE_MODULES_H__ + +#include "google_breakpad/processor/code_modules.h" + +namespace google_breakpad { + +template class linked_ptr; +template class RangeMap; + +class BasicCodeModules : public CodeModules { + public: + // Creates a new BasicCodeModules object given any existing CodeModules + // implementation. This is useful to make a copy of the data relevant to + // the CodeModules and CodeModule interfaces without requiring all of the + // resources that other implementations may require. A copy will be + // made of each contained CodeModule using CodeModule::Copy. + explicit BasicCodeModules(const CodeModules *that); + + virtual ~BasicCodeModules(); + + // See code_modules.h for descriptions of these methods. + virtual unsigned int module_count() const; + virtual const CodeModule* GetModuleForAddress(u_int64_t address) const; + virtual const CodeModule* GetMainModule() const; + virtual const CodeModule* GetModuleAtSequence(unsigned int sequence) const; + virtual const CodeModule* GetModuleAtIndex(unsigned int index) const; + virtual const CodeModules* Copy() const; + + private: + // The base address of the main module. + u_int64_t main_address_; + + // The map used to contain each CodeModule, keyed by each CodeModule's + // address range. + RangeMap > *map_; + + // Disallow copy constructor and assignment operator. + BasicCodeModules(const BasicCodeModules &that); + void operator=(const BasicCodeModules &that); +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_BASIC_CODE_MODULES_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver.cc b/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver.cc new file mode 100644 index 0000000..ff57140 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver.cc @@ -0,0 +1,451 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// basic_source_line_resolver.cc: BasicSourceLineResolver implementation. +// +// See basic_source_line_resolver.h and basic_source_line_resolver_types.h +// for documentation. + + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "processor/basic_source_line_resolver_types.h" +#include "processor/module_factory.h" + +#include "processor/tokenize.h" + +using std::map; +using std::vector; +using std::make_pair; + +namespace google_breakpad { + +static const char *kWhitespace = " \r\n"; + +BasicSourceLineResolver::BasicSourceLineResolver() : + SourceLineResolverBase(new BasicModuleFactory) { } + +bool BasicSourceLineResolver::Module::LoadMapFromMemory(char *memory_buffer) { + linked_ptr cur_func; + int line_number = 0; + char *save_ptr; + size_t map_buffer_length = strlen(memory_buffer); + + // If the length is 0, we can still pretend we have a symbol file. This is + // for scenarios that want to test symbol lookup, but don't necessarily care + // if certain modules do not have any information, like system libraries. + if (map_buffer_length == 0) { + return true; + } + + if (memory_buffer[map_buffer_length - 1] == '\n') { + memory_buffer[map_buffer_length - 1] = '\0'; + } + + char *buffer; + buffer = strtok_r(memory_buffer, "\r\n", &save_ptr); + + while (buffer != NULL) { + ++line_number; + + if (strncmp(buffer, "FILE ", 5) == 0) { + if (!ParseFile(buffer)) { + BPLOG(ERROR) << "ParseFile on buffer failed at " << + ":" << line_number; + return false; + } + } else if (strncmp(buffer, "STACK ", 6) == 0) { + if (!ParseStackInfo(buffer)) { + BPLOG(ERROR) << "ParseStackInfo failed at " << + ":" << line_number; + return false; + } + } else if (strncmp(buffer, "FUNC ", 5) == 0) { + cur_func.reset(ParseFunction(buffer)); + if (!cur_func.get()) { + BPLOG(ERROR) << "ParseFunction failed at " << + ":" << line_number; + return false; + } + // StoreRange will fail if the function has an invalid address or size. + // We'll silently ignore this, the function and any corresponding lines + // will be destroyed when cur_func is released. + functions_.StoreRange(cur_func->address, cur_func->size, cur_func); + } else if (strncmp(buffer, "PUBLIC ", 7) == 0) { + // Clear cur_func: public symbols don't contain line number information. + cur_func.reset(); + + if (!ParsePublicSymbol(buffer)) { + BPLOG(ERROR) << "ParsePublicSymbol failed at " << + ":" << line_number; + return false; + } + } else if (strncmp(buffer, "MODULE ", 7) == 0) { + // Ignore these. They're not of any use to BasicSourceLineResolver, + // which is fed modules by a SymbolSupplier. These lines are present to + // aid other tools in properly placing symbol files so that they can + // be accessed by a SymbolSupplier. + // + // MODULE + } else if (strncmp(buffer, "INFO ", 5) == 0) { + // Ignore these as well, they're similarly just for housekeeping. + // + // INFO CODE_ID + } else { + if (!cur_func.get()) { + BPLOG(ERROR) << "Found source line data without a function at " << + ":" << line_number; + return false; + } + Line *line = ParseLine(buffer); + if (!line) { + BPLOG(ERROR) << "ParseLine failed at " << line_number << " for " << + buffer; + return false; + } + cur_func->lines.StoreRange(line->address, line->size, + linked_ptr(line)); + } + buffer = strtok_r(NULL, "\r\n", &save_ptr); + } + return true; +} + +void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + + // First, look for a FUNC record that covers address. Use + // RetrieveNearestRange instead of RetrieveRange so that, if there + // is no such function, we can use the next function to bound the + // extent of the PUBLIC symbol we find, below. This does mean we + // need to check that address indeed falls within the function we + // find; do the range comparison in an overflow-friendly way. + linked_ptr func; + linked_ptr public_symbol; + MemAddr function_base; + MemAddr function_size; + MemAddr public_address; + if (functions_.RetrieveNearestRange(address, &func, + &function_base, &function_size) && + address >= function_base && address - function_base < function_size) { + frame->function_name = func->name; + frame->function_base = frame->module->base_address() + function_base; + + linked_ptr line; + MemAddr line_base; + if (func->lines.RetrieveRange(address, &line, &line_base, NULL)) { + FileMap::const_iterator it = files_.find(line->source_file_id); + if (it != files_.end()) { + frame->source_file_name = files_.find(line->source_file_id)->second; + } + frame->source_line = line->line; + frame->source_line_base = frame->module->base_address() + line_base; + } + } else if (public_symbols_.Retrieve(address, + &public_symbol, &public_address) && + (!func.get() || public_address > function_base)) { + frame->function_name = public_symbol->name; + frame->function_base = frame->module->base_address() + public_address; + } +} + +WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo( + const StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + scoped_ptr result(new WindowsFrameInfo()); + + // We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and + // WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order. + // WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that + // includes its own program string. + // WindowsFrameInfo::STACK_INFO_FPO is the older type + // corresponding to the FPO_DATA struct. See stackwalker_x86.cc. + linked_ptr frame_info; + if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA] + .RetrieveRange(address, &frame_info)) + || (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO] + .RetrieveRange(address, &frame_info))) { + result->CopyFrom(*frame_info.get()); + return result.release(); + } + + // Even without a relevant STACK line, many functions contain + // information about how much space their parameters consume on the + // stack. Use RetrieveNearestRange instead of RetrieveRange, so that + // we can use the function to bound the extent of the PUBLIC symbol, + // below. However, this does mean we need to check that ADDRESS + // falls within the retrieved function's range; do the range + // comparison in an overflow-friendly way. + linked_ptr function; + MemAddr function_base, function_size; + if (functions_.RetrieveNearestRange(address, &function, + &function_base, &function_size) && + address >= function_base && address - function_base < function_size) { + result->parameter_size = function->parameter_size; + result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE; + return result.release(); + } + + // PUBLIC symbols might have a parameter size. Use the function we + // found above to limit the range the public symbol covers. + linked_ptr public_symbol; + MemAddr public_address; + if (public_symbols_.Retrieve(address, &public_symbol, &public_address) && + (!function.get() || public_address > function_base)) { + result->parameter_size = public_symbol->parameter_size; + } + + return NULL; +} + +CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo( + const StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + MemAddr initial_base, initial_size; + string initial_rules; + + // Find the initial rule whose range covers this address. That + // provides an initial set of register recovery rules. Then, walk + // forward from the initial rule's starting address to frame's + // instruction address, applying delta rules. + if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules, + &initial_base, &initial_size)) { + return NULL; + } + + // Create a frame info structure, and populate it with the rules from + // the STACK CFI INIT record. + scoped_ptr rules(new CFIFrameInfo()); + if (!ParseCFIRuleSet(initial_rules, rules.get())) + return NULL; + + // Find the first delta rule that falls within the initial rule's range. + map::const_iterator delta = + cfi_delta_rules_.lower_bound(initial_base); + + // Apply delta rules up to and including the frame's address. + while (delta != cfi_delta_rules_.end() && delta->first <= address) { + ParseCFIRuleSet(delta->second, rules.get()); + delta++; + } + + return rules.release(); +} + +bool BasicSourceLineResolver::Module::ParseFile(char *file_line) { + // FILE + file_line += 5; // skip prefix + + vector tokens; + if (!Tokenize(file_line, kWhitespace, 2, &tokens)) { + return false; + } + + int index = atoi(tokens[0]); + if (index < 0) { + return false; + } + + char *filename = tokens[1]; + if (!filename) { + return false; + } + + files_.insert(make_pair(index, string(filename))); + return true; +} + +BasicSourceLineResolver::Function* +BasicSourceLineResolver::Module::ParseFunction(char *function_line) { + // FUNC
    + function_line += 5; // skip prefix + + vector tokens; + if (!Tokenize(function_line, kWhitespace, 4, &tokens)) { + return NULL; + } + + u_int64_t address = strtoull(tokens[0], NULL, 16); + u_int64_t size = strtoull(tokens[1], NULL, 16); + int stack_param_size = strtoull(tokens[2], NULL, 16); + char *name = tokens[3]; + + return new Function(name, address, size, stack_param_size); +} + +BasicSourceLineResolver::Line* BasicSourceLineResolver::Module::ParseLine( + char *line_line) { + //
    + vector tokens; + if (!Tokenize(line_line, kWhitespace, 4, &tokens)) { + return NULL; + } + + u_int64_t address = strtoull(tokens[0], NULL, 16); + u_int64_t size = strtoull(tokens[1], NULL, 16); + int line_number = atoi(tokens[2]); + int source_file = atoi(tokens[3]); + if (line_number <= 0) { + return NULL; + } + + return new Line(address, size, source_file, line_number); +} + +bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) { + // PUBLIC
    + + // Skip "PUBLIC " prefix. + public_line += 7; + + vector tokens; + if (!Tokenize(public_line, kWhitespace, 3, &tokens)) { + return false; + } + + u_int64_t address = strtoull(tokens[0], NULL, 16); + int stack_param_size = strtoull(tokens[1], NULL, 16); + char *name = tokens[2]; + + // A few public symbols show up with an address of 0. This has been seen + // in the dumped output of ntdll.pdb for symbols such as _CIlog, _CIpow, + // RtlDescribeChunkLZNT1, and RtlReserveChunkLZNT1. They would conflict + // with one another if they were allowed into the public_symbols_ map, + // but since the address is obviously invalid, gracefully accept them + // as input without putting them into the map. + if (address == 0) { + return true; + } + + linked_ptr symbol(new PublicSymbol(name, address, + stack_param_size)); + return public_symbols_.Store(address, symbol); +} + +bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) { + // Skip "STACK " prefix. + stack_info_line += 6; + + // Find the token indicating what sort of stack frame walking + // information this is. + while (*stack_info_line == ' ') + stack_info_line++; + const char *platform = stack_info_line; + while (!strchr(kWhitespace, *stack_info_line)) + stack_info_line++; + *stack_info_line++ = '\0'; + + // MSVC stack frame info. + if (strcmp(platform, "WIN") == 0) { + int type = 0; + u_int64_t rva, code_size; + linked_ptr + stack_frame_info(WindowsFrameInfo::ParseFromString(stack_info_line, + type, + rva, + code_size)); + if (stack_frame_info == NULL) + return false; + + // TODO(mmentovai): I wanted to use StoreRange's return value as this + // method's return value, but MSVC infrequently outputs stack info that + // violates the containment rules. This happens with a section of code + // in strncpy_s in test_app.cc (testdata/minidump2). There, problem looks + // like this: + // STACK WIN 4 4242 1a a 0 ... (STACK WIN 4 base size prolog 0 ...) + // STACK WIN 4 4243 2e 9 0 ... + // ContainedRangeMap treats these two blocks as conflicting. In reality, + // when the prolog lengths are taken into account, the actual code of + // these blocks doesn't conflict. However, we can't take the prolog lengths + // into account directly here because we'd wind up with a different set + // of range conflicts when MSVC outputs stack info like this: + // STACK WIN 4 1040 73 33 0 ... + // STACK WIN 4 105a 59 19 0 ... + // because in both of these entries, the beginning of the code after the + // prolog is at 0x1073, and the last byte of contained code is at 0x10b2. + // Perhaps we could get away with storing ranges by rva + prolog_size + // if ContainedRangeMap were modified to allow replacement of + // already-stored values. + + windows_frame_info_[type].StoreRange(rva, code_size, stack_frame_info); + return true; + } else if (strcmp(platform, "CFI") == 0) { + // DWARF CFI stack frame info + return ParseCFIFrameInfo(stack_info_line); + } else { + // Something unrecognized. + return false; + } +} + +bool BasicSourceLineResolver::Module::ParseCFIFrameInfo( + char *stack_info_line) { + char *cursor; + + // Is this an INIT record or a delta record? + char *init_or_address = strtok_r(stack_info_line, " \r\n", &cursor); + if (!init_or_address) + return false; + + if (strcmp(init_or_address, "INIT") == 0) { + // This record has the form "STACK INIT
    ". + char *address_field = strtok_r(NULL, " \r\n", &cursor); + if (!address_field) return false; + + char *size_field = strtok_r(NULL, " \r\n", &cursor); + if (!size_field) return false; + + char *initial_rules = strtok_r(NULL, "\r\n", &cursor); + if (!initial_rules) return false; + + MemAddr address = strtoul(address_field, NULL, 16); + MemAddr size = strtoul(size_field, NULL, 16); + cfi_initial_rules_.StoreRange(address, size, initial_rules); + return true; + } + + // This record has the form "STACK
    ". + char *address_field = init_or_address; + char *delta_rules = strtok_r(NULL, "\r\n", &cursor); + if (!delta_rules) return false; + MemAddr address = strtoul(address_field, NULL, 16); + cfi_delta_rules_[address] = delta_rules; + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_types.h b/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_types.h new file mode 100644 index 0000000..87b93db --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_types.h @@ -0,0 +1,161 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// basic_source_line_types.h: definition of nested classes/structs in +// BasicSourceLineResolver. It moves the definitions out of +// basic_source_line_resolver.cc, so that other classes could have access +// to these private nested types without including basic_source_line_resolver.cc +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__ +#define PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__ + +#include +#include + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "processor/source_line_resolver_base_types.h" + +#include "processor/address_map-inl.h" +#include "processor/range_map-inl.h" +#include "processor/contained_range_map-inl.h" + +#include "processor/linked_ptr.h" +#include "processor/scoped_ptr.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/cfi_frame_info.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +struct +BasicSourceLineResolver::Function : public SourceLineResolverBase::Function { + Function(const string &function_name, + MemAddr function_address, + MemAddr code_size, + int set_parameter_size) : Base(function_name, + function_address, + code_size, + set_parameter_size), + lines() { } + RangeMap< MemAddr, linked_ptr > lines; + private: + typedef SourceLineResolverBase::Function Base; +}; + + +class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module { + public: + explicit Module(const string &name) : name_(name) { } + virtual ~Module() { } + + // Loads a map from the given buffer in char* type. + // Does NOT have ownership of memory_buffer. + virtual bool LoadMapFromMemory(char *memory_buffer); + + // Looks up the given relative address, and fills the StackFrame struct + // with the result. + virtual void LookupAddress(StackFrame *frame) const; + + // If Windows stack walking information is available covering ADDRESS, + // return a WindowsFrameInfo structure describing it. If the information + // is not available, returns NULL. A NULL return value does not indicate + // an error. The caller takes ownership of any returned WindowsFrameInfo + // object. + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const; + + private: + // Friend declarations. + friend class BasicSourceLineResolver; + friend class ModuleComparer; + friend class ModuleSerializer; + + typedef std::map FileMap; + + // Parses a file declaration + bool ParseFile(char *file_line); + + // Parses a function declaration, returning a new Function object. + Function* ParseFunction(char *function_line); + + // Parses a line declaration, returning a new Line object. + Line* ParseLine(char *line_line); + + // Parses a PUBLIC symbol declaration, storing it in public_symbols_. + // Returns false if an error occurs. + bool ParsePublicSymbol(char *public_line); + + // Parses a STACK WIN or STACK CFI frame info declaration, storing + // it in the appropriate table. + bool ParseStackInfo(char *stack_info_line); + + // Parses a STACK CFI record, storing it in cfi_frame_info_. + bool ParseCFIFrameInfo(char *stack_info_line); + + string name_; + FileMap files_; + RangeMap< MemAddr, linked_ptr > functions_; + AddressMap< MemAddr, linked_ptr > public_symbols_; + + // Each element in the array is a ContainedRangeMap for a type + // listed in WindowsFrameInfoTypes. These are split by type because + // there may be overlaps between maps of different types, but some + // information is only available as certain types. + ContainedRangeMap< MemAddr, linked_ptr > + windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST]; + + // DWARF CFI stack walking data. The Module stores the initial rule sets + // and rule deltas as strings, just as they appear in the symbol file: + // although the file may contain hundreds of thousands of STACK CFI + // records, walking a stack will only ever use a few of them, so it's + // best to delay parsing a record until it's actually needed. + + // STACK CFI INIT records: for each range, an initial set of register + // recovery rules. The RangeMap's itself gives the starting and ending + // addresses. + RangeMap cfi_initial_rules_; + + // STACK CFI records: at a given address, the changes to the register + // recovery rules that take effect at that address. The map key is the + // starting address; the ending address is the key of the next entry in + // this map, or the end of the range as given by the cfi_initial_rules_ + // entry (which FindCFIFrameInfo looks up first). + std::map cfi_delta_rules_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_unittest.cc b/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_unittest.cc new file mode 100644 index 0000000..c241ea0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/basic_source_line_resolver_unittest.cc @@ -0,0 +1,407 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "breakpad_googletest_includes.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/memory_region.h" +#include "processor/linked_ptr.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/windows_frame_info.h" +#include "processor/cfi_frame_info.h" + +namespace { + +using std::string; +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CFIFrameInfo; +using google_breakpad::CodeModule; +using google_breakpad::MemoryRegion; +using google_breakpad::StackFrame; +using google_breakpad::WindowsFrameInfo; +using google_breakpad::linked_ptr; +using google_breakpad::scoped_ptr; + +class TestCodeModule : public CodeModule { + public: + TestCodeModule(string code_file) : code_file_(code_file) {} + virtual ~TestCodeModule() {} + + virtual u_int64_t base_address() const { return 0; } + virtual u_int64_t size() const { return 0xb000; } + virtual string code_file() const { return code_file_; } + virtual string code_identifier() const { return ""; } + virtual string debug_file() const { return ""; } + virtual string debug_identifier() const { return ""; } + virtual string version() const { return ""; } + virtual const CodeModule* Copy() const { + return new TestCodeModule(code_file_); + } + + private: + string code_file_; +}; + +// A mock memory region object, for use by the STACK CFI tests. +class MockMemoryRegion: public MemoryRegion { + u_int64_t GetBase() const { return 0x10000; } + u_int32_t GetSize() const { return 0x01000; } + bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { + *value = address & 0xff; + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { + *value = address & 0xffff; + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { + switch (address) { + case 0x10008: *value = 0x98ecadc3; break; // saved %ebx + case 0x1000c: *value = 0x878f7524; break; // saved %esi + case 0x10010: *value = 0x6312f9a5; break; // saved %edi + case 0x10014: *value = 0x10038; break; // caller's %ebp + case 0x10018: *value = 0xf6438648; break; // return address + default: *value = 0xdeadbeef; break; // junk + } + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { + *value = address; + return true; + } +}; + +// Verify that, for every association in ACTUAL, EXPECTED has the same +// association. (That is, ACTUAL's associations should be a subset of +// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and +// ".cfa". +static bool VerifyRegisters( + const char *file, int line, + const CFIFrameInfo::RegisterValueMap &expected, + const CFIFrameInfo::RegisterValueMap &actual) { + CFIFrameInfo::RegisterValueMap::const_iterator a; + a = actual.find(".cfa"); + if (a == actual.end()) + return false; + a = actual.find(".ra"); + if (a == actual.end()) + return false; + for (a = actual.begin(); a != actual.end(); a++) { + CFIFrameInfo::RegisterValueMap::const_iterator e = + expected.find(a->first); + if (e == expected.end()) { + fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n", + file, line, a->first.c_str(), a->second); + return false; + } + if (e->second != a->second) { + fprintf(stderr, + "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n", + file, line, a->first.c_str(), a->second, e->second); + return false; + } + // Don't complain if this doesn't recover all registers. Although + // the DWARF spec says that unmentioned registers are undefined, + // GCC uses omission to mean that they are unchanged. + } + return true; +} + + +static bool VerifyEmpty(const StackFrame &frame) { + if (frame.function_name.empty() && + frame.source_file_name.empty() && + frame.source_line == 0) + return true; + return false; +} + +static void ClearSourceLineInfo(StackFrame *frame) { + frame->function_name.clear(); + frame->module = NULL; + frame->source_file_name.clear(); + frame->source_line = 0; +} + +class TestBasicSourceLineResolver : public ::testing::Test { +public: + void SetUp() { + testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata"; + } + + BasicSourceLineResolver resolver; + string testdata_dir; +}; + +TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve) +{ + TestCodeModule module1("module1"); + ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out")); + ASSERT_TRUE(resolver.HasModule(&module1)); + TestCodeModule module2("module2"); + ASSERT_TRUE(resolver.LoadModule(&module2, testdata_dir + "/module2.out")); + ASSERT_TRUE(resolver.HasModule(&module2)); + + + StackFrame frame; + scoped_ptr windows_frame_info; + scoped_ptr cfi_frame_info; + frame.instruction = 0x1000; + frame.module = NULL; + resolver.FillSourceLineInfo(&frame); + ASSERT_FALSE(frame.module); + ASSERT_TRUE(frame.function_name.empty()); + ASSERT_EQ(frame.function_base, 0); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + ASSERT_EQ(frame.source_line_base, 0); + + frame.module = &module1; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function1_1"); + ASSERT_TRUE(frame.module); + ASSERT_EQ(frame.module->code_file(), "module1"); + ASSERT_EQ(frame.function_base, 0x1000); + ASSERT_EQ(frame.source_file_name, "file1_1.cc"); + ASSERT_EQ(frame.source_line, 44); + ASSERT_EQ(frame.source_line_base, 0x1000); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_EQ(windows_frame_info->program_string, + "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ ="); + + ClearSourceLineInfo(&frame); + frame.instruction = 0x800; + frame.module = &module1; + resolver.FillSourceLineInfo(&frame); + ASSERT_TRUE(VerifyEmpty(frame)); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_FALSE(windows_frame_info.get()); + + frame.instruction = 0x1280; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function1_3"); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_TRUE(windows_frame_info->program_string.empty()); + + frame.instruction = 0x1380; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function1_4"); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_FALSE(windows_frame_info->program_string.empty()); + + frame.instruction = 0x2000; + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_FALSE(windows_frame_info.get()); + + // module1 has STACK CFI records covering 3d40..3def; + // module2 has STACK CFI records covering 3df0..3e9f; + // check that FindCFIFrameInfo doesn't claim to find any outside those ranges. + frame.instruction = 0x3d3f; + frame.module = &module1; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_FALSE(cfi_frame_info.get()); + + frame.instruction = 0x3e9f; + frame.module = &module1; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_FALSE(cfi_frame_info.get()); + + CFIFrameInfo::RegisterValueMap current_registers; + CFIFrameInfo::RegisterValueMap caller_registers; + CFIFrameInfo::RegisterValueMap expected_caller_registers; + MockMemoryRegion memory; + + // Regardless of which instruction evaluation takes place at, it + // should produce the same values for the caller's registers. + expected_caller_registers[".cfa"] = 0x1001c; + expected_caller_registers[".ra"] = 0xf6438648; + expected_caller_registers["$ebp"] = 0x10038; + expected_caller_registers["$ebx"] = 0x98ecadc3; + expected_caller_registers["$esi"] = 0x878f7524; + expected_caller_registers["$edi"] = 0x6312f9a5; + + frame.instruction = 0x3d40; + frame.module = &module1; + current_registers.clear(); + current_registers["$esp"] = 0x10018; + current_registers["$ebp"] = 0x10038; + current_registers["$ebx"] = 0x98ecadc3; + current_registers["$esi"] = 0x878f7524; + current_registers["$edi"] = 0x6312f9a5; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers)); + + frame.instruction = 0x3d41; + current_registers["$esp"] = 0x10014; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers)); + + frame.instruction = 0x3d43; + current_registers["$ebp"] = 0x10014; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d54; + current_registers["$ebx"] = 0x6864f054U; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d5a; + current_registers["$esi"] = 0x6285f79aU; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d84; + current_registers["$edi"] = 0x64061449U; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x2900; + frame.module = &module1; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, string("PublicSymbol")); + + frame.instruction = 0x4000; + frame.module = &module1; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, string("LargeFunction")); + + frame.instruction = 0x2181; + frame.module = &module2; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function2_2"); + ASSERT_EQ(frame.function_base, 0x2170); + ASSERT_TRUE(frame.module); + ASSERT_EQ(frame.module->code_file(), "module2"); + ASSERT_EQ(frame.source_file_name, "file2_2.cc"); + ASSERT_EQ(frame.source_line, 21); + ASSERT_EQ(frame.source_line_base, 0x2180); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->prolog_size, 1); + + frame.instruction = 0x216f; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Public2_1"); + + ClearSourceLineInfo(&frame); + frame.instruction = 0x219f; + frame.module = &module2; + resolver.FillSourceLineInfo(&frame); + ASSERT_TRUE(frame.function_name.empty()); + + frame.instruction = 0x21a0; + frame.module = &module2; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Public2_2"); +} + +TEST_F(TestBasicSourceLineResolver, TestInvalidLoads) +{ + TestCodeModule module3("module3"); + ASSERT_FALSE(resolver.LoadModule(&module3, + testdata_dir + "/module3_bad.out")); + ASSERT_FALSE(resolver.HasModule(&module3)); + TestCodeModule module4("module4"); + ASSERT_FALSE(resolver.LoadModule(&module4, + testdata_dir + "/module4_bad.out")); + ASSERT_FALSE(resolver.HasModule(&module4)); + TestCodeModule module5("module5"); + ASSERT_FALSE(resolver.LoadModule(&module5, + testdata_dir + "/invalid-filename")); + ASSERT_FALSE(resolver.HasModule(&module5)); + TestCodeModule invalidmodule("invalid-module"); + ASSERT_FALSE(resolver.HasModule(&invalidmodule)); +} + +TEST_F(TestBasicSourceLineResolver, TestUnload) +{ + TestCodeModule module1("module1"); + ASSERT_FALSE(resolver.HasModule(&module1)); + ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out")); + ASSERT_TRUE(resolver.HasModule(&module1)); + resolver.UnloadModule(&module1); + ASSERT_FALSE(resolver.HasModule(&module1)); + ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out")); + ASSERT_TRUE(resolver.HasModule(&module1)); +} + +} // namespace + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/binarystream.cc b/src/lib/crashdump/gbreakpad/processor/binarystream.cc new file mode 100644 index 0000000..c8ee218 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/binarystream.cc @@ -0,0 +1,123 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "processor/binarystream.h" + +namespace google_breakpad { +using std::string; +using std::vector; + +binarystream &binarystream::operator>>(std::string &str) { + u_int16_t length; + *this >> length; + if (eof()) + return *this; + if (length == 0) { + str.clear(); + return *this; + } + vector buffer(length); + stream_.read(&buffer[0], length); + if (!eof()) + str.assign(&buffer[0], length); + return *this; +} + +binarystream &binarystream::operator>>(u_int8_t &u8) { + stream_.read((char *)&u8, 1); + return *this; +} + +binarystream &binarystream::operator>>(u_int16_t &u16) { + u_int16_t temp; + stream_.read((char *)&temp, 2); + if (!eof()) + u16 = ntohs(temp); + return *this; +} + +binarystream &binarystream::operator>>(u_int32_t &u32) { + u_int32_t temp; + stream_.read((char *)&temp, 4); + if (!eof()) + u32 = ntohl(temp); + return *this; +} + +binarystream &binarystream::operator>>(u_int64_t &u64) { + u_int32_t lower, upper; + *this >> lower >> upper; + if (!eof()) + u64 = static_cast(lower) | (static_cast(upper) << 32); + return *this; +} + +binarystream &binarystream::operator<<(const std::string &str) { + if (str.length() > USHRT_MAX) { + // truncate to 16-bit length + *this << static_cast(USHRT_MAX); + stream_.write(str.c_str(), USHRT_MAX); + } else { + *this << (u_int16_t)(str.length() & 0xFFFF); + stream_.write(str.c_str(), str.length()); + } + return *this; +} + +binarystream &binarystream::operator<<(u_int8_t u8) { + stream_.write((const char*)&u8, 1); + return *this; +} + +binarystream &binarystream::operator<<(u_int16_t u16) { + u16 = htons(u16); + stream_.write((const char*)&u16, 2); + return *this; +} + +binarystream &binarystream::operator<<(u_int32_t u32) { + u32 = htonl(u32); + stream_.write((const char*)&u32, 4); + return *this; +} + +binarystream &binarystream::operator<<(u_int64_t u64) { + // write 64-bit ints as two 32-bit ints, so we can byte-swap them easily + u_int32_t lower = static_cast(u64 & 0xFFFFFFFF); + u_int32_t upper = static_cast(u64 >> 32); + *this << lower << upper; + return *this; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/binarystream.h b/src/lib/crashdump/gbreakpad/processor/binarystream.h new file mode 100644 index 0000000..8769c25 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/binarystream.h @@ -0,0 +1,91 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// binarystream implements part of the std::iostream interface as a +// wrapper around std::stringstream to allow reading and writing +// std::string and integers of known size. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_BINARYSTREAM_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_BINARYSTREAM_H_ + +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { +using std::ios_base; +using std::ios; + +class binarystream { + public: + explicit binarystream(ios_base::openmode which = ios_base::out|ios_base::in) + : stream_(which) {} + explicit binarystream(const std::string &str, + ios_base::openmode which = ios_base::out|ios_base::in) + : stream_(str, which) {} + explicit binarystream(const char *str, size_t size, + ios_base::openmode which = ios_base::out|ios_base::in) + : stream_(std::string(str, size), which) {} + + binarystream &operator>>(std::string &str); + binarystream &operator>>(u_int8_t &u8); + binarystream &operator>>(u_int16_t &u16); + binarystream &operator>>(u_int32_t &u32); + binarystream &operator>>(u_int64_t &u64); + + // Note: strings are truncated at 65535 characters + binarystream &operator<<(const std::string &str); + binarystream &operator<<(u_int8_t u8); + binarystream &operator<<(u_int16_t u16); + binarystream &operator<<(u_int32_t u32); + binarystream &operator<<(u_int64_t u64); + + // Forward a few methods directly from the stream object + bool eof() const { return stream_.eof(); } + void clear() { stream_.clear(); } + std::string str() const { return stream_.str(); } + void str(const std::string &s) { stream_.str(s); } + + // Seek both read and write pointers to the beginning of the stream. + void rewind() { + stream_.seekg (0, ios::beg); + stream_.seekp (0, ios::beg); + // This is to clear all the error flags, since only the EOF flag is cleared + // with seekg(). + stream_.clear(); + } + + private: + std::stringstream stream_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_BINARYSTREAM_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/binarystream_unittest.cc b/src/lib/crashdump/gbreakpad/processor/binarystream_unittest.cc new file mode 100644 index 0000000..2ed76e2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/binarystream_unittest.cc @@ -0,0 +1,432 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "breakpad_googletest_includes.h" +#include "processor/binarystream.h" + +namespace { +using std::ios_base; +using std::string; +using std::vector; +using google_breakpad::binarystream; + + +class BinaryStreamBasicTest : public ::testing::Test { +protected: + binarystream stream; +}; + +TEST_F(BinaryStreamBasicTest, ReadU8) { + u_int8_t u8 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u8; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u8); + stream.rewind(); + stream.clear(); + stream << (u_int8_t)1; + ASSERT_FALSE(stream.eof()); + stream >> u8; + EXPECT_EQ(1, u8); + EXPECT_FALSE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadU16) { + u_int16_t u16 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u16; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u16); + stream.rewind(); + stream.clear(); + stream << (u_int16_t)1; + ASSERT_FALSE(stream.eof()); + stream >> u16; + EXPECT_EQ(1, u16); + EXPECT_FALSE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadU32) { + u_int32_t u32 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u32; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u32); + stream.rewind(); + stream.clear(); + stream << (u_int32_t)1; + ASSERT_FALSE(stream.eof()); + stream >> u32; + EXPECT_EQ(1, u32); + EXPECT_FALSE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadU64) { + u_int64_t u64 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u64; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u64); + stream.rewind(); + stream.clear(); + stream << (u_int64_t)1; + ASSERT_FALSE(stream.eof()); + stream >> u64; + EXPECT_EQ(1, u64); + EXPECT_FALSE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadString) { + string s(""); + ASSERT_FALSE(stream.eof()); + stream >> s; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ("", s); + // write an empty string to the stream, read it back + s = "abcd"; + stream.rewind(); + stream.clear(); + stream << string(""); + stream >> s; + EXPECT_EQ("", s); + EXPECT_FALSE(stream.eof()); + stream.rewind(); + stream.clear(); + stream << string("test"); + ASSERT_FALSE(stream.eof()); + stream >> s; + EXPECT_EQ("test", s); + EXPECT_FALSE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadEmptyString) { + string s("abc"); + stream << string(""); + stream >> s; + EXPECT_EQ("", s); +} + +TEST_F(BinaryStreamBasicTest, ReadMultiU8) { + const u_int8_t ea = 0, eb = 100, ec = 200, ed = 0xFF; + u_int8_t a, b, c, d, e; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + ASSERT_FALSE(stream.eof()); + e = 0; + stream >> e; + EXPECT_EQ(0, e); + ASSERT_TRUE(stream.eof()); + // try reading all at once, including one past eof + stream.rewind(); + stream.clear(); + ASSERT_FALSE(stream.eof()); + a = b = c = d = e = 0; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d >> e; + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadMultiU16) { + const u_int16_t ea = 0, eb = 0x100, ec = 0x8000, ed = 0xFFFF; + u_int16_t a, b, c, d, e; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + ASSERT_FALSE(stream.eof()); + e = 0; + stream >> e; + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); + // try reading all at once, including one past eof + stream.rewind(); + stream.clear(); + ASSERT_FALSE(stream.eof()); + a = b = c = d = e = 0; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d >> e; + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadMultiU32) { + const u_int32_t ea = 0, eb = 0x10000, ec = 0x8000000, ed = 0xFFFFFFFF; + u_int32_t a, b, c, d, e; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + ASSERT_FALSE(stream.eof()); + e = 0; + stream >> e; + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); + // try reading all at once, including one past eof + stream.rewind(); + stream.clear(); + ASSERT_FALSE(stream.eof()); + a = b = c = d = e = 0; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d >> e; + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadMultiU64) { + const u_int64_t ea = 0, eb = 0x10000, ec = 0x100000000ULL, + ed = 0xFFFFFFFFFFFFFFFFULL; + u_int64_t a, b, c, d, e; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + ASSERT_FALSE(stream.eof()); + e = 0; + stream >> e; + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); + // try reading all at once, including one past eof + stream.rewind(); + stream.clear(); + ASSERT_FALSE(stream.eof()); + a = b = c = d = e = 0; + stream << ea << eb << ec << ed; + stream >> a >> b >> c >> d >> e; + EXPECT_EQ(ea, a); + EXPECT_EQ(eb, b); + EXPECT_EQ(ec, c); + EXPECT_EQ(ed, d); + EXPECT_EQ(0, e); + EXPECT_TRUE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadMixed) { + const u_int8_t e8 = 0x10; + const u_int16_t e16 = 0x2020; + const u_int32_t e32 = 0x30303030; + const u_int64_t e64 = 0x4040404040404040ULL; + const string es = "test"; + u_int8_t u8 = 0; + u_int16_t u16 = 0; + u_int32_t u32 = 0; + u_int64_t u64 = 0; + string s("test"); + stream << e8 << e16 << e32 << e64 << es; + stream >> u8 >> u16 >> u32 >> u64 >> s; + EXPECT_FALSE(stream.eof()); + EXPECT_EQ(e8, u8); + EXPECT_EQ(e16, u16); + EXPECT_EQ(e32, u32); + EXPECT_EQ(e64, u64); + EXPECT_EQ(es, s); +} + +TEST_F(BinaryStreamBasicTest, ReadStringMissing) { + // ensure that reading a string where only the length is present fails + u_int16_t u16 = 8; + stream << u16; + stream.rewind(); + string s(""); + stream >> s; + EXPECT_EQ("", s); + EXPECT_TRUE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, ReadStringTruncated) { + // ensure that reading a string where not all the data is present fails + u_int16_t u16 = 8; + stream << u16; + stream << (u_int8_t)'t' << (u_int8_t)'e' << (u_int8_t)'s' << (u_int8_t)'t'; + stream.rewind(); + string s(""); + stream >> s; + EXPECT_EQ("", s); + EXPECT_TRUE(stream.eof()); +} + +TEST_F(BinaryStreamBasicTest, StreamByteLength) { + // Test that the stream buffer contains the right amount of data + stream << (u_int8_t)0 << (u_int16_t)1 << (u_int32_t)2 << (u_int64_t)3 + << string("test"); + string s = stream.str(); + EXPECT_EQ(21, s.length()); +} + +TEST_F(BinaryStreamBasicTest, AppendStreamResultsByteLength) { + // Test that appending the str() results from two streams + // gives the right byte length + binarystream stream2; + stream << (u_int8_t)0 << (u_int16_t)1; + stream2 << (u_int32_t)0 << (u_int64_t)2 + << string("test"); + string s = stream.str(); + string s2 = stream2.str(); + s.append(s2); + EXPECT_EQ(21, s.length()); +} + +TEST_F(BinaryStreamBasicTest, StreamSetStr) { + const string es("test"); + stream << es; + binarystream stream2; + stream2.str(stream.str()); + string s; + stream2 >> s; + EXPECT_FALSE(stream2.eof()); + EXPECT_EQ("test", s); + s = ""; + stream2.str(stream.str()); + stream2.rewind(); + stream2 >> s; + EXPECT_FALSE(stream2.eof()); + EXPECT_EQ("test", s); +} + +class BinaryStreamU8Test : public ::testing::Test { +protected: + binarystream stream; + + void SetUp() { + stream << (u_int8_t)1; + } +}; + +TEST_F(BinaryStreamU8Test, ReadU16) { + u_int16_t u16 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u16; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u16); +} + +TEST_F(BinaryStreamU8Test, ReadU32) { + u_int32_t u32 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u32; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u32); +} + +TEST_F(BinaryStreamU8Test, ReadU64) { + u_int64_t u64 = 0; + ASSERT_FALSE(stream.eof()); + stream >> u64; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ(0, u64); +} + +TEST_F(BinaryStreamU8Test, ReadString) { + string s(""); + ASSERT_FALSE(stream.eof()); + stream >> s; + ASSERT_TRUE(stream.eof()); + EXPECT_EQ("", s); +} + + +TEST(BinaryStreamTest, InitWithData) { + const char *data = "abcd"; + binarystream stream(data); + u_int8_t a, b, c, d; + stream >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); +} + +TEST(BinaryStreamTest, InitWithDataLeadingNull) { + const char *data = "\0abcd"; + binarystream stream(data, 5); + u_int8_t z, a, b, c, d; + stream >> z >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ(0, z); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); +} + +TEST(BinaryStreamTest, InitWithDataVector) { + vector data; + data.push_back('a'); + data.push_back('b'); + data.push_back('c'); + data.push_back('d'); + data.push_back('e'); + data.resize(4); + binarystream stream(&data[0], data.size()); + u_int8_t a, b, c, d; + stream >> a >> b >> c >> d; + ASSERT_FALSE(stream.eof()); + EXPECT_EQ('a', a); + EXPECT_EQ('b', b); + EXPECT_EQ('c', c); + EXPECT_EQ('d', d); +} + +} // namespace + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/call_stack.cc b/src/lib/crashdump/gbreakpad/processor/call_stack.cc new file mode 100644 index 0000000..e327671 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/call_stack.cc @@ -0,0 +1,53 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// call_stack.cc: A call stack comprised of stack frames. +// +// See call_stack.h for documentation. +// +// Author: Mark Mentovai + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/stack_frame.h" + +namespace google_breakpad { + +CallStack::~CallStack() { + Clear(); +} + +void CallStack::Clear() { + for (vector::const_iterator iterator = frames_.begin(); + iterator != frames_.end(); + ++iterator) { + delete *iterator; + } +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/cfi_frame_info-inl.h b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info-inl.h new file mode 100644 index 0000000..7e7af0a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info-inl.h @@ -0,0 +1,119 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// cfi_frame_info-inl.h: Definitions for cfi_frame_info.h inlined functions. + +#ifndef PROCESSOR_CFI_FRAME_INFO_INL_H_ +#define PROCESSOR_CFI_FRAME_INFO_INL_H_ + +#include + +namespace google_breakpad { + +template +bool SimpleCFIWalker::FindCallerRegisters( + const MemoryRegion &memory, + const CFIFrameInfo &cfi_frame_info, + const RawContextType &callee_context, + int callee_validity, + RawContextType *caller_context, + int *caller_validity) const { + typedef CFIFrameInfo::RegisterValueMap ValueMap; + ValueMap callee_registers; + ValueMap caller_registers; + // Just for brevity. + typename ValueMap::const_iterator caller_none = caller_registers.end(); + + // Populate callee_registers with register values from callee_context. + for (size_t i = 0; i < map_size_; i++) { + const RegisterSet &r = register_map_[i]; + if (callee_validity & r.validity_flag) + callee_registers[r.name] = callee_context.*r.context_member; + } + + // Apply the rules, and see what register values they yield. + if (!cfi_frame_info.FindCallerRegs(callee_registers, memory, + &caller_registers)) + return false; + + // Populate *caller_context with the values the rules placed in + // caller_registers. + memset(caller_context, 0xda, sizeof(*caller_context)); + *caller_validity = 0; + for (size_t i = 0; i < map_size_; i++) { + const RegisterSet &r = register_map_[i]; + typename ValueMap::const_iterator caller_entry; + + // Did the rules provide a value for this register by its name? + caller_entry = caller_registers.find(r.name); + if (caller_entry != caller_none) { + caller_context->*r.context_member = caller_entry->second; + *caller_validity |= r.validity_flag; + continue; + } + + // Did the rules provide a value for this register under its + // alternate name? + if (r.alternate_name) { + caller_entry = caller_registers.find(r.alternate_name); + if (caller_entry != caller_none) { + caller_context->*r.context_member = caller_entry->second; + *caller_validity |= r.validity_flag; + continue; + } + } + + // Is this a callee-saves register? The walker assumes that these + // still hold the caller's value if the CFI doesn't mention them. + // + // Note that other frame walkers may fail to recover callee-saves + // registers; for example, the x86 "traditional" strategy only + // recovers %eip, %esp, and %ebp, even though %ebx, %esi, and %edi + // are callee-saves, too. It is not correct to blindly set the + // valid bit for all callee-saves registers, without first + // checking its validity bit in the callee. + if (r.callee_saves && (callee_validity & r.validity_flag) != 0) { + caller_context->*r.context_member = callee_context.*r.context_member; + *caller_validity |= r.validity_flag; + continue; + } + + // Otherwise, the register's value is unknown. + } + + return true; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_CFI_FRAME_INFO_INL_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/cfi_frame_info.cc b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info.cc new file mode 100644 index 0000000..1ded99d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info.cc @@ -0,0 +1,182 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// cfi_frame_info.cc: Implementation of CFIFrameInfo class. +// See cfi_frame_info.h for details. + +#include "processor/cfi_frame_info.h" + +#include + +#include + +#include "processor/postfix_evaluator-inl.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +template +bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const { + // If there are not rules for both .ra and .cfa in effect at this address, + // don't use this CFI data for stack walking. + if (cfa_rule_.empty() || ra_rule_.empty()) + return false; + + RegisterValueMap working; + PostfixEvaluator evaluator(&working, &memory); + + caller_registers->clear(); + + // First, compute the CFA. + V cfa; + working = registers; + if (!evaluator.EvaluateForValue(cfa_rule_, &cfa)) + return false; + + // Then, compute the return address. + V ra; + working = registers; + working[".cfa"] = cfa; + if (!evaluator.EvaluateForValue(ra_rule_, &ra)) + return false; + + // Now, compute values for all the registers register_rules_ mentions. + for (RuleMap::const_iterator it = register_rules_.begin(); + it != register_rules_.end(); it++) { + V value; + working = registers; + working[".cfa"] = cfa; + if (!evaluator.EvaluateForValue(it->second, &value)) + return false; + (*caller_registers)[it->first] = value; + } + + (*caller_registers)[".ra"] = ra; + (*caller_registers)[".cfa"] = cfa; + + return true; +} + +// Explicit instantiations for 32-bit and 64-bit architectures. +template bool CFIFrameInfo::FindCallerRegs( + const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const; +template bool CFIFrameInfo::FindCallerRegs( + const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const; + +string CFIFrameInfo::Serialize() const { + std::ostringstream stream; + + if (!cfa_rule_.empty()) { + stream << ".cfa: " << cfa_rule_; + } + if (!ra_rule_.empty()) { + if (static_cast(stream.tellp()) != 0) + stream << " "; + stream << ".ra: " << ra_rule_; + } + for (RuleMap::const_iterator iter = register_rules_.begin(); + iter != register_rules_.end(); + ++iter) { + if (static_cast(stream.tellp()) != 0) + stream << " "; + stream << iter->first << ": " << iter->second; + } + + return stream.str(); +} + +bool CFIRuleParser::Parse(const string &rule_set) { + size_t rule_set_len = rule_set.size(); + scoped_array working_copy(new char[rule_set_len + 1]); + memcpy(working_copy.get(), rule_set.data(), rule_set_len); + working_copy[rule_set_len] = '\0'; + + name_.clear(); + expression_.clear(); + + char *cursor; + static const char token_breaks[] = " \t\r\n"; + char *token = strtok_r(working_copy.get(), token_breaks, &cursor); + + for (;;) { + // End of rule set? + if (!token) return Report(); + + // Register/pseudoregister name? + size_t token_len = strlen(token); + if (token_len >= 1 && token[token_len - 1] == ':') { + // Names can't be empty. + if (token_len < 2) return false; + // If there is any pending content, report it. + if (!name_.empty() || !expression_.empty()) { + if (!Report()) return false; + } + name_.assign(token, token_len - 1); + expression_.clear(); + } else { + // Another expression component. + assert(token_len > 0); // strtok_r guarantees this, I think. + if (!expression_.empty()) + expression_ += ' '; + expression_ += token; + } + token = strtok_r(NULL, token_breaks, &cursor); + } +} + +bool CFIRuleParser::Report() { + if (name_.empty() || expression_.empty()) return false; + if (name_ == ".cfa") handler_->CFARule(expression_); + else if (name_ == ".ra") handler_->RARule(expression_); + else handler_->RegisterRule(name_, expression_); + return true; +} + +void CFIFrameInfoParseHandler::CFARule(const string &expression) { + frame_info_->SetCFARule(expression); +} + +void CFIFrameInfoParseHandler::RARule(const string &expression) { + frame_info_->SetRARule(expression); +} + +void CFIFrameInfoParseHandler::RegisterRule(const string &name, + const string &expression) { + frame_info_->SetRegisterRule(name, expression); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/cfi_frame_info.h b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info.h new file mode 100644 index 0000000..fe06fb4 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info.h @@ -0,0 +1,275 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// cfi_frame_info.h: Define the CFIFrameInfo class, which holds the +// set of 'STACK CFI'-derived register recovery rules that apply at a +// given instruction. + +#ifndef PROCESSOR_CFI_FRAME_INFO_H_ +#define PROCESSOR_CFI_FRAME_INFO_H_ + +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::map; +using std::string; + +class MemoryRegion; + +// A set of rules for recovering the calling frame's registers' +// values, when the PC is at a given address in the current frame's +// function. See the description of 'STACK CFI' records at: +// +// http://code.google.com/p/google-breakpad/wiki/SymbolFiles +// +// To prepare an instance of CFIFrameInfo for use at a given +// instruction, first populate it with the rules from the 'STACK CFI +// INIT' record that covers that instruction, and then apply the +// changes given by the 'STACK CFI' records up to our instruction's +// address. Then, use the FindCallerRegs member function to apply the +// rules to the callee frame's register values, yielding the caller +// frame's register values. +class CFIFrameInfo { + public: + // A map from register names onto values. + template class RegisterValueMap: + public map { }; + + // Set the expression for computing a call frame address, return + // address, or register's value. At least the CFA rule and the RA + // rule must be set before calling FindCallerRegs. + void SetCFARule(const string &expression) { cfa_rule_ = expression; } + void SetRARule(const string &expression) { ra_rule_ = expression; } + void SetRegisterRule(const string ®ister_name, const string &expression) { + register_rules_[register_name] = expression; + } + + // Compute the values of the calling frame's registers, according to + // this rule set. Use ValueType in expression evaluation; this + // should be u_int32_t on machines with 32-bit addresses, or + // u_int64_t on machines with 64-bit addresses. + // + // Return true on success, false otherwise. + // + // MEMORY provides access to the contents of the stack. REGISTERS is + // a dictionary mapping the names of registers whose values are + // known in the current frame to their values. CALLER_REGISTERS is + // populated with the values of the recoverable registers in the + // frame that called the current frame. + // + // In addition, CALLER_REGISTERS[".ra"] will be the return address, + // and CALLER_REGISTERS[".cfa"] will be the call frame address. + // These may be helpful in computing the caller's PC and stack + // pointer, if their values are not explicitly specified. + template + bool FindCallerRegs(const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const; + + // Serialize the rules in this object into a string in the format + // of STACK CFI records. + string Serialize() const; + + private: + + // A map from register names onto evaluation rules. + typedef map RuleMap; + + // In this type, a "postfix expression" is an expression of the sort + // interpreted by google_breakpad::PostfixEvaluator. + + // A postfix expression for computing the current frame's CFA (call + // frame address). The CFA is a reference address for the frame that + // remains unchanged throughout the frame's lifetime. You should + // evaluate this expression with a dictionary initially populated + // with the values of the current frame's known registers. + string cfa_rule_; + + // The following expressions should be evaluated with a dictionary + // initially populated with the values of the current frame's known + // registers, and with ".cfa" set to the result of evaluating the + // cfa_rule expression, above. + + // A postfix expression for computing the current frame's return + // address. + string ra_rule_; + + // For a register named REG, rules[REG] is a postfix expression + // which leaves the value of REG in the calling frame on the top of + // the stack. You should evaluate this expression + RuleMap register_rules_; +}; + +// A parser for STACK CFI-style rule sets. +// This may seem bureaucratic: there's no legitimate run-time reason +// to use a parser/handler pattern for this, as it's not a likely +// reuse boundary. But doing so makes finer-grained unit testing +// possible. +class CFIRuleParser { + public: + + class Handler { + public: + Handler() { } + virtual ~Handler() { } + + // The input specifies EXPRESSION as the CFA/RA computation rule. + virtual void CFARule(const string &expression) = 0; + virtual void RARule(const string &expression) = 0; + + // The input specifies EXPRESSION as the recovery rule for register NAME. + virtual void RegisterRule(const string &name, const string &expression) = 0; + }; + + // Construct a parser which feeds its results to HANDLER. + CFIRuleParser(Handler *handler) : handler_(handler) { } + + // Parse RULE_SET as a set of CFA computation and RA/register + // recovery rules, as appearing in STACK CFI records. Report the + // results of parsing by making the appropriate calls to handler_. + // Return true if parsing was successful, false otherwise. + bool Parse(const string &rule_set); + + private: + // Report any accumulated rule to handler_ + bool Report(); + + // The handler to which the parser reports its findings. + Handler *handler_; + + // Working data. + string name_, expression_; +}; + +// A handler for rule set parsing that populates a CFIFrameInfo with +// the results. +class CFIFrameInfoParseHandler: public CFIRuleParser::Handler { + public: + // Populate FRAME_INFO with the results of parsing. + CFIFrameInfoParseHandler(CFIFrameInfo *frame_info) + : frame_info_(frame_info) { } + + void CFARule(const string &expression); + void RARule(const string &expression); + void RegisterRule(const string &name, const string &expression); + + private: + CFIFrameInfo *frame_info_; +}; + +// A utility class template for simple 'STACK CFI'-driven stack walkers. +// Given a CFIFrameInfo instance, a table describing the architecture's +// register set, and a context holding the last frame's registers, an +// instance of this class can populate a new context with the caller's +// registers. +// +// This class template doesn't use any internal knowledge of CFIFrameInfo +// or the other stack walking structures; it just uses the public interface +// of CFIFrameInfo to do the usual things. But the logic it handles should +// be common to many different architectures' stack walkers, so wrapping it +// up in a class should allow the walkers to share code. +// +// RegisterType should be the type of this architecture's registers, either +// u_int32_t or u_int64_t. RawContextType should be the raw context +// structure type for this architecture. +template +class SimpleCFIWalker { + public: + // A structure describing one architecture register. + struct RegisterSet { + // The register name, as it appears in STACK CFI rules. + const char *name; + + // An alternate name that the register's value might be found + // under in a register value dictionary, or NULL. When generating + // names, prefer NAME to this value. It's common to list ".cfa" as + // an alternative name for the stack pointer, and ".ra" as an + // alternative name for the instruction pointer. + const char *alternate_name; + + // True if the callee is expected to preserve the value of this + // register. If this flag is true for some register R, and the STACK + // CFI records provide no rule to recover R, then SimpleCFIWalker + // assumes that the callee has not changed R's value, and the caller's + // value for R is that currently in the callee's context. + bool callee_saves; + + // The ContextValidity flag representing the register's presence. + int validity_flag; + + // A pointer to the RawContextType member that holds the + // register's value. + RegisterType RawContextType::*context_member; + }; + + // Create a simple CFI-based frame walker, given a description of the + // architecture's register set. REGISTER_MAP is an array of + // RegisterSet structures; MAP_SIZE is the number of elements in the + // array. + SimpleCFIWalker(const RegisterSet *register_map, size_t map_size) + : register_map_(register_map), map_size_(map_size) { } + + // Compute the calling frame's raw context given the callee's raw + // context. + // + // Given: + // + // - MEMORY, holding the stack's contents, + // - CFI_FRAME_INFO, describing the called function, + // - CALLEE_CONTEXT, holding the called frame's registers, and + // - CALLEE_VALIDITY, indicating which registers in CALLEE_CONTEXT are valid, + // + // fill in CALLER_CONTEXT with the caller's register values, and set + // CALLER_VALIDITY to indicate which registers are valid in + // CALLER_CONTEXT. Return true on success, or false on failure. + bool FindCallerRegisters(const MemoryRegion &memory, + const CFIFrameInfo &cfi_frame_info, + const RawContextType &callee_context, + int callee_validity, + RawContextType *caller_context, + int *caller_validity) const; + + private: + const RegisterSet *register_map_; + size_t map_size_; +}; + +} // namespace google_breakpad + +#include "cfi_frame_info-inl.h" + +#endif // PROCESSOR_CFI_FRAME_INFO_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/cfi_frame_info_unittest.cc b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info_unittest.cc new file mode 100644 index 0000000..979d4a3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/cfi_frame_info_unittest.cc @@ -0,0 +1,545 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// cfi_frame_info_unittest.cc: Unit tests for CFIFrameInfo, +// CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker. + +#include + +#include "breakpad_googletest_includes.h" +#include "processor/cfi_frame_info.h" +#include "google_breakpad/processor/memory_region.h" + +using google_breakpad::CFIFrameInfo; +using google_breakpad::CFIFrameInfoParseHandler; +using google_breakpad::CFIRuleParser; +using google_breakpad::MemoryRegion; +using google_breakpad::SimpleCFIWalker; +using std::string; +using testing::_; +using testing::A; +using testing::AtMost; +using testing::DoAll; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class MockMemoryRegion: public MemoryRegion { + public: + MOCK_CONST_METHOD0(GetBase, u_int64_t()); + MOCK_CONST_METHOD0(GetSize, u_int32_t()); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int8_t *)); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int16_t *)); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int32_t *)); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int64_t *)); +}; + +// Handy definitions for all tests. +struct CFIFixture { + + // Set up the mock memory object to expect no references. + void ExpectNoMemoryReferences() { + EXPECT_CALL(memory, GetBase()).Times(0); + EXPECT_CALL(memory, GetSize()).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + } + + CFIFrameInfo cfi; + MockMemoryRegion memory; + CFIFrameInfo::RegisterValueMap registers, caller_registers; +}; + +class Simple: public CFIFixture, public Test { }; + +// FindCallerRegs should fail if no .cfa rule is provided. +TEST_F(Simple, NoCFA) { + ExpectNoMemoryReferences(); + + cfi.SetRARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(".ra: 0", cfi.Serialize()); +} + +// FindCallerRegs should fail if no .ra rule is provided. +TEST_F(Simple, NoRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(".cfa: 0", cfi.Serialize()); +} + +TEST_F(Simple, SetCFAAndRARule) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("330903416631436410"); + cfi.SetRARule("5870666104170902211"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]); + ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]); + + ASSERT_EQ(".cfa: 330903416631436410 .ra: 5870666104170902211", + cfi.Serialize()); +} + +TEST_F(Simple, SetManyRules) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -"); + cfi.SetRARule(".cfa 99804755 +"); + cfi.SetRegisterRule("register1", ".cfa 54370437 *"); + cfi.SetRegisterRule("vodkathumbscrewingly", "24076308 .cfa +"); + cfi.SetRegisterRule("pubvexingfjordschmaltzy", ".cfa 29801007 -"); + cfi.SetRegisterRule("uncopyrightables", "92642917 .cfa /"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(6U, caller_registers.size()); + ASSERT_EQ(7664691U, caller_registers[".cfa"]); + ASSERT_EQ(107469446U, caller_registers[".ra"]); + ASSERT_EQ(416732599139967ULL, caller_registers["register1"]); + ASSERT_EQ(31740999U, caller_registers["vodkathumbscrewingly"]); + ASSERT_EQ(-22136316ULL, caller_registers["pubvexingfjordschmaltzy"]); + ASSERT_EQ(12U, caller_registers["uncopyrightables"]); + ASSERT_EQ(".cfa: $temp1 68737028 = $temp2 61072337 = $temp1 $temp2 - " + ".ra: .cfa 99804755 + " + "pubvexingfjordschmaltzy: .cfa 29801007 - " + "register1: .cfa 54370437 * " + "uncopyrightables: 92642917 .cfa / " + "vodkathumbscrewingly: 24076308 .cfa +", + cfi.Serialize()); +} + +TEST_F(Simple, RulesOverride) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("330903416631436410"); + cfi.SetRARule("5870666104170902211"); + cfi.SetCFARule("2828089117179001"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]); + ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]); + ASSERT_EQ(".cfa: 2828089117179001 .ra: 5870666104170902211", + cfi.Serialize()); +} + +class Scope: public CFIFixture, public Test { }; + +// There should be no value for .cfa in scope when evaluating the CFA rule. +TEST_F(Scope, CFALacksCFA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule(".cfa"); + cfi.SetRARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// There should be no value for .ra in scope when evaluating the CFA rule. +TEST_F(Scope, CFALacksRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule(".ra"); + cfi.SetRARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// The current frame's registers should be in scope when evaluating +// the CFA rule. +TEST_F(Scope, CFASeesCurrentRegs) { + ExpectNoMemoryReferences(); + + registers[".baraminology"] = 0x06a7bc63e4f13893ULL; + registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL; + cfi.SetCFARule(".baraminology .ornithorhynchus +"); + cfi.SetRARule("0"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL, + caller_registers[".cfa"]); +} + +// .cfa should be in scope in the return address expression. +TEST_F(Scope, RASeesCFA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("48364076"); + cfi.SetRARule(".cfa"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(48364076U, caller_registers[".ra"]); +} + +// There should be no value for .ra in scope when evaluating the CFA rule. +TEST_F(Scope, RALacksRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("0"); + cfi.SetRARule(".ra"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// The current frame's registers should be in scope in the return +// address expression. +TEST_F(Scope, RASeesCurrentRegs) { + ExpectNoMemoryReferences(); + + registers["noachian"] = 0x54dc4a5d8e5eb503ULL; + cfi.SetCFARule("10359370"); + cfi.SetRARule("noachian"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]); +} + +// .cfa should be in scope for register rules. +TEST_F(Scope, RegistersSeeCFA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("6515179"); + cfi.SetRARule(".cfa"); + cfi.SetRegisterRule("rogerian", ".cfa"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(3U, caller_registers.size()); + ASSERT_EQ(6515179U, caller_registers["rogerian"]); +} + +// The return address should not be in scope for register rules. +TEST_F(Scope, RegsLackRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("42740329"); + cfi.SetRARule("27045204"); + cfi.SetRegisterRule("$r1", ".ra"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// Register rules can see the current frame's register values. +TEST_F(Scope, RegsSeeRegs) { + ExpectNoMemoryReferences(); + + registers["$r1"] = 0x6ed3582c4bedb9adULL; + registers["$r2"] = 0xd27d9e742b8df6d0ULL; + cfi.SetCFARule("88239303"); + cfi.SetRARule("30503835"); + cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2"); + cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(4U, caller_registers.size()); + ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]); + ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]); +} + +// Each rule's temporaries are separate. +TEST_F(Scope, SeparateTempsRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("$temp1 76569129 = $temp1"); + cfi.SetRARule("0"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + + cfi.SetCFARule("$temp1 76569129 = $temp1"); + cfi.SetRARule("$temp1"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +class MockCFIRuleParserHandler: public CFIRuleParser::Handler { + public: + MOCK_METHOD1(CFARule, void(const string &)); + MOCK_METHOD1(RARule, void(const string &)); + MOCK_METHOD2(RegisterRule, void(const string &, const string &)); +}; + +// A fixture class for testing CFIRuleParser. +class CFIParserFixture { + public: + CFIParserFixture() : parser(&mock_handler) { + // Expect no parsing results to be reported to mock_handler. Individual + // tests can override this. + EXPECT_CALL(mock_handler, CFARule(_)).Times(0); + EXPECT_CALL(mock_handler, RARule(_)).Times(0); + EXPECT_CALL(mock_handler, RegisterRule(_, _)).Times(0); + } + + MockCFIRuleParserHandler mock_handler; + CFIRuleParser parser; +}; + +class Parser: public CFIParserFixture, public Test { }; + +TEST_F(Parser, Empty) { + EXPECT_FALSE(parser.Parse("")); +} + +TEST_F(Parser, LoneColon) { + EXPECT_FALSE(parser.Parse(":")); +} + +TEST_F(Parser, CFANoExpr) { + EXPECT_FALSE(parser.Parse(".cfa:")); +} + +TEST_F(Parser, CFANoColonNoExpr) { + EXPECT_FALSE(parser.Parse(".cfa")); +} + +TEST_F(Parser, RANoExpr) { + EXPECT_FALSE(parser.Parse(".ra:")); +} + +TEST_F(Parser, RANoColonNoExpr) { + EXPECT_FALSE(parser.Parse(".ra")); +} + +TEST_F(Parser, RegNoExpr) { + EXPECT_FALSE(parser.Parse("reg:")); +} + +TEST_F(Parser, NoName) { + EXPECT_FALSE(parser.Parse("expr")); +} + +TEST_F(Parser, NoNameTwo) { + EXPECT_FALSE(parser.Parse("expr1 expr2")); +} + +TEST_F(Parser, StartsWithExpr) { + EXPECT_FALSE(parser.Parse("expr1 reg: expr2")); +} + +TEST_F(Parser, CFA) { + EXPECT_CALL(mock_handler, CFARule("spleen")).WillOnce(Return()); + EXPECT_TRUE(parser.Parse(".cfa: spleen")); +} + +TEST_F(Parser, RA) { + EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return()); + EXPECT_TRUE(parser.Parse(".ra: notoriety")); +} + +TEST_F(Parser, Reg) { + EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous")) + .WillOnce(Return()); + EXPECT_TRUE(parser.Parse("nemo: mellifluous")); +} + +TEST_F(Parser, CFARARegs) { + EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return()); + EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return()); + EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian")) + .WillOnce(Return()); + EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius")) + .WillOnce(Return()); + EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression " + "galba: praetorian otho: vitellius")); +} + +TEST_F(Parser, Whitespace) { + EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression")) + .WillOnce(Return()); + EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression")) + .WillOnce(Return()); + EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n " + "expression \n")); +} + +TEST_F(Parser, WhitespaceLoneColon) { + EXPECT_FALSE(parser.Parse(" \n:\t ")); +} + +TEST_F(Parser, EmptyName) { + EXPECT_CALL(mock_handler, RegisterRule("reg", _)) + .Times(AtMost(1)) + .WillRepeatedly(Return()); + EXPECT_FALSE(parser.Parse("reg: expr1 : expr2")); +} + +TEST_F(Parser, RuleLoneColon) { + EXPECT_CALL(mock_handler, RegisterRule("r1", "expr")) + .Times(AtMost(1)) + .WillRepeatedly(Return()); + EXPECT_FALSE(parser.Parse(" r1: expr :")); +} + +TEST_F(Parser, RegNoExprRule) { + EXPECT_CALL(mock_handler, RegisterRule("r1", "expr")) + .Times(AtMost(1)) + .WillRepeatedly(Return()); + EXPECT_FALSE(parser.Parse("r0: r1: expr")); +} + +class ParseHandlerFixture: public CFIFixture { + public: + ParseHandlerFixture() : CFIFixture(), handler(&cfi) { } + CFIFrameInfoParseHandler handler; +}; + +class ParseHandler: public ParseHandlerFixture, public Test { }; + +TEST_F(ParseHandler, CFARARule) { + handler.CFARule("reg-for-cfa"); + handler.RARule("reg-for-ra"); + registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL; + registers["reg-for-ra"] = 0x6301b475b8b91c02ULL; + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]); + ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]); +} + +TEST_F(ParseHandler, RegisterRules) { + handler.CFARule("reg-for-cfa"); + handler.RARule("reg-for-ra"); + handler.RegisterRule("reg1", "reg-for-reg1"); + handler.RegisterRule("reg2", "reg-for-reg2"); + registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL; + registers["reg-for-ra"] = 0x6301b475b8b91c02ULL; + registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL; + registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL; + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]); + ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]); + ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]); + ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]); +} + +struct SimpleCFIWalkerFixture { + struct RawContext { + u_int64_t r0, r1, r2, r3, r4, sp, pc; + }; + enum Validity { + R0_VALID = 0x01, + R1_VALID = 0x02, + R2_VALID = 0x04, + R3_VALID = 0x08, + R4_VALID = 0x10, + SP_VALID = 0x20, + PC_VALID = 0x40 + }; + typedef SimpleCFIWalker CFIWalker; + + SimpleCFIWalkerFixture() + : walker(register_map, + sizeof(register_map) / sizeof(register_map[0])) { } + + static CFIWalker::RegisterSet register_map[7]; + CFIFrameInfo call_frame_info; + CFIWalker walker; + MockMemoryRegion memory; + RawContext callee_context, caller_context; +}; + +SimpleCFIWalkerFixture::CFIWalker::RegisterSet +SimpleCFIWalkerFixture::register_map[7] = { + { "r0", NULL, true, R0_VALID, &RawContext::r0 }, + { "r1", NULL, true, R1_VALID, &RawContext::r1 }, + { "r2", NULL, false, R2_VALID, &RawContext::r2 }, + { "r3", NULL, false, R3_VALID, &RawContext::r3 }, + { "r4", NULL, true, R4_VALID, &RawContext::r4 }, + { "sp", ".cfa", true, SP_VALID, &RawContext::sp }, + { "pc", ".ra", true, PC_VALID, &RawContext::pc }, +}; + +class SimpleWalker: public SimpleCFIWalkerFixture, public Test { }; + +TEST_F(SimpleWalker, Walk) { + // Stack_top is the current stack pointer, pointing to the lowest + // address of a frame that looks like this (all 64-bit words): + // + // sp -> saved r0 + // garbage + // return address + // cfa -> + // + // r0 has been saved on the stack. + // r1 has been saved in r2. + // r2 and r3 are not recoverable. + // r4 is not recoverable, even though it is a callee-saves register. + // Some earlier frame's unwinder must have failed to recover it. + + u_int64_t stack_top = 0x83254944b20d5512ULL; + + // Saved r0. + EXPECT_CALL(memory, + GetMemoryAtAddress(stack_top, A())) + .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL), + Return(true))); + // Saved return address. + EXPECT_CALL(memory, + GetMemoryAtAddress(stack_top + 16, A())) + .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL), + Return(true))); + + call_frame_info.SetCFARule("sp 24 +"); + call_frame_info.SetRARule(".cfa 8 - ^"); + call_frame_info.SetRegisterRule("r0", ".cfa 24 - ^"); + call_frame_info.SetRegisterRule("r1", "r2"); + + callee_context.r0 = 0x94e030ca79edd119ULL; + callee_context.r1 = 0x937b4d7e95ce52d9ULL; + callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1 + // callee_context.r3 is not valid in callee. + // callee_context.r4 is not valid in callee. + callee_context.sp = stack_top; + callee_context.pc = 0x25b21b224311d280ULL; + int callee_validity = R0_VALID | R1_VALID | R2_VALID | SP_VALID | PC_VALID; + + memset(&caller_context, 0, sizeof(caller_context)); + + int caller_validity; + EXPECT_TRUE(walker.FindCallerRegisters(memory, call_frame_info, + callee_context, callee_validity, + &caller_context, &caller_validity)); + EXPECT_EQ(R0_VALID | R1_VALID | SP_VALID | PC_VALID, caller_validity); + EXPECT_EQ(0xdc1975eba8602302ULL, caller_context.r0); + EXPECT_EQ(0x5fe0027416b8b62aULL, caller_context.r1); + EXPECT_EQ(stack_top + 24, caller_context.sp); + EXPECT_EQ(0xba5ad6d9acce28deULL, caller_context.pc); +} diff --git a/src/lib/crashdump/gbreakpad/processor/contained_range_map-inl.h b/src/lib/crashdump/gbreakpad/processor/contained_range_map-inl.h new file mode 100644 index 0000000..4c0ad41 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/contained_range_map-inl.h @@ -0,0 +1,197 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// contained_range_map-inl.h: Hierarchically-organized range map implementation. +// +// See contained_range_map.h for documentation. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_CONTAINED_RANGE_MAP_INL_H__ +#define PROCESSOR_CONTAINED_RANGE_MAP_INL_H__ + +#include "processor/contained_range_map.h" + +#include + +#include "processor/logging.h" + + +namespace google_breakpad { + + +template +ContainedRangeMap::~ContainedRangeMap() { + // Clear frees the children pointed to by the map, and frees the map itself. + Clear(); +} + + +template +bool ContainedRangeMap::StoreRange( + const AddressType &base, const AddressType &size, const EntryType &entry) { + AddressType high = base + size - 1; + + // Check for undersize or overflow. + if (size <= 0 || high < base) { + //TODO(nealsid) We are commenting this out in order to prevent + // excessive logging. We plan to move to better logging as this + // failure happens quite often and is expected(see comment in + // basic_source_line_resolver.cc:671). + // BPLOG(INFO) << "StoreRange failed, " << HexString(base) << "+" + // << HexString(size) << ", " << HexString(high); + return false; + } + + if (!map_) + map_ = new AddressToRangeMap(); + + MapIterator iterator_base = map_->lower_bound(base); + MapIterator iterator_high = map_->lower_bound(high); + MapIterator iterator_end = map_->end(); + + if (iterator_base == iterator_high && iterator_base != iterator_end && + base >= iterator_base->second->base_) { + // The new range is entirely within an existing child range. + + // If the new range's geometry is exactly equal to an existing child + // range's, it violates the containment rules, and an attempt to store + // it must fail. iterator_base->first contains the key, which was the + // containing child's high address. + if (iterator_base->second->base_ == base && iterator_base->first == high) { + // TODO(nealsid): See the TODO above on why this is commented out. +// BPLOG(INFO) << "StoreRange failed, identical range is already " +// "present: " << HexString(base) << "+" << HexString(size); + return false; + } + + // Pass the new range on to the child to attempt to store. + return iterator_base->second->StoreRange(base, size, entry); + } + + // iterator_high might refer to an irrelevant range: one whose base address + // is higher than the new range's high address. Set contains_high to true + // only if iterator_high refers to a range that is at least partially + // within the new range. + bool contains_high = iterator_high != iterator_end && + high >= iterator_high->second->base_; + + // If the new range encompasses any existing child ranges, it must do so + // fully. Partial containment isn't allowed. + if ((iterator_base != iterator_end && base > iterator_base->second->base_) || + (contains_high && high < iterator_high->first)) { + // TODO(mmentovai): Some symbol files will trip this check frequently + // on STACK lines. Too many messages will be produced. These are more + // suitable for a DEBUG channel than an INFO channel. + // BPLOG(INFO) << "StoreRange failed, new range partially contains " + // "existing range: " << HexString(base) << "+" << + // HexString(size); + return false; + } + + // When copying and erasing contained ranges, the "end" iterator needs to + // point one past the last item of the range to copy. If contains_high is + // false, the iterator's already in the right place; the increment is safe + // because contains_high can't be true if iterator_high == iterator_end. + if (contains_high) + ++iterator_high; + + // Optimization: if the iterators are equal, no child ranges would be + // moved. Create the new child range with a NULL map to conserve space + // in leaf nodes, of which there will be many. + AddressToRangeMap *child_map = NULL; + + if (iterator_base != iterator_high) { + // The children of this range that are contained by the new range must + // be transferred over to the new range. Create the new child range map + // and copy the pointers to range maps it should contain into it. + child_map = new AddressToRangeMap(iterator_base, iterator_high); + + // Remove the copied child pointers from this range's map of children. + map_->erase(iterator_base, iterator_high); + } + + // Store the new range in the map by its high address. Any children that + // the new child range contains were formerly children of this range but + // are now this range's grandchildren. Ownership of these is transferred + // to the new child range. + map_->insert(MapValue(high, + new ContainedRangeMap(base, entry, child_map))); + return true; +} + + +template +bool ContainedRangeMap::RetrieveRange( + const AddressType &address, EntryType *entry) const { + BPLOG_IF(ERROR, !entry) << "ContainedRangeMap::RetrieveRange requires " + "|entry|"; + assert(entry); + + // If nothing was ever stored, then there's nothing to retrieve. + if (!map_) + return false; + + // Get an iterator to the child range whose high address is equal to or + // greater than the supplied address. If the supplied address is higher + // than all of the high addresses in the range, then this range does not + // contain a child at address, so return false. If the supplied address + // is lower than the base address of the child range, then it is not within + // the child range, so return false. + MapConstIterator iterator = map_->lower_bound(address); + if (iterator == map_->end() || address < iterator->second->base_) + return false; + + // The child in iterator->second contains the specified address. Find out + // if it has a more-specific descendant that also contains it. If it does, + // it will set |entry| appropriately. If not, set |entry| to the child. + if (!iterator->second->RetrieveRange(address, entry)) + *entry = iterator->second->entry_; + + return true; +} + + +template +void ContainedRangeMap::Clear() { + if (map_) { + MapConstIterator end = map_->end(); + for (MapConstIterator child = map_->begin(); child != end; ++child) + delete child->second; + + delete map_; + map_ = NULL; + } +} + + +} // namespace google_breakpad + + +#endif // PROCESSOR_CONTAINED_RANGE_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/contained_range_map.h b/src/lib/crashdump/gbreakpad/processor/contained_range_map.h new file mode 100644 index 0000000..1015ae8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/contained_range_map.h @@ -0,0 +1,150 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// contained_range_map.h: Hierarchically-organized range maps. +// +// A contained range map is similar to a standard range map, except it allows +// objects to be organized hierarchically. A contained range map allows +// objects to contain other objects. It is not sensitive to the order that +// objects are added to the map: larger, more general, containing objects +// may be added either before or after smaller, more specific, contained +// ones. +// +// Contained range maps guarantee that each object may only contain smaller +// objects than itself, and that a parent object may only contain child +// objects located entirely within the parent's address space. Attempts +// to introduce objects (via StoreRange) that violate these rules will fail. +// Retrieval (via RetrieveRange) always returns the most specific (smallest) +// object that contains the address being queried. Note that while it is +// not possible to insert two objects into a map that have exactly the same +// geometry (base address and size), it is possible to completely mask a +// larger object by inserting smaller objects that entirely fill the larger +// object's address space. +// +// Internally, contained range maps are implemented as a tree. Each tree +// node except for the root node describes an object in the map. Each node +// maintains its list of children in a map similar to a standard range map, +// keyed by the highest address that each child occupies. Each node's +// children occupy address ranges entirely within the node. The root node +// is the only node directly accessible to the user, and represents the +// entire address space. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_CONTAINED_RANGE_MAP_H__ +#define PROCESSOR_CONTAINED_RANGE_MAP_H__ + + +#include + + +namespace google_breakpad { + +// Forward declarations (for later friend declarations of specialized template). +template class ContainedRangeMapSerializer; + +template +class ContainedRangeMap { + public: + // The default constructor creates a ContainedRangeMap with no geometry + // and no entry, and as such is only suitable for the root node of a + // ContainedRangeMap tree. + ContainedRangeMap() : base_(), entry_(), map_(NULL) {} + + ~ContainedRangeMap(); + + // Inserts a range into the map. If the new range is encompassed by + // an existing child range, the new range is passed into the child range's + // StoreRange method. If the new range encompasses any existing child + // ranges, those child ranges are moved to the new range, becoming + // grandchildren of this ContainedRangeMap. Returns false for a + // parameter error, or if the ContainedRangeMap hierarchy guarantees + // would be violated. + bool StoreRange(const AddressType &base, + const AddressType &size, + const EntryType &entry); + + // Retrieves the most specific (smallest) descendant range encompassing + // the specified address. This method will only return entries held by + // child ranges, and not the entry contained by |this|. This is necessary + // to support a sparsely-populated root range. If no descendant range + // encompasses the address, returns false. + bool RetrieveRange(const AddressType &address, EntryType *entry) const; + + // Removes all children. Note that Clear only removes descendants, + // leaving the node on which it is called intact. Because the only + // meaningful things contained by a root node are descendants, this + // is sufficient to restore an entire ContainedRangeMap to its initial + // empty state when called on the root node. + void Clear(); + + private: + friend class ContainedRangeMapSerializer; + friend class ModuleComparer; + + // AddressToRangeMap stores pointers. This makes reparenting simpler in + // StoreRange, because it doesn't need to copy entire objects. + typedef std::map AddressToRangeMap; + typedef typename AddressToRangeMap::const_iterator MapConstIterator; + typedef typename AddressToRangeMap::iterator MapIterator; + typedef typename AddressToRangeMap::value_type MapValue; + + // Creates a new ContainedRangeMap with the specified base address, entry, + // and initial child map, which may be NULL. This is only used internally + // by ContainedRangeMap when it creates a new child. + ContainedRangeMap(const AddressType &base, const EntryType &entry, + AddressToRangeMap *map) + : base_(base), entry_(entry), map_(map) {} + + // The base address of this range. The high address does not need to + // be stored, because it is used as the key to an object in its parent's + // map, and all ContainedRangeMaps except for the root range are contained + // within maps. The root range does not actually contain an entry, so its + // base_ field is meaningless, and the fact that it has no parent and thus + // no key is unimportant. For this reason, the base_ field should only be + // is accessed on child ContainedRangeMap objects, and never on |this|. + const AddressType base_; + + // The entry corresponding to this range. The root range does not + // actually contain an entry, so its entry_ field is meaningless. For + // this reason, the entry_ field should only be accessed on child + // ContainedRangeMap objects, and never on |this|. + const EntryType entry_; + + // The map containing child ranges, keyed by each child range's high + // address. This is a pointer to avoid allocating map structures for + // leaf nodes, where they are not needed. + AddressToRangeMap *map_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_CONTAINED_RANGE_MAP_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/contained_range_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/contained_range_map_unittest.cc new file mode 100644 index 0000000..e5910da --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/contained_range_map_unittest.cc @@ -0,0 +1,263 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// contained_range_map_unittest.cc: Unit tests for ContainedRangeMap +// +// Author: Mark Mentovai + +#include + +#include "processor/contained_range_map-inl.h" + +#include "processor/logging.h" + + +#define ASSERT_TRUE(condition) \ + if (!(condition)) { \ + fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \ + return false; \ + } + +#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition)) + + +namespace { + + +using google_breakpad::ContainedRangeMap; + + +static bool RunTests() { + ContainedRangeMap crm; + + // First, do the StoreRange tests. This validates the containment + // rules. + ASSERT_TRUE (crm.StoreRange(10, 10, 1)); + ASSERT_FALSE(crm.StoreRange(10, 10, 2)); // exactly equal to 1 + ASSERT_FALSE(crm.StoreRange(11, 10, 3)); // begins inside 1 and extends up + ASSERT_FALSE(crm.StoreRange( 9, 10, 4)); // begins below 1 and ends inside + ASSERT_TRUE (crm.StoreRange(11, 9, 5)); // contained by existing + ASSERT_TRUE (crm.StoreRange(12, 7, 6)); + ASSERT_TRUE (crm.StoreRange( 9, 12, 7)); // contains existing + ASSERT_TRUE (crm.StoreRange( 9, 13, 8)); + ASSERT_TRUE (crm.StoreRange( 8, 14, 9)); + ASSERT_TRUE (crm.StoreRange(30, 3, 10)); + ASSERT_TRUE (crm.StoreRange(33, 3, 11)); + ASSERT_TRUE (crm.StoreRange(30, 6, 12)); // storable but totally masked + ASSERT_TRUE (crm.StoreRange(40, 8, 13)); // will be totally masked + ASSERT_TRUE (crm.StoreRange(40, 4, 14)); + ASSERT_TRUE (crm.StoreRange(44, 4, 15)); + ASSERT_FALSE(crm.StoreRange(32, 10, 16)); // begins in #10, ends in #14 + ASSERT_FALSE(crm.StoreRange(50, 0, 17)); // zero length + ASSERT_TRUE (crm.StoreRange(50, 10, 18)); + ASSERT_TRUE (crm.StoreRange(50, 1, 19)); + ASSERT_TRUE (crm.StoreRange(59, 1, 20)); + ASSERT_TRUE (crm.StoreRange(60, 1, 21)); + ASSERT_TRUE (crm.StoreRange(69, 1, 22)); + ASSERT_TRUE (crm.StoreRange(60, 10, 23)); + ASSERT_TRUE (crm.StoreRange(68, 1, 24)); + ASSERT_TRUE (crm.StoreRange(61, 1, 25)); + ASSERT_TRUE (crm.StoreRange(61, 8, 26)); + ASSERT_FALSE(crm.StoreRange(59, 9, 27)); + ASSERT_FALSE(crm.StoreRange(59, 10, 28)); + ASSERT_FALSE(crm.StoreRange(59, 11, 29)); + ASSERT_TRUE (crm.StoreRange(70, 10, 30)); + ASSERT_TRUE (crm.StoreRange(74, 2, 31)); + ASSERT_TRUE (crm.StoreRange(77, 2, 32)); + ASSERT_FALSE(crm.StoreRange(72, 6, 33)); + ASSERT_TRUE (crm.StoreRange(80, 3, 34)); + ASSERT_TRUE (crm.StoreRange(81, 1, 35)); + ASSERT_TRUE (crm.StoreRange(82, 1, 36)); + ASSERT_TRUE (crm.StoreRange(83, 3, 37)); + ASSERT_TRUE (crm.StoreRange(84, 1, 38)); + ASSERT_TRUE (crm.StoreRange(83, 1, 39)); + ASSERT_TRUE (crm.StoreRange(86, 5, 40)); + ASSERT_TRUE (crm.StoreRange(88, 1, 41)); + ASSERT_TRUE (crm.StoreRange(90, 1, 42)); + ASSERT_TRUE (crm.StoreRange(86, 1, 43)); + ASSERT_TRUE (crm.StoreRange(87, 1, 44)); + ASSERT_TRUE (crm.StoreRange(89, 1, 45)); + ASSERT_TRUE (crm.StoreRange(87, 4, 46)); + ASSERT_TRUE (crm.StoreRange(87, 3, 47)); + ASSERT_FALSE(crm.StoreRange(86, 2, 48)); + + // Each element in test_data contains the expected result when calling + // RetrieveRange on an address. + const int test_data[] = { + 0, // 0 + 0, // 1 + 0, // 2 + 0, // 3 + 0, // 4 + 0, // 5 + 0, // 6 + 0, // 7 + 9, // 8 + 7, // 9 + 1, // 10 + 5, // 11 + 6, // 12 + 6, // 13 + 6, // 14 + 6, // 15 + 6, // 16 + 6, // 17 + 6, // 18 + 5, // 19 + 7, // 20 + 8, // 21 + 0, // 22 + 0, // 23 + 0, // 24 + 0, // 25 + 0, // 26 + 0, // 27 + 0, // 28 + 0, // 29 + 10, // 30 + 10, // 31 + 10, // 32 + 11, // 33 + 11, // 34 + 11, // 35 + 0, // 36 + 0, // 37 + 0, // 38 + 0, // 39 + 14, // 40 + 14, // 41 + 14, // 42 + 14, // 43 + 15, // 44 + 15, // 45 + 15, // 46 + 15, // 47 + 0, // 48 + 0, // 49 + 19, // 50 + 18, // 51 + 18, // 52 + 18, // 53 + 18, // 54 + 18, // 55 + 18, // 56 + 18, // 57 + 18, // 58 + 20, // 59 + 21, // 60 + 25, // 61 + 26, // 62 + 26, // 63 + 26, // 64 + 26, // 65 + 26, // 66 + 26, // 67 + 24, // 68 + 22, // 69 + 30, // 70 + 30, // 71 + 30, // 72 + 30, // 73 + 31, // 74 + 31, // 75 + 30, // 76 + 32, // 77 + 32, // 78 + 30, // 79 + 34, // 80 + 35, // 81 + 36, // 82 + 39, // 83 + 38, // 84 + 37, // 85 + 43, // 86 + 44, // 87 + 41, // 88 + 45, // 89 + 42, // 90 + 0, // 91 + 0, // 92 + 0, // 93 + 0, // 94 + 0, // 95 + 0, // 96 + 0, // 97 + 0, // 98 + 0 // 99 + }; + unsigned int test_high = sizeof(test_data) / sizeof(int); + + // Now, do the RetrieveRange tests. This further validates that the + // objects were stored properly and that retrieval returns the correct + // object. + // If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a + // new test_data array will be printed. Exercise caution when doing this. + // Be sure to verify the results manually! +#ifdef GENERATE_TEST_DATA + printf(" const int test_data[] = {\n"); +#endif // GENERATE_TEST_DATA + + for (unsigned int address = 0; address < test_high; ++address) { + int value; + if (!crm.RetrieveRange(address, &value)) + value = 0; + +#ifndef GENERATE_TEST_DATA + // Don't use ASSERT inside the loop because it won't show the failed + // |address|, and the line number will always be the same. That makes + // it difficult to figure out which test failed. + if (value != test_data[address]) { + fprintf(stderr, "FAIL: retrieve %d expected %d observed %d @ %s:%d\n", + address, test_data[address], value, __FILE__, __LINE__); + return false; + } +#else // !GENERATE_TEST_DATA + printf(" %d%c%s // %d\n", value, + address == test_high - 1 ? ' ' : ',', + value < 10 ? " " : "", + address); +#endif // !GENERATE_TEST_DATA + } + +#ifdef GENERATE_TEST_DATA + printf(" };\n"); +#endif // GENERATE_TEST_DATA + + return true; +} + + +} // namespace + + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + return RunTests() ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/disassembler_x86.cc b/src/lib/crashdump/gbreakpad/processor/disassembler_x86.cc new file mode 100644 index 0000000..45a235d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/disassembler_x86.cc @@ -0,0 +1,241 @@ +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// disassembler_x86.cc: simple x86 disassembler. +// +// Provides single step disassembly of x86 bytecode and flags instructions +// that utilize known bad register values. +// +// Author: Cris Neckar + +#include "processor/disassembler_x86.h" + +#include +#include + +namespace google_breakpad { + +DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode, + u_int32_t size, + u_int32_t virtual_address) : + bytecode_(bytecode), + size_(size), + virtual_address_(virtual_address), + current_byte_offset_(0), + current_inst_offset_(0), + instr_valid_(false), + register_valid_(false), + pushed_bad_value_(false), + end_of_block_(false), + flags_(0) { + libdis::x86_init(libdis::opt_none, NULL, NULL); +} + +DisassemblerX86::~DisassemblerX86() { + if (instr_valid_) + libdis::x86_oplist_free(¤t_instr_); + + libdis::x86_cleanup(); +} + +u_int32_t DisassemblerX86::NextInstruction() { + if (instr_valid_) + libdis::x86_oplist_free(¤t_instr_); + + if (current_byte_offset_ >= size_) { + instr_valid_ = false; + return 0; + } + u_int32_t instr_size = 0; + instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_, + virtual_address_, current_byte_offset_, + ¤t_instr_); + if (instr_size == 0) { + instr_valid_ = false; + return 0; + } + + current_byte_offset_ += instr_size; + current_inst_offset_++; + instr_valid_ = libdis::x86_insn_is_valid(¤t_instr_); + if (!instr_valid_) + return 0; + + if (current_instr_.type == libdis::insn_return) + end_of_block_ = true; + libdis::x86_op_t *src = libdis::x86_get_src_operand(¤t_instr_); + libdis::x86_op_t *dest = libdis::x86_get_dest_operand(¤t_instr_); + + if (register_valid_) { + switch (current_instr_.group) { + // Flag branches based off of bad registers and calls that occur + // after pushing bad values. + case libdis::insn_controlflow: + switch (current_instr_.type) { + case libdis::insn_jmp: + case libdis::insn_jcc: + case libdis::insn_call: + case libdis::insn_callcc: + if (dest) { + switch (dest->type) { + case libdis::op_expression: + if (dest->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_BRANCH_TARGET; + break; + case libdis::op_register: + if (dest->data.reg.id == bad_register_.id) + flags_ |= DISX86_BAD_BRANCH_TARGET; + break; + default: + if (pushed_bad_value_ && + (current_instr_.type == libdis::insn_call || + current_instr_.type == libdis::insn_callcc)) + flags_ |= DISX86_BAD_ARGUMENT_PASSED; + break; + } + } + break; + default: + break; + } + break; + + // Flag block data operations that use bad registers for src or dest. + case libdis::insn_string: + if (dest && dest->type == libdis::op_expression && + dest->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_BLOCK_WRITE; + if (src && src->type == libdis::op_expression && + src->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_BLOCK_READ; + break; + + // Flag comparisons based on bad data. + case libdis::insn_comparison: + if ((dest && dest->type == libdis::op_expression && + dest->data.expression.base.id == bad_register_.id) || + (src && src->type == libdis::op_expression && + src->data.expression.base.id == bad_register_.id) || + (dest && dest->type == libdis::op_register && + dest->data.reg.id == bad_register_.id) || + (src && src->type == libdis::op_register && + src->data.reg.id == bad_register_.id)) + flags_ |= DISX86_BAD_COMPARISON; + break; + + // Flag any other instruction which derefs a bad register for + // src or dest. + default: + if (dest && dest->type == libdis::op_expression && + dest->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_WRITE; + if (src && src->type == libdis::op_expression && + src->data.expression.base.id == bad_register_.id) + flags_ |= DISX86_BAD_READ; + break; + } + } + + // When a register is marked as tainted check if it is pushed. + // TODO(cdn): may also want to check for MOVs into EBP offsets. + if (register_valid_ && dest && current_instr_.type == libdis::insn_push) { + switch (dest->type) { + case libdis::op_expression: + if (dest->data.expression.base.id == bad_register_.id || + dest->data.expression.index.id == bad_register_.id) + pushed_bad_value_ = true; + break; + case libdis::op_register: + if (dest->data.reg.id == bad_register_.id) + pushed_bad_value_ = true; + break; + default: + break; + } + } + + // Check if a tainted register value is clobbered. + // For conditional MOVs and XCHGs assume that + // there is a hit. + if (register_valid_) { + switch (current_instr_.type) { + case libdis::insn_xor: + if (src && src->type == libdis::op_register && + dest && dest->type == libdis::op_register && + src->data.reg.id == bad_register_.id && + src->data.reg.id == dest->data.reg.id) + register_valid_ = false; + break; + case libdis::insn_pop: + case libdis::insn_mov: + case libdis::insn_movcc: + if (dest && dest->type == libdis::op_register && + dest->data.reg.id == bad_register_.id) + register_valid_ = false; + break; + case libdis::insn_popregs: + register_valid_ = false; + break; + case libdis::insn_xchg: + case libdis::insn_xchgcc: + if (dest && dest->type == libdis::op_register && + src && src->type == libdis::op_register) { + if (dest->data.reg.id == bad_register_.id) + memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t)); + else if (src->data.reg.id == bad_register_.id) + memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t)); + } + break; + default: + break; + } + } + + return instr_size; +} + +bool DisassemblerX86::setBadRead() { + if (!instr_valid_) + return false; + + libdis::x86_op_t *operand = libdis::x86_get_src_operand(¤t_instr_); + if (!operand || operand->type != libdis::op_expression) + return false; + + memcpy(&bad_register_, &operand->data.expression.base, + sizeof(libdis::x86_reg_t)); + register_valid_ = true; + return true; +} + +bool DisassemblerX86::setBadWrite() { + if (!instr_valid_) + return false; + + libdis::x86_op_t *operand = libdis::x86_get_dest_operand(¤t_instr_); + if (!operand || operand->type != libdis::op_expression) + return false; + + memcpy(&bad_register_, &operand->data.expression.base, + sizeof(libdis::x86_reg_t)); + register_valid_ = true; + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/disassembler_x86.h b/src/lib/crashdump/gbreakpad/processor/disassembler_x86.h new file mode 100644 index 0000000..3bdd558 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/disassembler_x86.h @@ -0,0 +1,126 @@ +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// disassembler_x86.h: Basic x86 bytecode disassembler +// +// Provides a simple disassembler which wraps libdisasm. This allows simple +// tests to be run against bytecode to test for various properties. +// +// Author: Cris Neckar + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace libdis { +#include "third_party/libdisasm/libdis.h" +} + +namespace google_breakpad { + +enum { + DISX86_NONE = 0x0, + DISX86_BAD_BRANCH_TARGET = 0x1, + DISX86_BAD_ARGUMENT_PASSED = 0x2, + DISX86_BAD_WRITE = 0x4, + DISX86_BAD_BLOCK_WRITE = 0x8, + DISX86_BAD_READ = 0x10, + DISX86_BAD_BLOCK_READ = 0x20, + DISX86_BAD_COMPARISON = 0x40 +}; + +class DisassemblerX86 { + public: + // TODO(cdn): Modify this class to take a MemoryRegion instead of just + // a raw buffer. This will make it easier to use this on arbitrary + // minidumps without first copying out the code segment. + DisassemblerX86(const u_int8_t *bytecode, u_int32_t, u_int32_t); + ~DisassemblerX86(); + + // This walks to the next instruction in the memory region and + // sets flags based on the type of instruction and previous state + // including any registers marked as bad through setBadRead() + // or setBadWrite(). This method can be called in a loop to + // disassemble until the end of a region. + u_int32_t NextInstruction(); + + // Indicates whether the current disassembled instruction was valid. + bool currentInstructionValid() { return instr_valid_; } + + // Returns the current instruction as defined in libdis.h, + // or NULL if the current instruction is not valid. + const libdis::x86_insn_t* currentInstruction() { + return instr_valid_ ? ¤t_instr_ : NULL; + } + + // Returns the type of the current instruction as defined in libdis.h. + libdis::x86_insn_group currentInstructionGroup() { + return current_instr_.group; + } + + // Indicates whether a return instruction has been encountered. + bool endOfBlock() { return end_of_block_; } + + // The flags set so far for the disassembly. + u_int16_t flags() { return flags_; } + + // This sets an indicator that the register used to determine + // src or dest for the current instruction is tainted. These can + // be used after examining the current instruction to indicate, + // for example that a bad read or write occurred and the pointer + // stored in the register is currently invalid. + bool setBadRead(); + bool setBadWrite(); + + protected: + const u_int8_t *bytecode_; + u_int32_t size_; + u_int32_t virtual_address_; + u_int32_t current_byte_offset_; + u_int32_t current_inst_offset_; + + bool instr_valid_; + libdis::x86_insn_t current_instr_; + + // TODO(cdn): Maybe also track an expression's index register. + // ex: mov eax, [ebx + ecx]; ebx is base, ecx is index. + bool register_valid_; + libdis::x86_reg_t bad_register_; + + bool pushed_bad_value_; + bool end_of_block_; + + u_int16_t flags_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/disassembler_x86_unittest.cc b/src/lib/crashdump/gbreakpad/processor/disassembler_x86_unittest.cc new file mode 100644 index 0000000..b6884c1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/disassembler_x86_unittest.cc @@ -0,0 +1,244 @@ +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "breakpad_googletest_includes.h" +#include "processor/disassembler_x86.h" +#include "third_party/libdisasm/libdis.h" + +namespace { + +using google_breakpad::DisassemblerX86; + +unsigned char just_return[] = "\xc3"; // retn + +unsigned char invalid_instruction[] = "\x00"; // invalid + +unsigned char read_eax_jmp_eax[] = + "\x8b\x18" // mov ebx, [eax]; + "\x33\xc9" // xor ebx, ebx; + "\xff\x20" // jmp eax; + "\xc3"; // retn; + +unsigned char write_eax_arg_to_call[] = + "\x89\xa8\x00\x02\x00\x00" // mov [eax+200], ebp; + "\xc1\xeb\x02" // shr ebx, 2; + "\x50" // push eax; + "\xe8\xd1\x24\x77\x88" // call something; + "\xc3"; // retn; + +unsigned char read_edi_stosb[] = + "\x8b\x07" // mov eax, [edi]; + "\x8b\xc8" // mov ecx, eax; + "\xf3\xaa" // rep stosb; + "\xc3"; // retn; + +unsigned char read_clobber_write[] = + "\x03\x18" // add ebx, [eax]; + "\x8b\xc1" // mov eax, ecx; + "\x89\x10" // mov [eax], edx; + "\xc3"; // retn; + +unsigned char read_xchg_write[] = + "\x03\x18" // add ebx, [eax]; + "\x91" // xchg eax, ecx; + "\x89\x18" // mov [eax], ebx; + "\x89\x11" // mov [ecx], edx; + "\xc3"; // retn; + +unsigned char read_cmp[] = + "\x03\x18" // add ebx, [eax]; + "\x83\xf8\x00" // cmp eax, 0; + "\x74\x04" // je +4; + "\xc3"; // retn; + +TEST(DisassemblerX86Test, SimpleReturnInstruction) { + DisassemblerX86 dis(just_return, sizeof(just_return)-1, 0); + EXPECT_EQ(1, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(true, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); + const libdis::x86_insn_t* instruction = dis.currentInstruction(); + EXPECT_EQ(libdis::insn_controlflow, instruction->group); + EXPECT_EQ(libdis::insn_return, instruction->type); + EXPECT_EQ(0, dis.NextInstruction()); + EXPECT_EQ(false, dis.currentInstructionValid()); + EXPECT_EQ(NULL, dis.currentInstruction()); +} + +TEST(DisassemblerX86Test, SimpleInvalidInstruction) { + DisassemblerX86 dis(invalid_instruction, sizeof(invalid_instruction)-1, 0); + EXPECT_EQ(0, dis.NextInstruction()); + EXPECT_EQ(false, dis.currentInstructionValid()); +} + +TEST(DisassemblerX86Test, BadReadLeadsToBranch) { + DisassemblerX86 dis(read_eax_jmp_eax, sizeof(read_eax_jmp_eax)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_logic, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_BRANCH_TARGET, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadWriteLeadsToPushedArg) { + DisassemblerX86 dis(write_eax_arg_to_call, + sizeof(write_eax_arg_to_call)-1, 0); + EXPECT_EQ(6, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadWrite()); + EXPECT_EQ(3, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(1, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(5, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_ARGUMENT_PASSED, dis.flags()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); + EXPECT_EQ(false, dis.endOfBlock()); +} + + +TEST(DisassemblerX86Test, BadReadLeadsToBlockWrite) { + DisassemblerX86 dis(read_edi_stosb, sizeof(read_edi_stosb)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_BLOCK_WRITE, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_string, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadReadClobberThenWrite) { + DisassemblerX86 dis(read_clobber_write, sizeof(read_clobber_write)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadReadXCHGThenWrite) { + DisassemblerX86 dis(read_xchg_write, sizeof(read_xchg_write)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(1, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_WRITE, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup()); +} + +TEST(DisassemblerX86Test, BadReadThenCMP) { + DisassemblerX86 dis(read_cmp, sizeof(read_cmp)-1, 0); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(0, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup()); + EXPECT_EQ(true, dis.setBadRead()); + EXPECT_EQ(3, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_comparison, dis.currentInstructionGroup()); + EXPECT_EQ(2, dis.NextInstruction()); + EXPECT_EQ(true, dis.currentInstructionValid()); + EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags()); + EXPECT_EQ(false, dis.endOfBlock()); + EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup()); +} +} + diff --git a/src/lib/crashdump/gbreakpad/processor/exploitability.cc b/src/lib/crashdump/gbreakpad/processor/exploitability.cc new file mode 100644 index 0000000..eb0b4d3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/exploitability.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// exploitability_engine.cc: Generic exploitability engine. +// +// See exploitable_engine.h for documentation. +// +// Author: Cris Neckar + + +#include + +#include "google_breakpad/processor/exploitability.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/process_state.h" +#include "processor/exploitability_win.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +Exploitability::Exploitability(Minidump *dump, + ProcessState *process_state) + : dump_(dump), + process_state_(process_state) {} + +ExploitabilityRating Exploitability::CheckExploitability() { + return CheckPlatformExploitability(); +} + +Exploitability *Exploitability::ExploitabilityForPlatform( + Minidump *dump, + ProcessState *process_state) { + Exploitability *platform_exploitability = NULL; + MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo(); + if (!minidump_system_info) + return NULL; + + const MDRawSystemInfo *raw_system_info = + minidump_system_info->system_info(); + if (!raw_system_info) + return NULL; + + switch (raw_system_info->platform_id) { + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: { + platform_exploitability = new ExploitabilityWin(dump, + process_state); + break; + } + case MD_OS_MAC_OS_X: + case MD_OS_IOS: + case MD_OS_LINUX: + case MD_OS_UNIX: + case MD_OS_SOLARIS: + default: { + platform_exploitability = NULL; + break; + } + } + + BPLOG_IF(ERROR, !platform_exploitability) << + "No Exploitability module for platform: " << + process_state->system_info()->os; + return platform_exploitability; +} + +bool Exploitability::AddressIsAscii(u_int64_t address) { + for (int i = 0; i < 8; i++) { + u_int8_t byte = (address >> (8*i)) & 0xff; + if ((byte >= ' ' && byte <= '~') || byte == 0) + continue; + return false; + } + return true; +} + +} // namespace google_breakpad + diff --git a/src/lib/crashdump/gbreakpad/processor/exploitability_unittest.cc b/src/lib/crashdump/gbreakpad/processor/exploitability_unittest.cc new file mode 100644 index 0000000..4de6f1d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/exploitability_unittest.cc @@ -0,0 +1,255 @@ +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "breakpad_googletest_includes.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/minidump_processor.h" +#include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/symbol_supplier.h" + +namespace google_breakpad { +class MockMinidump : public Minidump { + public: + MockMinidump() : Minidump("") { + } + + MOCK_METHOD0(Read, bool()); + MOCK_CONST_METHOD0(path, string()); + MOCK_CONST_METHOD0(header, const MDRawHeader*()); + MOCK_METHOD0(GetThreadList, MinidumpThreadList*()); +}; +} + +namespace { + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::CodeModule; +using google_breakpad::MinidumpProcessor; +using google_breakpad::MinidumpThreadList; +using google_breakpad::MinidumpThread; +using google_breakpad::MockMinidump; +using google_breakpad::ProcessState; +using google_breakpad::SymbolSupplier; +using google_breakpad::SystemInfo; +using std::string; + +class TestSymbolSupplier : public SymbolSupplier { + public: + TestSymbolSupplier() : interrupt_(false) {} + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file); + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); + + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); + + virtual void FreeSymbolData(const CodeModule *module) { } + // When set to true, causes the SymbolSupplier to return INTERRUPT + void set_interrupt(bool interrupt) { interrupt_ = interrupt; } + + private: + bool interrupt_; +}; + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file) { + + if (interrupt_) { + return INTERRUPT; + } + + return NOT_FOUND; +} + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + return GetSymbolFile(module, system_info, symbol_file); +} + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + return GetSymbolFile(module, system_info, symbol_file); +} + +TEST(ExploitabilityTest, TestWindowsEngine) { + TestSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver, true); + ProcessState state; + + string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_block_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_clobber_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_conditional.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_then_jmp.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_read_av_xchg_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_write_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/ascii_write_av_arg_to_call.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/null_read_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/null_write_av.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/stack_exhaustion.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/exec_av_on_stack.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/write_av_non_null.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABLITY_MEDIUM, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/read_av_non_null.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/read_av_clobber_write.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW, + state.exploitability()); + + minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/read_av_conditional.dmp"; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW, + state.exploitability()); +} +} diff --git a/src/lib/crashdump/gbreakpad/processor/exploitability_win.cc b/src/lib/crashdump/gbreakpad/processor/exploitability_win.cc new file mode 100644 index 0000000..443635f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/exploitability_win.cc @@ -0,0 +1,290 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// exploitability_win.cc: Windows specific exploitability engine. +// +// Provides a guess at the exploitability of the crash for the Windows +// platform given a minidump and process_state. +// +// Author: Cris Neckar + +#include + +#include "processor/exploitability_win.h" + +#include "google_breakpad/common/minidump_exception_win32.h" +#include "google_breakpad/processor/minidump.h" +#include "processor/disassembler_x86.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + +#include "third_party/libdisasm/libdis.h" + +namespace google_breakpad { + +// The cutoff that we use to judge if and address is likely an offset +// from various interesting addresses. +static const u_int64_t kProbableNullOffset = 4096; +static const u_int64_t kProbableStackOffset = 8192; + +// The various cutoffs for the different ratings. +static const size_t kHighCutoff = 100; +static const size_t kMediumCutoff = 80; +static const size_t kLowCutoff = 50; +static const size_t kInterestingCutoff = 25; + +// Predefined incremental values for conditional weighting. +static const size_t kTinyBump = 5; +static const size_t kSmallBump = 20; +static const size_t kMediumBump = 50; +static const size_t kLargeBump = 70; +static const size_t kHugeBump = 90; + +// The maximum number of bytes to disassemble past the program counter. +static const size_t kDisassembleBytesBeyondPC = 2048; + +ExploitabilityWin::ExploitabilityWin(Minidump *dump, + ProcessState *process_state) + : Exploitability(dump, process_state) { } + +ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() { + MinidumpException *exception = dump_->GetException(); + if (!exception) { + BPLOG(INFO) << "Minidump does not have exception record."; + return EXPLOITABILITY_ERR_PROCESSING; + } + + const MDRawExceptionStream *raw_exception = exception->exception(); + if (!raw_exception) { + BPLOG(INFO) << "Could not obtain raw exception info."; + return EXPLOITABILITY_ERR_PROCESSING; + } + + const MinidumpContext *context = exception->GetContext(); + if (!context) { + BPLOG(INFO) << "Could not obtain exception context."; + return EXPLOITABILITY_ERR_PROCESSING; + } + + MinidumpMemoryList *memory_list = dump_->GetMemoryList(); + bool memory_available = true; + if (!memory_list) { + BPLOG(INFO) << "Minidump memory segments not available."; + memory_available = false; + } + u_int64_t address = process_state_->crash_address(); + u_int32_t exception_code = raw_exception->exception_record.exception_code; + + u_int32_t exploitability_weight = 0; + + u_int64_t stack_ptr = 0; + u_int64_t instruction_ptr = 0; + u_int64_t this_ptr = 0; + + switch (context->GetContextCPU()) { + case MD_CONTEXT_X86: + stack_ptr = context->GetContextX86()->esp; + instruction_ptr = context->GetContextX86()->eip; + this_ptr = context->GetContextX86()->ecx; + break; + case MD_CONTEXT_AMD64: + stack_ptr = context->GetContextAMD64()->rsp; + instruction_ptr = context->GetContextAMD64()->rip; + this_ptr = context->GetContextAMD64()->rcx; + break; + default: + BPLOG(INFO) << "Unsupported architecture."; + return EXPLOITABILITY_ERR_PROCESSING; + } + + // Check if we are executing on the stack. + if (instruction_ptr <= (stack_ptr + kProbableStackOffset) && + instruction_ptr >= (stack_ptr - kProbableStackOffset)) + exploitability_weight += kHugeBump; + + switch (exception_code) { + // This is almost certainly recursion. + case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW: + exploitability_weight += kTinyBump; + break; + + // These exceptions tend to be benign and we can generally ignore them. + case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO: + case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW: + case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO: + case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT: + case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW: + case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW: + case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR: + exploitability_weight += kTinyBump; + break; + + // These exceptions will typically mean that we have jumped where we + // shouldn't. + case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION: + case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION: + case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION: + exploitability_weight += kLargeBump; + break; + + // These represent bugs in exception handlers. + case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION: + case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION: + exploitability_weight += kSmallBump; + break; + + case MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION: + case MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN: + exploitability_weight += kHugeBump; + break; + + case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION: + exploitability_weight += kLargeBump; + break; + + case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION: + bool near_null = (address <= kProbableNullOffset); + bool bad_read = false; + bool bad_write = false; + if (raw_exception->exception_record.number_parameters >= 1) { + MDAccessViolationTypeWin av_type = + static_cast + (raw_exception->exception_record.exception_information[0]); + switch (av_type) { + case MD_ACCESS_VIOLATION_WIN_READ: + bad_read = true; + if (near_null) + exploitability_weight += kSmallBump; + else + exploitability_weight += kMediumBump; + break; + case MD_ACCESS_VIOLATION_WIN_WRITE: + bad_write = true; + if (near_null) + exploitability_weight += kSmallBump; + else + exploitability_weight += kHugeBump; + break; + case MD_ACCESS_VIOLATION_WIN_EXEC: + if (near_null) + exploitability_weight += kSmallBump; + else + exploitability_weight += kHugeBump; + break; + default: + BPLOG(INFO) << "Unrecognized access violation type."; + return EXPLOITABILITY_ERR_PROCESSING; + break; + } + MinidumpMemoryRegion *instruction_region = 0; + if (memory_available) { + instruction_region = + memory_list->GetMemoryRegionForAddress(instruction_ptr); + } + if (!near_null && instruction_region && + context->GetContextCPU() == MD_CONTEXT_X86 && + (bad_read || bad_write)) { + // Perform checks related to memory around instruction pointer. + u_int32_t memory_offset = + instruction_ptr - instruction_region->GetBase(); + u_int32_t available_memory = + instruction_region->GetSize() - memory_offset; + available_memory = available_memory > kDisassembleBytesBeyondPC ? + kDisassembleBytesBeyondPC : available_memory; + if (available_memory) { + const u_int8_t *raw_memory = + instruction_region->GetMemory() + memory_offset; + DisassemblerX86 disassembler(raw_memory, + available_memory, + instruction_ptr); + disassembler.NextInstruction(); + if (bad_read) + disassembler.setBadRead(); + else + disassembler.setBadWrite(); + if (disassembler.currentInstructionValid()) { + // Check if the faulting instruction falls into one of + // several interesting groups. + switch (disassembler.currentInstructionGroup()) { + case libdis::insn_controlflow: + exploitability_weight += kLargeBump; + break; + case libdis::insn_string: + exploitability_weight += kHugeBump; + break; + default: + break; + } + // Loop the disassembler through the code and check if it + // IDed any interesting conditions in the near future. + // Multiple flags may be set so treat each equally. + while (disassembler.NextInstruction() && + disassembler.currentInstructionValid() && + !disassembler.endOfBlock()) + continue; + if (disassembler.flags() & DISX86_BAD_BRANCH_TARGET) + exploitability_weight += kLargeBump; + if (disassembler.flags() & DISX86_BAD_ARGUMENT_PASSED) + exploitability_weight += kTinyBump; + if (disassembler.flags() & DISX86_BAD_WRITE) + exploitability_weight += kMediumBump; + if (disassembler.flags() & DISX86_BAD_BLOCK_WRITE) + exploitability_weight += kMediumBump; + if (disassembler.flags() & DISX86_BAD_READ) + exploitability_weight += kTinyBump; + if (disassembler.flags() & DISX86_BAD_BLOCK_READ) + exploitability_weight += kTinyBump; + if (disassembler.flags() & DISX86_BAD_COMPARISON) + exploitability_weight += kTinyBump; + } + } + } + if (!near_null && AddressIsAscii(address)) + exploitability_weight += kMediumBump; + } else { + BPLOG(INFO) << "Access violation type parameter missing."; + return EXPLOITABILITY_ERR_PROCESSING; + } + } + + // Based on the calculated weight we return a simplified classification. + BPLOG(INFO) << "Calculated exploitability weight: " << exploitability_weight; + if (exploitability_weight >= kHighCutoff) + return EXPLOITABILITY_HIGH; + if (exploitability_weight >= kMediumCutoff) + return EXPLOITABLITY_MEDIUM; + if (exploitability_weight >= kLowCutoff) + return EXPLOITABILITY_LOW; + if (exploitability_weight >= kInterestingCutoff) + return EXPLOITABILITY_INTERESTING; + + return EXPLOITABILITY_NONE; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/exploitability_win.h b/src/lib/crashdump/gbreakpad/processor/exploitability_win.h new file mode 100644 index 0000000..4e08aef --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/exploitability_win.h @@ -0,0 +1,55 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// exploitability_win.h: Windows specific exploitability engine. +// +// Provides a guess at the exploitability of the crash for the Windows +// platform given a minidump and process_state. +// +// Author: Cris Neckar + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_ + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/exploitability.h" + +namespace google_breakpad { + +class ExploitabilityWin : public Exploitability { + public: + ExploitabilityWin(Minidump *dump, + ProcessState *process_state); + + virtual ExploitabilityRating CheckPlatformExploitability(); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver.cc b/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver.cc new file mode 100644 index 0000000..45c1f0f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver.cc @@ -0,0 +1,260 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// fast_source_line_resolver.cc: FastSourceLineResolver is a concrete class that +// implements SourceLineResolverInterface. Both FastSourceLineResolver and +// BasicSourceLineResolver inherit from SourceLineResolverBase class to reduce +// code redundancy. +// +// See fast_source_line_resolver.h and fast_source_line_resolver_types.h +// for more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include "google_breakpad/processor/fast_source_line_resolver.h" +#include "processor/fast_source_line_resolver_types.h" + +#include +#include + +#include "processor/module_factory.h" +#include "processor/scoped_ptr.h" + +using std::map; +using std::make_pair; + +namespace google_breakpad { + +FastSourceLineResolver::FastSourceLineResolver() + : SourceLineResolverBase(new FastModuleFactory) { } + +bool FastSourceLineResolver::ShouldDeleteMemoryBufferAfterLoadModule() { + return false; +} + +void FastSourceLineResolver::Module::LookupAddress(StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + + // First, look for a FUNC record that covers address. Use + // RetrieveNearestRange instead of RetrieveRange so that, if there + // is no such function, we can use the next function to bound the + // extent of the PUBLIC symbol we find, below. This does mean we + // need to check that address indeed falls within the function we + // find; do the range comparison in an overflow-friendly way. + scoped_ptr func(new Function); + const Function* func_ptr = 0; + scoped_ptr public_symbol(new PublicSymbol); + const PublicSymbol* public_symbol_ptr = 0; + MemAddr function_base; + MemAddr function_size; + MemAddr public_address; + + if (functions_.RetrieveNearestRange(address, func_ptr, + &function_base, &function_size) && + address >= function_base && address - function_base < function_size) { + func.get()->CopyFrom(func_ptr); + frame->function_name = func->name; + frame->function_base = frame->module->base_address() + function_base; + + scoped_ptr line(new Line); + const Line* line_ptr = 0; + MemAddr line_base; + if (func->lines.RetrieveRange(address, line_ptr, &line_base, NULL)) { + line.get()->CopyFrom(line_ptr); + FileMap::iterator it = files_.find(line->source_file_id); + if (it != files_.end()) { + frame->source_file_name = + files_.find(line->source_file_id).GetValuePtr(); + } + frame->source_line = line->line; + frame->source_line_base = frame->module->base_address() + line_base; + } + } else if (public_symbols_.Retrieve(address, + public_symbol_ptr, &public_address) && + (!func_ptr || public_address > function_base)) { + public_symbol.get()->CopyFrom(public_symbol_ptr); + frame->function_name = public_symbol->name; + frame->function_base = frame->module->base_address() + public_address; + } +} + +// WFI: WindowsFrameInfo. +// Returns a WFI object reading from a raw memory chunk of data +WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) { + // The first 4Bytes of int data are unused. + // They corresponds to "int valid;" data member of WFI. + const u_int32_t *para_uint32 = reinterpret_cast( + raw + sizeof(int32_t)); + + u_int32_t prolog_size = para_uint32[0];; + u_int32_t epilog_size = para_uint32[1]; + u_int32_t parameter_size = para_uint32[2]; + u_int32_t saved_register_size = para_uint32[3]; + u_int32_t local_size = para_uint32[4]; + u_int32_t max_stack_size = para_uint32[5]; + const char *boolean = reinterpret_cast(para_uint32 + 6); + bool allocates_base_pointer = (*boolean != 0); + std::string program_string = boolean + 1; + + return WindowsFrameInfo(prolog_size, + epilog_size, + parameter_size, + saved_register_size, + local_size, + max_stack_size, + allocates_base_pointer, + program_string); +} + +// Loads a map from the given buffer in char* type. +// Does NOT take ownership of mem_buffer. +// In addition, treat mem_buffer as const char*. +bool FastSourceLineResolver::Module::LoadMapFromMemory(char *mem_buffer) { + if (!mem_buffer) return false; + + const u_int32_t *map_sizes = reinterpret_cast(mem_buffer); + + unsigned int header_size = kNumberMaps_ * sizeof(unsigned int); + + // offsets[]: an array of offset addresses (with respect to mem_buffer), + // for each "Static***Map" component of Module. + // "Static***Map": static version of std::map or map wrapper, i.e., StaticMap, + // StaticAddressMap, StaticContainedRangeMap, and StaticRangeMap. + unsigned int offsets[kNumberMaps_]; + offsets[0] = header_size; + for (int i = 1; i < kNumberMaps_; ++i) { + offsets[i] = offsets[i - 1] + map_sizes[i - 1]; + } + + // Use pointers to construct Static*Map data members in Module: + int map_id = 0; + files_ = StaticMap(mem_buffer + offsets[map_id++]); + functions_ = + StaticRangeMap(mem_buffer + offsets[map_id++]); + public_symbols_ = + StaticAddressMap(mem_buffer + offsets[map_id++]); + for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i) + windows_frame_info_[i] = + StaticContainedRangeMap(mem_buffer + offsets[map_id++]); + + cfi_initial_rules_ = + StaticRangeMap(mem_buffer + offsets[map_id++]); + cfi_delta_rules_ = StaticMap(mem_buffer + offsets[map_id++]); + + return true; +} + +WindowsFrameInfo *FastSourceLineResolver::Module::FindWindowsFrameInfo( + const StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + scoped_ptr result(new WindowsFrameInfo()); + + // We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and + // WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order. + // WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that + // includes its own program string. + // WindowsFrameInfo::STACK_INFO_FPO is the older type + // corresponding to the FPO_DATA struct. See stackwalker_x86.cc. + const char* frame_info_ptr; + if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA] + .RetrieveRange(address, frame_info_ptr)) + || (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO] + .RetrieveRange(address, frame_info_ptr))) { + result->CopyFrom(CopyWFI(frame_info_ptr)); + return result.release(); + } + + // Even without a relevant STACK line, many functions contain + // information about how much space their parameters consume on the + // stack. Use RetrieveNearestRange instead of RetrieveRange, so that + // we can use the function to bound the extent of the PUBLIC symbol, + // below. However, this does mean we need to check that ADDRESS + // falls within the retrieved function's range; do the range + // comparison in an overflow-friendly way. + scoped_ptr function(new Function); + const Function* function_ptr = 0; + MemAddr function_base, function_size; + if (functions_.RetrieveNearestRange(address, function_ptr, + &function_base, &function_size) && + address >= function_base && address - function_base < function_size) { + function.get()->CopyFrom(function_ptr); + result->parameter_size = function->parameter_size; + result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE; + return result.release(); + } + + // PUBLIC symbols might have a parameter size. Use the function we + // found above to limit the range the public symbol covers. + scoped_ptr public_symbol(new PublicSymbol); + const PublicSymbol* public_symbol_ptr = 0; + MemAddr public_address; + if (public_symbols_.Retrieve(address, public_symbol_ptr, &public_address) && + (!function_ptr || public_address > function_base)) { + public_symbol.get()->CopyFrom(public_symbol_ptr); + result->parameter_size = public_symbol->parameter_size; + } + + return NULL; +} + +CFIFrameInfo *FastSourceLineResolver::Module::FindCFIFrameInfo( + const StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + MemAddr initial_base, initial_size; + const char* initial_rules = NULL; + + // Find the initial rule whose range covers this address. That + // provides an initial set of register recovery rules. Then, walk + // forward from the initial rule's starting address to frame's + // instruction address, applying delta rules. + if (!cfi_initial_rules_.RetrieveRange(address, initial_rules, + &initial_base, &initial_size)) { + return NULL; + } + + // Create a frame info structure, and populate it with the rules from + // the STACK CFI INIT record. + scoped_ptr rules(new CFIFrameInfo()); + if (!ParseCFIRuleSet(initial_rules, rules.get())) + return NULL; + + // Find the first delta rule that falls within the initial rule's range. + StaticMap::iterator delta = + cfi_delta_rules_.lower_bound(initial_base); + + // Apply delta rules up to and including the frame's address. + while (delta != cfi_delta_rules_.end() && delta.GetKey() <= address) { + ParseCFIRuleSet(delta.GetValuePtr(), rules.get()); + delta++; + } + + return rules.release(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_types.h b/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_types.h new file mode 100644 index 0000000..c4cec60 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_types.h @@ -0,0 +1,179 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// fast_source_line_resolver_types.h: definition of nested classes/structs in +// FastSourceLineResolver. It moves the definitions out of +// fast_source_line_resolver.cc, so that other classes could have access +// to these private nested types without including fast_source_line_resolver.cc +// +// Author: lambxsy@google.com (Siyang Xie) + +#ifndef PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__ +#define PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__ + +#include "google_breakpad/processor/fast_source_line_resolver.h" +#include "processor/source_line_resolver_base_types.h" + +#include +#include + +#include "google_breakpad/processor/stack_frame.h" +#include "processor/cfi_frame_info.h" +#include "processor/static_address_map-inl.h" +#include "processor/static_contained_range_map-inl.h" +#include "processor/static_map.h" +#include "processor/static_range_map-inl.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +struct FastSourceLineResolver::Line : public SourceLineResolverBase::Line { + void CopyFrom(const Line *line_ptr) { + const char *raw = reinterpret_cast(line_ptr); + CopyFrom(raw); + } + + // De-serialize the memory data of a Line. + void CopyFrom(const char *raw) { + address = *(reinterpret_cast(raw)); + size = *(reinterpret_cast(raw + sizeof(address))); + source_file_id = *(reinterpret_cast( + raw + 2 * sizeof(address))); + line = *(reinterpret_cast( + raw + 2 * sizeof(address) + sizeof(source_file_id))); + } +}; + +struct FastSourceLineResolver::Function : +public SourceLineResolverBase::Function { + void CopyFrom(const Function *func_ptr) { + const char *raw = reinterpret_cast(func_ptr); + CopyFrom(raw); + } + + // De-serialize the memory data of a Function. + void CopyFrom(const char *raw) { + size_t name_size = strlen(raw) + 1; + name = raw; + address = *(reinterpret_cast(raw + name_size)); + size = *(reinterpret_cast( + raw + name_size + sizeof(MemAddr))); + parameter_size = *(reinterpret_cast( + raw + name_size + 2 * sizeof(MemAddr))); + lines = StaticRangeMap( + raw + name_size + 2 * sizeof(MemAddr) + sizeof(int32_t)); + } + + StaticRangeMap lines; +}; + +struct FastSourceLineResolver::PublicSymbol : +public SourceLineResolverBase::PublicSymbol { + void CopyFrom(const PublicSymbol *public_symbol_ptr) { + const char *raw = reinterpret_cast(public_symbol_ptr); + CopyFrom(raw); + } + + // De-serialize the memory data of a PublicSymbol. + void CopyFrom(const char *raw) { + size_t name_size = strlen(raw) + 1; + name = raw; + address = *(reinterpret_cast(raw + name_size)); + parameter_size = *(reinterpret_cast( + raw + name_size + sizeof(MemAddr))); + } +}; + +class FastSourceLineResolver::Module: public SourceLineResolverBase::Module { + public: + explicit Module(const string &name) : name_(name) { } + virtual ~Module() { } + + // Looks up the given relative address, and fills the StackFrame struct + // with the result. + virtual void LookupAddress(StackFrame *frame) const; + + // Loads a map from the given buffer in char* type. + virtual bool LoadMapFromMemory(char *memory_buffer); + + // If Windows stack walking information is available covering ADDRESS, + // return a WindowsFrameInfo structure describing it. If the information + // is not available, returns NULL. A NULL return value does not indicate + // an error. The caller takes ownership of any returned WindowsFrameInfo + // object. + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const; + + // Number of serialized map components of Module. + static const int kNumberMaps_ = 5 + WindowsFrameInfo::STACK_INFO_LAST; + + private: + friend class FastSourceLineResolver; + friend class ModuleComparer; + typedef StaticMap FileMap; + + string name_; + StaticMap files_; + StaticRangeMap functions_; + StaticAddressMap public_symbols_; + + // Each element in the array is a ContainedRangeMap for a type + // listed in WindowsFrameInfoTypes. These are split by type because + // there may be overlaps between maps of different types, but some + // information is only available as certain types. + StaticContainedRangeMap + windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST]; + + // DWARF CFI stack walking data. The Module stores the initial rule sets + // and rule deltas as strings, just as they appear in the symbol file: + // although the file may contain hundreds of thousands of STACK CFI + // records, walking a stack will only ever use a few of them, so it's + // best to delay parsing a record until it's actually needed. + // + // STACK CFI INIT records: for each range, an initial set of register + // recovery rules. The RangeMap's itself gives the starting and ending + // addresses. + StaticRangeMap cfi_initial_rules_; + + // STACK CFI records: at a given address, the changes to the register + // recovery rules that take effect at that address. The map key is the + // starting address; the ending address is the key of the next entry in + // this map, or the end of the range as given by the cfi_initial_rules_ + // entry (which FindCFIFrameInfo looks up first). + StaticMap cfi_delta_rules_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_unittest.cc b/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_unittest.cc new file mode 100644 index 0000000..a4a9209 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/fast_source_line_resolver_unittest.cc @@ -0,0 +1,477 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// fast_source_line_resolver_unittest.cc: Unit tests for FastSourceLineResolver. +// Two different approaches for testing fast source line resolver: +// First, use the same unit test data for basic source line resolver. +// Second, read data from symbol files, load them as basic modules, and then +// serialize them and load the serialized data as fast modules. Then compare +// modules to assure the fast module contains exactly the same data as +// basic module. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/memory_region.h" +#include "processor/logging.h" +#include "processor/module_serializer.h" +#include "processor/module_comparer.h" + +namespace { + +using std::string; +using google_breakpad::SourceLineResolverBase; +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::FastSourceLineResolver; +using google_breakpad::ModuleSerializer; +using google_breakpad::ModuleComparer; +using google_breakpad::CFIFrameInfo; +using google_breakpad::CodeModule; +using google_breakpad::MemoryRegion; +using google_breakpad::StackFrame; +using google_breakpad::WindowsFrameInfo; +using google_breakpad::linked_ptr; +using google_breakpad::scoped_ptr; + +class TestCodeModule : public CodeModule { + public: + explicit TestCodeModule(string code_file) : code_file_(code_file) {} + virtual ~TestCodeModule() {} + + virtual u_int64_t base_address() const { return 0; } + virtual u_int64_t size() const { return 0xb000; } + virtual string code_file() const { return code_file_; } + virtual string code_identifier() const { return ""; } + virtual string debug_file() const { return ""; } + virtual string debug_identifier() const { return ""; } + virtual string version() const { return ""; } + virtual const CodeModule* Copy() const { + return new TestCodeModule(code_file_); + } + + private: + string code_file_; +}; + +// A mock memory region object, for use by the STACK CFI tests. +class MockMemoryRegion: public MemoryRegion { + u_int64_t GetBase() const { return 0x10000; } + u_int32_t GetSize() const { return 0x01000; } + bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { + *value = address & 0xff; + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { + *value = address & 0xffff; + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { + switch (address) { + case 0x10008: *value = 0x98ecadc3; break; // saved %ebx + case 0x1000c: *value = 0x878f7524; break; // saved %esi + case 0x10010: *value = 0x6312f9a5; break; // saved %edi + case 0x10014: *value = 0x10038; break; // caller's %ebp + case 0x10018: *value = 0xf6438648; break; // return address + default: *value = 0xdeadbeef; break; // junk + } + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { + *value = address; + return true; + } +}; + +// Verify that, for every association in ACTUAL, EXPECTED has the same +// association. (That is, ACTUAL's associations should be a subset of +// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and +// ".cfa". +static bool VerifyRegisters( + const char *file, int line, + const CFIFrameInfo::RegisterValueMap &expected, + const CFIFrameInfo::RegisterValueMap &actual) { + CFIFrameInfo::RegisterValueMap::const_iterator a; + a = actual.find(".cfa"); + if (a == actual.end()) + return false; + a = actual.find(".ra"); + if (a == actual.end()) + return false; + for (a = actual.begin(); a != actual.end(); a++) { + CFIFrameInfo::RegisterValueMap::const_iterator e = + expected.find(a->first); + if (e == expected.end()) { + fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n", + file, line, a->first.c_str(), a->second); + return false; + } + if (e->second != a->second) { + fprintf(stderr, + "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n", + file, line, a->first.c_str(), a->second, e->second); + return false; + } + // Don't complain if this doesn't recover all registers. Although + // the DWARF spec says that unmentioned registers are undefined, + // GCC uses omission to mean that they are unchanged. + } + return true; +} + +static bool VerifyEmpty(const StackFrame &frame) { + if (frame.function_name.empty() && + frame.source_file_name.empty() && + frame.source_line == 0) + return true; + return false; +} + +static void ClearSourceLineInfo(StackFrame *frame) { + frame->function_name.clear(); + frame->module = NULL; + frame->source_file_name.clear(); + frame->source_line = 0; +} + +class TestFastSourceLineResolver : public ::testing::Test { + public: + void SetUp() { + testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata"; + } + + string symbol_file(int file_index) { + std::stringstream ss; + ss << testdata_dir << "/module" << file_index << ".out"; + return ss.str(); + } + + ModuleSerializer serializer; + BasicSourceLineResolver basic_resolver; + FastSourceLineResolver fast_resolver; + ModuleComparer module_comparer; + + string testdata_dir; +}; + +// Test adapted from basic_source_line_resolver_unittest. +TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) { + TestCodeModule module1("module1"); + ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1))); + ASSERT_TRUE(basic_resolver.HasModule(&module1)); + // Convert module1 to fast_module: + ASSERT_TRUE(serializer.ConvertOneModule( + module1.code_file(), &basic_resolver, &fast_resolver)); + ASSERT_TRUE(fast_resolver.HasModule(&module1)); + + TestCodeModule module2("module2"); + ASSERT_TRUE(basic_resolver.LoadModule(&module2, symbol_file(2))); + ASSERT_TRUE(basic_resolver.HasModule(&module2)); + // Convert module2 to fast_module: + ASSERT_TRUE(serializer.ConvertOneModule( + module2.code_file(), &basic_resolver, &fast_resolver)); + ASSERT_TRUE(fast_resolver.HasModule(&module2)); + + StackFrame frame; + scoped_ptr windows_frame_info; + scoped_ptr cfi_frame_info; + frame.instruction = 0x1000; + frame.module = NULL; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_FALSE(frame.module); + ASSERT_TRUE(frame.function_name.empty()); + ASSERT_EQ(frame.function_base, 0); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + ASSERT_EQ(frame.source_line_base, 0); + + frame.module = &module1; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function1_1"); + ASSERT_TRUE(frame.module); + ASSERT_EQ(frame.module->code_file(), "module1"); + ASSERT_EQ(frame.function_base, 0x1000); + ASSERT_EQ(frame.source_file_name, "file1_1.cc"); + ASSERT_EQ(frame.source_line, 44); + ASSERT_EQ(frame.source_line_base, 0x1000); + windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_EQ(windows_frame_info->program_string, + "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ ="); + + ClearSourceLineInfo(&frame); + frame.instruction = 0x800; + frame.module = &module1; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_TRUE(VerifyEmpty(frame)); + windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); + ASSERT_FALSE(windows_frame_info.get()); + + frame.instruction = 0x1280; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function1_3"); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_TRUE(windows_frame_info->program_string.empty()); + + frame.instruction = 0x1380; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function1_4"); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_FALSE(windows_frame_info->program_string.empty()); + + frame.instruction = 0x2000; + windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); + ASSERT_FALSE(windows_frame_info.get()); + + // module1 has STACK CFI records covering 3d40..3def; + // module2 has STACK CFI records covering 3df0..3e9f; + // check that FindCFIFrameInfo doesn't claim to find any outside those ranges. + frame.instruction = 0x3d3f; + frame.module = &module1; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_FALSE(cfi_frame_info.get()); + + frame.instruction = 0x3e9f; + frame.module = &module1; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_FALSE(cfi_frame_info.get()); + + CFIFrameInfo::RegisterValueMap current_registers; + CFIFrameInfo::RegisterValueMap caller_registers; + CFIFrameInfo::RegisterValueMap expected_caller_registers; + MockMemoryRegion memory; + + // Regardless of which instruction evaluation takes place at, it + // should produce the same values for the caller's registers. + expected_caller_registers[".cfa"] = 0x1001c; + expected_caller_registers[".ra"] = 0xf6438648; + expected_caller_registers["$ebp"] = 0x10038; + expected_caller_registers["$ebx"] = 0x98ecadc3; + expected_caller_registers["$esi"] = 0x878f7524; + expected_caller_registers["$edi"] = 0x6312f9a5; + + frame.instruction = 0x3d40; + frame.module = &module1; + current_registers.clear(); + current_registers["$esp"] = 0x10018; + current_registers["$ebp"] = 0x10038; + current_registers["$ebx"] = 0x98ecadc3; + current_registers["$esi"] = 0x878f7524; + current_registers["$edi"] = 0x6312f9a5; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers)); + + frame.instruction = 0x3d41; + current_registers["$esp"] = 0x10014; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers)); + + frame.instruction = 0x3d43; + current_registers["$ebp"] = 0x10014; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d54; + current_registers["$ebx"] = 0x6864f054U; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d5a; + current_registers["$esi"] = 0x6285f79aU; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d84; + current_registers["$edi"] = 0x64061449U; + cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x2900; + frame.module = &module1; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, string("PublicSymbol")); + + frame.instruction = 0x4000; + frame.module = &module1; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, string("LargeFunction")); + + frame.instruction = 0x2181; + frame.module = &module2; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Function2_2"); + ASSERT_EQ(frame.function_base, 0x2170); + ASSERT_TRUE(frame.module); + ASSERT_EQ(frame.module->code_file(), "module2"); + ASSERT_EQ(frame.source_file_name, "file2_2.cc"); + ASSERT_EQ(frame.source_line, 21); + ASSERT_EQ(frame.source_line_base, 0x2180); + windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->prolog_size, 1); + + frame.instruction = 0x216f; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Public2_1"); + + ClearSourceLineInfo(&frame); + frame.instruction = 0x219f; + frame.module = &module2; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_TRUE(frame.function_name.empty()); + + frame.instruction = 0x21a0; + frame.module = &module2; + fast_resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, "Public2_2"); +} + +TEST_F(TestFastSourceLineResolver, TestInvalidLoads) { + TestCodeModule module3("module3"); + ASSERT_FALSE(basic_resolver.LoadModule(&module3, + testdata_dir + "/module3_bad.out")); + ASSERT_FALSE(basic_resolver.HasModule(&module3)); + // Convert module3 to fast_module: + ASSERT_FALSE(serializer.ConvertOneModule(module3.code_file(), + &basic_resolver, + &fast_resolver)); + ASSERT_FALSE(fast_resolver.HasModule(&module3)); + + TestCodeModule module4("module4"); + ASSERT_FALSE(basic_resolver.LoadModule(&module4, + testdata_dir + "/module4_bad.out")); + ASSERT_FALSE(basic_resolver.HasModule(&module4)); + // Convert module4 to fast_module: + ASSERT_FALSE(serializer.ConvertOneModule(module4.code_file(), + &basic_resolver, + &fast_resolver)); + ASSERT_FALSE(fast_resolver.HasModule(&module4)); + + TestCodeModule module5("module5"); + ASSERT_FALSE(fast_resolver.LoadModule(&module5, + testdata_dir + "/invalid-filename")); + ASSERT_FALSE(fast_resolver.HasModule(&module5)); + + TestCodeModule invalidmodule("invalid-module"); + ASSERT_FALSE(fast_resolver.HasModule(&invalidmodule)); +} + +TEST_F(TestFastSourceLineResolver, TestUnload) { + TestCodeModule module1("module1"); + ASSERT_FALSE(basic_resolver.HasModule(&module1)); + + ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1))); + ASSERT_TRUE(basic_resolver.HasModule(&module1)); + // Convert module1 to fast_module. + ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(), + &basic_resolver, + &fast_resolver)); + ASSERT_TRUE(fast_resolver.HasModule(&module1)); + basic_resolver.UnloadModule(&module1); + fast_resolver.UnloadModule(&module1); + ASSERT_FALSE(fast_resolver.HasModule(&module1)); + + ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1))); + ASSERT_TRUE(basic_resolver.HasModule(&module1)); + // Convert module1 to fast_module. + ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(), + &basic_resolver, + &fast_resolver)); + ASSERT_TRUE(fast_resolver.HasModule(&module1)); +} + +TEST_F(TestFastSourceLineResolver, CompareModule) { + char *symbol_data; + string symbol_data_string; + string filename; + + for (int module_index = 0; module_index < 3; ++module_index) { + std::stringstream ss; + ss << testdata_dir << "/module" << module_index << ".out"; + filename = ss.str(); + ASSERT_TRUE(SourceLineResolverBase::ReadSymbolFile( + &symbol_data, symbol_file(module_index))); + symbol_data_string = symbol_data; + delete [] symbol_data; + ASSERT_TRUE(module_comparer.Compare(symbol_data_string)); + } +} + +} // namespace + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/linked_ptr.h b/src/lib/crashdump/gbreakpad/processor/linked_ptr.h new file mode 100644 index 0000000..1a4ef7d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/linked_ptr.h @@ -0,0 +1,193 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). + +#ifndef PROCESSOR_LINKED_PTR_H__ +#define PROCESSOR_LINKED_PTR_H__ + +namespace google_breakpad { + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) { + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) p = p->next_; + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true iff we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() { + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { depart(); capture(ptr); } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + // Release ownership of the pointed object and returns it. + // Sole ownership by this linked_ptr object is required. + T* release() { + bool last = link_.depart(); + T* v = value_; + value_ = NULL; + return v; + } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +} // namespace google_breakpad + +#endif // PROCESSOR_LINKED_PTR_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/logging.cc b/src/lib/crashdump/gbreakpad/processor/logging.cc new file mode 100644 index 0000000..70f6958 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/logging.cc @@ -0,0 +1,112 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// logging.cc: Breakpad logging +// +// See logging.h for documentation. +// +// Author: Mark Mentovai + +#include +#include +#include +#include +#include + +#include "processor/logging.h" +#include "processor/pathname_stripper.h" + +#ifdef _WIN32 +#define snprintf _snprintf +#endif + +namespace google_breakpad { + +LogStream::LogStream(std::ostream &stream, Severity severity, + const char *file, int line) + : stream_(stream) { + time_t clock; + time(&clock); + struct tm tm_struct; +#ifdef _WIN32 + localtime_s(&tm_struct, &clock); +#else + localtime_r(&clock, &tm_struct); +#endif + char time_string[20]; + strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", &tm_struct); + + const char *severity_string = "UNKNOWN_SEVERITY"; + switch (severity) { + case SEVERITY_INFO: + severity_string = "INFO"; + break; + case SEVERITY_ERROR: + severity_string = "ERROR"; + break; + } + + stream_ << time_string << ": " << PathnameStripper::File(file) << ":" << + line << ": " << severity_string << ": "; +} + +LogStream::~LogStream() { + stream_ << std::endl; +} + +std::string HexString(u_int32_t number) { + char buffer[11]; + snprintf(buffer, sizeof(buffer), "0x%x", number); + return std::string(buffer); +} + +std::string HexString(u_int64_t number) { + char buffer[19]; + snprintf(buffer, sizeof(buffer), "0x%" PRIx64, number); + return std::string(buffer); +} + +std::string HexString(int number) { + char buffer[19]; + snprintf(buffer, sizeof(buffer), "0x%x", number); + return std::string(buffer); +} + +int ErrnoString(std::string *error_string) { + assert(error_string); + + // strerror isn't necessarily thread-safe. strerror_r would be preferrable, + // but GNU libc uses a nonstandard strerror_r by default, which returns a + // char* (rather than an int success indicator) and doesn't necessarily + // use the supplied buffer. + error_string->assign(strerror(errno)); + return errno; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/logging.h b/src/lib/crashdump/gbreakpad/processor/logging.h new file mode 100644 index 0000000..642506d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/logging.h @@ -0,0 +1,162 @@ +// Copyright (c) 2007, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// logging.h: Breakpad logging +// +// Breakpad itself uses Breakpad logging with statements of the form: +// BPLOG(severity) << "message"; +// severity may be INFO, ERROR, or other values defined in this file. +// +// BPLOG is an overridable macro so that users can customize Breakpad's +// logging. Left at the default, logging messages are sent to stderr along +// with a timestamp and the source code location that produced a message. +// The streams may be changed by redefining BPLOG_*_STREAM, the logging +// behavior may be changed by redefining BPLOG_*, and the entire logging +// system may be overridden by redefining BPLOG(severity). These +// redefinitions may be passed to the preprocessor as a command-line flag +// (-D). +// +// If an additional header is required to override Breakpad logging, it can +// be specified by the BP_LOGGING_INCLUDE macro. If defined, this header +// will #include the header specified by that macro. +// +// If any initialization is needed before logging, it can be performed by +// a function called through the BPLOG_INIT macro. Each main function of +// an executable program in the Breakpad processor library calls +// BPLOG_INIT(&argc, &argv); before any logging can be performed; define +// BPLOG_INIT appropriately if initialization is required. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_LOGGING_H__ +#define PROCESSOR_LOGGING_H__ + +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +#ifdef BP_LOGGING_INCLUDE +#include BP_LOGGING_INCLUDE +#endif // BP_LOGGING_INCLUDE + +namespace google_breakpad { + +// These are defined in Microsoft headers. +#ifdef SEVERITY_ERROR +#undef SEVERITY_ERROR +#endif + +#ifdef ERROR +#undef ERROR +#endif + +class LogStream { + public: + enum Severity { + SEVERITY_INFO, + SEVERITY_ERROR + }; + + // Begin logging a message to the stream identified by |stream|, at the + // indicated severity. The file and line parameters should be set so as to + // identify the line of source code that is producing a message. + LogStream(std::ostream &stream, Severity severity, + const char *file, int line); + + // Finish logging by printing a newline and flushing the output stream. + ~LogStream(); + + template std::ostream& operator<<(const T &t) { + return stream_ << t; + } + + private: + std::ostream &stream_; + + // Disallow copy constructor and assignment operator + explicit LogStream(const LogStream &that); + void operator=(const LogStream &that); +}; + +// This class is used to explicitly ignore values in the conditional logging +// macros. This avoids compiler warnings like "value computed is not used" +// and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() {} + + // This has to be an operator with a precedence lower than << but higher + // than ?: + void operator&(std::ostream &) {} +}; + +// Returns number formatted as a hexadecimal string, such as "0x7b". +std::string HexString(u_int32_t number); +std::string HexString(u_int64_t number); +std::string HexString(int number); + +// Returns the error code as set in the global errno variable, and sets +// error_string, a required argument, to a string describing that error +// code. +int ErrnoString(std::string *error_string); + +} // namespace google_breakpad + +#ifndef BPLOG_INIT +#define BPLOG_INIT(pargc, pargv) +#endif // BPLOG_INIT + +#ifndef BPLOG +#define BPLOG(severity) BPLOG_ ## severity +#endif // BPLOG + +#ifndef BPLOG_INFO +#ifndef BPLOG_INFO_STREAM +#define BPLOG_INFO_STREAM std::clog +#endif // BPLOG_INFO_STREAM +#define BPLOG_INFO google_breakpad::LogStream(BPLOG_INFO_STREAM, \ + google_breakpad::LogStream::SEVERITY_INFO, \ + __FILE__, __LINE__) +#endif // BPLOG_INFO + +#ifndef BPLOG_ERROR +#ifndef BPLOG_ERROR_STREAM +#define BPLOG_ERROR_STREAM std::cerr +#endif // BPLOG_ERROR_STREAM +#define BPLOG_ERROR google_breakpad::LogStream(BPLOG_ERROR_STREAM, \ + google_breakpad::LogStream::SEVERITY_ERROR, \ + __FILE__, __LINE__) +#endif // BPLOG_ERROR + +#define BPLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : \ + google_breakpad::LogMessageVoidify() & BPLOG(severity) + +#endif // PROCESSOR_LOGGING_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/map_serializers-inl.h b/src/lib/crashdump/gbreakpad/processor/map_serializers-inl.h new file mode 100644 index 0000000..d68e8b9 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/map_serializers-inl.h @@ -0,0 +1,266 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// map_serializers_inl.h: implementation for serializing std::map and its +// wrapper classes. +// +// See map_serializers.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_MAP_SERIALIZERS_INL_H__ +#define PROCESSOR_MAP_SERIALIZERS_INL_H__ + +#include +#include + +#include "processor/map_serializers.h" +#include "processor/simple_serializer.h" + +#include "processor/address_map-inl.h" +#include "processor/range_map-inl.h" +#include "processor/contained_range_map-inl.h" + +#include "processor/logging.h" + +namespace google_breakpad { + +template +size_t StdMapSerializer::SizeOf( + const std::map &m) const { + size_t size = 0; + size_t header_size = (1 + m.size()) * sizeof(u_int32_t); + size += header_size; + + typename std::map::const_iterator iter; + for (iter = m.begin(); iter != m.end(); ++iter) { + size += key_serializer_.SizeOf(iter->first); + size += value_serializer_.SizeOf(iter->second); + } + return size; +} + +template +char *StdMapSerializer::Write(const std::map &m, + char *dest) const { + if (!dest) { + BPLOG(ERROR) << "StdMapSerializer failed: write to NULL address."; + return NULL; + } + char *start_address = dest; + + // Write header: + // Number of nodes. + dest = SimpleSerializer::Write(m.size(), dest); + // Nodes offsets. + u_int32_t *offsets = reinterpret_cast(dest); + dest += sizeof(u_int32_t) * m.size(); + + char *key_address = dest; + dest += sizeof(Key) * m.size(); + + // Traverse map. + typename std::map::const_iterator iter; + int index = 0; + for (iter = m.begin(); iter != m.end(); ++iter, ++index) { + offsets[index] = static_cast(dest - start_address); + key_address = key_serializer_.Write(iter->first, key_address); + dest = value_serializer_.Write(iter->second, dest); + } + return dest; +} + +template +char *StdMapSerializer::Serialize( + const std::map &m, unsigned int *size) const { + // Compute size of memory to be allocated. + unsigned int size_to_alloc = SizeOf(m); + // Allocate memory. + char *serialized_data = new char[size_to_alloc]; + if (!serialized_data) { + BPLOG(INFO) << "StdMapSerializer memory allocation failed."; + if (size) *size = 0; + return NULL; + } + // Write serialized data into memory. + Write(m, serialized_data); + + if (size) *size = size_to_alloc; + return serialized_data; +} + +template +size_t RangeMapSerializer::SizeOf( + const RangeMap &m) const { + size_t size = 0; + size_t header_size = (1 + m.map_.size()) * sizeof(u_int32_t); + size += header_size; + + typename std::map::const_iterator iter; + for (iter = m.map_.begin(); iter != m.map_.end(); ++iter) { + // Size of key (high address). + size += address_serializer_.SizeOf(iter->first); + // Size of base (low address). + size += address_serializer_.SizeOf(iter->second.base()); + // Size of entry. + size += entry_serializer_.SizeOf(iter->second.entry()); + } + return size; +} + +template +char *RangeMapSerializer::Write( + const RangeMap &m, char *dest) const { + if (!dest) { + BPLOG(ERROR) << "RangeMapSerializer failed: write to NULL address."; + return NULL; + } + char *start_address = dest; + + // Write header: + // Number of nodes. + dest = SimpleSerializer::Write(m.map_.size(), dest); + // Nodes offsets. + u_int32_t *offsets = reinterpret_cast(dest); + dest += sizeof(u_int32_t) * m.map_.size(); + + char *key_address = dest; + dest += sizeof(Address) * m.map_.size(); + + // Traverse map. + typename std::map::const_iterator iter; + int index = 0; + for (iter = m.map_.begin(); iter != m.map_.end(); ++iter, ++index) { + offsets[index] = static_cast(dest - start_address); + key_address = address_serializer_.Write(iter->first, key_address); + dest = address_serializer_.Write(iter->second.base(), dest); + dest = entry_serializer_.Write(iter->second.entry(), dest); + } + return dest; +} + +template +char *RangeMapSerializer::Serialize( + const RangeMap &m, unsigned int *size) const { + // Compute size of memory to be allocated. + unsigned int size_to_alloc = SizeOf(m); + // Allocate memory. + char *serialized_data = new char[size_to_alloc]; + if (!serialized_data) { + BPLOG(INFO) << "RangeMapSerializer memory allocation failed."; + if (size) *size = 0; + return NULL; + } + + // Write serialized data into memory. + Write(m, serialized_data); + + if (size) *size = size_to_alloc; + return serialized_data; +} + + +template +size_t ContainedRangeMapSerializer::SizeOf( + const ContainedRangeMap *m) const { + size_t size = 0; + size_t header_size = addr_serializer_.SizeOf(m->base_) + + entry_serializer_.SizeOf(m->entry_) + + sizeof(u_int32_t); + size += header_size; + // In case m.map_ == NULL, we treat it as an empty map: + size += sizeof(u_int32_t); + if (m->map_) { + size += m->map_->size() * sizeof(u_int32_t); + typename Map::const_iterator iter; + for (iter = m->map_->begin(); iter != m->map_->end(); ++iter) { + size += addr_serializer_.SizeOf(iter->first); + // Recursive calculation of size: + size += SizeOf(iter->second); + } + } + return size; +} + +template +char *ContainedRangeMapSerializer::Write( + const ContainedRangeMap *m, char *dest) const { + if (!dest) { + BPLOG(ERROR) << "StdMapSerializer failed: write to NULL address."; + return NULL; + } + dest = addr_serializer_.Write(m->base_, dest); + dest = SimpleSerializer::Write(entry_serializer_.SizeOf(m->entry_), + dest); + dest = entry_serializer_.Write(m->entry_, dest); + + // Write map<: + char *map_address = dest; + if (m->map_ == NULL) { + dest = SimpleSerializer::Write(0, dest); + } else { + dest = SimpleSerializer::Write(m->map_->size(), dest); + u_int32_t *offsets = reinterpret_cast(dest); + dest += sizeof(u_int32_t) * m->map_->size(); + + char *key_address = dest; + dest += sizeof(AddrType) * m->map_->size(); + + // Traverse map. + typename Map::const_iterator iter; + int index = 0; + for (iter = m->map_->begin(); iter != m->map_->end(); ++iter, ++index) { + offsets[index] = static_cast(dest - map_address); + key_address = addr_serializer_.Write(iter->first, key_address); + // Recursively write. + dest = Write(iter->second, dest); + } + } + return dest; +} + +template +char *ContainedRangeMapSerializer::Serialize( + const ContainedRangeMap *m, unsigned int *size) const { + unsigned int size_to_alloc = SizeOf(m); + // Allocating memory. + char *serialized_data = new char[size_to_alloc]; + if (!serialized_data) { + BPLOG(INFO) << "ContainedRangeMapSerializer memory allocation failed."; + if (size) *size = 0; + return NULL; + } + Write(m, serialized_data); + if (size) *size = size_to_alloc; + return serialized_data; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_MAP_SERIALIZERS_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/map_serializers.h b/src/lib/crashdump/gbreakpad/processor/map_serializers.h new file mode 100644 index 0000000..a0b9d3f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/map_serializers.h @@ -0,0 +1,168 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// map_serializers.h: defines templates for serializing std::map and its +// wrappers: AddressMap, RangeMap, and ContainedRangeMap. +// +// Author: Siyang Xie (lambxsy@google.com) + + +#ifndef PROCESSOR_MAP_SERIALIZERS_H__ +#define PROCESSOR_MAP_SERIALIZERS_H__ + +#include +#include + +#include "processor/simple_serializer.h" + +#include "processor/address_map-inl.h" +#include "processor/range_map-inl.h" +#include "processor/contained_range_map-inl.h" + +namespace google_breakpad { + +// StdMapSerializer allocates memory and serializes an std::map instance into a +// chunk of memory data. +template +class StdMapSerializer { + public: + // Calculate the memory size of serialized data. + size_t SizeOf(const std::map &m) const; + + // Writes the serialized data to memory with start address = dest, + // and returns the "end" of data, i.e., return the address follow the final + // byte of data. + // NOTE: caller has to allocate enough memory before invoke Write() method. + char* Write(const std::map &m, char* dest) const; + + // Serializes a std::map object into a chunk of memory data with format + // described in "StaticMap.h" comment. + // Returns a pointer to the serialized data. If size != NULL, *size is set + // to the size of serialized data, i.e., SizeOf(m). + // Caller has the ownership of memory allocated as "new char[]". + char* Serialize(const std::map &m, unsigned int *size) const; + + private: + SimpleSerializer key_serializer_; + SimpleSerializer value_serializer_; +}; + +// AddressMapSerializer allocates memory and serializes an AddressMap into a +// chunk of memory data. +template +class AddressMapSerializer { + public: + // Calculate the memory size of serialized data. + size_t SizeOf(const AddressMap &m) const { + return std_map_serializer_.SizeOf(m.map_); + } + + // Write the serialized data to specified memory location. Return the "end" + // of data, i.e., return the address after the final byte of data. + // NOTE: caller has to allocate enough memory before invoke Write() method. + char* Write(const AddressMap &m, char *dest) const { + return std_map_serializer_.Write(m.map_, dest); + } + + // Serializes an AddressMap object into a chunk of memory data. + // Returns a pointer to the serialized data. If size != NULL, *size is set + // to the size of serialized data, i.e., SizeOf(m). + // Caller has the ownership of memory allocated as "new char[]". + char* Serialize(const AddressMap &m, unsigned int *size) const { + return std_map_serializer_.Serialize(m.map_, size); + } + + private: + // AddressMapSerializer is a simple wrapper of StdMapSerializer, just as + // AddressMap is a simple wrapper of std::map. + StdMapSerializer std_map_serializer_; +}; + +// RangeMapSerializer allocates memory and serializes a RangeMap instance into a +// chunk of memory data. +template +class RangeMapSerializer { + public: + // Calculate the memory size of serialized data. + size_t SizeOf(const RangeMap &m) const; + + // Write the serialized data to specified memory location. Return the "end" + // of data, i.e., return the address after the final byte of data. + // NOTE: caller has to allocate enough memory before invoke Write() method. + char* Write(const RangeMap &m, char* dest) const; + + // Serializes a RangeMap object into a chunk of memory data. + // Returns a pointer to the serialized data. If size != NULL, *size is set + // to the size of serialized data, i.e., SizeOf(m). + // Caller has the ownership of memory allocated as "new char[]". + char* Serialize(const RangeMap &m, unsigned int *size) const; + + private: + // Convenient type name for Range. + typedef typename RangeMap::Range Range; + + // Serializer for RangeMap's key and Range::base_. + SimpleSerializer
    address_serializer_; + // Serializer for RangeMap::Range::entry_. + SimpleSerializer entry_serializer_; +}; + +// ContainedRangeMapSerializer allocates memory and serializes a +// ContainedRangeMap instance into a chunk of memory data. +template +class ContainedRangeMapSerializer { + public: + // Calculate the memory size of serialized data. + size_t SizeOf(const ContainedRangeMap *m) const; + + // Write the serialized data to specified memory location. Return the "end" + // of data, i.e., return the address after the final byte of data. + // NOTE: caller has to allocate enough memory before invoke Write() method. + char* Write(const ContainedRangeMap *m, + char* dest) const; + + // Serializes a ContainedRangeMap object into a chunk of memory data. + // Returns a pointer to the serialized data. If size != NULL, *size is set + // to the size of serialized data, i.e., SizeOf(m). + // Caller has the ownership of memory allocated as "new char[]". + char* Serialize(const ContainedRangeMap *m, + unsigned int *size) const; + + private: + // Convenient type name for the underlying map type. + typedef std::map*> Map; + + // Serializer for addresses and entries stored in ContainedRangeMap. + SimpleSerializer addr_serializer_; + SimpleSerializer entry_serializer_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_MAP_SERIALIZERS_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/map_serializers_unittest.cc b/src/lib/crashdump/gbreakpad/processor/map_serializers_unittest.cc new file mode 100644 index 0000000..abaef97 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/map_serializers_unittest.cc @@ -0,0 +1,388 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// map_serializers_unittest.cc: Unit tests for std::map serializer and +// std::map wrapper serializers. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include +#include +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "map_serializers-inl.h" + +#include "processor/address_map-inl.h" +#include "processor/range_map-inl.h" +#include "processor/contained_range_map-inl.h" + +typedef int32_t AddrType; +typedef int32_t EntryType; + +const int kSizeOfInt = 4; + +class TestStdMapSerializer : public ::testing::Test { + protected: + void SetUp() { + serialized_size_ = 0; + serialized_data_ = NULL; + } + + void TearDown() { + delete [] serialized_data_; + } + + std::map std_map_; + google_breakpad::StdMapSerializer serializer_; + u_int32_t serialized_size_; + char *serialized_data_; +}; + +TEST_F(TestStdMapSerializer, EmptyMapTestCase) { + const int32_t correct_data[] = { 0 }; + u_int32_t correct_size = sizeof(correct_data); + + // std_map_ is empty. + serialized_data_ = serializer_.Serialize(std_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestStdMapSerializer, MapWithTwoElementsTestCase) { + const int32_t correct_data[] = { + // # of nodes + 2, + // Offsets + 20, 24, + // Keys + 1, 3, + // Values + 2, 6 + }; + u_int32_t correct_size = sizeof(correct_data); + + std_map_.insert(std::make_pair(1, 2)); + std_map_.insert(std::make_pair(3, 6)); + + serialized_data_ = serializer_.Serialize(std_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestStdMapSerializer, MapWithFiveElementsTestCase) { + const int32_t correct_data[] = { + // # of nodes + 5, + // Offsets + 44, 48, 52, 56, 60, + // Keys + 1, 2, 3, 4, 5, + // Values + 11, 12, 13, 14, 15 + }; + u_int32_t correct_size = sizeof(correct_data); + + for (int i = 1; i < 6; ++i) + std_map_.insert(std::make_pair(i, 10 + i)); + + serialized_data_ = serializer_.Serialize(std_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +class TestAddressMapSerializer : public ::testing::Test { + protected: + void SetUp() { + serialized_size_ = 0; + serialized_data_ = 0; + } + + void TearDown() { + delete [] serialized_data_; + } + + google_breakpad::AddressMap address_map_; + google_breakpad::AddressMapSerializer serializer_; + u_int32_t serialized_size_; + char *serialized_data_; +}; + +TEST_F(TestAddressMapSerializer, EmptyMapTestCase) { + const int32_t correct_data[] = { 0 }; + u_int32_t correct_size = sizeof(correct_data); + + // std_map_ is empty. + serialized_data_ = serializer_.Serialize(address_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestAddressMapSerializer, MapWithTwoElementsTestCase) { + const int32_t correct_data[] = { + // # of nodes + 2, + // Offsets + 20, 24, + // Keys + 1, 3, + // Values + 2, 6 + }; + u_int32_t correct_size = sizeof(correct_data); + + address_map_.Store(1, 2); + address_map_.Store(3, 6); + + serialized_data_ = serializer_.Serialize(address_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestAddressMapSerializer, MapWithFourElementsTestCase) { + const int32_t correct_data[] = { + // # of nodes + 4, + // Offsets + 36, 40, 44, 48, + // Keys + -6, -4, 8, 123, + // Values + 2, 3, 5, 8 + }; + u_int32_t correct_size = sizeof(correct_data); + + address_map_.Store(-6, 2); + address_map_.Store(-4, 3); + address_map_.Store(8, 5); + address_map_.Store(123, 8); + + serialized_data_ = serializer_.Serialize(address_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + + +class TestRangeMapSerializer : public ::testing::Test { + protected: + void SetUp() { + serialized_size_ = 0; + serialized_data_ = 0; + } + + void TearDown() { + delete [] serialized_data_; + } + + google_breakpad::RangeMap range_map_; + google_breakpad::RangeMapSerializer serializer_; + u_int32_t serialized_size_; + char *serialized_data_; +}; + +TEST_F(TestRangeMapSerializer, EmptyMapTestCase) { + const int32_t correct_data[] = { 0 }; + u_int32_t correct_size = sizeof(correct_data); + + // range_map_ is empty. + serialized_data_ = serializer_.Serialize(range_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestRangeMapSerializer, MapWithOneRangeTestCase) { + const int32_t correct_data[] = { + // # of nodes + 1, + // Offsets + 12, + // Keys: high address + 10, + // Values: (low address, entry) pairs + 1, 6 + }; + u_int32_t correct_size = sizeof(correct_data); + + range_map_.StoreRange(1, 10, 6); + + serialized_data_ = serializer_.Serialize(range_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestRangeMapSerializer, MapWithThreeRangesTestCase) { + const int32_t correct_data[] = { + // # of nodes + 3, + // Offsets + 28, 36, 44, + // Keys: high address + 5, 9, 20, + // Values: (low address, entry) pairs + 2, 1, 6, 2, 10, 3 + }; + u_int32_t correct_size = sizeof(correct_data); + + ASSERT_TRUE(range_map_.StoreRange(2, 4, 1)); + ASSERT_TRUE(range_map_.StoreRange(6, 4, 2)); + ASSERT_TRUE(range_map_.StoreRange(10, 11, 3)); + + serialized_data_ = serializer_.Serialize(range_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + + +class TestContainedRangeMapSerializer : public ::testing::Test { + protected: + void SetUp() { + serialized_size_ = 0; + serialized_data_ = 0; + } + + void TearDown() { + delete [] serialized_data_; + } + + google_breakpad::ContainedRangeMap crm_map_; + google_breakpad::ContainedRangeMapSerializer serializer_; + u_int32_t serialized_size_; + char *serialized_data_; +}; + +TEST_F(TestContainedRangeMapSerializer, EmptyMapTestCase) { + const int32_t correct_data[] = { + 0, // base address of root + 4, // size of entry + 0, // entry stored at root + 0 // empty map stored at root + }; + u_int32_t correct_size = sizeof(correct_data); + + // crm_map_ is empty. + serialized_data_ = serializer_.Serialize(&crm_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestContainedRangeMapSerializer, MapWithOneRangeTestCase) { + const int32_t correct_data[] = { + 0, // base address of root + 4, // size of entry + 0, // entry stored at root + // Map stored at root node: + 1, // # of nodes + 12, // offset + 9, // key + // value: a child ContainedRangeMap + 3, // base address of child CRM + 4, // size of entry + -1, // entry stored in child CRM + 0 // empty sub-map stored in child CRM + }; + u_int32_t correct_size = sizeof(correct_data); + + crm_map_.StoreRange(3, 7, -1); + + serialized_data_ = serializer_.Serialize(&crm_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + +TEST_F(TestContainedRangeMapSerializer, MapWithTwoLevelsTestCase) { + // Tree structure of ranges: + // root level 0 + // | + // map + // / \ level 1: child1, child2 + // 2~8 10~20 + // | | + // map map + // / \ | + // 3~4 6~7 16-20 level 2: grandchild1, grandchild2, grandchild3 + + const int32_t correct_data[] = { + // root: base, entry_size, entry + 0, 4, 0, + // root's map: # of nodes, offset1, offset2, key1, key2 + 2, 20, 84, 8, 20, + // child1: base, entry_size, entry: + 2, 4, -1, + // child1's map: # of nodes, offset1, offset2, key1, key2 + 2, 20, 36, 4, 7, + // grandchild1: base, entry_size, entry, empty_map + 3, 4, -1, 0, + // grandchild2: base, entry_size, entry, empty_map + 6, 4, -1, 0, + // child2: base, entry_size, entry: + 10, 4, -1, + // child2's map: # of nodes, offset1, key1 + 1, 12, 20, + // grandchild3: base, entry_size, entry, empty_map + 16, 4, -1, 0 + }; + u_int32_t correct_size = sizeof(correct_data); + + // Store child1. + ASSERT_TRUE(crm_map_.StoreRange(2, 7, -1)); + // Store child2. + ASSERT_TRUE(crm_map_.StoreRange(10, 11, -1)); + // Store grandchild1. + ASSERT_TRUE(crm_map_.StoreRange(3, 2, -1)); + // Store grandchild2. + ASSERT_TRUE(crm_map_.StoreRange(6, 2, -1)); + // Store grandchild3. + ASSERT_TRUE(crm_map_.StoreRange(16, 5, -1)); + + serialized_data_ = serializer_.Serialize(&crm_map_, &serialized_size_); + + EXPECT_EQ(correct_size, serialized_size_); + EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0); +} + + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/minidump.cc b/src/lib/crashdump/gbreakpad/processor/minidump.cc new file mode 100644 index 0000000..3ca52ff --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump.cc @@ -0,0 +1,4219 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump.cc: A minidump reader. +// +// See minidump.h for documentation. +// +// Author: Mark Mentovai + +#include "google_breakpad/processor/minidump.h" + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +typedef SSIZE_T ssize_t; +#define PRIx64 "llx" +#define PRIx32 "lx" +#define snprintf _snprintf +#else // _WIN32 +#include +#define O_BINARY 0 +#endif // _WIN32 + +#include +#include +#include +#include +#include + +#include "processor/range_map-inl.h" + +#include "processor/basic_code_module.h" +#include "processor/basic_code_modules.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + + +namespace google_breakpad { + + +using std::istream; +using std::ifstream; +using std::numeric_limits; +using std::vector; + + +// +// Swapping routines +// +// Inlining these doesn't increase code size significantly, and it saves +// a whole lot of unnecessary jumping back and forth. +// + + +// Swapping an 8-bit quantity is a no-op. This function is only provided +// to account for certain templatized operations that require swapping for +// wider types but handle u_int8_t too +// (MinidumpMemoryRegion::GetMemoryAtAddressInternal). +static inline void Swap(u_int8_t* value) { +} + + +// Optimization: don't need to AND the furthest right shift, because we're +// shifting an unsigned quantity. The standard requires zero-filling in this +// case. If the quantities were signed, a bitmask whould be needed for this +// right shift to avoid an arithmetic shift (which retains the sign bit). +// The furthest left shift never needs to be ANDed bitmask. + + +static inline void Swap(u_int16_t* value) { + *value = (*value >> 8) | + (*value << 8); +} + + +static inline void Swap(u_int32_t* value) { + *value = (*value >> 24) | + ((*value >> 8) & 0x0000ff00) | + ((*value << 8) & 0x00ff0000) | + (*value << 24); +} + + +static inline void Swap(u_int64_t* value) { + u_int32_t* value32 = reinterpret_cast(value); + Swap(&value32[0]); + Swap(&value32[1]); + u_int32_t temp = value32[0]; + value32[0] = value32[1]; + value32[1] = temp; +} + + +// Given a pointer to a 128-bit int in the minidump data, set the "low" +// and "high" fields appropriately. +static void Normalize128(u_int128_t* value, bool is_big_endian) { + // The struct format is [high, low], so if the format is big-endian, + // the most significant bytes will already be in the high field. + if (!is_big_endian) { + u_int64_t temp = value->low; + value->low = value->high; + value->high = temp; + } +} + +// This just swaps each int64 half of the 128-bit value. +// The value should also be normalized by calling Normalize128(). +static void Swap(u_int128_t* value) { + Swap(&value->low); + Swap(&value->high); +} + + +static inline void Swap(MDLocationDescriptor* location_descriptor) { + Swap(&location_descriptor->data_size); + Swap(&location_descriptor->rva); +} + + +static inline void Swap(MDMemoryDescriptor* memory_descriptor) { + Swap(&memory_descriptor->start_of_memory_range); + Swap(&memory_descriptor->memory); +} + + +static inline void Swap(MDGUID* guid) { + Swap(&guid->data1); + Swap(&guid->data2); + Swap(&guid->data3); + // Don't swap guid->data4[] because it contains 8-bit quantities. +} + + +// +// Character conversion routines +// + + +// Standard wide-character conversion routines depend on the system's own +// idea of what width a wide character should be: some use 16 bits, and +// some use 32 bits. For the purposes of a minidump, wide strings are +// always represented with 16-bit UTF-16 chracters. iconv isn't available +// everywhere, and its interface varies where it is available. iconv also +// deals purely with char* pointers, so in addition to considering the swap +// parameter, a converter that uses iconv would also need to take the host +// CPU's endianness into consideration. It doesn't seems worth the trouble +// of making it a dependency when we don't care about anything but UTF-16. +static string* UTF16ToUTF8(const vector& in, + bool swap) { + scoped_ptr out(new string()); + + // Set the string's initial capacity to the number of UTF-16 characters, + // because the UTF-8 representation will always be at least this long. + // If the UTF-8 representation is longer, the string will grow dynamically. + out->reserve(in.size()); + + for (vector::const_iterator iterator = in.begin(); + iterator != in.end(); + ++iterator) { + // Get a 16-bit value from the input + u_int16_t in_word = *iterator; + if (swap) + Swap(&in_word); + + // Convert the input value (in_word) into a Unicode code point (unichar). + u_int32_t unichar; + if (in_word >= 0xdc00 && in_word <= 0xdcff) { + BPLOG(ERROR) << "UTF16ToUTF8 found low surrogate " << + HexString(in_word) << " without high"; + return NULL; + } else if (in_word >= 0xd800 && in_word <= 0xdbff) { + // High surrogate. + unichar = (in_word - 0xd7c0) << 10; + if (++iterator == in.end()) { + BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << + HexString(in_word) << " at end of string"; + return NULL; + } + u_int32_t high_word = in_word; + in_word = *iterator; + if (in_word < 0xdc00 || in_word > 0xdcff) { + BPLOG(ERROR) << "UTF16ToUTF8 found high surrogate " << + HexString(high_word) << " without low " << + HexString(in_word); + return NULL; + } + unichar |= in_word & 0x03ff; + } else { + // The ordinary case, a single non-surrogate Unicode character encoded + // as a single 16-bit value. + unichar = in_word; + } + + // Convert the Unicode code point (unichar) into its UTF-8 representation, + // appending it to the out string. + if (unichar < 0x80) { + (*out) += unichar; + } else if (unichar < 0x800) { + (*out) += 0xc0 | (unichar >> 6); + (*out) += 0x80 | (unichar & 0x3f); + } else if (unichar < 0x10000) { + (*out) += 0xe0 | (unichar >> 12); + (*out) += 0x80 | ((unichar >> 6) & 0x3f); + (*out) += 0x80 | (unichar & 0x3f); + } else if (unichar < 0x200000) { + (*out) += 0xf0 | (unichar >> 18); + (*out) += 0x80 | ((unichar >> 12) & 0x3f); + (*out) += 0x80 | ((unichar >> 6) & 0x3f); + (*out) += 0x80 | (unichar & 0x3f); + } else { + BPLOG(ERROR) << "UTF16ToUTF8 cannot represent high value " << + HexString(unichar) << " in UTF-8"; + return NULL; + } + } + + return out.release(); +} + +// Return the smaller of the number of code units in the UTF-16 string, +// not including the terminating null word, or maxlen. +static size_t UTF16codeunits(const u_int16_t *string, size_t maxlen) { + size_t count = 0; + while (count < maxlen && string[count] != 0) + count++; + return count; +} + + +// +// MinidumpObject +// + + +MinidumpObject::MinidumpObject(Minidump* minidump) + : minidump_(minidump), + valid_(false) { +} + + +// +// MinidumpStream +// + + +MinidumpStream::MinidumpStream(Minidump* minidump) + : MinidumpObject(minidump) { +} + + +// +// MinidumpContext +// + + +MinidumpContext::MinidumpContext(Minidump* minidump) + : MinidumpStream(minidump), + context_flags_(0), + context_() { +} + + +MinidumpContext::~MinidumpContext() { + FreeContext(); +} + + +bool MinidumpContext::Read(u_int32_t expected_size) { + valid_ = false; + + FreeContext(); + + // First, figure out what type of CPU this context structure is for. + // For some reason, the AMD64 Context doesn't have context_flags + // at the beginning of the structure, so special case it here. + if (expected_size == sizeof(MDRawContextAMD64)) { + BPLOG(INFO) << "MinidumpContext: looks like AMD64 context"; + + scoped_ptr context_amd64(new MDRawContextAMD64()); + if (!minidump_->ReadBytes(context_amd64.get(), + sizeof(MDRawContextAMD64))) { + BPLOG(ERROR) << "MinidumpContext could not read amd64 context"; + return false; + } + + if (minidump_->swap()) + Swap(&context_amd64->context_flags); + + u_int32_t cpu_type = context_amd64->context_flags & MD_CONTEXT_CPU_MASK; + + if (cpu_type != MD_CONTEXT_AMD64) { + //TODO: fall through to switch below? + // need a Tell method to be able to SeekSet back to beginning + // http://code.google.com/p/google-breakpad/issues/detail?id=224 + BPLOG(ERROR) << "MinidumpContext not actually amd64 context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext amd64 does not match system info"; + return false; + } + + // Normalize the 128-bit types in the dump. + // Since this is AMD64, by definition, the values are little-endian. + for (unsigned int vr_index = 0; + vr_index < MD_CONTEXT_AMD64_VR_COUNT; + ++vr_index) + Normalize128(&context_amd64->vector_register[vr_index], false); + + if (minidump_->swap()) { + Swap(&context_amd64->p1_home); + Swap(&context_amd64->p2_home); + Swap(&context_amd64->p3_home); + Swap(&context_amd64->p4_home); + Swap(&context_amd64->p5_home); + Swap(&context_amd64->p6_home); + // context_flags is already swapped + Swap(&context_amd64->mx_csr); + Swap(&context_amd64->cs); + Swap(&context_amd64->ds); + Swap(&context_amd64->es); + Swap(&context_amd64->fs); + Swap(&context_amd64->ss); + Swap(&context_amd64->eflags); + Swap(&context_amd64->dr0); + Swap(&context_amd64->dr1); + Swap(&context_amd64->dr2); + Swap(&context_amd64->dr3); + Swap(&context_amd64->dr6); + Swap(&context_amd64->dr7); + Swap(&context_amd64->rax); + Swap(&context_amd64->rcx); + Swap(&context_amd64->rdx); + Swap(&context_amd64->rbx); + Swap(&context_amd64->rsp); + Swap(&context_amd64->rbp); + Swap(&context_amd64->rsi); + Swap(&context_amd64->rdi); + Swap(&context_amd64->r8); + Swap(&context_amd64->r9); + Swap(&context_amd64->r10); + Swap(&context_amd64->r11); + Swap(&context_amd64->r12); + Swap(&context_amd64->r13); + Swap(&context_amd64->r14); + Swap(&context_amd64->r15); + Swap(&context_amd64->rip); + //FIXME: I'm not sure what actually determines + // which member of the union {flt_save, sse_registers} + // is valid. We're not currently using either, + // but it would be good to have them swapped properly. + + for (unsigned int vr_index = 0; + vr_index < MD_CONTEXT_AMD64_VR_COUNT; + ++vr_index) + Swap(&context_amd64->vector_register[vr_index]); + Swap(&context_amd64->vector_control); + Swap(&context_amd64->debug_control); + Swap(&context_amd64->last_branch_to_rip); + Swap(&context_amd64->last_branch_from_rip); + Swap(&context_amd64->last_exception_to_rip); + Swap(&context_amd64->last_exception_from_rip); + } + + context_flags_ = context_amd64->context_flags; + + context_.amd64 = context_amd64.release(); + } + else { + u_int32_t context_flags; + if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) { + BPLOG(ERROR) << "MinidumpContext could not read context flags"; + return false; + } + if (minidump_->swap()) + Swap(&context_flags); + + u_int32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK; + if (cpu_type == 0) { + // Unfortunately the flag for MD_CONTEXT_ARM that was taken + // from a Windows CE SDK header conflicts in practice with + // the CONTEXT_XSTATE flag. MD_CONTEXT_ARM has been renumbered, + // but handle dumps with the legacy value gracefully here. + if (context_flags & MD_CONTEXT_ARM_OLD) { + context_flags |= MD_CONTEXT_ARM; + context_flags &= ~MD_CONTEXT_ARM_OLD; + cpu_type = MD_CONTEXT_ARM; + } + } + + // Allocate the context structure for the correct CPU and fill it. The + // casts are slightly unorthodox, but it seems better to do that than to + // maintain a separate pointer for each type of CPU context structure + // when only one of them will be used. + switch (cpu_type) { + case MD_CONTEXT_X86: { + if (expected_size != sizeof(MDRawContextX86)) { + BPLOG(ERROR) << "MinidumpContext x86 size mismatch, " << + expected_size << " != " << sizeof(MDRawContextX86); + return false; + } + + scoped_ptr context_x86(new MDRawContextX86()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_x86->context_flags = context_flags; + + size_t flags_size = sizeof(context_x86->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_x86.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextX86) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read x86 context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext x86 does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_x86->context_flags was already swapped. + Swap(&context_x86->dr0); + Swap(&context_x86->dr1); + Swap(&context_x86->dr2); + Swap(&context_x86->dr3); + Swap(&context_x86->dr6); + Swap(&context_x86->dr7); + Swap(&context_x86->float_save.control_word); + Swap(&context_x86->float_save.status_word); + Swap(&context_x86->float_save.tag_word); + Swap(&context_x86->float_save.error_offset); + Swap(&context_x86->float_save.error_selector); + Swap(&context_x86->float_save.data_offset); + Swap(&context_x86->float_save.data_selector); + // context_x86->float_save.register_area[] contains 8-bit quantities + // and does not need to be swapped. + Swap(&context_x86->float_save.cr0_npx_state); + Swap(&context_x86->gs); + Swap(&context_x86->fs); + Swap(&context_x86->es); + Swap(&context_x86->ds); + Swap(&context_x86->edi); + Swap(&context_x86->esi); + Swap(&context_x86->ebx); + Swap(&context_x86->edx); + Swap(&context_x86->ecx); + Swap(&context_x86->eax); + Swap(&context_x86->ebp); + Swap(&context_x86->eip); + Swap(&context_x86->cs); + Swap(&context_x86->eflags); + Swap(&context_x86->esp); + Swap(&context_x86->ss); + // context_x86->extended_registers[] contains 8-bit quantities and + // does not need to be swapped. + } + + context_.x86 = context_x86.release(); + + break; + } + + case MD_CONTEXT_PPC: { + if (expected_size != sizeof(MDRawContextPPC)) { + BPLOG(ERROR) << "MinidumpContext ppc size mismatch, " << + expected_size << " != " << sizeof(MDRawContextPPC); + return false; + } + + scoped_ptr context_ppc(new MDRawContextPPC()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_ppc->context_flags = context_flags; + + size_t flags_size = sizeof(context_ppc->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_ppc.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextPPC) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read ppc context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext ppc does not match system info"; + return false; + } + + // Normalize the 128-bit types in the dump. + // Since this is PowerPC, by definition, the values are big-endian. + for (unsigned int vr_index = 0; + vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; + ++vr_index) { + Normalize128(&context_ppc->vector_save.save_vr[vr_index], true); + } + + if (minidump_->swap()) { + // context_ppc->context_flags was already swapped. + Swap(&context_ppc->srr0); + Swap(&context_ppc->srr1); + for (unsigned int gpr_index = 0; + gpr_index < MD_CONTEXT_PPC_GPR_COUNT; + ++gpr_index) { + Swap(&context_ppc->gpr[gpr_index]); + } + Swap(&context_ppc->cr); + Swap(&context_ppc->xer); + Swap(&context_ppc->lr); + Swap(&context_ppc->ctr); + Swap(&context_ppc->mq); + Swap(&context_ppc->vrsave); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; + ++fpr_index) { + Swap(&context_ppc->float_save.fpregs[fpr_index]); + } + // Don't swap context_ppc->float_save.fpscr_pad because it is only + // used for padding. + Swap(&context_ppc->float_save.fpscr); + for (unsigned int vr_index = 0; + vr_index < MD_VECTORSAVEAREA_PPC_VR_COUNT; + ++vr_index) { + Swap(&context_ppc->vector_save.save_vr[vr_index]); + } + Swap(&context_ppc->vector_save.save_vscr); + // Don't swap the padding fields in vector_save. + Swap(&context_ppc->vector_save.save_vrvalid); + } + + context_.ppc = context_ppc.release(); + + break; + } + + case MD_CONTEXT_SPARC: { + if (expected_size != sizeof(MDRawContextSPARC)) { + BPLOG(ERROR) << "MinidumpContext sparc size mismatch, " << + expected_size << " != " << sizeof(MDRawContextSPARC); + return false; + } + + scoped_ptr context_sparc(new MDRawContextSPARC()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_sparc->context_flags = context_flags; + + size_t flags_size = sizeof(context_sparc->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_sparc.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextSPARC) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read sparc context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext sparc does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_sparc->context_flags was already swapped. + for (unsigned int gpr_index = 0; + gpr_index < MD_CONTEXT_SPARC_GPR_COUNT; + ++gpr_index) { + Swap(&context_sparc->g_r[gpr_index]); + } + Swap(&context_sparc->ccr); + Swap(&context_sparc->pc); + Swap(&context_sparc->npc); + Swap(&context_sparc->y); + Swap(&context_sparc->asi); + Swap(&context_sparc->fprs); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; + ++fpr_index) { + Swap(&context_sparc->float_save.regs[fpr_index]); + } + Swap(&context_sparc->float_save.filler); + Swap(&context_sparc->float_save.fsr); + } + context_.ctx_sparc = context_sparc.release(); + + break; + } + + case MD_CONTEXT_ARM: { + if (expected_size != sizeof(MDRawContextARM)) { + BPLOG(ERROR) << "MinidumpContext arm size mismatch, " << + expected_size << " != " << sizeof(MDRawContextARM); + return false; + } + + scoped_ptr context_arm(new MDRawContextARM()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_arm->context_flags = context_flags; + + size_t flags_size = sizeof(context_arm->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_arm.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextARM) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read arm context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext arm does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_arm->context_flags was already swapped. + for (unsigned int ireg_index = 0; + ireg_index < MD_CONTEXT_ARM_GPR_COUNT; + ++ireg_index) { + Swap(&context_arm->iregs[ireg_index]); + } + Swap(&context_arm->cpsr); + Swap(&context_arm->float_save.fpscr); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; + ++fpr_index) { + Swap(&context_arm->float_save.regs[fpr_index]); + } + for (unsigned int fpe_index = 0; + fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; + ++fpe_index) { + Swap(&context_arm->float_save.extra[fpe_index]); + } + } + context_.arm = context_arm.release(); + + break; + } + + default: { + // Unknown context type - Don't log as an error yet. Let the + // caller work that out. + BPLOG(INFO) << "MinidumpContext unknown context type " << + HexString(cpu_type); + return false; + break; + } + } + context_flags_ = context_flags; + } + + valid_ = true; + return true; +} + + +u_int32_t MinidumpContext::GetContextCPU() const { + if (!valid_) { + // Don't log a message, GetContextCPU can be legitimately called with + // valid_ false by FreeContext, which is called by Read. + return 0; + } + + return context_flags_ & MD_CONTEXT_CPU_MASK; +} + + +const MDRawContextX86* MinidumpContext::GetContextX86() const { + if (GetContextCPU() != MD_CONTEXT_X86) { + BPLOG(ERROR) << "MinidumpContext cannot get x86 context"; + return NULL; + } + + return context_.x86; +} + + +const MDRawContextPPC* MinidumpContext::GetContextPPC() const { + if (GetContextCPU() != MD_CONTEXT_PPC) { + BPLOG(ERROR) << "MinidumpContext cannot get ppc context"; + return NULL; + } + + return context_.ppc; +} + +const MDRawContextAMD64* MinidumpContext::GetContextAMD64() const { + if (GetContextCPU() != MD_CONTEXT_AMD64) { + BPLOG(ERROR) << "MinidumpContext cannot get amd64 context"; + return NULL; + } + + return context_.amd64; +} + +const MDRawContextSPARC* MinidumpContext::GetContextSPARC() const { + if (GetContextCPU() != MD_CONTEXT_SPARC) { + BPLOG(ERROR) << "MinidumpContext cannot get sparc context"; + return NULL; + } + + return context_.ctx_sparc; +} + +const MDRawContextARM* MinidumpContext::GetContextARM() const { + if (GetContextCPU() != MD_CONTEXT_ARM) { + BPLOG(ERROR) << "MinidumpContext cannot get arm context"; + return NULL; + } + + return context_.arm; +} + +void MinidumpContext::FreeContext() { + switch (GetContextCPU()) { + case MD_CONTEXT_X86: + delete context_.x86; + break; + + case MD_CONTEXT_PPC: + delete context_.ppc; + break; + + case MD_CONTEXT_AMD64: + delete context_.amd64; + break; + + case MD_CONTEXT_SPARC: + delete context_.ctx_sparc; + break; + + case MD_CONTEXT_ARM: + delete context_.arm; + break; + + default: + // There is no context record (valid_ is false) or there's a + // context record for an unknown CPU (shouldn't happen, only known + // records are stored by Read). + break; + } + + context_flags_ = 0; + context_.base = NULL; +} + + +bool MinidumpContext::CheckAgainstSystemInfo(u_int32_t context_cpu_type) { + // It's OK if the minidump doesn't contain an MD_SYSTEM_INFO_STREAM, + // as this function just implements a sanity check. + MinidumpSystemInfo* system_info = minidump_->GetSystemInfo(); + if (!system_info) { + BPLOG(INFO) << "MinidumpContext could not be compared against " + "MinidumpSystemInfo"; + return true; + } + + // If there is an MD_SYSTEM_INFO_STREAM, it should contain valid system info. + const MDRawSystemInfo* raw_system_info = system_info->system_info(); + if (!raw_system_info) { + BPLOG(INFO) << "MinidumpContext could not be compared against " + "MDRawSystemInfo"; + return false; + } + + MDCPUArchitecture system_info_cpu_type = static_cast( + raw_system_info->processor_architecture); + + // Compare the CPU type of the context record to the CPU type in the + // minidump's system info stream. + bool return_value = false; + switch (context_cpu_type) { + case MD_CONTEXT_X86: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_X86 || + system_info_cpu_type == MD_CPU_ARCHITECTURE_X86_WIN64 || + system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) { + return_value = true; + } + break; + + case MD_CONTEXT_PPC: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_PPC) + return_value = true; + break; + + case MD_CONTEXT_AMD64: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_AMD64) + return_value = true; + break; + + case MD_CONTEXT_SPARC: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) + return_value = true; + break; + + case MD_CONTEXT_ARM: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM) + return_value = true; + break; + } + + BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << + HexString(context_cpu_type) << + " wrong for MinidumpSystemInfo CPU " << + HexString(system_info_cpu_type); + + return return_value; +} + + +void MinidumpContext::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpContext cannot print invalid data"; + return; + } + + switch (GetContextCPU()) { + case MD_CONTEXT_X86: { + const MDRawContextX86* context_x86 = GetContextX86(); + printf("MDRawContextX86\n"); + printf(" context_flags = 0x%x\n", + context_x86->context_flags); + printf(" dr0 = 0x%x\n", context_x86->dr0); + printf(" dr1 = 0x%x\n", context_x86->dr1); + printf(" dr2 = 0x%x\n", context_x86->dr2); + printf(" dr3 = 0x%x\n", context_x86->dr3); + printf(" dr6 = 0x%x\n", context_x86->dr6); + printf(" dr7 = 0x%x\n", context_x86->dr7); + printf(" float_save.control_word = 0x%x\n", + context_x86->float_save.control_word); + printf(" float_save.status_word = 0x%x\n", + context_x86->float_save.status_word); + printf(" float_save.tag_word = 0x%x\n", + context_x86->float_save.tag_word); + printf(" float_save.error_offset = 0x%x\n", + context_x86->float_save.error_offset); + printf(" float_save.error_selector = 0x%x\n", + context_x86->float_save.error_selector); + printf(" float_save.data_offset = 0x%x\n", + context_x86->float_save.data_offset); + printf(" float_save.data_selector = 0x%x\n", + context_x86->float_save.data_selector); + printf(" float_save.register_area[%2d] = 0x", + MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE); + for (unsigned int register_index = 0; + register_index < MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE; + ++register_index) { + printf("%02x", context_x86->float_save.register_area[register_index]); + } + printf("\n"); + printf(" float_save.cr0_npx_state = 0x%x\n", + context_x86->float_save.cr0_npx_state); + printf(" gs = 0x%x\n", context_x86->gs); + printf(" fs = 0x%x\n", context_x86->fs); + printf(" es = 0x%x\n", context_x86->es); + printf(" ds = 0x%x\n", context_x86->ds); + printf(" edi = 0x%x\n", context_x86->edi); + printf(" esi = 0x%x\n", context_x86->esi); + printf(" ebx = 0x%x\n", context_x86->ebx); + printf(" edx = 0x%x\n", context_x86->edx); + printf(" ecx = 0x%x\n", context_x86->ecx); + printf(" eax = 0x%x\n", context_x86->eax); + printf(" ebp = 0x%x\n", context_x86->ebp); + printf(" eip = 0x%x\n", context_x86->eip); + printf(" cs = 0x%x\n", context_x86->cs); + printf(" eflags = 0x%x\n", context_x86->eflags); + printf(" esp = 0x%x\n", context_x86->esp); + printf(" ss = 0x%x\n", context_x86->ss); + printf(" extended_registers[%3d] = 0x", + MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE); + for (unsigned int register_index = 0; + register_index < MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE; + ++register_index) { + printf("%02x", context_x86->extended_registers[register_index]); + } + printf("\n\n"); + + break; + } + + case MD_CONTEXT_PPC: { + const MDRawContextPPC* context_ppc = GetContextPPC(); + printf("MDRawContextPPC\n"); + printf(" context_flags = 0x%x\n", + context_ppc->context_flags); + printf(" srr0 = 0x%x\n", context_ppc->srr0); + printf(" srr1 = 0x%x\n", context_ppc->srr1); + for (unsigned int gpr_index = 0; + gpr_index < MD_CONTEXT_PPC_GPR_COUNT; + ++gpr_index) { + printf(" gpr[%2d] = 0x%x\n", + gpr_index, context_ppc->gpr[gpr_index]); + } + printf(" cr = 0x%x\n", context_ppc->cr); + printf(" xer = 0x%x\n", context_ppc->xer); + printf(" lr = 0x%x\n", context_ppc->lr); + printf(" ctr = 0x%x\n", context_ppc->ctr); + printf(" mq = 0x%x\n", context_ppc->mq); + printf(" vrsave = 0x%x\n", context_ppc->vrsave); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT; + ++fpr_index) { + printf(" float_save.fpregs[%2d] = 0x%" PRIx64 "\n", + fpr_index, context_ppc->float_save.fpregs[fpr_index]); + } + printf(" float_save.fpscr = 0x%x\n", + context_ppc->float_save.fpscr); + // TODO(mmentovai): print the 128-bit quantities in + // context_ppc->vector_save. This isn't done yet because printf + // doesn't support 128-bit quantities, and printing them using + // PRIx64 as two 64-bit quantities requires knowledge of the CPU's + // byte ordering. + printf(" vector_save.save_vrvalid = 0x%x\n", + context_ppc->vector_save.save_vrvalid); + printf("\n"); + + break; + } + + case MD_CONTEXT_AMD64: { + const MDRawContextAMD64* context_amd64 = GetContextAMD64(); + printf("MDRawContextAMD64\n"); + printf(" p1_home = 0x%" PRIx64 "\n", + context_amd64->p1_home); + printf(" p2_home = 0x%" PRIx64 "\n", + context_amd64->p2_home); + printf(" p3_home = 0x%" PRIx64 "\n", + context_amd64->p3_home); + printf(" p4_home = 0x%" PRIx64 "\n", + context_amd64->p4_home); + printf(" p5_home = 0x%" PRIx64 "\n", + context_amd64->p5_home); + printf(" p6_home = 0x%" PRIx64 "\n", + context_amd64->p6_home); + printf(" context_flags = 0x%x\n", + context_amd64->context_flags); + printf(" mx_csr = 0x%x\n", + context_amd64->mx_csr); + printf(" cs = 0x%x\n", context_amd64->cs); + printf(" ds = 0x%x\n", context_amd64->ds); + printf(" es = 0x%x\n", context_amd64->es); + printf(" fs = 0x%x\n", context_amd64->fs); + printf(" gs = 0x%x\n", context_amd64->gs); + printf(" ss = 0x%x\n", context_amd64->ss); + printf(" eflags = 0x%x\n", context_amd64->eflags); + printf(" dr0 = 0x%" PRIx64 "\n", context_amd64->dr0); + printf(" dr1 = 0x%" PRIx64 "\n", context_amd64->dr1); + printf(" dr2 = 0x%" PRIx64 "\n", context_amd64->dr2); + printf(" dr3 = 0x%" PRIx64 "\n", context_amd64->dr3); + printf(" dr6 = 0x%" PRIx64 "\n", context_amd64->dr6); + printf(" dr7 = 0x%" PRIx64 "\n", context_amd64->dr7); + printf(" rax = 0x%" PRIx64 "\n", context_amd64->rax); + printf(" rcx = 0x%" PRIx64 "\n", context_amd64->rcx); + printf(" rdx = 0x%" PRIx64 "\n", context_amd64->rdx); + printf(" rbx = 0x%" PRIx64 "\n", context_amd64->rbx); + printf(" rsp = 0x%" PRIx64 "\n", context_amd64->rsp); + printf(" rbp = 0x%" PRIx64 "\n", context_amd64->rbp); + printf(" rsi = 0x%" PRIx64 "\n", context_amd64->rsi); + printf(" rdi = 0x%" PRIx64 "\n", context_amd64->rdi); + printf(" r8 = 0x%" PRIx64 "\n", context_amd64->r8); + printf(" r9 = 0x%" PRIx64 "\n", context_amd64->r9); + printf(" r10 = 0x%" PRIx64 "\n", context_amd64->r10); + printf(" r11 = 0x%" PRIx64 "\n", context_amd64->r11); + printf(" r12 = 0x%" PRIx64 "\n", context_amd64->r12); + printf(" r13 = 0x%" PRIx64 "\n", context_amd64->r13); + printf(" r14 = 0x%" PRIx64 "\n", context_amd64->r14); + printf(" r15 = 0x%" PRIx64 "\n", context_amd64->r15); + printf(" rip = 0x%" PRIx64 "\n", context_amd64->rip); + //TODO: print xmm, vector, debug registers + printf("\n"); + break; + } + + case MD_CONTEXT_SPARC: { + const MDRawContextSPARC* context_sparc = GetContextSPARC(); + printf("MDRawContextSPARC\n"); + printf(" context_flags = 0x%x\n", + context_sparc->context_flags); + for (unsigned int g_r_index = 0; + g_r_index < MD_CONTEXT_SPARC_GPR_COUNT; + ++g_r_index) { + printf(" g_r[%2d] = 0x%" PRIx64 "\n", + g_r_index, context_sparc->g_r[g_r_index]); + } + printf(" ccr = 0x%" PRIx64 "\n", context_sparc->ccr); + printf(" pc = 0x%" PRIx64 "\n", context_sparc->pc); + printf(" npc = 0x%" PRIx64 "\n", context_sparc->npc); + printf(" y = 0x%" PRIx64 "\n", context_sparc->y); + printf(" asi = 0x%" PRIx64 "\n", context_sparc->asi); + printf(" fprs = 0x%" PRIx64 "\n", context_sparc->fprs); + + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT; + ++fpr_index) { + printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", + fpr_index, context_sparc->float_save.regs[fpr_index]); + } + printf(" float_save.filler = 0x%" PRIx64 "\n", + context_sparc->float_save.filler); + printf(" float_save.fsr = 0x%" PRIx64 "\n", + context_sparc->float_save.fsr); + break; + } + + case MD_CONTEXT_ARM: { + const MDRawContextARM* context_arm = GetContextARM(); + printf("MDRawContextARM\n"); + printf(" context_flags = 0x%x\n", + context_arm->context_flags); + for (unsigned int ireg_index = 0; + ireg_index < MD_CONTEXT_ARM_GPR_COUNT; + ++ireg_index) { + printf(" iregs[%2d] = 0x%x\n", + ireg_index, context_arm->iregs[ireg_index]); + } + printf(" cpsr = 0x%x\n", context_arm->cpsr); + printf(" float_save.fpscr = 0x%" PRIx64 "\n", + context_arm->float_save.fpscr); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; + ++fpr_index) { + printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", + fpr_index, context_arm->float_save.regs[fpr_index]); + } + for (unsigned int fpe_index = 0; + fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; + ++fpe_index) { + printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n", + fpe_index, context_arm->float_save.extra[fpe_index]); + } + + break; + } + + default: { + break; + } + } +} + + +// +// MinidumpMemoryRegion +// + + +u_int32_t MinidumpMemoryRegion::max_bytes_ = 1024 * 1024; // 1MB + + +MinidumpMemoryRegion::MinidumpMemoryRegion(Minidump* minidump) + : MinidumpObject(minidump), + descriptor_(NULL), + memory_(NULL) { +} + + +MinidumpMemoryRegion::~MinidumpMemoryRegion() { + delete memory_; +} + + +void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) { + descriptor_ = descriptor; + valid_ = descriptor && + descriptor_->memory.data_size <= + numeric_limits::max() - + descriptor_->start_of_memory_range; +} + + +const u_int8_t* MinidumpMemoryRegion::GetMemory() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory"; + return NULL; + } + + if (!memory_) { + if (descriptor_->memory.data_size == 0) { + BPLOG(ERROR) << "MinidumpMemoryRegion is empty"; + return NULL; + } + + if (!minidump_->SeekSet(descriptor_->memory.rva)) { + BPLOG(ERROR) << "MinidumpMemoryRegion could not seek to memory region"; + return NULL; + } + + if (descriptor_->memory.data_size > max_bytes_) { + BPLOG(ERROR) << "MinidumpMemoryRegion size " << + descriptor_->memory.data_size << " exceeds maximum " << + max_bytes_; + return NULL; + } + + scoped_ptr< vector > memory( + new vector(descriptor_->memory.data_size)); + + if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size)) { + BPLOG(ERROR) << "MinidumpMemoryRegion could not read memory region"; + return NULL; + } + + memory_ = memory.release(); + } + + return &(*memory_)[0]; +} + + +u_int64_t MinidumpMemoryRegion::GetBase() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase"; + return static_cast(-1); + } + + return descriptor_->start_of_memory_range; +} + + +u_int32_t MinidumpMemoryRegion::GetSize() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize"; + return 0; + } + + return descriptor_->memory.data_size; +} + + +void MinidumpMemoryRegion::FreeMemory() { + delete memory_; + memory_ = NULL; +} + + +template +bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address, + T* value) const { + BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal " + "requires |value|"; + assert(value); + *value = 0; + + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for " + "GetMemoryAtAddressInternal"; + return false; + } + + // Common failure case + if (address < descriptor_->start_of_memory_range || + sizeof(T) > numeric_limits::max() - address || + address + sizeof(T) > descriptor_->start_of_memory_range + + descriptor_->memory.data_size) { + BPLOG(INFO) << "MinidumpMemoryRegion request out of range: " << + HexString(address) << "+" << sizeof(T) << "/" << + HexString(descriptor_->start_of_memory_range) << "+" << + HexString(descriptor_->memory.data_size); + return false; + } + + const u_int8_t* memory = GetMemory(); + if (!memory) { + // GetMemory already logged a perfectly good message. + return false; + } + + // If the CPU requires memory accesses to be aligned, this can crash. + // x86 and ppc are able to cope, though. + *value = *reinterpret_cast( + &memory[address - descriptor_->start_of_memory_range]); + + if (minidump_->swap()) + Swap(value); + + return true; +} + + +bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, + u_int8_t* value) const { + return GetMemoryAtAddressInternal(address, value); +} + + +bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, + u_int16_t* value) const { + return GetMemoryAtAddressInternal(address, value); +} + + +bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, + u_int32_t* value) const { + return GetMemoryAtAddressInternal(address, value); +} + + +bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, + u_int64_t* value) const { + return GetMemoryAtAddressInternal(address, value); +} + + +void MinidumpMemoryRegion::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpMemoryRegion cannot print invalid data"; + return; + } + + const u_int8_t* memory = GetMemory(); + if (memory) { + printf("0x"); + for (unsigned int byte_index = 0; + byte_index < descriptor_->memory.data_size; + byte_index++) { + printf("%02x", memory[byte_index]); + } + printf("\n"); + } else { + printf("No memory\n"); + } +} + + +// +// MinidumpThread +// + + +MinidumpThread::MinidumpThread(Minidump* minidump) + : MinidumpObject(minidump), + thread_(), + memory_(NULL), + context_(NULL) { +} + + +MinidumpThread::~MinidumpThread() { + delete memory_; + delete context_; +} + + +bool MinidumpThread::Read() { + // Invalidate cached data. + delete memory_; + memory_ = NULL; + delete context_; + context_ = NULL; + + valid_ = false; + + if (!minidump_->ReadBytes(&thread_, sizeof(thread_))) { + BPLOG(ERROR) << "MinidumpThread cannot read thread"; + return false; + } + + if (minidump_->swap()) { + Swap(&thread_.thread_id); + Swap(&thread_.suspend_count); + Swap(&thread_.priority_class); + Swap(&thread_.priority); + Swap(&thread_.teb); + Swap(&thread_.stack); + Swap(&thread_.thread_context); + } + + // Check for base + size overflow or undersize. + if (thread_.stack.memory.data_size == 0 || + thread_.stack.memory.data_size > numeric_limits::max() - + thread_.stack.start_of_memory_range) { + BPLOG(ERROR) << "MinidumpThread has a memory region problem, " << + HexString(thread_.stack.start_of_memory_range) << "+" << + HexString(thread_.stack.memory.data_size); + return false; + } + + memory_ = new MinidumpMemoryRegion(minidump_); + memory_->SetDescriptor(&thread_.stack); + + valid_ = true; + return true; +} + + +MinidumpMemoryRegion* MinidumpThread::GetMemory() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThread for GetMemory"; + return NULL; + } + + return memory_; +} + + +MinidumpContext* MinidumpThread::GetContext() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThread for GetContext"; + return NULL; + } + + if (!context_) { + if (!minidump_->SeekSet(thread_.thread_context.rva)) { + BPLOG(ERROR) << "MinidumpThread cannot seek to context"; + return NULL; + } + + scoped_ptr context(new MinidumpContext(minidump_)); + + if (!context->Read(thread_.thread_context.data_size)) { + BPLOG(ERROR) << "MinidumpThread cannot read context"; + return NULL; + } + + context_ = context.release(); + } + + return context_; +} + + +bool MinidumpThread::GetThreadID(u_int32_t *thread_id) const { + BPLOG_IF(ERROR, !thread_id) << "MinidumpThread::GetThreadID requires " + "|thread_id|"; + assert(thread_id); + *thread_id = 0; + + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThread for GetThreadID"; + return false; + } + + *thread_id = thread_.thread_id; + return true; +} + + +void MinidumpThread::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpThread cannot print invalid data"; + return; + } + + printf("MDRawThread\n"); + printf(" thread_id = 0x%x\n", thread_.thread_id); + printf(" suspend_count = %d\n", thread_.suspend_count); + printf(" priority_class = 0x%x\n", thread_.priority_class); + printf(" priority = 0x%x\n", thread_.priority); + printf(" teb = 0x%" PRIx64 "\n", thread_.teb); + printf(" stack.start_of_memory_range = 0x%" PRIx64 "\n", + thread_.stack.start_of_memory_range); + printf(" stack.memory.data_size = 0x%x\n", + thread_.stack.memory.data_size); + printf(" stack.memory.rva = 0x%x\n", thread_.stack.memory.rva); + printf(" thread_context.data_size = 0x%x\n", + thread_.thread_context.data_size); + printf(" thread_context.rva = 0x%x\n", + thread_.thread_context.rva); + + MinidumpContext* context = GetContext(); + if (context) { + printf("\n"); + context->Print(); + } else { + printf(" (no context)\n"); + printf("\n"); + } + + MinidumpMemoryRegion* memory = GetMemory(); + if (memory) { + printf("Stack\n"); + memory->Print(); + } else { + printf("No stack\n"); + } + printf("\n"); +} + + +// +// MinidumpThreadList +// + + +u_int32_t MinidumpThreadList::max_threads_ = 4096; + + +MinidumpThreadList::MinidumpThreadList(Minidump* minidump) + : MinidumpStream(minidump), + id_to_thread_map_(), + threads_(NULL), + thread_count_(0) { +} + + +MinidumpThreadList::~MinidumpThreadList() { + delete threads_; +} + + +bool MinidumpThreadList::Read(u_int32_t expected_size) { + // Invalidate cached data. + id_to_thread_map_.clear(); + delete threads_; + threads_ = NULL; + thread_count_ = 0; + + valid_ = false; + + u_int32_t thread_count; + if (expected_size < sizeof(thread_count)) { + BPLOG(ERROR) << "MinidumpThreadList count size mismatch, " << + expected_size << " < " << sizeof(thread_count); + return false; + } + if (!minidump_->ReadBytes(&thread_count, sizeof(thread_count))) { + BPLOG(ERROR) << "MinidumpThreadList cannot read thread count"; + return false; + } + + if (minidump_->swap()) + Swap(&thread_count); + + if (thread_count > numeric_limits::max() / sizeof(MDRawThread)) { + BPLOG(ERROR) << "MinidumpThreadList thread count " << thread_count << + " would cause multiplication overflow"; + return false; + } + + if (expected_size != sizeof(thread_count) + + thread_count * sizeof(MDRawThread)) { + // may be padded with 4 bytes on 64bit ABIs for alignment + if (expected_size == sizeof(thread_count) + 4 + + thread_count * sizeof(MDRawThread)) { + u_int32_t useless; + if (!minidump_->ReadBytes(&useless, 4)) { + BPLOG(ERROR) << "MinidumpThreadList cannot read threadlist padded bytes"; + return false; + } + } else { + BPLOG(ERROR) << "MinidumpThreadList size mismatch, " << expected_size << + " != " << sizeof(thread_count) + + thread_count * sizeof(MDRawThread); + return false; + } + } + + + if (thread_count > max_threads_) { + BPLOG(ERROR) << "MinidumpThreadList count " << thread_count << + " exceeds maximum " << max_threads_; + return false; + } + + if (thread_count != 0) { + scoped_ptr threads( + new MinidumpThreads(thread_count, MinidumpThread(minidump_))); + + for (unsigned int thread_index = 0; + thread_index < thread_count; + ++thread_index) { + MinidumpThread* thread = &(*threads)[thread_index]; + + // Assume that the file offset is correct after the last read. + if (!thread->Read()) { + BPLOG(ERROR) << "MinidumpThreadList cannot read thread " << + thread_index << "/" << thread_count; + return false; + } + + u_int32_t thread_id; + if (!thread->GetThreadID(&thread_id)) { + BPLOG(ERROR) << "MinidumpThreadList cannot get thread ID for thread " << + thread_index << "/" << thread_count; + return false; + } + + if (GetThreadByID(thread_id)) { + // Another thread with this ID is already in the list. Data error. + BPLOG(ERROR) << "MinidumpThreadList found multiple threads with ID " << + HexString(thread_id) << " at thread " << + thread_index << "/" << thread_count; + return false; + } + id_to_thread_map_[thread_id] = thread; + } + + threads_ = threads.release(); + } + + thread_count_ = thread_count; + + valid_ = true; + return true; +} + + +MinidumpThread* MinidumpThreadList::GetThreadAtIndex(unsigned int index) + const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThreadList for GetThreadAtIndex"; + return NULL; + } + + if (index >= thread_count_) { + BPLOG(ERROR) << "MinidumpThreadList index out of range: " << + index << "/" << thread_count_; + return NULL; + } + + return &(*threads_)[index]; +} + + +MinidumpThread* MinidumpThreadList::GetThreadByID(u_int32_t thread_id) { + // Don't check valid_. Read calls this method before everything is + // validated. It is safe to not check valid_ here. + return id_to_thread_map_[thread_id]; +} + + +void MinidumpThreadList::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpThreadList cannot print invalid data"; + return; + } + + printf("MinidumpThreadList\n"); + printf(" thread_count = %d\n", thread_count_); + printf("\n"); + + for (unsigned int thread_index = 0; + thread_index < thread_count_; + ++thread_index) { + printf("thread[%d]\n", thread_index); + + (*threads_)[thread_index].Print(); + } +} + + +// +// MinidumpModule +// + + +u_int32_t MinidumpModule::max_cv_bytes_ = 32768; +u_int32_t MinidumpModule::max_misc_bytes_ = 32768; + + +MinidumpModule::MinidumpModule(Minidump* minidump) + : MinidumpObject(minidump), + module_valid_(false), + has_debug_info_(false), + module_(), + name_(NULL), + cv_record_(NULL), + cv_record_signature_(MD_CVINFOUNKNOWN_SIGNATURE), + misc_record_(NULL) { +} + + +MinidumpModule::~MinidumpModule() { + delete name_; + delete cv_record_; + delete misc_record_; +} + + +bool MinidumpModule::Read() { + // Invalidate cached data. + delete name_; + name_ = NULL; + delete cv_record_; + cv_record_ = NULL; + cv_record_signature_ = MD_CVINFOUNKNOWN_SIGNATURE; + delete misc_record_; + misc_record_ = NULL; + + module_valid_ = false; + has_debug_info_ = false; + valid_ = false; + + if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) { + BPLOG(ERROR) << "MinidumpModule cannot read module"; + return false; + } + + if (minidump_->swap()) { + Swap(&module_.base_of_image); + Swap(&module_.size_of_image); + Swap(&module_.checksum); + Swap(&module_.time_date_stamp); + Swap(&module_.module_name_rva); + Swap(&module_.version_info.signature); + Swap(&module_.version_info.struct_version); + Swap(&module_.version_info.file_version_hi); + Swap(&module_.version_info.file_version_lo); + Swap(&module_.version_info.product_version_hi); + Swap(&module_.version_info.product_version_lo); + Swap(&module_.version_info.file_flags_mask); + Swap(&module_.version_info.file_flags); + Swap(&module_.version_info.file_os); + Swap(&module_.version_info.file_type); + Swap(&module_.version_info.file_subtype); + Swap(&module_.version_info.file_date_hi); + Swap(&module_.version_info.file_date_lo); + Swap(&module_.cv_record); + Swap(&module_.misc_record); + // Don't swap reserved fields because their contents are unknown (as + // are their proper widths). + } + + // Check for base + size overflow or undersize. + if (module_.size_of_image == 0 || + module_.size_of_image > + numeric_limits::max() - module_.base_of_image) { + BPLOG(ERROR) << "MinidumpModule has a module problem, " << + HexString(module_.base_of_image) << "+" << + HexString(module_.size_of_image); + return false; + } + + module_valid_ = true; + return true; +} + + +bool MinidumpModule::ReadAuxiliaryData() { + if (!module_valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for ReadAuxiliaryData"; + return false; + } + + // Each module must have a name. + name_ = minidump_->ReadString(module_.module_name_rva); + if (!name_) { + BPLOG(ERROR) << "MinidumpModule could not read name"; + return false; + } + + // At this point, we have enough info for the module to be valid. + valid_ = true; + + // CodeView and miscellaneous debug records are only required if the + // module indicates that they exist. + if (module_.cv_record.data_size && !GetCVRecord(NULL)) { + BPLOG(ERROR) << "MinidumpModule has no CodeView record, " + "but one was expected"; + return false; + } + + if (module_.misc_record.data_size && !GetMiscRecord(NULL)) { + BPLOG(ERROR) << "MinidumpModule has no miscellaneous debug record, " + "but one was expected"; + return false; + } + + has_debug_info_ = true; + return true; +} + + +string MinidumpModule::code_file() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for code_file"; + return ""; + } + + return *name_; +} + + +string MinidumpModule::code_identifier() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for code_identifier"; + return ""; + } + + if (!has_debug_info_) + return ""; + + MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo(); + if (!minidump_system_info) { + BPLOG(ERROR) << "MinidumpModule code_identifier requires " + "MinidumpSystemInfo"; + return ""; + } + + const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info(); + if (!raw_system_info) { + BPLOG(ERROR) << "MinidumpModule code_identifier requires MDRawSystemInfo"; + return ""; + } + + string identifier; + + switch (raw_system_info->platform_id) { + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: { + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + char identifier_string[17]; + snprintf(identifier_string, sizeof(identifier_string), "%08X%x", + module_.time_date_stamp, module_.size_of_image); + identifier = identifier_string; + break; + } + + case MD_OS_MAC_OS_X: + case MD_OS_IOS: + case MD_OS_SOLARIS: + case MD_OS_LINUX: { + // TODO(mmentovai): support uuid extension if present, otherwise fall + // back to version (from LC_ID_DYLIB?), otherwise fall back to something + // else. + identifier = "id"; + break; + } + + default: { + // Without knowing what OS generated the dump, we can't generate a good + // identifier. Return an empty string, signalling failure. + BPLOG(ERROR) << "MinidumpModule code_identifier requires known platform, " + "found " << HexString(raw_system_info->platform_id); + break; + } + } + + return identifier; +} + + +string MinidumpModule::debug_file() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for debug_file"; + return ""; + } + + if (!has_debug_info_) + return ""; + + string file; + // Prefer the CodeView record if present. + if (cv_record_) { + if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { + // It's actually an MDCVInfoPDB70 structure. + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast(&(*cv_record_)[0]); + assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); + + // GetCVRecord guarantees pdb_file_name is null-terminated. + file = reinterpret_cast(cv_record_70->pdb_file_name); + } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { + // It's actually an MDCVInfoPDB20 structure. + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast(&(*cv_record_)[0]); + assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); + + // GetCVRecord guarantees pdb_file_name is null-terminated. + file = reinterpret_cast(cv_record_20->pdb_file_name); + } + + // If there's a CodeView record but it doesn't match a known signature, + // try the miscellaneous record. + } + + if (file.empty()) { + // No usable CodeView record. Try the miscellaneous debug record. + if (misc_record_) { + const MDImageDebugMisc* misc_record = + reinterpret_cast(&(*misc_record_)[0]); + if (!misc_record->unicode) { + // If it's not Unicode, just stuff it into the string. It's unclear + // if misc_record->data is 0-terminated, so use an explicit size. + file = string( + reinterpret_cast(misc_record->data), + module_.misc_record.data_size - MDImageDebugMisc_minsize); + } else { + // There's a misc_record but it encodes the debug filename in UTF-16. + // (Actually, because miscellaneous records are so old, it's probably + // UCS-2.) Convert it to UTF-8 for congruity with the other strings + // that this method (and all other methods in the Minidump family) + // return. + + unsigned int bytes = + module_.misc_record.data_size - MDImageDebugMisc_minsize; + if (bytes % 2 == 0) { + unsigned int utf16_words = bytes / 2; + + // UTF16ToUTF8 expects a vector, so create a temporary one + // and copy the UTF-16 data into it. + vector string_utf16(utf16_words); + if (utf16_words) + memcpy(&string_utf16[0], &misc_record->data, bytes); + + // GetMiscRecord already byte-swapped the data[] field if it contains + // UTF-16, so pass false as the swap argument. + scoped_ptr new_file(UTF16ToUTF8(string_utf16, false)); + file = *new_file; + } + } + } + } + + // Relatively common case + BPLOG_IF(INFO, file.empty()) << "MinidumpModule could not determine " + "debug_file for " << *name_; + + return file; +} + + +string MinidumpModule::debug_identifier() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for debug_identifier"; + return ""; + } + + if (!has_debug_info_) + return ""; + + string identifier; + + // Use the CodeView record if present. + if (cv_record_) { + if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { + // It's actually an MDCVInfoPDB70 structure. + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast(&(*cv_record_)[0]); + assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); + + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + char identifier_string[41]; + snprintf(identifier_string, sizeof(identifier_string), + "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%x", + cv_record_70->signature.data1, + cv_record_70->signature.data2, + cv_record_70->signature.data3, + cv_record_70->signature.data4[0], + cv_record_70->signature.data4[1], + cv_record_70->signature.data4[2], + cv_record_70->signature.data4[3], + cv_record_70->signature.data4[4], + cv_record_70->signature.data4[5], + cv_record_70->signature.data4[6], + cv_record_70->signature.data4[7], + cv_record_70->age); + identifier = identifier_string; + } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { + // It's actually an MDCVInfoPDB20 structure. + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast(&(*cv_record_)[0]); + assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); + + // Use the same format that the MS symbol server uses in filesystem + // hierarchies. + char identifier_string[17]; + snprintf(identifier_string, sizeof(identifier_string), + "%08X%x", cv_record_20->signature, cv_record_20->age); + identifier = identifier_string; + } + } + + // TODO(mmentovai): if there's no usable CodeView record, there might be a + // miscellaneous debug record. It only carries a filename, though, and no + // identifier. I'm not sure what the right thing to do for the identifier + // is in that case, but I don't expect to find many modules without a + // CodeView record (or some other Breakpad extension structure in place of + // a CodeView record). Treat it as an error (empty identifier) for now. + + // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier(). + + // Relatively common case + BPLOG_IF(INFO, identifier.empty()) << "MinidumpModule could not determine " + "debug_identifier for " << *name_; + + return identifier; +} + + +string MinidumpModule::version() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for version"; + return ""; + } + + string version; + + if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE && + module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) { + char version_string[24]; + snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u", + module_.version_info.file_version_hi >> 16, + module_.version_info.file_version_hi & 0xffff, + module_.version_info.file_version_lo >> 16, + module_.version_info.file_version_lo & 0xffff); + version = version_string; + } + + // TODO(mmentovai): possibly support other struct types in place of + // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use + // a different structure that better represents versioning facilities on + // Mac OS X and Linux, instead of forcing them to adhere to the dotted + // quad of 16-bit ints that Windows uses. + + BPLOG_IF(INFO, version.empty()) << "MinidumpModule could not determine " + "version for " << *name_; + + return version; +} + + +const CodeModule* MinidumpModule::Copy() const { + return new BasicCodeModule(this); +} + + +const u_int8_t* MinidumpModule::GetCVRecord(u_int32_t* size) { + if (!module_valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for GetCVRecord"; + return NULL; + } + + if (!cv_record_) { + // This just guards against 0-sized CodeView records; more specific checks + // are used when the signature is checked against various structure types. + if (module_.cv_record.data_size == 0) { + return NULL; + } + + if (!minidump_->SeekSet(module_.cv_record.rva)) { + BPLOG(ERROR) << "MinidumpModule could not seek to CodeView record"; + return NULL; + } + + if (module_.cv_record.data_size > max_cv_bytes_) { + BPLOG(ERROR) << "MinidumpModule CodeView record size " << + module_.cv_record.data_size << " exceeds maximum " << + max_cv_bytes_; + return NULL; + } + + // Allocating something that will be accessed as MDCVInfoPDB70 or + // MDCVInfoPDB20 but is allocated as u_int8_t[] can cause alignment + // problems. x86 and ppc are able to cope, though. This allocation + // style is needed because the MDCVInfoPDB70 or MDCVInfoPDB20 are + // variable-sized due to their pdb_file_name fields; these structures + // are not MDCVInfoPDB70_minsize or MDCVInfoPDB20_minsize and treating + // them as such would result in incomplete structures or overruns. + scoped_ptr< vector > cv_record( + new vector(module_.cv_record.data_size)); + + if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size)) { + BPLOG(ERROR) << "MinidumpModule could not read CodeView record"; + return NULL; + } + + u_int32_t signature = MD_CVINFOUNKNOWN_SIGNATURE; + if (module_.cv_record.data_size > sizeof(signature)) { + MDCVInfoPDB70* cv_record_signature = + reinterpret_cast(&(*cv_record)[0]); + signature = cv_record_signature->cv_signature; + if (minidump_->swap()) + Swap(&signature); + } + + if (signature == MD_CVINFOPDB70_SIGNATURE) { + // Now that the structure type is known, recheck the size. + if (MDCVInfoPDB70_minsize > module_.cv_record.data_size) { + BPLOG(ERROR) << "MinidumpModule CodeView7 record size mismatch, " << + MDCVInfoPDB70_minsize << " > " << + module_.cv_record.data_size; + return NULL; + } + + if (minidump_->swap()) { + MDCVInfoPDB70* cv_record_70 = + reinterpret_cast(&(*cv_record)[0]); + Swap(&cv_record_70->cv_signature); + Swap(&cv_record_70->signature); + Swap(&cv_record_70->age); + // Don't swap cv_record_70.pdb_file_name because it's an array of 8-bit + // quantities. (It's a path, is it UTF-8?) + } + + // The last field of either structure is null-terminated 8-bit character + // data. Ensure that it's null-terminated. + if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { + BPLOG(ERROR) << "MinidumpModule CodeView7 record string is not " + "0-terminated"; + return NULL; + } + } else if (signature == MD_CVINFOPDB20_SIGNATURE) { + // Now that the structure type is known, recheck the size. + if (MDCVInfoPDB20_minsize > module_.cv_record.data_size) { + BPLOG(ERROR) << "MinidumpModule CodeView2 record size mismatch, " << + MDCVInfoPDB20_minsize << " > " << + module_.cv_record.data_size; + return NULL; + } + if (minidump_->swap()) { + MDCVInfoPDB20* cv_record_20 = + reinterpret_cast(&(*cv_record)[0]); + Swap(&cv_record_20->cv_header.signature); + Swap(&cv_record_20->cv_header.offset); + Swap(&cv_record_20->signature); + Swap(&cv_record_20->age); + // Don't swap cv_record_20.pdb_file_name because it's an array of 8-bit + // quantities. (It's a path, is it UTF-8?) + } + + // The last field of either structure is null-terminated 8-bit character + // data. Ensure that it's null-terminated. + if ((*cv_record)[module_.cv_record.data_size - 1] != '\0') { + BPLOG(ERROR) << "MindumpModule CodeView2 record string is not " + "0-terminated"; + return NULL; + } + } + + // If the signature doesn't match something above, it's not something + // that Breakpad can presently handle directly. Because some modules in + // the wild contain such CodeView records as MD_CVINFOCV50_SIGNATURE, + // don't bail out here - allow the data to be returned to the user, + // although byte-swapping can't be done. + + // Store the vector type because that's how storage was allocated, but + // return it casted to u_int8_t*. + cv_record_ = cv_record.release(); + cv_record_signature_ = signature; + } + + if (size) + *size = module_.cv_record.data_size; + + return &(*cv_record_)[0]; +} + + +const MDImageDebugMisc* MinidumpModule::GetMiscRecord(u_int32_t* size) { + if (!module_valid_) { + BPLOG(ERROR) << "Invalid MinidumpModule for GetMiscRecord"; + return NULL; + } + + if (!misc_record_) { + if (module_.misc_record.data_size == 0) { + return NULL; + } + + if (MDImageDebugMisc_minsize > module_.misc_record.data_size) { + BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record " + "size mismatch, " << MDImageDebugMisc_minsize << " > " << + module_.misc_record.data_size; + return NULL; + } + + if (!minidump_->SeekSet(module_.misc_record.rva)) { + BPLOG(ERROR) << "MinidumpModule could not seek to miscellaneous " + "debugging record"; + return NULL; + } + + if (module_.misc_record.data_size > max_misc_bytes_) { + BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record size " << + module_.misc_record.data_size << " exceeds maximum " << + max_misc_bytes_; + return NULL; + } + + // Allocating something that will be accessed as MDImageDebugMisc but + // is allocated as u_int8_t[] can cause alignment problems. x86 and + // ppc are able to cope, though. This allocation style is needed + // because the MDImageDebugMisc is variable-sized due to its data field; + // this structure is not MDImageDebugMisc_minsize and treating it as such + // would result in an incomplete structure or an overrun. + scoped_ptr< vector > misc_record_mem( + new vector(module_.misc_record.data_size)); + MDImageDebugMisc* misc_record = + reinterpret_cast(&(*misc_record_mem)[0]); + + if (!minidump_->ReadBytes(misc_record, module_.misc_record.data_size)) { + BPLOG(ERROR) << "MinidumpModule could not read miscellaneous debugging " + "record"; + return NULL; + } + + if (minidump_->swap()) { + Swap(&misc_record->data_type); + Swap(&misc_record->length); + // Don't swap misc_record.unicode because it's an 8-bit quantity. + // Don't swap the reserved fields for the same reason, and because + // they don't contain any valid data. + if (misc_record->unicode) { + // There is a potential alignment problem, but shouldn't be a problem + // in practice due to the layout of MDImageDebugMisc. + u_int16_t* data16 = reinterpret_cast(&(misc_record->data)); + unsigned int dataBytes = module_.misc_record.data_size - + MDImageDebugMisc_minsize; + unsigned int dataLength = dataBytes / 2; + for (unsigned int characterIndex = 0; + characterIndex < dataLength; + ++characterIndex) { + Swap(&data16[characterIndex]); + } + } + } + + if (module_.misc_record.data_size != misc_record->length) { + BPLOG(ERROR) << "MinidumpModule miscellaneous debugging record data " + "size mismatch, " << module_.misc_record.data_size << + " != " << misc_record->length; + return NULL; + } + + // Store the vector type because that's how storage was allocated, but + // return it casted to MDImageDebugMisc*. + misc_record_ = misc_record_mem.release(); + } + + if (size) + *size = module_.misc_record.data_size; + + return reinterpret_cast(&(*misc_record_)[0]); +} + + +void MinidumpModule::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpModule cannot print invalid data"; + return; + } + + printf("MDRawModule\n"); + printf(" base_of_image = 0x%" PRIx64 "\n", + module_.base_of_image); + printf(" size_of_image = 0x%x\n", + module_.size_of_image); + printf(" checksum = 0x%x\n", + module_.checksum); + printf(" time_date_stamp = 0x%x\n", + module_.time_date_stamp); + printf(" module_name_rva = 0x%x\n", + module_.module_name_rva); + printf(" version_info.signature = 0x%x\n", + module_.version_info.signature); + printf(" version_info.struct_version = 0x%x\n", + module_.version_info.struct_version); + printf(" version_info.file_version = 0x%x:0x%x\n", + module_.version_info.file_version_hi, + module_.version_info.file_version_lo); + printf(" version_info.product_version = 0x%x:0x%x\n", + module_.version_info.product_version_hi, + module_.version_info.product_version_lo); + printf(" version_info.file_flags_mask = 0x%x\n", + module_.version_info.file_flags_mask); + printf(" version_info.file_flags = 0x%x\n", + module_.version_info.file_flags); + printf(" version_info.file_os = 0x%x\n", + module_.version_info.file_os); + printf(" version_info.file_type = 0x%x\n", + module_.version_info.file_type); + printf(" version_info.file_subtype = 0x%x\n", + module_.version_info.file_subtype); + printf(" version_info.file_date = 0x%x:0x%x\n", + module_.version_info.file_date_hi, + module_.version_info.file_date_lo); + printf(" cv_record.data_size = %d\n", + module_.cv_record.data_size); + printf(" cv_record.rva = 0x%x\n", + module_.cv_record.rva); + printf(" misc_record.data_size = %d\n", + module_.misc_record.data_size); + printf(" misc_record.rva = 0x%x\n", + module_.misc_record.rva); + + printf(" (code_file) = \"%s\"\n", code_file().c_str()); + printf(" (code_identifier) = \"%s\"\n", + code_identifier().c_str()); + + u_int32_t cv_record_size; + const u_int8_t *cv_record = GetCVRecord(&cv_record_size); + if (cv_record) { + if (cv_record_signature_ == MD_CVINFOPDB70_SIGNATURE) { + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast(cv_record); + assert(cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE); + + printf(" (cv_record).cv_signature = 0x%x\n", + cv_record_70->cv_signature); + printf(" (cv_record).signature = %08x-%04x-%04x-%02x%02x-", + cv_record_70->signature.data1, + cv_record_70->signature.data2, + cv_record_70->signature.data3, + cv_record_70->signature.data4[0], + cv_record_70->signature.data4[1]); + for (unsigned int guidIndex = 2; + guidIndex < 8; + ++guidIndex) { + printf("%02x", cv_record_70->signature.data4[guidIndex]); + } + printf("\n"); + printf(" (cv_record).age = %d\n", + cv_record_70->age); + printf(" (cv_record).pdb_file_name = \"%s\"\n", + cv_record_70->pdb_file_name); + } else if (cv_record_signature_ == MD_CVINFOPDB20_SIGNATURE) { + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast(cv_record); + assert(cv_record_20->cv_header.signature == MD_CVINFOPDB20_SIGNATURE); + + printf(" (cv_record).cv_header.signature = 0x%x\n", + cv_record_20->cv_header.signature); + printf(" (cv_record).cv_header.offset = 0x%x\n", + cv_record_20->cv_header.offset); + printf(" (cv_record).signature = 0x%x\n", + cv_record_20->signature); + printf(" (cv_record).age = %d\n", + cv_record_20->age); + printf(" (cv_record).pdb_file_name = \"%s\"\n", + cv_record_20->pdb_file_name); + } else { + printf(" (cv_record) = "); + for (unsigned int cv_byte_index = 0; + cv_byte_index < cv_record_size; + ++cv_byte_index) { + printf("%02x", cv_record[cv_byte_index]); + } + printf("\n"); + } + } else { + printf(" (cv_record) = (null)\n"); + } + + const MDImageDebugMisc* misc_record = GetMiscRecord(NULL); + if (misc_record) { + printf(" (misc_record).data_type = 0x%x\n", + misc_record->data_type); + printf(" (misc_record).length = 0x%x\n", + misc_record->length); + printf(" (misc_record).unicode = %d\n", + misc_record->unicode); + // Don't bother printing the UTF-16, we don't really even expect to ever + // see this misc_record anyway. + if (misc_record->unicode) + printf(" (misc_record).data = \"%s\"\n", + misc_record->data); + else + printf(" (misc_record).data = (UTF-16)\n"); + } else { + printf(" (misc_record) = (null)\n"); + } + + printf(" (debug_file) = \"%s\"\n", debug_file().c_str()); + printf(" (debug_identifier) = \"%s\"\n", + debug_identifier().c_str()); + printf(" (version) = \"%s\"\n", version().c_str()); + printf("\n"); +} + + +// +// MinidumpModuleList +// + + +u_int32_t MinidumpModuleList::max_modules_ = 1024; + + +MinidumpModuleList::MinidumpModuleList(Minidump* minidump) + : MinidumpStream(minidump), + range_map_(new RangeMap()), + modules_(NULL), + module_count_(0) { +} + + +MinidumpModuleList::~MinidumpModuleList() { + delete range_map_; + delete modules_; +} + + +bool MinidumpModuleList::Read(u_int32_t expected_size) { + // Invalidate cached data. + range_map_->Clear(); + delete modules_; + modules_ = NULL; + module_count_ = 0; + + valid_ = false; + + u_int32_t module_count; + if (expected_size < sizeof(module_count)) { + BPLOG(ERROR) << "MinidumpModuleList count size mismatch, " << + expected_size << " < " << sizeof(module_count); + return false; + } + if (!minidump_->ReadBytes(&module_count, sizeof(module_count))) { + BPLOG(ERROR) << "MinidumpModuleList could not read module count"; + return false; + } + + if (minidump_->swap()) + Swap(&module_count); + + if (module_count > numeric_limits::max() / MD_MODULE_SIZE) { + BPLOG(ERROR) << "MinidumpModuleList module count " << module_count << + " would cause multiplication overflow"; + return false; + } + + if (expected_size != sizeof(module_count) + + module_count * MD_MODULE_SIZE) { + // may be padded with 4 bytes on 64bit ABIs for alignment + if (expected_size == sizeof(module_count) + 4 + + module_count * MD_MODULE_SIZE) { + u_int32_t useless; + if (!minidump_->ReadBytes(&useless, 4)) { + BPLOG(ERROR) << "MinidumpModuleList cannot read modulelist padded bytes"; + return false; + } + } else { + BPLOG(ERROR) << "MinidumpModuleList size mismatch, " << expected_size << + " != " << sizeof(module_count) + + module_count * MD_MODULE_SIZE; + return false; + } + } + + if (module_count > max_modules_) { + BPLOG(ERROR) << "MinidumpModuleList count " << module_count_ << + " exceeds maximum " << max_modules_; + return false; + } + + if (module_count != 0) { + scoped_ptr modules( + new MinidumpModules(module_count, MinidumpModule(minidump_))); + + for (unsigned int module_index = 0; + module_index < module_count; + ++module_index) { + MinidumpModule* module = &(*modules)[module_index]; + + // Assume that the file offset is correct after the last read. + if (!module->Read()) { + BPLOG(ERROR) << "MinidumpModuleList could not read module " << + module_index << "/" << module_count; + return false; + } + } + + // Loop through the module list once more to read additional data and + // build the range map. This is done in a second pass because + // MinidumpModule::ReadAuxiliaryData seeks around, and if it were + // included in the loop above, additional seeks would be needed where + // none are now to read contiguous data. + for (unsigned int module_index = 0; + module_index < module_count; + ++module_index) { + MinidumpModule* module = &(*modules)[module_index]; + + // ReadAuxiliaryData fails if any data that the module indicates should + // exist is missing, but we treat some such cases as valid anyway. See + // issue #222: if a debugging record is of a format that's too large to + // handle, it shouldn't render the entire dump invalid. Check module + // validity before giving up. + if (!module->ReadAuxiliaryData() && !module->valid()) { + BPLOG(ERROR) << "MinidumpModuleList could not read required module " + "auxiliary data for module " << + module_index << "/" << module_count; + return false; + } + + // It is safe to use module->code_file() after successfully calling + // module->ReadAuxiliaryData or noting that the module is valid. + + u_int64_t base_address = module->base_address(); + u_int64_t module_size = module->size(); + if (base_address == static_cast(-1)) { + BPLOG(ERROR) << "MinidumpModuleList found bad base address " + "for module " << module_index << "/" << module_count << + ", " << module->code_file(); + return false; + } + + if (!range_map_->StoreRange(base_address, module_size, module_index)) { + BPLOG(ERROR) << "MinidumpModuleList could not store module " << + module_index << "/" << module_count << ", " << + module->code_file() << ", " << + HexString(base_address) << "+" << + HexString(module_size); + return false; + } + } + + modules_ = modules.release(); + } + + module_count_ = module_count; + + valid_ = true; + return true; +} + + +const MinidumpModule* MinidumpModuleList::GetModuleForAddress( + u_int64_t address) const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleForAddress"; + return NULL; + } + + unsigned int module_index; + if (!range_map_->RetrieveRange(address, &module_index, NULL, NULL)) { + BPLOG(INFO) << "MinidumpModuleList has no module at " << + HexString(address); + return NULL; + } + + return GetModuleAtIndex(module_index); +} + + +const MinidumpModule* MinidumpModuleList::GetMainModule() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModuleList for GetMainModule"; + return NULL; + } + + // The main code module is the first one present in a minidump file's + // MDRawModuleList. + return GetModuleAtIndex(0); +} + + +const MinidumpModule* MinidumpModuleList::GetModuleAtSequence( + unsigned int sequence) const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtSequence"; + return NULL; + } + + if (sequence >= module_count_) { + BPLOG(ERROR) << "MinidumpModuleList sequence out of range: " << + sequence << "/" << module_count_; + return NULL; + } + + unsigned int module_index; + if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, NULL, NULL)) { + BPLOG(ERROR) << "MinidumpModuleList has no module at sequence " << sequence; + return NULL; + } + + return GetModuleAtIndex(module_index); +} + + +const MinidumpModule* MinidumpModuleList::GetModuleAtIndex( + unsigned int index) const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpModuleList for GetModuleAtIndex"; + return NULL; + } + + if (index >= module_count_) { + BPLOG(ERROR) << "MinidumpModuleList index out of range: " << + index << "/" << module_count_; + return NULL; + } + + return &(*modules_)[index]; +} + + +const CodeModules* MinidumpModuleList::Copy() const { + return new BasicCodeModules(this); +} + + +void MinidumpModuleList::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpModuleList cannot print invalid data"; + return; + } + + printf("MinidumpModuleList\n"); + printf(" module_count = %d\n", module_count_); + printf("\n"); + + for (unsigned int module_index = 0; + module_index < module_count_; + ++module_index) { + printf("module[%d]\n", module_index); + + (*modules_)[module_index].Print(); + } +} + + +// +// MinidumpMemoryList +// + + +u_int32_t MinidumpMemoryList::max_regions_ = 4096; + + +MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) + : MinidumpStream(minidump), + range_map_(new RangeMap()), + descriptors_(NULL), + regions_(NULL), + region_count_(0) { +} + + +MinidumpMemoryList::~MinidumpMemoryList() { + delete range_map_; + delete descriptors_; + delete regions_; +} + + +bool MinidumpMemoryList::Read(u_int32_t expected_size) { + // Invalidate cached data. + delete descriptors_; + descriptors_ = NULL; + delete regions_; + regions_ = NULL; + range_map_->Clear(); + region_count_ = 0; + + valid_ = false; + + u_int32_t region_count; + if (expected_size < sizeof(region_count)) { + BPLOG(ERROR) << "MinidumpMemoryList count size mismatch, " << + expected_size << " < " << sizeof(region_count); + return false; + } + if (!minidump_->ReadBytes(®ion_count, sizeof(region_count))) { + BPLOG(ERROR) << "MinidumpMemoryList could not read memory region count"; + return false; + } + + if (minidump_->swap()) + Swap(®ion_count); + + if (region_count > + numeric_limits::max() / sizeof(MDMemoryDescriptor)) { + BPLOG(ERROR) << "MinidumpMemoryList region count " << region_count << + " would cause multiplication overflow"; + return false; + } + + if (expected_size != sizeof(region_count) + + region_count * sizeof(MDMemoryDescriptor)) { + // may be padded with 4 bytes on 64bit ABIs for alignment + if (expected_size == sizeof(region_count) + 4 + + region_count * sizeof(MDMemoryDescriptor)) { + u_int32_t useless; + if (!minidump_->ReadBytes(&useless, 4)) { + BPLOG(ERROR) << "MinidumpMemoryList cannot read memorylist padded bytes"; + return false; + } + } else { + BPLOG(ERROR) << "MinidumpMemoryList size mismatch, " << expected_size << + " != " << sizeof(region_count) + + region_count * sizeof(MDMemoryDescriptor); + return false; + } + } + + if (region_count > max_regions_) { + BPLOG(ERROR) << "MinidumpMemoryList count " << region_count << + " exceeds maximum " << max_regions_; + return false; + } + + if (region_count != 0) { + scoped_ptr descriptors( + new MemoryDescriptors(region_count)); + + // Read the entire array in one fell swoop, instead of reading one entry + // at a time in the loop. + if (!minidump_->ReadBytes(&(*descriptors)[0], + sizeof(MDMemoryDescriptor) * region_count)) { + BPLOG(ERROR) << "MinidumpMemoryList could not read memory region list"; + return false; + } + + scoped_ptr regions( + new MemoryRegions(region_count, MinidumpMemoryRegion(minidump_))); + + for (unsigned int region_index = 0; + region_index < region_count; + ++region_index) { + MDMemoryDescriptor* descriptor = &(*descriptors)[region_index]; + + if (minidump_->swap()) + Swap(descriptor); + + u_int64_t base_address = descriptor->start_of_memory_range; + u_int32_t region_size = descriptor->memory.data_size; + + // Check for base + size overflow or undersize. + if (region_size == 0 || + region_size > numeric_limits::max() - base_address) { + BPLOG(ERROR) << "MinidumpMemoryList has a memory region problem, " << + " region " << region_index << "/" << region_count << + ", " << HexString(base_address) << "+" << + HexString(region_size); + return false; + } + + if (!range_map_->StoreRange(base_address, region_size, region_index)) { + BPLOG(ERROR) << "MinidumpMemoryList could not store memory region " << + region_index << "/" << region_count << ", " << + HexString(base_address) << "+" << + HexString(region_size); + return false; + } + + (*regions)[region_index].SetDescriptor(descriptor); + } + + descriptors_ = descriptors.release(); + regions_ = regions.release(); + } + + region_count_ = region_count; + + valid_ = true; + return true; +} + + +MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionAtIndex( + unsigned int index) { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionAtIndex"; + return NULL; + } + + if (index >= region_count_) { + BPLOG(ERROR) << "MinidumpMemoryList index out of range: " << + index << "/" << region_count_; + return NULL; + } + + return &(*regions_)[index]; +} + + +MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress( + u_int64_t address) { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryList for GetMemoryRegionForAddress"; + return NULL; + } + + unsigned int region_index; + if (!range_map_->RetrieveRange(address, ®ion_index, NULL, NULL)) { + BPLOG(INFO) << "MinidumpMemoryList has no memory region at " << + HexString(address); + return NULL; + } + + return GetMemoryRegionAtIndex(region_index); +} + + +void MinidumpMemoryList::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpMemoryList cannot print invalid data"; + return; + } + + printf("MinidumpMemoryList\n"); + printf(" region_count = %d\n", region_count_); + printf("\n"); + + for (unsigned int region_index = 0; + region_index < region_count_; + ++region_index) { + MDMemoryDescriptor* descriptor = &(*descriptors_)[region_index]; + printf("region[%d]\n", region_index); + printf("MDMemoryDescriptor\n"); + printf(" start_of_memory_range = 0x%" PRIx64 "\n", + descriptor->start_of_memory_range); + printf(" memory.data_size = 0x%x\n", descriptor->memory.data_size); + printf(" memory.rva = 0x%x\n", descriptor->memory.rva); + MinidumpMemoryRegion* region = GetMemoryRegionAtIndex(region_index); + if (region) { + printf("Memory\n"); + region->Print(); + } else { + printf("No memory\n"); + } + printf("\n"); + } +} + + +// +// MinidumpException +// + + +MinidumpException::MinidumpException(Minidump* minidump) + : MinidumpStream(minidump), + exception_(), + context_(NULL) { +} + + +MinidumpException::~MinidumpException() { + delete context_; +} + + +bool MinidumpException::Read(u_int32_t expected_size) { + // Invalidate cached data. + delete context_; + context_ = NULL; + + valid_ = false; + + if (expected_size != sizeof(exception_)) { + BPLOG(ERROR) << "MinidumpException size mismatch, " << expected_size << + " != " << sizeof(exception_); + return false; + } + + if (!minidump_->ReadBytes(&exception_, sizeof(exception_))) { + BPLOG(ERROR) << "MinidumpException cannot read exception"; + return false; + } + + if (minidump_->swap()) { + Swap(&exception_.thread_id); + // exception_.__align is for alignment only and does not need to be + // swapped. + Swap(&exception_.exception_record.exception_code); + Swap(&exception_.exception_record.exception_flags); + Swap(&exception_.exception_record.exception_record); + Swap(&exception_.exception_record.exception_address); + Swap(&exception_.exception_record.number_parameters); + // exception_.exception_record.__align is for alignment only and does not + // need to be swapped. + for (unsigned int parameter_index = 0; + parameter_index < MD_EXCEPTION_MAXIMUM_PARAMETERS; + ++parameter_index) { + Swap(&exception_.exception_record.exception_information[parameter_index]); + } + Swap(&exception_.thread_context); + } + + valid_ = true; + return true; +} + + +bool MinidumpException::GetThreadID(u_int32_t *thread_id) const { + BPLOG_IF(ERROR, !thread_id) << "MinidumpException::GetThreadID requires " + "|thread_id|"; + assert(thread_id); + *thread_id = 0; + + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpException for GetThreadID"; + return false; + } + + *thread_id = exception_.thread_id; + return true; +} + + +MinidumpContext* MinidumpException::GetContext() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpException for GetContext"; + return NULL; + } + + if (!context_) { + if (!minidump_->SeekSet(exception_.thread_context.rva)) { + BPLOG(ERROR) << "MinidumpException cannot seek to context"; + return NULL; + } + + scoped_ptr context(new MinidumpContext(minidump_)); + + // Don't log as an error if we can still fall back on the thread's context + // (which must be possible if we got this far.) + if (!context->Read(exception_.thread_context.data_size)) { + BPLOG(INFO) << "MinidumpException cannot read context"; + return NULL; + } + + context_ = context.release(); + } + + return context_; +} + + +void MinidumpException::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpException cannot print invalid data"; + return; + } + + printf("MDException\n"); + printf(" thread_id = 0x%x\n", + exception_.thread_id); + printf(" exception_record.exception_code = 0x%x\n", + exception_.exception_record.exception_code); + printf(" exception_record.exception_flags = 0x%x\n", + exception_.exception_record.exception_flags); + printf(" exception_record.exception_record = 0x%" PRIx64 "\n", + exception_.exception_record.exception_record); + printf(" exception_record.exception_address = 0x%" PRIx64 "\n", + exception_.exception_record.exception_address); + printf(" exception_record.number_parameters = %d\n", + exception_.exception_record.number_parameters); + for (unsigned int parameterIndex = 0; + parameterIndex < exception_.exception_record.number_parameters; + ++parameterIndex) { + printf(" exception_record.exception_information[%2d] = 0x%" PRIx64 "\n", + parameterIndex, + exception_.exception_record.exception_information[parameterIndex]); + } + printf(" thread_context.data_size = %d\n", + exception_.thread_context.data_size); + printf(" thread_context.rva = 0x%x\n", + exception_.thread_context.rva); + MinidumpContext* context = GetContext(); + if (context) { + printf("\n"); + context->Print(); + } else { + printf(" (no context)\n"); + printf("\n"); + } +} + +// +// MinidumpAssertion +// + + +MinidumpAssertion::MinidumpAssertion(Minidump* minidump) + : MinidumpStream(minidump), + assertion_(), + expression_(), + function_(), + file_() { +} + + +MinidumpAssertion::~MinidumpAssertion() { +} + + +bool MinidumpAssertion::Read(u_int32_t expected_size) { + // Invalidate cached data. + valid_ = false; + + if (expected_size != sizeof(assertion_)) { + BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << + " != " << sizeof(assertion_); + return false; + } + + if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { + BPLOG(ERROR) << "MinidumpAssertion cannot read assertion"; + return false; + } + + // Each of {expression, function, file} is a UTF-16 string, + // we'll convert them to UTF-8 for ease of use. + // expression + // Since we don't have an explicit byte length for each string, + // we use UTF16codeunits to calculate word length, then derive byte + // length from that. + u_int32_t word_length = UTF16codeunits(assertion_.expression, + sizeof(assertion_.expression)); + if (word_length > 0) { + u_int32_t byte_length = word_length * 2; + vector expression_utf16(word_length); + memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); + + scoped_ptr new_expression(UTF16ToUTF8(expression_utf16, + minidump_->swap())); + if (new_expression.get()) + expression_ = *new_expression; + } + + // assertion + word_length = UTF16codeunits(assertion_.function, + sizeof(assertion_.function)); + if (word_length) { + u_int32_t byte_length = word_length * 2; + vector function_utf16(word_length); + memcpy(&function_utf16[0], &assertion_.function[0], byte_length); + scoped_ptr new_function(UTF16ToUTF8(function_utf16, + minidump_->swap())); + if (new_function.get()) + function_ = *new_function; + } + + // file + word_length = UTF16codeunits(assertion_.file, + sizeof(assertion_.file)); + if (word_length > 0) { + u_int32_t byte_length = word_length * 2; + vector file_utf16(word_length); + memcpy(&file_utf16[0], &assertion_.file[0], byte_length); + scoped_ptr new_file(UTF16ToUTF8(file_utf16, + minidump_->swap())); + if (new_file.get()) + file_ = *new_file; + } + + if (minidump_->swap()) { + Swap(&assertion_.line); + Swap(&assertion_.type); + } + + valid_ = true; + return true; +} + +void MinidumpAssertion::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data"; + return; + } + + printf("MDAssertion\n"); + printf(" expression = %s\n", + expression_.c_str()); + printf(" function = %s\n", + function_.c_str()); + printf(" file = %s\n", + file_.c_str()); + printf(" line = %u\n", + assertion_.line); + printf(" type = %u\n", + assertion_.type); + printf("\n"); +} + +// +// MinidumpSystemInfo +// + + +MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump) + : MinidumpStream(minidump), + system_info_(), + csd_version_(NULL), + cpu_vendor_(NULL) { +} + + +MinidumpSystemInfo::~MinidumpSystemInfo() { + delete csd_version_; + delete cpu_vendor_; +} + + +bool MinidumpSystemInfo::Read(u_int32_t expected_size) { + // Invalidate cached data. + delete csd_version_; + csd_version_ = NULL; + delete cpu_vendor_; + cpu_vendor_ = NULL; + + valid_ = false; + + if (expected_size != sizeof(system_info_)) { + BPLOG(ERROR) << "MinidumpSystemInfo size mismatch, " << expected_size << + " != " << sizeof(system_info_); + return false; + } + + if (!minidump_->ReadBytes(&system_info_, sizeof(system_info_))) { + BPLOG(ERROR) << "MinidumpSystemInfo cannot read system info"; + return false; + } + + if (minidump_->swap()) { + Swap(&system_info_.processor_architecture); + Swap(&system_info_.processor_level); + Swap(&system_info_.processor_revision); + // number_of_processors and product_type are 8-bit quantities and need no + // swapping. + Swap(&system_info_.major_version); + Swap(&system_info_.minor_version); + Swap(&system_info_.build_number); + Swap(&system_info_.platform_id); + Swap(&system_info_.csd_version_rva); + Swap(&system_info_.suite_mask); + // Don't swap the reserved2 field because its contents are unknown. + + if (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || + system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64) { + for (unsigned int i = 0; i < 3; ++i) + Swap(&system_info_.cpu.x86_cpu_info.vendor_id[i]); + Swap(&system_info_.cpu.x86_cpu_info.version_information); + Swap(&system_info_.cpu.x86_cpu_info.feature_information); + Swap(&system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); + } else { + for (unsigned int i = 0; i < 2; ++i) + Swap(&system_info_.cpu.other_cpu_info.processor_features[i]); + } + } + + valid_ = true; + return true; +} + + +string MinidumpSystemInfo::GetOS() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetOS"; + return NULL; + } + + string os; + + switch (system_info_.platform_id) { + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: + os = "windows"; + break; + + case MD_OS_MAC_OS_X: + os = "mac"; + break; + + case MD_OS_IOS: + os = "ios"; + break; + + case MD_OS_LINUX: + os = "linux"; + break; + + case MD_OS_SOLARIS: + os = "solaris"; + break; + + default: + BPLOG(ERROR) << "MinidumpSystemInfo unknown OS for platform " << + HexString(system_info_.platform_id); + break; + } + + return os; +} + + +string MinidumpSystemInfo::GetCPU() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPU"; + return ""; + } + + string cpu; + + switch (system_info_.processor_architecture) { + case MD_CPU_ARCHITECTURE_X86: + case MD_CPU_ARCHITECTURE_X86_WIN64: + cpu = "x86"; + break; + + case MD_CPU_ARCHITECTURE_AMD64: + cpu = "x86-64"; + break; + + case MD_CPU_ARCHITECTURE_PPC: + cpu = "ppc"; + break; + + case MD_CPU_ARCHITECTURE_SPARC: + cpu = "sparc"; + break; + + case MD_CPU_ARCHITECTURE_ARM: + cpu = "arm"; + break; + + default: + BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << + HexString(system_info_.processor_architecture); + break; + } + + return cpu; +} + + +const string* MinidumpSystemInfo::GetCSDVersion() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCSDVersion"; + return NULL; + } + + if (!csd_version_) + csd_version_ = minidump_->ReadString(system_info_.csd_version_rva); + + BPLOG_IF(ERROR, !csd_version_) << "MinidumpSystemInfo could not read " + "CSD version"; + + return csd_version_; +} + + +const string* MinidumpSystemInfo::GetCPUVendor() { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpSystemInfo for GetCPUVendor"; + return NULL; + } + + // CPU vendor information can only be determined from x86 minidumps. + if (!cpu_vendor_ && + (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || + system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) { + char cpu_vendor_string[13]; + snprintf(cpu_vendor_string, sizeof(cpu_vendor_string), + "%c%c%c%c%c%c%c%c%c%c%c%c", + system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff, + system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff, + system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff); + cpu_vendor_ = new string(cpu_vendor_string); + } + + return cpu_vendor_; +} + + +void MinidumpSystemInfo::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpSystemInfo cannot print invalid data"; + return; + } + + printf("MDRawSystemInfo\n"); + printf(" processor_architecture = %d\n", + system_info_.processor_architecture); + printf(" processor_level = %d\n", + system_info_.processor_level); + printf(" processor_revision = 0x%x\n", + system_info_.processor_revision); + printf(" number_of_processors = %d\n", + system_info_.number_of_processors); + printf(" product_type = %d\n", + system_info_.product_type); + printf(" major_version = %d\n", + system_info_.major_version); + printf(" minor_version = %d\n", + system_info_.minor_version); + printf(" build_number = %d\n", + system_info_.build_number); + printf(" platform_id = %d\n", + system_info_.platform_id); + printf(" csd_version_rva = 0x%x\n", + system_info_.csd_version_rva); + printf(" suite_mask = 0x%x\n", + system_info_.suite_mask); + for (unsigned int i = 0; i < 3; ++i) { + printf(" cpu.x86_cpu_info.vendor_id[%d] = 0x%x\n", + i, system_info_.cpu.x86_cpu_info.vendor_id[i]); + } + printf(" cpu.x86_cpu_info.version_information = 0x%x\n", + system_info_.cpu.x86_cpu_info.version_information); + printf(" cpu.x86_cpu_info.feature_information = 0x%x\n", + system_info_.cpu.x86_cpu_info.feature_information); + printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n", + system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); + const string* csd_version = GetCSDVersion(); + if (csd_version) { + printf(" (csd_version) = \"%s\"\n", + csd_version->c_str()); + } else { + printf(" (csd_version) = (null)\n"); + } + const string* cpu_vendor = GetCPUVendor(); + if (cpu_vendor) { + printf(" (cpu_vendor) = \"%s\"\n", + cpu_vendor->c_str()); + } else { + printf(" (cpu_vendor) = (null)\n"); + } + printf("\n"); +} + + +// +// MinidumpMiscInfo +// + + +MinidumpMiscInfo::MinidumpMiscInfo(Minidump* minidump) + : MinidumpStream(minidump), + misc_info_() { +} + + +bool MinidumpMiscInfo::Read(u_int32_t expected_size) { + valid_ = false; + + if (expected_size != MD_MISCINFO_SIZE && + expected_size != MD_MISCINFO2_SIZE) { + BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << expected_size << + " != " << MD_MISCINFO_SIZE << ", " << MD_MISCINFO2_SIZE << + ")"; + return false; + } + + if (!minidump_->ReadBytes(&misc_info_, expected_size)) { + BPLOG(ERROR) << "MinidumpMiscInfo cannot read miscellaneous info"; + return false; + } + + if (minidump_->swap()) { + Swap(&misc_info_.size_of_info); + Swap(&misc_info_.flags1); + Swap(&misc_info_.process_id); + Swap(&misc_info_.process_create_time); + Swap(&misc_info_.process_user_time); + Swap(&misc_info_.process_kernel_time); + if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { + Swap(&misc_info_.processor_max_mhz); + Swap(&misc_info_.processor_current_mhz); + Swap(&misc_info_.processor_mhz_limit); + Swap(&misc_info_.processor_max_idle_state); + Swap(&misc_info_.processor_current_idle_state); + } + } + + if (expected_size != misc_info_.size_of_info) { + BPLOG(ERROR) << "MinidumpMiscInfo size mismatch, " << + expected_size << " != " << misc_info_.size_of_info; + return false; + } + + valid_ = true; + return true; +} + + +void MinidumpMiscInfo::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpMiscInfo cannot print invalid data"; + return; + } + + printf("MDRawMiscInfo\n"); + printf(" size_of_info = %d\n", misc_info_.size_of_info); + printf(" flags1 = 0x%x\n", misc_info_.flags1); + printf(" process_id = 0x%x\n", misc_info_.process_id); + printf(" process_create_time = 0x%x\n", + misc_info_.process_create_time); + printf(" process_user_time = 0x%x\n", + misc_info_.process_user_time); + printf(" process_kernel_time = 0x%x\n", + misc_info_.process_kernel_time); + if (misc_info_.size_of_info > MD_MISCINFO_SIZE) { + printf(" processor_max_mhz = %d\n", + misc_info_.processor_max_mhz); + printf(" processor_current_mhz = %d\n", + misc_info_.processor_current_mhz); + printf(" processor_mhz_limit = %d\n", + misc_info_.processor_mhz_limit); + printf(" processor_max_idle_state = 0x%x\n", + misc_info_.processor_max_idle_state); + printf(" processor_current_idle_state = 0x%x\n", + misc_info_.processor_current_idle_state); + } + printf("\n"); +} + + +// +// MinidumpBreakpadInfo +// + + +MinidumpBreakpadInfo::MinidumpBreakpadInfo(Minidump* minidump) + : MinidumpStream(minidump), + breakpad_info_() { +} + + +bool MinidumpBreakpadInfo::Read(u_int32_t expected_size) { + valid_ = false; + + if (expected_size != sizeof(breakpad_info_)) { + BPLOG(ERROR) << "MinidumpBreakpadInfo size mismatch, " << expected_size << + " != " << sizeof(breakpad_info_); + return false; + } + + if (!minidump_->ReadBytes(&breakpad_info_, sizeof(breakpad_info_))) { + BPLOG(ERROR) << "MinidumpBreakpadInfo cannot read Breakpad info"; + return false; + } + + if (minidump_->swap()) { + Swap(&breakpad_info_.validity); + Swap(&breakpad_info_.dump_thread_id); + Swap(&breakpad_info_.requesting_thread_id); + } + + valid_ = true; + return true; +} + + +bool MinidumpBreakpadInfo::GetDumpThreadID(u_int32_t *thread_id) const { + BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetDumpThreadID " + "requires |thread_id|"; + assert(thread_id); + *thread_id = 0; + + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetDumpThreadID"; + return false; + } + + if (!(breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID)) { + BPLOG(INFO) << "MinidumpBreakpadInfo has no dump thread"; + return false; + } + + *thread_id = breakpad_info_.dump_thread_id; + return true; +} + + +bool MinidumpBreakpadInfo::GetRequestingThreadID(u_int32_t *thread_id) + const { + BPLOG_IF(ERROR, !thread_id) << "MinidumpBreakpadInfo::GetRequestingThreadID " + "requires |thread_id|"; + assert(thread_id); + *thread_id = 0; + + if (!thread_id || !valid_) { + BPLOG(ERROR) << "Invalid MinidumpBreakpadInfo for GetRequestingThreadID"; + return false; + } + + if (!(breakpad_info_.validity & + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID)) { + BPLOG(INFO) << "MinidumpBreakpadInfo has no requesting thread"; + return false; + } + + *thread_id = breakpad_info_.requesting_thread_id; + return true; +} + + +void MinidumpBreakpadInfo::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpBreakpadInfo cannot print invalid data"; + return; + } + + printf("MDRawBreakpadInfo\n"); + printf(" validity = 0x%x\n", breakpad_info_.validity); + + if (breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID) { + printf(" dump_thread_id = 0x%x\n", breakpad_info_.dump_thread_id); + } else { + printf(" dump_thread_id = (invalid)\n"); + } + + if (breakpad_info_.validity & MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID) { + printf(" requesting_thread_id = 0x%x\n", + breakpad_info_.requesting_thread_id); + } else { + printf(" requesting_thread_id = (invalid)\n"); + } + + printf("\n"); +} + + +// +// MinidumpMemoryInfo +// + + +MinidumpMemoryInfo::MinidumpMemoryInfo(Minidump* minidump) + : MinidumpObject(minidump), + memory_info_() { +} + + +bool MinidumpMemoryInfo::IsExecutable() const { + u_int32_t protection = + memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; + return protection == MD_MEMORY_PROTECT_EXECUTE || + protection == MD_MEMORY_PROTECT_EXECUTE_READ || + protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE; +} + + +bool MinidumpMemoryInfo::IsWritable() const { + u_int32_t protection = + memory_info_.protection & MD_MEMORY_PROTECTION_ACCESS_MASK; + return protection == MD_MEMORY_PROTECT_READWRITE || + protection == MD_MEMORY_PROTECT_WRITECOPY || + protection == MD_MEMORY_PROTECT_EXECUTE_READWRITE || + protection == MD_MEMORY_PROTECT_EXECUTE_WRITECOPY; +} + + +bool MinidumpMemoryInfo::Read() { + valid_ = false; + + if (!minidump_->ReadBytes(&memory_info_, sizeof(memory_info_))) { + BPLOG(ERROR) << "MinidumpMemoryInfo cannot read memory info"; + return false; + } + + if (minidump_->swap()) { + Swap(&memory_info_.base_address); + Swap(&memory_info_.allocation_base); + Swap(&memory_info_.allocation_protection); + Swap(&memory_info_.region_size); + Swap(&memory_info_.state); + Swap(&memory_info_.protection); + Swap(&memory_info_.type); + } + + // Check for base + size overflow or undersize. + if (memory_info_.region_size == 0 || + memory_info_.region_size > numeric_limits::max() - + memory_info_.base_address) { + BPLOG(ERROR) << "MinidumpMemoryInfo has a memory region problem, " << + HexString(memory_info_.base_address) << "+" << + HexString(memory_info_.region_size); + return false; + } + + valid_ = true; + return true; +} + + +void MinidumpMemoryInfo::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpMemoryInfo cannot print invalid data"; + return; + } + + printf("MDRawMemoryInfo\n"); + printf(" base_address = 0x%" PRIx64 "\n", + memory_info_.base_address); + printf(" allocation_base = 0x%" PRIx64 "\n", + memory_info_.allocation_base); + printf(" allocation_protection = 0x%x\n", + memory_info_.allocation_protection); + printf(" region_size = 0x%" PRIx64 "\n", memory_info_.region_size); + printf(" state = 0x%x\n", memory_info_.state); + printf(" protection = 0x%x\n", memory_info_.protection); + printf(" type = 0x%x\n", memory_info_.type); +} + + +// +// MinidumpMemoryInfoList +// + + +MinidumpMemoryInfoList::MinidumpMemoryInfoList(Minidump* minidump) + : MinidumpStream(minidump), + range_map_(new RangeMap()), + infos_(NULL), + info_count_(0) { +} + + +MinidumpMemoryInfoList::~MinidumpMemoryInfoList() { + delete range_map_; + delete infos_; +} + + +bool MinidumpMemoryInfoList::Read(u_int32_t expected_size) { + // Invalidate cached data. + delete infos_; + infos_ = NULL; + range_map_->Clear(); + info_count_ = 0; + + valid_ = false; + + MDRawMemoryInfoList header; + if (expected_size < sizeof(MDRawMemoryInfoList)) { + BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << + expected_size << " < " << sizeof(MDRawMemoryInfoList); + return false; + } + if (!minidump_->ReadBytes(&header, sizeof(header))) { + BPLOG(ERROR) << "MinidumpMemoryInfoList could not read header"; + return false; + } + + if (minidump_->swap()) { + Swap(&header.size_of_header); + Swap(&header.size_of_entry); + Swap(&header.number_of_entries); + } + + // Sanity check that the header is the expected size. + //TODO(ted): could possibly handle this more gracefully, assuming + // that future versions of the structs would be backwards-compatible. + if (header.size_of_header != sizeof(MDRawMemoryInfoList)) { + BPLOG(ERROR) << "MinidumpMemoryInfoList header size mismatch, " << + header.size_of_header << " != " << + sizeof(MDRawMemoryInfoList); + return false; + } + + // Sanity check that the entries are the expected size. + if (header.size_of_entry != sizeof(MDRawMemoryInfo)) { + BPLOG(ERROR) << "MinidumpMemoryInfoList entry size mismatch, " << + header.size_of_entry << " != " << + sizeof(MDRawMemoryInfo); + return false; + } + + if (header.number_of_entries > + numeric_limits::max() / sizeof(MDRawMemoryInfo)) { + BPLOG(ERROR) << "MinidumpMemoryInfoList info count " << + header.number_of_entries << + " would cause multiplication overflow"; + return false; + } + + if (expected_size != sizeof(MDRawMemoryInfoList) + + header.number_of_entries * sizeof(MDRawMemoryInfo)) { + BPLOG(ERROR) << "MinidumpMemoryInfoList size mismatch, " << expected_size << + " != " << sizeof(MDRawMemoryInfoList) + + header.number_of_entries * sizeof(MDRawMemoryInfo); + return false; + } + + if (header.number_of_entries != 0) { + scoped_ptr infos( + new MinidumpMemoryInfos(header.number_of_entries, + MinidumpMemoryInfo(minidump_))); + + for (unsigned int index = 0; + index < header.number_of_entries; + ++index) { + MinidumpMemoryInfo* info = &(*infos)[index]; + + // Assume that the file offset is correct after the last read. + if (!info->Read()) { + BPLOG(ERROR) << "MinidumpMemoryInfoList cannot read info " << + index << "/" << header.number_of_entries; + return false; + } + + u_int64_t base_address = info->GetBase(); + u_int32_t region_size = info->GetSize(); + + if (!range_map_->StoreRange(base_address, region_size, index)) { + BPLOG(ERROR) << "MinidumpMemoryInfoList could not store" + " memory region " << + index << "/" << header.number_of_entries << ", " << + HexString(base_address) << "+" << + HexString(region_size); + return false; + } + } + + infos_ = infos.release(); + } + + info_count_ = header.number_of_entries; + + valid_ = true; + return true; +} + + +const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoAtIndex( + unsigned int index) const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for GetMemoryInfoAtIndex"; + return NULL; + } + + if (index >= info_count_) { + BPLOG(ERROR) << "MinidumpMemoryInfoList index out of range: " << + index << "/" << info_count_; + return NULL; + } + + return &(*infos_)[index]; +} + + +const MinidumpMemoryInfo* MinidumpMemoryInfoList::GetMemoryInfoForAddress( + u_int64_t address) const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpMemoryInfoList for" + " GetMemoryInfoForAddress"; + return NULL; + } + + unsigned int info_index; + if (!range_map_->RetrieveRange(address, &info_index, NULL, NULL)) { + BPLOG(INFO) << "MinidumpMemoryInfoList has no memory info at " << + HexString(address); + return NULL; + } + + return GetMemoryInfoAtIndex(info_index); +} + + +void MinidumpMemoryInfoList::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpMemoryInfoList cannot print invalid data"; + return; + } + + printf("MinidumpMemoryInfoList\n"); + printf(" info_count = %d\n", info_count_); + printf("\n"); + + for (unsigned int info_index = 0; + info_index < info_count_; + ++info_index) { + printf("info[%d]\n", info_index); + (*infos_)[info_index].Print(); + printf("\n"); + } +} + + +// +// Minidump +// + + +u_int32_t Minidump::max_streams_ = 128; +unsigned int Minidump::max_string_length_ = 1024; + + +Minidump::Minidump(const string& path) + : header_(), + directory_(NULL), + stream_map_(new MinidumpStreamMap()), + path_(path), + stream_(NULL), + swap_(false), + valid_(false) { +} + +Minidump::Minidump(istream& stream) + : header_(), + directory_(NULL), + stream_map_(new MinidumpStreamMap()), + path_(), + stream_(&stream), + swap_(false), + valid_(false) { +} + +Minidump::~Minidump() { + if (stream_) { + BPLOG(INFO) << "Minidump closing minidump"; + } + if (!path_.empty()) { + delete stream_; + } + delete directory_; + delete stream_map_; +} + + +bool Minidump::Open() { + if (stream_ != NULL) { + BPLOG(INFO) << "Minidump reopening minidump " << path_; + + // The file is already open. Seek to the beginning, which is the position + // the file would be at if it were opened anew. + return SeekSet(0); + } + + stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary); + if (!stream_ || !stream_->good()) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Minidump could not open minidump " << path_ << + ", error " << error_code << ": " << error_string; + return false; + } + + BPLOG(INFO) << "Minidump opened minidump " << path_; + return true; +} + + +bool Minidump::Read() { + // Invalidate cached data. + delete directory_; + directory_ = NULL; + stream_map_->clear(); + + valid_ = false; + + if (!Open()) { + BPLOG(ERROR) << "Minidump cannot open minidump"; + return false; + } + + if (!ReadBytes(&header_, sizeof(MDRawHeader))) { + BPLOG(ERROR) << "Minidump cannot read header"; + return false; + } + + if (header_.signature != MD_HEADER_SIGNATURE) { + // The file may be byte-swapped. Under the present architecture, these + // classes don't know or need to know what CPU (or endianness) the + // minidump was produced on in order to parse it. Use the signature as + // a byte order marker. + u_int32_t signature_swapped = header_.signature; + Swap(&signature_swapped); + if (signature_swapped != MD_HEADER_SIGNATURE) { + // This isn't a minidump or a byte-swapped minidump. + BPLOG(ERROR) << "Minidump header signature mismatch: (" << + HexString(header_.signature) << ", " << + HexString(signature_swapped) << ") != " << + HexString(MD_HEADER_SIGNATURE); + return false; + } + swap_ = true; + } else { + // The file is not byte-swapped. Set swap_ false (it may have been true + // if the object is being reused?) + swap_ = false; + } + + BPLOG(INFO) << "Minidump " << (swap_ ? "" : "not ") << + "byte-swapping minidump"; + + if (swap_) { + Swap(&header_.signature); + Swap(&header_.version); + Swap(&header_.stream_count); + Swap(&header_.stream_directory_rva); + Swap(&header_.checksum); + Swap(&header_.time_date_stamp); + Swap(&header_.flags); + } + + // Version check. The high 16 bits of header_.version contain something + // else "implementation specific." + if ((header_.version & 0x0000ffff) != MD_HEADER_VERSION) { + BPLOG(ERROR) << "Minidump version mismatch: " << + HexString(header_.version & 0x0000ffff) << " != " << + HexString(MD_HEADER_VERSION); + return false; + } + + if (!SeekSet(header_.stream_directory_rva)) { + BPLOG(ERROR) << "Minidump cannot seek to stream directory"; + return false; + } + + if (header_.stream_count > max_streams_) { + BPLOG(ERROR) << "Minidump stream count " << header_.stream_count << + " exceeds maximum " << max_streams_; + return false; + } + + if (header_.stream_count != 0) { + scoped_ptr directory( + new MinidumpDirectoryEntries(header_.stream_count)); + + // Read the entire array in one fell swoop, instead of reading one entry + // at a time in the loop. + if (!ReadBytes(&(*directory)[0], + sizeof(MDRawDirectory) * header_.stream_count)) { + BPLOG(ERROR) << "Minidump cannot read stream directory"; + return false; + } + + for (unsigned int stream_index = 0; + stream_index < header_.stream_count; + ++stream_index) { + MDRawDirectory* directory_entry = &(*directory)[stream_index]; + + if (swap_) { + Swap(&directory_entry->stream_type); + Swap(&directory_entry->location); + } + + // Initialize the stream_map_ map, which speeds locating a stream by + // type. + unsigned int stream_type = directory_entry->stream_type; + switch (stream_type) { + case MD_THREAD_LIST_STREAM: + case MD_MODULE_LIST_STREAM: + case MD_MEMORY_LIST_STREAM: + case MD_EXCEPTION_STREAM: + case MD_SYSTEM_INFO_STREAM: + case MD_MISC_INFO_STREAM: + case MD_BREAKPAD_INFO_STREAM: { + if (stream_map_->find(stream_type) != stream_map_->end()) { + // Another stream with this type was already found. A minidump + // file should contain at most one of each of these stream types. + BPLOG(ERROR) << "Minidump found multiple streams of type " << + stream_type << ", but can only deal with one"; + return false; + } + // Fall through to default + } + + default: { + // Overwrites for stream types other than those above, but it's + // expected to be the user's burden in that case. + (*stream_map_)[stream_type].stream_index = stream_index; + } + } + } + + directory_ = directory.release(); + } + + valid_ = true; + return true; +} + + +MinidumpThreadList* Minidump::GetThreadList() { + MinidumpThreadList* thread_list; + return GetStream(&thread_list); +} + + +MinidumpModuleList* Minidump::GetModuleList() { + MinidumpModuleList* module_list; + return GetStream(&module_list); +} + + +MinidumpMemoryList* Minidump::GetMemoryList() { + MinidumpMemoryList* memory_list; + return GetStream(&memory_list); +} + + +MinidumpException* Minidump::GetException() { + MinidumpException* exception; + return GetStream(&exception); +} + +MinidumpAssertion* Minidump::GetAssertion() { + MinidumpAssertion* assertion; + return GetStream(&assertion); +} + + +MinidumpSystemInfo* Minidump::GetSystemInfo() { + MinidumpSystemInfo* system_info; + return GetStream(&system_info); +} + + +MinidumpMiscInfo* Minidump::GetMiscInfo() { + MinidumpMiscInfo* misc_info; + return GetStream(&misc_info); +} + + +MinidumpBreakpadInfo* Minidump::GetBreakpadInfo() { + MinidumpBreakpadInfo* breakpad_info; + return GetStream(&breakpad_info); +} + +MinidumpMemoryInfoList* Minidump::GetMemoryInfoList() { + MinidumpMemoryInfoList* memory_info_list; + return GetStream(&memory_info_list); +} + + +void Minidump::Print() { + if (!valid_) { + BPLOG(ERROR) << "Minidump cannot print invalid data"; + return; + } + + printf("MDRawHeader\n"); + printf(" signature = 0x%x\n", header_.signature); + printf(" version = 0x%x\n", header_.version); + printf(" stream_count = %d\n", header_.stream_count); + printf(" stream_directory_rva = 0x%x\n", header_.stream_directory_rva); + printf(" checksum = 0x%x\n", header_.checksum); + struct tm timestruct; +#ifdef _WIN32 + gmtime_s(×truct, reinterpret_cast(&header_.time_date_stamp)); +#else + gmtime_r(reinterpret_cast(&header_.time_date_stamp), ×truct); +#endif + char timestr[20]; + strftime(timestr, 20, "%Y-%m-%d %H:%M:%S", ×truct); + printf(" time_date_stamp = 0x%x %s\n", header_.time_date_stamp, + timestr); + printf(" flags = 0x%" PRIx64 "\n", header_.flags); + printf("\n"); + + for (unsigned int stream_index = 0; + stream_index < header_.stream_count; + ++stream_index) { + MDRawDirectory* directory_entry = &(*directory_)[stream_index]; + + printf("mDirectory[%d]\n", stream_index); + printf("MDRawDirectory\n"); + printf(" stream_type = %d\n", directory_entry->stream_type); + printf(" location.data_size = %d\n", + directory_entry->location.data_size); + printf(" location.rva = 0x%x\n", directory_entry->location.rva); + printf("\n"); + } + + printf("Streams:\n"); + for (MinidumpStreamMap::const_iterator iterator = stream_map_->begin(); + iterator != stream_map_->end(); + ++iterator) { + u_int32_t stream_type = iterator->first; + MinidumpStreamInfo info = iterator->second; + printf(" stream type 0x%x at index %d\n", stream_type, info.stream_index); + } + printf("\n"); +} + + +const MDRawDirectory* Minidump::GetDirectoryEntryAtIndex(unsigned int index) + const { + if (!valid_) { + BPLOG(ERROR) << "Invalid Minidump for GetDirectoryEntryAtIndex"; + return NULL; + } + + if (index >= header_.stream_count) { + BPLOG(ERROR) << "Minidump stream directory index out of range: " << + index << "/" << header_.stream_count; + return NULL; + } + + return &(*directory_)[index]; +} + + +bool Minidump::ReadBytes(void* bytes, size_t count) { + // Can't check valid_ because Read needs to call this method before + // validity can be determined. + if (!stream_) { + return false; + } + stream_->read(static_cast(bytes), count); + size_t bytes_read = stream_->gcount(); + if (bytes_read != count) { + if (bytes_read == size_t(-1)) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string; + } else { + BPLOG(ERROR) << "ReadBytes: read " << bytes_read << "/" << count; + } + return false; + } + return true; +} + + +bool Minidump::SeekSet(off_t offset) { + // Can't check valid_ because Read needs to call this method before + // validity can be determined. + if (!stream_) { + return false; + } + stream_->seekg(offset, std::ios_base::beg); + if (!stream_->good()) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string; + return false; + } + return true; +} + +off_t Minidump::Tell() { + if (!valid_ || !stream_) { + return (off_t)-1; + } + + return stream_->tellg(); +} + + +string* Minidump::ReadString(off_t offset) { + if (!valid_) { + BPLOG(ERROR) << "Invalid Minidump for ReadString"; + return NULL; + } + if (!SeekSet(offset)) { + BPLOG(ERROR) << "ReadString could not seek to string at offset " << offset; + return NULL; + } + + u_int32_t bytes; + if (!ReadBytes(&bytes, sizeof(bytes))) { + BPLOG(ERROR) << "ReadString could not read string size at offset " << + offset; + return NULL; + } + if (swap_) + Swap(&bytes); + + if (bytes % 2 != 0) { + BPLOG(ERROR) << "ReadString found odd-sized " << bytes << + "-byte string at offset " << offset; + return NULL; + } + unsigned int utf16_words = bytes / 2; + + if (utf16_words > max_string_length_) { + BPLOG(ERROR) << "ReadString string length " << utf16_words << + " exceeds maximum " << max_string_length_ << + " at offset " << offset; + return NULL; + } + + vector string_utf16(utf16_words); + + if (utf16_words) { + if (!ReadBytes(&string_utf16[0], bytes)) { + BPLOG(ERROR) << "ReadString could not read " << bytes << + "-byte string at offset " << offset; + return NULL; + } + } + + return UTF16ToUTF8(string_utf16, swap_); +} + + +bool Minidump::SeekToStreamType(u_int32_t stream_type, + u_int32_t* stream_length) { + BPLOG_IF(ERROR, !stream_length) << "Minidump::SeekToStreamType requires " + "|stream_length|"; + assert(stream_length); + *stream_length = 0; + + if (!valid_) { + BPLOG(ERROR) << "Invalid Mindump for SeekToStreamType"; + return false; + } + + MinidumpStreamMap::const_iterator iterator = stream_map_->find(stream_type); + if (iterator == stream_map_->end()) { + // This stream type didn't exist in the directory. + BPLOG(INFO) << "SeekToStreamType: type " << stream_type << " not present"; + return false; + } + + MinidumpStreamInfo info = iterator->second; + if (info.stream_index >= header_.stream_count) { + BPLOG(ERROR) << "SeekToStreamType: type " << stream_type << + " out of range: " << + info.stream_index << "/" << header_.stream_count; + return false; + } + + MDRawDirectory* directory_entry = &(*directory_)[info.stream_index]; + if (!SeekSet(directory_entry->location.rva)) { + BPLOG(ERROR) << "SeekToStreamType could not seek to stream type " << + stream_type; + return false; + } + + *stream_length = directory_entry->location.data_size; + + return true; +} + + +template +T* Minidump::GetStream(T** stream) { + // stream is a garbage parameter that's present only to account for C++'s + // inability to overload a method based solely on its return type. + + const u_int32_t stream_type = T::kStreamType; + + BPLOG_IF(ERROR, !stream) << "Minidump::GetStream type " << stream_type << + " requires |stream|"; + assert(stream); + *stream = NULL; + + if (!valid_) { + BPLOG(ERROR) << "Invalid Minidump for GetStream type " << stream_type; + return NULL; + } + + MinidumpStreamMap::iterator iterator = stream_map_->find(stream_type); + if (iterator == stream_map_->end()) { + // This stream type didn't exist in the directory. + BPLOG(INFO) << "GetStream: type " << stream_type << " not present"; + return NULL; + } + + // Get a pointer so that the stored stream field can be altered. + MinidumpStreamInfo* info = &iterator->second; + + if (info->stream) { + // This cast is safe because info.stream is only populated by this + // method, and there is a direct correlation between T and stream_type. + *stream = static_cast(info->stream); + return *stream; + } + + u_int32_t stream_length; + if (!SeekToStreamType(stream_type, &stream_length)) { + BPLOG(ERROR) << "GetStream could not seek to stream type " << stream_type; + return NULL; + } + + scoped_ptr new_stream(new T(this)); + + if (!new_stream->Read(stream_length)) { + BPLOG(ERROR) << "GetStream could not read stream type " << stream_type; + return NULL; + } + + *stream = new_stream.release(); + info->stream = *stream; + return *stream; +} + + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_dump.cc b/src/lib/crashdump/gbreakpad/processor/minidump_dump.cc new file mode 100644 index 0000000..9e70a72 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_dump.cc @@ -0,0 +1,214 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump_dump.cc: Print the contents of a minidump file in somewhat +// readable text. +// +// Author: Mark Mentovai + +#include +#include + +#include "client/linux/minidump_writer/minidump_extension_linux.h" +#include "google_breakpad/processor/minidump.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + +namespace { + +using google_breakpad::Minidump; +using google_breakpad::MinidumpThreadList; +using google_breakpad::MinidumpModuleList; +using google_breakpad::MinidumpMemoryInfoList; +using google_breakpad::MinidumpMemoryList; +using google_breakpad::MinidumpException; +using google_breakpad::MinidumpAssertion; +using google_breakpad::MinidumpSystemInfo; +using google_breakpad::MinidumpMiscInfo; +using google_breakpad::MinidumpBreakpadInfo; + +static void DumpRawStream(Minidump *minidump, + u_int32_t stream_type, + const char *stream_name, + int *errors) { + u_int32_t length = 0; + if (!minidump->SeekToStreamType(stream_type, &length)) { + return; + } + + printf("Stream %s:\n", stream_name); + + if (length == 0) { + printf("\n"); + return; + } + std::vector contents(length); + if (!minidump->ReadBytes(&contents[0], length)) { + ++*errors; + BPLOG(ERROR) << "minidump.ReadBytes failed"; + return; + } + size_t current_offset = 0; + while (current_offset < length) { + size_t remaining = length - current_offset; + // Printf requires an int and direct casting from size_t results + // in compatibility warnings. + u_int32_t int_remaining = remaining; + printf("%.*s", int_remaining, &contents[current_offset]); + char *next_null = reinterpret_cast( + memchr(&contents[current_offset], 0, remaining)); + if (next_null == NULL) + break; + printf("\\0\n"); + size_t null_offset = next_null - &contents[0]; + current_offset = null_offset + 1; + } + printf("\n\n"); +} + +static bool PrintMinidumpDump(const char *minidump_file) { + Minidump minidump(minidump_file); + if (!minidump.Read()) { + BPLOG(ERROR) << "minidump.Read() failed"; + return false; + } + minidump.Print(); + + int errors = 0; + + MinidumpThreadList *thread_list = minidump.GetThreadList(); + if (!thread_list) { + ++errors; + BPLOG(ERROR) << "minidump.GetThreadList() failed"; + } else { + thread_list->Print(); + } + + MinidumpModuleList *module_list = minidump.GetModuleList(); + if (!module_list) { + ++errors; + BPLOG(ERROR) << "minidump.GetModuleList() failed"; + } else { + module_list->Print(); + } + + MinidumpMemoryList *memory_list = minidump.GetMemoryList(); + if (!memory_list) { + ++errors; + BPLOG(ERROR) << "minidump.GetMemoryList() failed"; + } else { + memory_list->Print(); + } + + MinidumpException *exception = minidump.GetException(); + if (!exception) { + BPLOG(INFO) << "minidump.GetException() failed"; + } else { + exception->Print(); + } + + MinidumpAssertion *assertion = minidump.GetAssertion(); + if (!assertion) { + BPLOG(INFO) << "minidump.GetAssertion() failed"; + } else { + assertion->Print(); + } + + MinidumpSystemInfo *system_info = minidump.GetSystemInfo(); + if (!system_info) { + ++errors; + BPLOG(ERROR) << "minidump.GetSystemInfo() failed"; + } else { + system_info->Print(); + } + + MinidumpMiscInfo *misc_info = minidump.GetMiscInfo(); + if (!misc_info) { + ++errors; + BPLOG(ERROR) << "minidump.GetMiscInfo() failed"; + } else { + misc_info->Print(); + } + + MinidumpBreakpadInfo *breakpad_info = minidump.GetBreakpadInfo(); + if (!breakpad_info) { + // Breakpad info is optional, so don't treat this as an error. + BPLOG(INFO) << "minidump.GetBreakpadInfo() failed"; + } else { + breakpad_info->Print(); + } + + MinidumpMemoryInfoList *memory_info_list = minidump.GetMemoryInfoList(); + if (!memory_info_list) { + ++errors; + BPLOG(ERROR) << "minidump.GetMemoryInfoList() failed"; + } else { + memory_info_list->Print(); + } + + DumpRawStream(&minidump, + MD_LINUX_CMD_LINE, + "MD_LINUX_CMD_LINE", + &errors); + DumpRawStream(&minidump, + MD_LINUX_ENVIRON, + "MD_LINUX_ENVIRON", + &errors); + DumpRawStream(&minidump, + MD_LINUX_LSB_RELEASE, + "MD_LINUX_LSB_RELEASE", + &errors); + DumpRawStream(&minidump, + MD_LINUX_PROC_STATUS, + "MD_LINUX_PROC_STATUS", + &errors); + DumpRawStream(&minidump, + MD_LINUX_CPU_INFO, + "MD_LINUX_CPU_INFO", + &errors); + DumpRawStream(&minidump, + MD_LINUX_MAPS, + "MD_LINUX_MAPS", + &errors); + + return errors == 0; +} + +} // namespace + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + if (argc != 2) { + fprintf(stderr, "usage: %s \n", argv[0]); + return 1; + } + + return PrintMinidumpDump(argv[1]) ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_dump_test b/src/lib/crashdump/gbreakpad/processor/minidump_dump_test new file mode 100644 index 0000000..fb62ace --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_dump_test @@ -0,0 +1,36 @@ +#!/bin/sh + +# Copyright (c) 2006, Google Inc. +# 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 Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER 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. + +testdata_dir=$srcdir/src/processor/testdata +./src/processor/minidump_dump $testdata_dir/minidump2.dmp | \ + tr -d '\015' | \ + diff -u $testdata_dir/minidump2.dump.out - +exit $? diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_processor.cc b/src/lib/crashdump/gbreakpad/processor/minidump_processor.cc new file mode 100644 index 0000000..40b2873 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_processor.cc @@ -0,0 +1,1133 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "google_breakpad/processor/minidump_processor.h" + +#include +#include + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/exploitability.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_x86.h" + +namespace google_breakpad { + +MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : supplier_(supplier), resolver_(resolver), + enable_exploitability_(false) { +} + +MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier, + SourceLineResolverInterface *resolver, + bool enable_exploitability) + : supplier_(supplier), resolver_(resolver), + enable_exploitability_(enable_exploitability) { +} + +MinidumpProcessor::~MinidumpProcessor() { +} + +ProcessResult MinidumpProcessor::Process( + Minidump *dump, ProcessState *process_state) { + assert(dump); + assert(process_state); + + process_state->Clear(); + + const MDRawHeader *header = dump->header(); + if (!header) { + BPLOG(ERROR) << "Minidump " << dump->path() << " has no header"; + return PROCESS_ERROR_NO_MINIDUMP_HEADER; + } + process_state->time_date_stamp_ = header->time_date_stamp; + + bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_); + bool has_os_info = GetOSInfo(dump, &process_state->system_info_); + + u_int32_t dump_thread_id = 0; + bool has_dump_thread = false; + u_int32_t requesting_thread_id = 0; + bool has_requesting_thread = false; + + MinidumpBreakpadInfo *breakpad_info = dump->GetBreakpadInfo(); + if (breakpad_info) { + has_dump_thread = breakpad_info->GetDumpThreadID(&dump_thread_id); + has_requesting_thread = + breakpad_info->GetRequestingThreadID(&requesting_thread_id); + } + + MinidumpException *exception = dump->GetException(); + if (exception) { + process_state->crashed_ = true; + has_requesting_thread = exception->GetThreadID(&requesting_thread_id); + + process_state->crash_reason_ = GetCrashReason( + dump, &process_state->crash_address_); + } + + // This will just return an empty string if it doesn't exist. + process_state->assertion_ = GetAssertion(dump); + + MinidumpModuleList *module_list = dump->GetModuleList(); + + // Put a copy of the module list into ProcessState object. This is not + // necessarily a MinidumpModuleList, but it adheres to the CodeModules + // interface, which is all that ProcessState needs to expose. + if (module_list) + process_state->modules_ = module_list->Copy(); + + MinidumpThreadList *threads = dump->GetThreadList(); + if (!threads) { + BPLOG(ERROR) << "Minidump " << dump->path() << " has no thread list"; + return PROCESS_ERROR_NO_THREAD_LIST; + } + + BPLOG(INFO) << "Minidump " << dump->path() << " has " << + (has_cpu_info ? "" : "no ") << "CPU info, " << + (has_os_info ? "" : "no ") << "OS info, " << + (breakpad_info != NULL ? "" : "no ") << "Breakpad info, " << + (exception != NULL ? "" : "no ") << "exception, " << + (module_list != NULL ? "" : "no ") << "module list, " << + (threads != NULL ? "" : "no ") << "thread list, " << + (has_dump_thread ? "" : "no ") << "dump thread, and " << + (has_requesting_thread ? "" : "no ") << "requesting thread"; + + bool interrupted = false; + bool found_requesting_thread = false; + unsigned int thread_count = threads->thread_count(); + for (unsigned int thread_index = 0; + thread_index < thread_count; + ++thread_index) { + char thread_string_buffer[64]; + snprintf(thread_string_buffer, sizeof(thread_string_buffer), "%d/%d", + thread_index, thread_count); + string thread_string = dump->path() + ":" + thread_string_buffer; + + MinidumpThread *thread = threads->GetThreadAtIndex(thread_index); + if (!thread) { + BPLOG(ERROR) << "Could not get thread for " << thread_string; + return PROCESS_ERROR_GETTING_THREAD; + } + + u_int32_t thread_id; + if (!thread->GetThreadID(&thread_id)) { + BPLOG(ERROR) << "Could not get thread ID for " << thread_string; + return PROCESS_ERROR_GETTING_THREAD_ID; + } + + thread_string += " id " + HexString(thread_id); + BPLOG(INFO) << "Looking at thread " << thread_string; + + // If this thread is the thread that produced the minidump, don't process + // it. Because of the problems associated with a thread producing a + // dump of itself (when both its context and its stack are in flux), + // processing that stack wouldn't provide much useful data. + if (has_dump_thread && thread_id == dump_thread_id) { + continue; + } + + MinidumpContext *context = thread->GetContext(); + + if (has_requesting_thread && thread_id == requesting_thread_id) { + if (found_requesting_thread) { + // There can't be more than one requesting thread. + BPLOG(ERROR) << "Duplicate requesting thread: " << thread_string; + return PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS; + } + + // Use processed_state->threads_.size() instead of thread_index. + // thread_index points to the thread index in the minidump, which + // might be greater than the thread index in the threads vector if + // any of the minidump's threads are skipped and not placed into the + // processed threads vector. The thread vector's current size will + // be the index of the current thread when it's pushed into the + // vector. + process_state->requesting_thread_ = process_state->threads_.size(); + + found_requesting_thread = true; + + if (process_state->crashed_) { + // Use the exception record's context for the crashed thread, instead + // of the thread's own context. For the crashed thread, the thread's + // own context is the state inside the exception handler. Using it + // would not result in the expected stack trace from the time of the + // crash. If the exception context is invalid, however, we fall back + // on the thread context. + MinidumpContext *ctx = exception->GetContext(); + context = ctx ? ctx : thread->GetContext(); + } + } + + MinidumpMemoryRegion *thread_memory = thread->GetMemory(); + if (!thread_memory) { + BPLOG(ERROR) << "No memory region for " << thread_string; + return PROCESS_ERROR_NO_MEMORY_FOR_THREAD; + } + + // Use process_state->modules_ instead of module_list, because the + // |modules| argument will be used to populate the |module| fields in + // the returned StackFrame objects, which will be placed into the + // returned ProcessState object. module_list's lifetime is only as + // long as the Minidump object: it will be deleted when this function + // returns. process_state->modules_ is owned by the ProcessState object + // (just like the StackFrame objects), and is much more suitable for this + // task. + scoped_ptr stackwalker( + Stackwalker::StackwalkerForCPU(process_state->system_info(), + context, + thread_memory, + process_state->modules_, + supplier_, + resolver_)); + if (!stackwalker.get()) { + BPLOG(ERROR) << "No stackwalker for " << thread_string; + return PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD; + } + + scoped_ptr stack(new CallStack()); + if (!stackwalker->Walk(stack.get())) { + BPLOG(INFO) << "Stackwalker interrupt (missing symbols?) at " << + thread_string; + interrupted = true; + } + process_state->threads_.push_back(stack.release()); + process_state->thread_memory_regions_.push_back(thread_memory); + } + + if (interrupted) { + BPLOG(INFO) << "Processing interrupted for " << dump->path(); + return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED; + } + + // If a requesting thread was indicated, it must be present. + if (has_requesting_thread && !found_requesting_thread) { + // Don't mark as an error, but invalidate the requesting thread + BPLOG(ERROR) << "Minidump indicated requesting thread " << + HexString(requesting_thread_id) << ", not found in " << + dump->path(); + process_state->requesting_thread_ = -1; + } + + // Exploitability defaults to EXPLOITABILITY_NOT_ANALYZED + process_state->exploitability_ = EXPLOITABILITY_NOT_ANALYZED; + + // If an exploitability run was requested we perform the platform specific + // rating. + if (enable_exploitability_) { + scoped_ptr exploitability( + Exploitability::ExploitabilityForPlatform(dump, process_state)); + // The engine will be null if the platform is not supported + if (exploitability != NULL) { + process_state->exploitability_ = exploitability->CheckExploitability(); + } else { + process_state->exploitability_ = EXPLOITABILITY_ERR_NOENGINE; + } + } + + BPLOG(INFO) << "Processed " << dump->path(); + return PROCESS_OK; +} + +ProcessResult MinidumpProcessor::Process( + const string &minidump_file, ProcessState *process_state) { + BPLOG(INFO) << "Processing minidump in file " << minidump_file; + + Minidump dump(minidump_file); + if (!dump.Read()) { + BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read"; + return PROCESS_ERROR_MINIDUMP_NOT_FOUND; + } + + return Process(&dump, process_state); +} + +// Returns the MDRawSystemInfo from a minidump, or NULL if system info is +// not available from the minidump. If system_info is non-NULL, it is used +// to pass back the MinidumpSystemInfo object. +static const MDRawSystemInfo* GetSystemInfo(Minidump *dump, + MinidumpSystemInfo **system_info) { + MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo(); + if (!minidump_system_info) + return NULL; + + if (system_info) + *system_info = minidump_system_info; + + return minidump_system_info->system_info(); +} + +// static +bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) { + assert(dump); + assert(info); + + info->cpu.clear(); + info->cpu_info.clear(); + + MinidumpSystemInfo *system_info; + const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info); + if (!raw_system_info) + return false; + + switch (raw_system_info->processor_architecture) { + case MD_CPU_ARCHITECTURE_X86: + case MD_CPU_ARCHITECTURE_AMD64: { + if (raw_system_info->processor_architecture == + MD_CPU_ARCHITECTURE_X86) + info->cpu = "x86"; + else + info->cpu = "amd64"; + + const string *cpu_vendor = system_info->GetCPUVendor(); + if (cpu_vendor) { + info->cpu_info = *cpu_vendor; + info->cpu_info.append(" "); + } + + char x86_info[36]; + snprintf(x86_info, sizeof(x86_info), "family %u model %u stepping %u", + raw_system_info->processor_level, + raw_system_info->processor_revision >> 8, + raw_system_info->processor_revision & 0xff); + info->cpu_info.append(x86_info); + break; + } + + case MD_CPU_ARCHITECTURE_PPC: { + info->cpu = "ppc"; + break; + } + + case MD_CPU_ARCHITECTURE_SPARC: { + info->cpu = "sparc"; + break; + } + + case MD_CPU_ARCHITECTURE_ARM: { + info->cpu = "arm"; + break; + } + + default: { + // Assign the numeric architecture ID into the CPU string. + char cpu_string[7]; + snprintf(cpu_string, sizeof(cpu_string), "0x%04x", + raw_system_info->processor_architecture); + info->cpu = cpu_string; + break; + } + } + + info->cpu_count = raw_system_info->number_of_processors; + + return true; +} + +// static +bool MinidumpProcessor::GetOSInfo(Minidump *dump, SystemInfo *info) { + assert(dump); + assert(info); + + info->os.clear(); + info->os_short.clear(); + info->os_version.clear(); + + MinidumpSystemInfo *system_info; + const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info); + if (!raw_system_info) + return false; + + info->os_short = system_info->GetOS(); + + switch (raw_system_info->platform_id) { + case MD_OS_WIN32_NT: { + info->os = "Windows NT"; + break; + } + + case MD_OS_WIN32_WINDOWS: { + info->os = "Windows"; + break; + } + + case MD_OS_MAC_OS_X: { + info->os = "Mac OS X"; + break; + } + + case MD_OS_IOS: { + info->os = "iOS"; + break; + } + + case MD_OS_LINUX: { + info->os = "Linux"; + break; + } + + case MD_OS_SOLARIS: { + info->os = "Solaris"; + break; + } + + default: { + // Assign the numeric platform ID into the OS string. + char os_string[11]; + snprintf(os_string, sizeof(os_string), "0x%08x", + raw_system_info->platform_id); + info->os = os_string; + break; + } + } + + char os_version_string[33]; + snprintf(os_version_string, sizeof(os_version_string), "%u.%u.%u", + raw_system_info->major_version, + raw_system_info->minor_version, + raw_system_info->build_number); + info->os_version = os_version_string; + + const string *csd_version = system_info->GetCSDVersion(); + if (csd_version) { + info->os_version.append(" "); + info->os_version.append(*csd_version); + } + + return true; +} + +// static +string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { + MinidumpException *exception = dump->GetException(); + if (!exception) + return ""; + + const MDRawExceptionStream *raw_exception = exception->exception(); + if (!raw_exception) + return ""; + + if (address) + *address = raw_exception->exception_record.exception_address; + + // The reason value is OS-specific and possibly CPU-specific. Set up + // sensible numeric defaults for the reason string in case we can't + // map the codes to a string (because there's no system info, or because + // it's an unrecognized platform, or because it's an unrecognized code.) + char reason_string[24]; + u_int32_t exception_code = raw_exception->exception_record.exception_code; + u_int32_t exception_flags = raw_exception->exception_record.exception_flags; + snprintf(reason_string, sizeof(reason_string), "0x%08x / 0x%08x", + exception_code, exception_flags); + string reason = reason_string; + + const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, NULL); + if (!raw_system_info) + return reason; + + switch (raw_system_info->platform_id) { + case MD_OS_MAC_OS_X: + case MD_OS_IOS: { + char flags_string[11]; + snprintf(flags_string, sizeof(flags_string), "0x%08x", exception_flags); + switch (exception_code) { + case MD_EXCEPTION_MAC_BAD_ACCESS: + reason = "EXC_BAD_ACCESS / "; + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS: + reason.append("KERN_INVALID_ADDRESS"); + break; + case MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE: + reason.append("KERN_PROTECTION_FAILURE"); + break; + case MD_EXCEPTION_CODE_MAC_NO_ACCESS: + reason.append("KERN_NO_ACCESS"); + break; + case MD_EXCEPTION_CODE_MAC_MEMORY_FAILURE: + reason.append("KERN_MEMORY_FAILURE"); + break; + case MD_EXCEPTION_CODE_MAC_MEMORY_ERROR: + reason.append("KERN_MEMORY_ERROR"); + break; + // These are ppc only but shouldn't be a problem as they're + // unused on x86 + case MD_EXCEPTION_CODE_MAC_PPC_VM_PROT_READ: + reason.append("EXC_PPC_VM_PROT_READ"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_BADSPACE: + reason.append("EXC_PPC_BADSPACE"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_UNALIGNED: + reason.append("EXC_PPC_UNALIGNED"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + case MD_EXCEPTION_MAC_BAD_INSTRUCTION: + reason = "EXC_BAD_INSTRUCTION / "; + switch (raw_system_info->processor_architecture) { + case MD_CPU_ARCHITECTURE_PPC: { + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_PPC_INVALID_SYSCALL: + reason.append("EXC_PPC_INVALID_SYSCALL"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_UNIMPLEMENTED_INSTRUCTION: + reason.append("EXC_PPC_UNIPL_INST"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_INSTRUCTION: + reason.append("EXC_PPC_PRIVINST"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_REGISTER: + reason.append("EXC_PPC_PRIVREG"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_TRACE: + reason.append("EXC_PPC_TRACE"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_PERFORMANCE_MONITOR: + reason.append("EXC_PPC_PERFMON"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + case MD_CPU_ARCHITECTURE_X86: { + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_X86_INVALID_OPERATION: + reason.append("EXC_I386_INVOP"); + break; + case MD_EXCEPTION_CODE_MAC_X86_INVALID_TASK_STATE_SEGMENT: + reason.append("EXC_INVTSSFLT"); + break; + case MD_EXCEPTION_CODE_MAC_X86_SEGMENT_NOT_PRESENT: + reason.append("EXC_SEGNPFLT"); + break; + case MD_EXCEPTION_CODE_MAC_X86_STACK_FAULT: + reason.append("EXC_STKFLT"); + break; + case MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT: + reason.append("EXC_GPFLT"); + break; + case MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT: + reason.append("EXC_ALIGNFLT"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + case MD_EXCEPTION_MAC_ARITHMETIC: + reason = "EXC_ARITHMETIC / "; + switch (raw_system_info->processor_architecture) { + case MD_CPU_ARCHITECTURE_PPC: { + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_PPC_OVERFLOW: + reason.append("EXC_PPC_OVERFLOW"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_ZERO_DIVIDE: + reason.append("EXC_PPC_ZERO_DIVIDE"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_INEXACT: + reason.append("EXC_FLT_INEXACT"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_ZERO_DIVIDE: + reason.append("EXC_PPC_FLT_ZERO_DIVIDE"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_UNDERFLOW: + reason.append("EXC_PPC_FLT_UNDERFLOW"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_OVERFLOW: + reason.append("EXC_PPC_FLT_OVERFLOW"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_FLOAT_NOT_A_NUMBER: + reason.append("EXC_PPC_FLT_NOT_A_NUMBER"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_NO_EMULATION: + reason.append("EXC_PPC_NOEMULATION"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_ALTIVEC_ASSIST: + reason.append("EXC_PPC_ALTIVECASSIST"); + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + case MD_CPU_ARCHITECTURE_X86: { + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_X86_DIV: + reason.append("EXC_I386_DIV"); + break; + case MD_EXCEPTION_CODE_MAC_X86_INTO: + reason.append("EXC_I386_INTO"); + break; + case MD_EXCEPTION_CODE_MAC_X86_NOEXT: + reason.append("EXC_I386_NOEXT"); + break; + case MD_EXCEPTION_CODE_MAC_X86_EXTOVR: + reason.append("EXC_I386_EXTOVR"); + break; + case MD_EXCEPTION_CODE_MAC_X86_EXTERR: + reason.append("EXC_I386_EXTERR"); + break; + case MD_EXCEPTION_CODE_MAC_X86_EMERR: + reason.append("EXC_I386_EMERR"); + break; + case MD_EXCEPTION_CODE_MAC_X86_BOUND: + reason.append("EXC_I386_BOUND"); + break; + case MD_EXCEPTION_CODE_MAC_X86_SSEEXTERR: + reason.append("EXC_I386_SSEEXTERR"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + case MD_EXCEPTION_MAC_EMULATION: + reason = "EXC_EMULATION / "; + reason.append(flags_string); + break; + case MD_EXCEPTION_MAC_SOFTWARE: + reason = "EXC_SOFTWARE / "; + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_ABORT: + reason.append("SIGABRT"); + break; + case MD_EXCEPTION_CODE_MAC_NS_EXCEPTION: + reason.append("UNCAUGHT_NS_EXCEPTION"); + break; + // These are ppc only but shouldn't be a problem as they're + // unused on x86 + case MD_EXCEPTION_CODE_MAC_PPC_TRAP: + reason.append("EXC_PPC_TRAP"); + break; + case MD_EXCEPTION_CODE_MAC_PPC_MIGRATE: + reason.append("EXC_PPC_MIGRATE"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + case MD_EXCEPTION_MAC_BREAKPOINT: + reason = "EXC_BREAKPOINT / "; + switch (raw_system_info->processor_architecture) { + case MD_CPU_ARCHITECTURE_PPC: { + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_PPC_BREAKPOINT: + reason.append("EXC_PPC_BREAKPOINT"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + case MD_CPU_ARCHITECTURE_X86: { + switch (exception_flags) { + case MD_EXCEPTION_CODE_MAC_X86_SGL: + reason.append("EXC_I386_SGL"); + break; + case MD_EXCEPTION_CODE_MAC_X86_BPT: + reason.append("EXC_I386_BPT"); + break; + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + default: + reason.append(flags_string); + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + case MD_EXCEPTION_MAC_SYSCALL: + reason = "EXC_SYSCALL / "; + reason.append(flags_string); + break; + case MD_EXCEPTION_MAC_MACH_SYSCALL: + reason = "EXC_MACH_SYSCALL / "; + reason.append(flags_string); + break; + case MD_EXCEPTION_MAC_RPC_ALERT: + reason = "EXC_RPC_ALERT / "; + reason.append(flags_string); + break; + } + break; + } + + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: { + switch (exception_code) { + case MD_EXCEPTION_CODE_WIN_CONTROL_C: + reason = "DBG_CONTROL_C"; + break; + case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION: + reason = "EXCEPTION_GUARD_PAGE"; + break; + case MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT: + reason = "EXCEPTION_DATATYPE_MISALIGNMENT"; + break; + case MD_EXCEPTION_CODE_WIN_BREAKPOINT: + reason = "EXCEPTION_BREAKPOINT"; + break; + case MD_EXCEPTION_CODE_WIN_SINGLE_STEP: + reason = "EXCEPTION_SINGLE_STEP"; + break; + case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION: + // For EXCEPTION_ACCESS_VIOLATION, Windows puts the address that + // caused the fault in exception_information[1]. + // exception_information[0] is 0 if the violation was caused by + // an attempt to read data and 1 if it was an attempt to write + // data. + // This information is useful in addition to the code address, which + // will be present in the crash thread's instruction field anyway. + if (raw_exception->exception_record.number_parameters >= 1) { + MDAccessViolationTypeWin av_type = + static_cast + (raw_exception->exception_record.exception_information[0]); + switch (av_type) { + case MD_ACCESS_VIOLATION_WIN_READ: + reason = "EXCEPTION_ACCESS_VIOLATION_READ"; + break; + case MD_ACCESS_VIOLATION_WIN_WRITE: + reason = "EXCEPTION_ACCESS_VIOLATION_WRITE"; + break; + case MD_ACCESS_VIOLATION_WIN_EXEC: + reason = "EXCEPTION_ACCESS_VIOLATION_EXEC"; + break; + default: + reason = "EXCEPTION_ACCESS_VIOLATION"; + break; + } + } else { + reason = "EXCEPTION_ACCESS_VIOLATION"; + } + if (address && + raw_exception->exception_record.number_parameters >= 2) { + *address = + raw_exception->exception_record.exception_information[1]; + } + break; + case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR: + reason = "EXCEPTION_IN_PAGE_ERROR"; + break; + case MD_EXCEPTION_CODE_WIN_INVALID_HANDLE: + reason = "EXCEPTION_INVALID_HANDLE"; + break; + case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION: + reason = "EXCEPTION_ILLEGAL_INSTRUCTION"; + break; + case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION: + reason = "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + break; + case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION: + reason = "EXCEPTION_INVALID_DISPOSITION"; + break; + case MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED: + reason = "EXCEPTION_BOUNDS_EXCEEDED"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND: + reason = "EXCEPTION_FLT_DENORMAL_OPERAND"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO: + reason = "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT: + reason = "EXCEPTION_FLT_INEXACT_RESULT"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION: + reason = "EXCEPTION_FLT_INVALID_OPERATION"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW: + reason = "EXCEPTION_FLT_OVERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK: + reason = "EXCEPTION_FLT_STACK_CHECK"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW: + reason = "EXCEPTION_FLT_UNDERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO: + reason = "EXCEPTION_INT_DIVIDE_BY_ZERO"; + break; + case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW: + reason = "EXCEPTION_INT_OVERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION: + reason = "EXCEPTION_PRIV_INSTRUCTION"; + break; + case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW: + reason = "EXCEPTION_STACK_OVERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK: + reason = "EXCEPTION_POSSIBLE_DEADLOCK"; + break; + case MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN: + reason = "EXCEPTION_STACK_BUFFER_OVERRUN"; + break; + case MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION: + reason = "EXCEPTION_HEAP_CORRUPTION"; + break; + case MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION: + reason = "Unhandled C++ Exception"; + break; + default: + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + + case MD_OS_LINUX: { + switch (exception_code) { + case MD_EXCEPTION_CODE_LIN_SIGHUP: + reason = "SIGHUP"; + break; + case MD_EXCEPTION_CODE_LIN_SIGINT: + reason = "SIGINT"; + break; + case MD_EXCEPTION_CODE_LIN_SIGQUIT: + reason = "SIGQUIT"; + break; + case MD_EXCEPTION_CODE_LIN_SIGILL: + reason = "SIGILL"; + break; + case MD_EXCEPTION_CODE_LIN_SIGTRAP: + reason = "SIGTRAP"; + break; + case MD_EXCEPTION_CODE_LIN_SIGABRT: + reason = "SIGABRT"; + break; + case MD_EXCEPTION_CODE_LIN_SIGBUS: + reason = "SIGBUS"; + break; + case MD_EXCEPTION_CODE_LIN_SIGFPE: + reason = "SIGFPE"; + break; + case MD_EXCEPTION_CODE_LIN_SIGKILL: + reason = "SIGKILL"; + break; + case MD_EXCEPTION_CODE_LIN_SIGUSR1: + reason = "SIGUSR1"; + break; + case MD_EXCEPTION_CODE_LIN_SIGSEGV: + reason = "SIGSEGV"; + break; + case MD_EXCEPTION_CODE_LIN_SIGUSR2: + reason = "SIGUSR2"; + break; + case MD_EXCEPTION_CODE_LIN_SIGPIPE: + reason = "SIGPIPE"; + break; + case MD_EXCEPTION_CODE_LIN_SIGALRM: + reason = "SIGALRM"; + break; + case MD_EXCEPTION_CODE_LIN_SIGTERM: + reason = "SIGTERM"; + break; + case MD_EXCEPTION_CODE_LIN_SIGSTKFLT: + reason = "SIGSTKFLT"; + break; + case MD_EXCEPTION_CODE_LIN_SIGCHLD: + reason = "SIGCHLD"; + break; + case MD_EXCEPTION_CODE_LIN_SIGCONT: + reason = "SIGCONT"; + break; + case MD_EXCEPTION_CODE_LIN_SIGSTOP: + reason = "SIGSTOP"; + break; + case MD_EXCEPTION_CODE_LIN_SIGTSTP: + reason = "SIGTSTP"; + break; + case MD_EXCEPTION_CODE_LIN_SIGTTIN: + reason = "SIGTTIN"; + break; + case MD_EXCEPTION_CODE_LIN_SIGTTOU: + reason = "SIGTTOU"; + break; + case MD_EXCEPTION_CODE_LIN_SIGURG: + reason = "SIGURG"; + break; + case MD_EXCEPTION_CODE_LIN_SIGXCPU: + reason = "SIGXCPU"; + break; + case MD_EXCEPTION_CODE_LIN_SIGXFSZ: + reason = "SIGXFSZ"; + break; + case MD_EXCEPTION_CODE_LIN_SIGVTALRM: + reason = "SIGVTALRM"; + break; + case MD_EXCEPTION_CODE_LIN_SIGPROF: + reason = "SIGPROF"; + break; + case MD_EXCEPTION_CODE_LIN_SIGWINCH: + reason = "SIGWINCH"; + break; + case MD_EXCEPTION_CODE_LIN_SIGIO: + reason = "SIGIO"; + break; + case MD_EXCEPTION_CODE_LIN_SIGPWR: + reason = "SIGPWR"; + break; + case MD_EXCEPTION_CODE_LIN_SIGSYS: + reason = "SIGSYS"; + break; + default: + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + + case MD_OS_SOLARIS: { + switch (exception_code) { + case MD_EXCEPTION_CODE_SOL_SIGHUP: + reason = "SIGHUP"; + break; + case MD_EXCEPTION_CODE_SOL_SIGINT: + reason = "SIGINT"; + break; + case MD_EXCEPTION_CODE_SOL_SIGQUIT: + reason = "SIGQUIT"; + break; + case MD_EXCEPTION_CODE_SOL_SIGILL: + reason = "SIGILL"; + break; + case MD_EXCEPTION_CODE_SOL_SIGTRAP: + reason = "SIGTRAP"; + break; + case MD_EXCEPTION_CODE_SOL_SIGIOT: + reason = "SIGIOT | SIGABRT"; + break; + case MD_EXCEPTION_CODE_SOL_SIGEMT: + reason = "SIGEMT"; + break; + case MD_EXCEPTION_CODE_SOL_SIGFPE: + reason = "SIGFPE"; + break; + case MD_EXCEPTION_CODE_SOL_SIGKILL: + reason = "SIGKILL"; + break; + case MD_EXCEPTION_CODE_SOL_SIGBUS: + reason = "SIGBUS"; + break; + case MD_EXCEPTION_CODE_SOL_SIGSEGV: + reason = "SIGSEGV"; + break; + case MD_EXCEPTION_CODE_SOL_SIGSYS: + reason = "SIGSYS"; + break; + case MD_EXCEPTION_CODE_SOL_SIGPIPE: + reason = "SIGPIPE"; + break; + case MD_EXCEPTION_CODE_SOL_SIGALRM: + reason = "SIGALRM"; + break; + case MD_EXCEPTION_CODE_SOL_SIGTERM: + reason = "SIGTERM"; + break; + case MD_EXCEPTION_CODE_SOL_SIGUSR1: + reason = "SIGUSR1"; + break; + case MD_EXCEPTION_CODE_SOL_SIGUSR2: + reason = "SIGUSR2"; + break; + case MD_EXCEPTION_CODE_SOL_SIGCLD: + reason = "SIGCLD | SIGCHLD"; + break; + case MD_EXCEPTION_CODE_SOL_SIGPWR: + reason = "SIGPWR"; + break; + case MD_EXCEPTION_CODE_SOL_SIGWINCH: + reason = "SIGWINCH"; + break; + case MD_EXCEPTION_CODE_SOL_SIGURG: + reason = "SIGURG"; + break; + case MD_EXCEPTION_CODE_SOL_SIGPOLL: + reason = "SIGPOLL | SIGIO"; + break; + case MD_EXCEPTION_CODE_SOL_SIGSTOP: + reason = "SIGSTOP"; + break; + case MD_EXCEPTION_CODE_SOL_SIGTSTP: + reason = "SIGTSTP"; + break; + case MD_EXCEPTION_CODE_SOL_SIGCONT: + reason = "SIGCONT"; + break; + case MD_EXCEPTION_CODE_SOL_SIGTTIN: + reason = "SIGTTIN"; + break; + case MD_EXCEPTION_CODE_SOL_SIGTTOU: + reason = "SIGTTOU"; + break; + case MD_EXCEPTION_CODE_SOL_SIGVTALRM: + reason = "SIGVTALRM"; + break; + case MD_EXCEPTION_CODE_SOL_SIGPROF: + reason = "SIGPROF"; + break; + case MD_EXCEPTION_CODE_SOL_SIGXCPU: + reason = "SIGXCPU"; + break; + case MD_EXCEPTION_CODE_SOL_SIGXFSZ: + reason = "SIGXFSZ"; + break; + case MD_EXCEPTION_CODE_SOL_SIGWAITING: + reason = "SIGWAITING"; + break; + case MD_EXCEPTION_CODE_SOL_SIGLWP: + reason = "SIGLWP"; + break; + case MD_EXCEPTION_CODE_SOL_SIGFREEZE: + reason = "SIGFREEZE"; + break; + case MD_EXCEPTION_CODE_SOL_SIGTHAW: + reason = "SIGTHAW"; + break; + case MD_EXCEPTION_CODE_SOL_SIGCANCEL: + reason = "SIGCANCEL"; + break; + case MD_EXCEPTION_CODE_SOL_SIGLOST: + reason = "SIGLOST"; + break; + case MD_EXCEPTION_CODE_SOL_SIGXRES: + reason = "SIGXRES"; + break; + case MD_EXCEPTION_CODE_SOL_SIGJVM1: + reason = "SIGJVM1"; + break; + case MD_EXCEPTION_CODE_SOL_SIGJVM2: + reason = "SIGJVM2"; + break; + default: + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + break; + } + + default: { + BPLOG(INFO) << "Unknown exception reason " << reason; + break; + } + } + + return reason; +} + +// static +string MinidumpProcessor::GetAssertion(Minidump *dump) { + MinidumpAssertion *assertion = dump->GetAssertion(); + if (!assertion) + return ""; + + const MDRawAssertionInfo *raw_assertion = assertion->assertion(); + if (!raw_assertion) + return ""; + + string assertion_string; + switch (raw_assertion->type) { + case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER: + assertion_string = "Invalid parameter passed to library function"; + break; + case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL: + assertion_string = "Pure virtual function called"; + break; + default: { + char assertion_type[32]; + sprintf(assertion_type, "0x%08x", raw_assertion->type); + assertion_string = "Unknown assertion type "; + assertion_string += assertion_type; + break; + } + } + + string expression = assertion->expression(); + if (!expression.empty()) { + assertion_string.append(" " + expression); + } + + string function = assertion->function(); + if (!function.empty()) { + assertion_string.append(" in function " + function); + } + + string file = assertion->file(); + if (!file.empty()) { + assertion_string.append(", in file " + file); + } + + if (raw_assertion->line != 0) { + char assertion_line[32]; + sprintf(assertion_line, "%u", raw_assertion->line); + assertion_string.append(" at line "); + assertion_string.append(assertion_line); + } + + return assertion_string; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_processor_unittest.cc b/src/lib/crashdump/gbreakpad/processor/minidump_processor_unittest.cc new file mode 100644 index 0000000..fcac48f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_processor_unittest.cc @@ -0,0 +1,380 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Unit test for MinidumpProcessor. Uses a pre-generated minidump and +// corresponding symbol file, and checks the stack frames for correctness. + +#include + +#include +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/minidump_processor.h" +#include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/symbol_supplier.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + +using std::map; + +namespace google_breakpad { +class MockMinidump : public Minidump { + public: + MockMinidump() : Minidump("") { + } + + MOCK_METHOD0(Read, bool()); + MOCK_CONST_METHOD0(path, string()); + MOCK_CONST_METHOD0(header, const MDRawHeader*()); + MOCK_METHOD0(GetThreadList, MinidumpThreadList*()); +}; +} + +namespace { + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::CodeModule; +using google_breakpad::MinidumpProcessor; +using google_breakpad::MinidumpThreadList; +using google_breakpad::MinidumpThread; +using google_breakpad::MockMinidump; +using google_breakpad::ProcessState; +using google_breakpad::scoped_ptr; +using google_breakpad::SymbolSupplier; +using google_breakpad::SystemInfo; +using std::string; +using ::testing::_; +using ::testing::Mock; +using ::testing::Ne; +using ::testing::Property; +using ::testing::Return; + +static const char *kSystemInfoOS = "Windows NT"; +static const char *kSystemInfoOSShort = "windows"; +static const char *kSystemInfoOSVersion = "5.1.2600 Service Pack 2"; +static const char *kSystemInfoCPU = "x86"; +static const char *kSystemInfoCPUInfo = + "GenuineIntel family 6 model 13 stepping 8"; + +#define ASSERT_TRUE_ABORT(cond) \ + if (!(cond)) { \ + fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ + abort(); \ + } + +#define ASSERT_EQ_ABORT(e1, e2) ASSERT_TRUE_ABORT((e1) == (e2)) + +class TestSymbolSupplier : public SymbolSupplier { + public: + TestSymbolSupplier() : interrupt_(false) {} + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file); + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); + + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); + + virtual void FreeSymbolData(const CodeModule *module); + + // When set to true, causes the SymbolSupplier to return INTERRUPT + void set_interrupt(bool interrupt) { interrupt_ = interrupt; } + + private: + bool interrupt_; + map memory_buffers_; +}; + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file) { + ASSERT_TRUE_ABORT(module); + ASSERT_TRUE_ABORT(system_info); + ASSERT_EQ_ABORT(system_info->cpu, kSystemInfoCPU); + ASSERT_EQ_ABORT(system_info->cpu_info, kSystemInfoCPUInfo); + ASSERT_EQ_ABORT(system_info->os, kSystemInfoOS); + ASSERT_EQ_ABORT(system_info->os_short, kSystemInfoOSShort); + ASSERT_EQ_ABORT(system_info->os_version, kSystemInfoOSVersion); + + if (interrupt_) { + return INTERRUPT; + } + + if (module && module->code_file() == "c:\\test_app.exe") { + *symbol_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/symbols/test_app.pdb/" + + module->debug_identifier() + + "/test_app.sym"; + return FOUND; + } + + return NOT_FOUND; +} + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info, + symbol_file); + if (s == FOUND) { + std::ifstream in(symbol_file->c_str()); + std::getline(in, *symbol_data, std::string::traits_type::to_char_type( + std::string::traits_type::eof())); + in.close(); + } + + return s; +} + +SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + string symbol_data_string; + SymbolSupplier::SymbolResult s = GetSymbolFile(module, + system_info, + symbol_file, + &symbol_data_string); + if (s == FOUND) { + unsigned int size = symbol_data_string.size() + 1; + *symbol_data = new char[size]; + if (*symbol_data == NULL) { + BPLOG(ERROR) << "Memory allocation failed for module: " + << module->code_file() << " size: " << size; + return INTERRUPT; + } + strcpy(*symbol_data, symbol_data_string.c_str()); + memory_buffers_.insert(make_pair(module->code_file(), *symbol_data)); + } + + return s; +} + +void TestSymbolSupplier::FreeSymbolData(const CodeModule *module) { + map::iterator it = memory_buffers_.find(module->code_file()); + if (it != memory_buffers_.end()) { + delete [] it->second; + memory_buffers_.erase(it); + } +} + +// A mock symbol supplier that always returns NOT_FOUND; one current +// use for testing the processor's caching of symbol lookups. +class MockSymbolSupplier : public SymbolSupplier { + public: + MockSymbolSupplier() { } + MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*, + const SystemInfo*, + string*)); + MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*, + const SystemInfo*, + string*, + string*)); + MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule*, + const SystemInfo*, + string*, + char**)); + MOCK_METHOD1(FreeSymbolData, void(const CodeModule*)); +}; + +class MinidumpProcessorTest : public ::testing::Test { +}; + +TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) { + MockMinidump dump; + TestSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver); + ProcessState state; + + EXPECT_EQ(processor.Process("nonexistent minidump", &state), + google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND); + + EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump")); + EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true)); + + MDRawHeader fakeHeader; + fakeHeader.time_date_stamp = 0; + EXPECT_CALL(dump, header()).WillOnce(Return((MDRawHeader*)NULL)). + WillRepeatedly(Return(&fakeHeader)); + EXPECT_EQ(processor.Process(&dump, &state), + google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER); + + EXPECT_CALL(dump, GetThreadList()). + WillOnce(Return((MinidumpThreadList*)NULL)); + EXPECT_EQ(processor.Process(&dump, &state), + google_breakpad::PROCESS_ERROR_NO_THREAD_LIST); +} + +// This test case verifies that the symbol supplier is only consulted +// once per minidump per module. +TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) { + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver); + + string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/minidump2.dmp"; + ProcessState state; + EXPECT_CALL(supplier, GetCStringSymbolData( + Property(&google_breakpad::CodeModule::code_file, + "c:\\test_app.exe"), + _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND)); + EXPECT_CALL(supplier, GetCStringSymbolData( + Property(&google_breakpad::CodeModule::code_file, + Ne("c:\\test_app.exe")), + _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND)); + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&supplier)); + + // We need to verify that across minidumps, the processor will refetch + // symbol files, even with the same symbol supplier. + EXPECT_CALL(supplier, GetCStringSymbolData( + Property(&google_breakpad::CodeModule::code_file, + "c:\\test_app.exe"), + _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND)); + EXPECT_CALL(supplier, GetCStringSymbolData( + Property(&google_breakpad::CodeModule::code_file, + Ne("c:\\test_app.exe")), + _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND)); + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); +} + +TEST_F(MinidumpProcessorTest, TestBasicProcessing) { + TestSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver); + + string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/minidump2.dmp"; + + ProcessState state; + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + ASSERT_EQ(state.system_info()->os, kSystemInfoOS); + ASSERT_EQ(state.system_info()->os_short, kSystemInfoOSShort); + ASSERT_EQ(state.system_info()->os_version, kSystemInfoOSVersion); + ASSERT_EQ(state.system_info()->cpu, kSystemInfoCPU); + ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo); + ASSERT_TRUE(state.crashed()); + ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION_WRITE"); + ASSERT_EQ(state.crash_address(), 0x45U); + ASSERT_EQ(state.threads()->size(), size_t(1)); + ASSERT_EQ(state.requesting_thread(), 0); + + CallStack *stack = state.threads()->at(0); + ASSERT_TRUE(stack); + ASSERT_EQ(stack->frames()->size(), 4U); + + ASSERT_TRUE(stack->frames()->at(0)->module); + ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000U); + ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe"); + ASSERT_EQ(stack->frames()->at(0)->function_name, + "`anonymous namespace'::CrashFunction"); + ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc"); + ASSERT_EQ(stack->frames()->at(0)->source_line, 58); + + ASSERT_TRUE(stack->frames()->at(1)->module); + ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000U); + ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe"); + ASSERT_EQ(stack->frames()->at(1)->function_name, "main"); + ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc"); + ASSERT_EQ(stack->frames()->at(1)->source_line, 65); + + // This comes from the CRT + ASSERT_TRUE(stack->frames()->at(2)->module); + ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000U); + ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe"); + ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup"); + ASSERT_EQ(stack->frames()->at(2)->source_file_name, + "f:\\sp\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c"); + ASSERT_EQ(stack->frames()->at(2)->source_line, 327); + + // No debug info available for kernel32.dll + ASSERT_TRUE(stack->frames()->at(3)->module); + ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000U); + ASSERT_EQ(stack->frames()->at(3)->module->code_file(), + "C:\\WINDOWS\\system32\\kernel32.dll"); + ASSERT_TRUE(stack->frames()->at(3)->function_name.empty()); + ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty()); + ASSERT_EQ(stack->frames()->at(3)->source_line, 0); + + ASSERT_EQ(state.modules()->module_count(), 13U); + ASSERT_TRUE(state.modules()->GetMainModule()); + ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "c:\\test_app.exe"); + ASSERT_FALSE(state.modules()->GetModuleForAddress(0)); + ASSERT_EQ(state.modules()->GetMainModule(), + state.modules()->GetModuleForAddress(0x400000)); + ASSERT_EQ(state.modules()->GetModuleForAddress(0x7c801234)->debug_file(), + "kernel32.pdb"); + ASSERT_EQ(state.modules()->GetModuleForAddress(0x77d43210)->version(), + "5.1.2600.2622"); + + // Test that disabled exploitability engine defaults to + // EXPLOITABILITY_NOT_ANALYZED. + ASSERT_EQ(google_breakpad::EXPLOITABILITY_NOT_ANALYZED, + state.exploitability()); + + // Test that the symbol supplier can interrupt processing + state.Clear(); + supplier.set_interrupt(true); + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED); +} +} // namespace + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk.cc b/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk.cc new file mode 100644 index 0000000..86f679e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk.cc @@ -0,0 +1,587 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// minidump_stackwalk.cc: Process a minidump with MinidumpProcessor, printing +// the results, including stack traces. +// +// Author: Mark Mentovai + +#include +#include +#include + +#include +#include + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/minidump_processor.h" +#include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/logging.h" +#include "processor/pathname_stripper.h" +#include "processor/scoped_ptr.h" +#include "processor/simple_symbol_supplier.h" + +namespace { + +using std::string; +using std::vector; +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::CodeModule; +using google_breakpad::CodeModules; +using google_breakpad::MinidumpModule; +using google_breakpad::MinidumpProcessor; +using google_breakpad::PathnameStripper; +using google_breakpad::ProcessState; +using google_breakpad::scoped_ptr; +using google_breakpad::SimpleSymbolSupplier; +using google_breakpad::StackFrame; +using google_breakpad::StackFramePPC; +using google_breakpad::StackFrameSPARC; +using google_breakpad::StackFrameX86; +using google_breakpad::StackFrameAMD64; +using google_breakpad::StackFrameARM; + +// Separator character for machine readable output. +static const char kOutputSeparator = '|'; + +// PrintRegister prints a register's name and value to stdout. It will +// print four registers on a line. For the first register in a set, +// pass 0 for |start_col|. For registers in a set, pass the most recent +// return value of PrintRegister. +// The caller is responsible for printing the final newline after a set +// of registers is completely printed, regardless of the number of calls +// to PrintRegister. +static const int kMaxWidth = 80; // optimize for an 80-column terminal +static int PrintRegister(const char *name, u_int32_t value, int start_col) { + char buffer[64]; + snprintf(buffer, sizeof(buffer), " %5s = 0x%08x", name, value); + + if (start_col + strlen(buffer) > kMaxWidth) { + start_col = 0; + printf("\n "); + } + fputs(buffer, stdout); + + return start_col + strlen(buffer); +} + +// PrintRegister64 does the same thing, but for 64-bit registers. +static int PrintRegister64(const char *name, u_int64_t value, int start_col) { + char buffer[64]; + snprintf(buffer, sizeof(buffer), " %5s = 0x%016" PRIx64 , name, value); + + if (start_col + strlen(buffer) > kMaxWidth) { + start_col = 0; + printf("\n "); + } + fputs(buffer, stdout); + + return start_col + strlen(buffer); +} + +// StripSeparator takes a string |original| and returns a copy +// of the string with all occurences of |kOutputSeparator| removed. +static string StripSeparator(const string &original) { + string result = original; + string::size_type position = 0; + while ((position = result.find(kOutputSeparator, position)) != string::npos) { + result.erase(position, 1); + } + position = 0; + while ((position = result.find('\n', position)) != string::npos) { + result.erase(position, 1); + } + return result; +} + +// PrintStack prints the call stack in |stack| to stdout, in a reasonably +// useful form. Module, function, and source file names are displayed if +// they are available. The code offset to the base code address of the +// source line, function, or module is printed, preferring them in that +// order. If no source line, function, or module information is available, +// an absolute code offset is printed. +// +// If |cpu| is a recognized CPU name, relevant register state for each stack +// frame printed is also output, if available. +static void PrintStack(const CallStack *stack, const string &cpu) { + int frame_count = stack->frames()->size(); + for (int frame_index = 0; frame_index < frame_count; ++frame_index) { + const StackFrame *frame = stack->frames()->at(frame_index); + printf("%2d ", frame_index); + + if (frame->module) { + printf("%s", PathnameStripper::File(frame->module->code_file()).c_str()); + if (!frame->function_name.empty()) { + printf("!%s", frame->function_name.c_str()); + if (!frame->source_file_name.empty()) { + string source_file = PathnameStripper::File(frame->source_file_name); + printf(" [%s : %d + 0x%" PRIx64 "]", + source_file.c_str(), + frame->source_line, + frame->instruction - frame->source_line_base); + } else { + printf(" + 0x%" PRIx64, frame->instruction - frame->function_base); + } + } else { + printf(" + 0x%" PRIx64, + frame->instruction - frame->module->base_address()); + } + } else { + printf("0x%" PRIx64, frame->instruction); + } + printf("\n "); + + int sequence = 0; + if (cpu == "x86") { + const StackFrameX86 *frame_x86 = + reinterpret_cast(frame); + + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP) + sequence = PrintRegister("eip", frame_x86->context.eip, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP) + sequence = PrintRegister("esp", frame_x86->context.esp, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP) + sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX) + sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI) + sequence = PrintRegister("esi", frame_x86->context.esi, sequence); + if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI) + sequence = PrintRegister("edi", frame_x86->context.edi, sequence); + if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) { + sequence = PrintRegister("eax", frame_x86->context.eax, sequence); + sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence); + sequence = PrintRegister("edx", frame_x86->context.edx, sequence); + sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); + } + } else if (cpu == "ppc") { + const StackFramePPC *frame_ppc = + reinterpret_cast(frame); + + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0) + sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence); + if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1) + sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence); + } else if (cpu == "amd64") { + const StackFrameAMD64 *frame_amd64 = + reinterpret_cast(frame); + + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX) + sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12) + sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13) + sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14) + sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15) + sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) + sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) + sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) + sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence); + } else if (cpu == "sparc") { + const StackFrameSPARC *frame_sparc = + reinterpret_cast(frame); + + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP) + sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP) + sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence); + if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) + sequence = PrintRegister("pc", frame_sparc->context.pc, sequence); + } else if (cpu == "arm") { + const StackFrameARM *frame_arm = + reinterpret_cast(frame); + + // General-purpose callee-saves registers. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4) + sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5) + sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6) + sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7) + sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8) + sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9) + sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10) + sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence); + + // Registers with a dedicated or conventional purpose. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP) + sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) + sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR) + sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC) + sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence); + } + printf("\n Found by: %s\n", frame->trust_description().c_str()); + } +} + +// PrintStackMachineReadable prints the call stack in |stack| to stdout, +// in the following machine readable pipe-delimited text format: +// thread number|frame number|module|function|source file|line|offset +// +// Module, function, source file, and source line may all be empty +// depending on availability. The code offset follows the same rules as +// PrintStack above. +static void PrintStackMachineReadable(int thread_num, const CallStack *stack) { + int frame_count = stack->frames()->size(); + for (int frame_index = 0; frame_index < frame_count; ++frame_index) { + const StackFrame *frame = stack->frames()->at(frame_index); + printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index, + kOutputSeparator); + + if (frame->module) { + assert(!frame->module->code_file().empty()); + printf("%s", StripSeparator(PathnameStripper::File( + frame->module->code_file())).c_str()); + if (!frame->function_name.empty()) { + printf("%c%s", kOutputSeparator, + StripSeparator(frame->function_name).c_str()); + if (!frame->source_file_name.empty()) { + printf("%c%s%c%d%c0x%" PRIx64, + kOutputSeparator, + StripSeparator(frame->source_file_name).c_str(), + kOutputSeparator, + frame->source_line, + kOutputSeparator, + frame->instruction - frame->source_line_base); + } else { + printf("%c%c%c0x%" PRIx64, + kOutputSeparator, // empty source file + kOutputSeparator, // empty source line + kOutputSeparator, + frame->instruction - frame->function_base); + } + } else { + printf("%c%c%c%c0x%" PRIx64, + kOutputSeparator, // empty function name + kOutputSeparator, // empty source file + kOutputSeparator, // empty source line + kOutputSeparator, + frame->instruction - frame->module->base_address()); + } + } else { + // the printf before this prints a trailing separator for module name + printf("%c%c%c%c0x%" PRIx64, + kOutputSeparator, // empty function name + kOutputSeparator, // empty source file + kOutputSeparator, // empty source line + kOutputSeparator, + frame->instruction); + } + printf("\n"); + } +} + +static void PrintModules(const CodeModules *modules) { + if (!modules) + return; + + printf("\n"); + printf("Loaded modules:\n"); + + u_int64_t main_address = 0; + const CodeModule *main_module = modules->GetMainModule(); + if (main_module) { + main_address = main_module->base_address(); + } + + unsigned int module_count = modules->module_count(); + for (unsigned int module_sequence = 0; + module_sequence < module_count; + ++module_sequence) { + const CodeModule *module = modules->GetModuleAtSequence(module_sequence); + u_int64_t base_address = module->base_address(); + printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s\n", + base_address, base_address + module->size() - 1, + PathnameStripper::File(module->code_file()).c_str(), + module->version().empty() ? "???" : module->version().c_str(), + main_module != NULL && base_address == main_address ? + " (main)" : ""); + } +} + +// PrintModulesMachineReadable outputs a list of loaded modules, +// one per line, in the following machine-readable pipe-delimited +// text format: +// Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}| +// {Base Address}|{Max Address}|{Main} +static void PrintModulesMachineReadable(const CodeModules *modules) { + if (!modules) + return; + + u_int64_t main_address = 0; + const CodeModule *main_module = modules->GetMainModule(); + if (main_module) { + main_address = main_module->base_address(); + } + + unsigned int module_count = modules->module_count(); + for (unsigned int module_sequence = 0; + module_sequence < module_count; + ++module_sequence) { + const CodeModule *module = modules->GetModuleAtSequence(module_sequence); + u_int64_t base_address = module->base_address(); + printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n", + kOutputSeparator, + StripSeparator(PathnameStripper::File(module->code_file())).c_str(), + kOutputSeparator, StripSeparator(module->version()).c_str(), + kOutputSeparator, + StripSeparator(PathnameStripper::File(module->debug_file())).c_str(), + kOutputSeparator, + StripSeparator(module->debug_identifier()).c_str(), + kOutputSeparator, base_address, + kOutputSeparator, base_address + module->size() - 1, + kOutputSeparator, + main_module != NULL && base_address == main_address ? 1 : 0); + } +} + +static void PrintProcessState(const ProcessState& process_state) { + // Print OS and CPU information. + string cpu = process_state.system_info()->cpu; + string cpu_info = process_state.system_info()->cpu_info; + printf("Operating system: %s\n", process_state.system_info()->os.c_str()); + printf(" %s\n", + process_state.system_info()->os_version.c_str()); + printf("CPU: %s\n", cpu.c_str()); + if (!cpu_info.empty()) { + // This field is optional. + printf(" %s\n", cpu_info.c_str()); + } + printf(" %d CPU%s\n", + process_state.system_info()->cpu_count, + process_state.system_info()->cpu_count != 1 ? "s" : ""); + printf("\n"); + + // Print crash information. + if (process_state.crashed()) { + printf("Crash reason: %s\n", process_state.crash_reason().c_str()); + printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address()); + } else { + printf("No crash\n"); + } + + string assertion = process_state.assertion(); + if (!assertion.empty()) { + printf("Assertion: %s\n", assertion.c_str()); + } + + // If the thread that requested the dump is known, print it first. + int requesting_thread = process_state.requesting_thread(); + if (requesting_thread != -1) { + printf("\n"); + printf("Thread %d (%s)\n", + requesting_thread, + process_state.crashed() ? "crashed" : + "requested dump, did not crash"); + PrintStack(process_state.threads()->at(requesting_thread), cpu); + } + + // Print all of the threads in the dump. + int thread_count = process_state.threads()->size(); + for (int thread_index = 0; thread_index < thread_count; ++thread_index) { + if (thread_index != requesting_thread) { + // Don't print the crash thread again, it was already printed. + printf("\n"); + printf("Thread %d\n", thread_index); + PrintStack(process_state.threads()->at(thread_index), cpu); + } + } + + PrintModules(process_state.modules()); +} + +static void PrintProcessStateMachineReadable(const ProcessState& process_state) +{ + // Print OS and CPU information. + // OS|{OS Name}|{OS Version} + // CPU|{CPU Name}|{CPU Info}|{Number of CPUs} + printf("OS%c%s%c%s\n", kOutputSeparator, + StripSeparator(process_state.system_info()->os).c_str(), + kOutputSeparator, + StripSeparator(process_state.system_info()->os_version).c_str()); + printf("CPU%c%s%c%s%c%d\n", kOutputSeparator, + StripSeparator(process_state.system_info()->cpu).c_str(), + kOutputSeparator, + // this may be empty + StripSeparator(process_state.system_info()->cpu_info).c_str(), + kOutputSeparator, + process_state.system_info()->cpu_count); + + int requesting_thread = process_state.requesting_thread(); + + // Print crash information. + // Crash|{Crash Reason}|{Crash Address}|{Crashed Thread} + printf("Crash%c", kOutputSeparator); + if (process_state.crashed()) { + printf("%s%c0x%" PRIx64 "%c", + StripSeparator(process_state.crash_reason()).c_str(), + kOutputSeparator, process_state.crash_address(), kOutputSeparator); + } else { + // print assertion info, if available, in place of crash reason, + // instead of the unhelpful "No crash" + string assertion = process_state.assertion(); + if (!assertion.empty()) { + printf("%s%c%c", StripSeparator(assertion).c_str(), + kOutputSeparator, kOutputSeparator); + } else { + printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + } + } + + if (requesting_thread != -1) { + printf("%d\n", requesting_thread); + } else { + printf("\n"); + } + + PrintModulesMachineReadable(process_state.modules()); + + // blank line to indicate start of threads + printf("\n"); + + // If the thread that requested the dump is known, print it first. + if (requesting_thread != -1) { + PrintStackMachineReadable(requesting_thread, + process_state.threads()->at(requesting_thread)); + } + + // Print all of the threads in the dump. + int thread_count = process_state.threads()->size(); + for (int thread_index = 0; thread_index < thread_count; ++thread_index) { + if (thread_index != requesting_thread) { + // Don't print the crash thread again, it was already printed. + PrintStackMachineReadable(thread_index, + process_state.threads()->at(thread_index)); + } + } +} + +// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if +// non-empty, is the base directory of a symbol storage area, laid out in +// the format required by SimpleSymbolSupplier. If such a storage area +// is specified, it is made available for use by the MinidumpProcessor. +// +// Returns the value of MinidumpProcessor::Process. If processing succeeds, +// prints identifying OS and CPU information from the minidump, crash +// information if the minidump was produced as a result of a crash, and +// call stacks for each thread contained in the minidump. All information +// is printed to stdout. +static bool PrintMinidumpProcess(const string &minidump_file, + const vector &symbol_paths, + bool machine_readable) { + scoped_ptr symbol_supplier; + if (!symbol_paths.empty()) { + // TODO(mmentovai): check existence of symbol_path if specified? + symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths)); + } + + BasicSourceLineResolver resolver; + MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver); + + // Process the minidump. + ProcessState process_state; + if (minidump_processor.Process(minidump_file, &process_state) != + google_breakpad::PROCESS_OK) { + BPLOG(ERROR) << "MinidumpProcessor::Process failed"; + return false; + } + + if (machine_readable) { + PrintProcessStateMachineReadable(process_state); + } else { + PrintProcessState(process_state); + } + + return true; +} + +} // namespace + +static void usage(const char *program_name) { + fprintf(stderr, "usage: %s [-m] [symbol-path ...]\n" + " -m : Output in machine-readable format\n", + program_name); +} + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + if (argc < 2) { + usage(argv[0]); + return 1; + } + + const char *minidump_file; + bool machine_readable; + int symbol_path_arg; + + if (strcmp(argv[1], "-m") == 0) { + if (argc < 3) { + usage(argv[0]); + return 1; + } + + machine_readable = true; + minidump_file = argv[2]; + symbol_path_arg = 3; + } else { + machine_readable = false; + minidump_file = argv[1]; + symbol_path_arg = 2; + } + + // extra arguments are symbol paths + std::vector symbol_paths; + if (argc > symbol_path_arg) { + for (int argi = symbol_path_arg; argi < argc; ++argi) + symbol_paths.push_back(argv[argi]); + } + + return PrintMinidumpProcess(minidump_file, + symbol_paths, + machine_readable) ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_machine_readable_test b/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_machine_readable_test new file mode 100644 index 0000000..2aadb24 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_machine_readable_test @@ -0,0 +1,37 @@ +#!/bin/sh + +# Copyright (c) 2007, Google Inc. +# 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 Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER 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. + +testdata_dir=$srcdir/src/processor/testdata +./src/processor/minidump_stackwalk -m $testdata_dir/minidump2.dmp \ + $testdata_dir/symbols | \ + tr -d '\015' | \ + diff -u $testdata_dir/minidump2.stackwalk.machine_readable.out - +exit $? diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_test b/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_test new file mode 100644 index 0000000..f979027 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_stackwalk_test @@ -0,0 +1,37 @@ +#!/bin/sh + +# Copyright (c) 2006, Google Inc. +# 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 Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER 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. + +testdata_dir=$srcdir/src/processor/testdata +./src/processor/minidump_stackwalk $testdata_dir/minidump2.dmp \ + $testdata_dir/symbols | \ + tr -d '\015' | \ + diff -u $testdata_dir/minidump2.stackwalk.out - +exit $? diff --git a/src/lib/crashdump/gbreakpad/processor/minidump_unittest.cc b/src/lib/crashdump/gbreakpad/processor/minidump_unittest.cc new file mode 100644 index 0000000..d1c91a8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/minidump_unittest.cc @@ -0,0 +1,907 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Unit test for Minidump. Uses a pre-generated minidump and +// verifies that certain streams are correct. + +#include +#include +#include +#include +#include +#include +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/minidump.h" +#include "processor/logging.h" +#include "processor/synth_minidump.h" + +namespace { + +using google_breakpad::Minidump; +using google_breakpad::MinidumpContext; +using google_breakpad::MinidumpException; +using google_breakpad::MinidumpMemoryInfo; +using google_breakpad::MinidumpMemoryInfoList; +using google_breakpad::MinidumpMemoryList; +using google_breakpad::MinidumpMemoryRegion; +using google_breakpad::MinidumpModule; +using google_breakpad::MinidumpModuleList; +using google_breakpad::MinidumpSystemInfo; +using google_breakpad::MinidumpThread; +using google_breakpad::MinidumpThreadList; +using google_breakpad::SynthMinidump::Context; +using google_breakpad::SynthMinidump::Dump; +using google_breakpad::SynthMinidump::Exception; +using google_breakpad::SynthMinidump::Memory; +using google_breakpad::SynthMinidump::Module; +using google_breakpad::SynthMinidump::Stream; +using google_breakpad::SynthMinidump::String; +using google_breakpad::SynthMinidump::SystemInfo; +using google_breakpad::SynthMinidump::Thread; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using std::ifstream; +using std::istringstream; +using std::string; +using std::vector; +using ::testing::Return; + +class MinidumpTest : public ::testing::Test { +public: + void SetUp() { + minidump_file_ = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/minidump2.dmp"; + } + string minidump_file_; +}; + +TEST_F(MinidumpTest, TestMinidumpFromFile) { + Minidump minidump(minidump_file_); + ASSERT_EQ(minidump.path(), minidump_file_); + ASSERT_TRUE(minidump.Read()); + const MDRawHeader* header = minidump.header(); + ASSERT_NE(header, (MDRawHeader*)NULL); + ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE)); + //TODO: add more checks here +} + +TEST_F(MinidumpTest, TestMinidumpFromStream) { + // read minidump contents into memory, construct a stringstream around them + ifstream file_stream(minidump_file_.c_str(), std::ios::in); + ASSERT_TRUE(file_stream.good()); + vector bytes; + file_stream.seekg(0, std::ios_base::end); + ASSERT_TRUE(file_stream.good()); + bytes.resize(file_stream.tellg()); + file_stream.seekg(0, std::ios_base::beg); + ASSERT_TRUE(file_stream.good()); + file_stream.read(&bytes[0], bytes.size()); + ASSERT_TRUE(file_stream.good()); + string str(&bytes[0], bytes.size()); + istringstream stream(str); + ASSERT_TRUE(stream.good()); + + // now read minidump from stringstream + Minidump minidump(stream); + ASSERT_EQ(minidump.path(), ""); + ASSERT_TRUE(minidump.Read()); + const MDRawHeader* header = minidump.header(); + ASSERT_NE(header, (MDRawHeader*)NULL); + ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE)); + //TODO: add more checks here +} + +TEST(Dump, ReadBackEmpty) { + Dump dump(0); + dump.Finish(); + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream stream(contents); + Minidump minidump(stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(0U, minidump.GetDirectoryEntryCount()); +} + +TEST(Dump, ReadBackEmptyBigEndian) { + Dump big_minidump(0, kBigEndian); + big_minidump.Finish(); + string contents; + ASSERT_TRUE(big_minidump.GetContents(&contents)); + istringstream stream(contents); + Minidump minidump(stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(0U, minidump.GetDirectoryEntryCount()); +} + +TEST(Dump, OneStream) { + Dump dump(0, kBigEndian); + Stream stream(dump, 0xfbb7fa2bU); + stream.Append("stream contents"); + dump.Add(&stream); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ(0xfbb7fa2bU, dir->stream_type); + + u_int32_t stream_length; + ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length)); + ASSERT_EQ(15U, stream_length); + char stream_contents[15]; + ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents))); + EXPECT_EQ(string("stream contents"), + string(stream_contents, sizeof(stream_contents))); + + EXPECT_FALSE(minidump.GetThreadList()); + EXPECT_FALSE(minidump.GetModuleList()); + EXPECT_FALSE(minidump.GetMemoryList()); + EXPECT_FALSE(minidump.GetException()); + EXPECT_FALSE(minidump.GetAssertion()); + EXPECT_FALSE(minidump.GetSystemInfo()); + EXPECT_FALSE(minidump.GetMiscInfo()); + EXPECT_FALSE(minidump.GetBreakpadInfo()); +} + +TEST(Dump, OneMemory) { + Dump dump(0, kBigEndian); + Memory memory(dump, 0x309d68010bd21b2cULL); + memory.Append("memory contents"); + dump.Add(&memory); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_MEMORY_LIST_STREAM, dir->stream_type); + + MinidumpMemoryList *memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(memory_list != NULL); + ASSERT_EQ(1U, memory_list->region_count()); + + MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0); + ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase()); + ASSERT_EQ(15U, region1->GetSize()); + const u_int8_t *region1_bytes = region1->GetMemory(); + ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0); +} + +// One thread --- and its requisite entourage. +TEST(Dump, OneThread) { + Dump dump(0, kLittleEndian); + Memory stack(dump, 0x2326a0fa); + stack.Append("stack for thread"); + + MDRawContextX86 raw_context; + raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL; + raw_context.edi = 0x3ecba80d; + raw_context.esi = 0x382583b9; + raw_context.ebx = 0x7fccc03f; + raw_context.edx = 0xf62f8ec2; + raw_context.ecx = 0x46a6a6a8; + raw_context.eax = 0x6a5025e2; + raw_context.ebp = 0xd9fabb4a; + raw_context.eip = 0x6913f540; + raw_context.cs = 0xbffe6eda; + raw_context.eflags = 0xb2ce1e2d; + raw_context.esp = 0x659caaa4; + raw_context.ss = 0x2e951ef7; + Context context(dump, raw_context); + + Thread thread(dump, 0xa898f11b, stack, context, + 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL); + + dump.Add(&stack); + dump.Add(&context); + dump.Add(&thread); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(2U, minidump.GetDirectoryEntryCount()); + + MinidumpMemoryList *md_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(md_memory_list != NULL); + ASSERT_EQ(1U, md_memory_list->region_count()); + + MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0); + ASSERT_EQ(0x2326a0faU, md_region->GetBase()); + ASSERT_EQ(16U, md_region->GetSize()); + const u_int8_t *region_bytes = md_region->GetMemory(); + ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0); + + MinidumpThreadList *thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list != NULL); + ASSERT_EQ(1U, thread_list->thread_count()); + + MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(md_thread != NULL); + u_int32_t thread_id; + ASSERT_TRUE(md_thread->GetThreadID(&thread_id)); + ASSERT_EQ(0xa898f11bU, thread_id); + MinidumpMemoryRegion *md_stack = md_thread->GetMemory(); + ASSERT_TRUE(md_stack != NULL); + ASSERT_EQ(0x2326a0faU, md_stack->GetBase()); + ASSERT_EQ(16U, md_stack->GetSize()); + const u_int8_t *md_stack_bytes = md_stack->GetMemory(); + ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0); + + MinidumpContext *md_context = md_thread->GetContext(); + ASSERT_TRUE(md_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU()); + const MDRawContextX86 *md_raw_context = md_context->GetContextX86(); + ASSERT_TRUE(md_raw_context != NULL); + ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL), + (md_raw_context->context_flags + & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL))); + EXPECT_EQ(0x3ecba80dU, raw_context.edi); + EXPECT_EQ(0x382583b9U, raw_context.esi); + EXPECT_EQ(0x7fccc03fU, raw_context.ebx); + EXPECT_EQ(0xf62f8ec2U, raw_context.edx); + EXPECT_EQ(0x46a6a6a8U, raw_context.ecx); + EXPECT_EQ(0x6a5025e2U, raw_context.eax); + EXPECT_EQ(0xd9fabb4aU, raw_context.ebp); + EXPECT_EQ(0x6913f540U, raw_context.eip); + EXPECT_EQ(0xbffe6edaU, raw_context.cs); + EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags); + EXPECT_EQ(0x659caaa4U, raw_context.esp); + EXPECT_EQ(0x2e951ef7U, raw_context.ss); +} + +TEST(Dump, OneModule) { + static const MDVSFixedFileInfo fixed_file_info = { + 0xb2fba33a, // signature + 0x33d7a728, // struct_version + 0x31afcb20, // file_version_hi + 0xe51cdab1, // file_version_lo + 0xd1ea6907, // product_version_hi + 0x03032857, // product_version_lo + 0x11bf71d7, // file_flags_mask + 0x5fb8cdbf, // file_flags + 0xe45d0d5d, // file_os + 0x107d9562, // file_type + 0x5a8844d4, // file_subtype + 0xa8d30b20, // file_date_hi + 0x651c3e4e // file_date_lo + }; + + Dump dump(0, kBigEndian); + String module_name(dump, "single module"); + Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd, + module_name, + 0xb1054d2a, + 0x34571371, + fixed_file_info, // from synth_minidump_unittest_data.h + NULL, NULL); + + dump.Add(&module); + dump.Add(&module_name); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_MODULE_LIST_STREAM, dir->stream_type); + + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + ASSERT_EQ(1U, md_module_list->module_count()); + + const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0); + ASSERT_TRUE(md_module != NULL); + ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address()); + ASSERT_EQ(0xada542bd, md_module->size()); + ASSERT_EQ("single module", md_module->code_file()); + + const MDRawModule *md_raw_module = md_module->module(); + ASSERT_TRUE(md_raw_module != NULL); + ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp); + ASSERT_EQ(0x34571371U, md_raw_module->checksum); + ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info, + sizeof(fixed_file_info)) == 0); +} + +TEST(Dump, OneSystemInfo) { + Dump dump(0, kLittleEndian); + String csd_version(dump, "Petulant Pierogi"); + SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version); + + dump.Add(&system_info); + dump.Add(&csd_version); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type); + + MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(md_system_info != NULL); + ASSERT_EQ("windows", md_system_info->GetOS()); + ASSERT_EQ("x86", md_system_info->GetCPU()); + ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion()); + ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor()); +} + +TEST(Dump, BigDump) { + Dump dump(0, kLittleEndian); + + // A SystemInfo stream. + String csd_version(dump, "Munificent Macaque"); + SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version); + dump.Add(&csd_version); + dump.Add(&system_info); + + // Five threads! + Memory stack0(dump, 0x70b9ebfc); + stack0.Append("stack for thread zero"); + MDRawContextX86 raw_context0; + raw_context0.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context0.eip = 0xaf0709e4; + Context context0(dump, raw_context0); + Thread thread0(dump, 0xbbef4432, stack0, context0, + 0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL); + dump.Add(&stack0); + dump.Add(&context0); + dump.Add(&thread0); + + Memory stack1(dump, 0xf988cc45); + stack1.Append("stack for thread one"); + MDRawContextX86 raw_context1; + raw_context1.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context1.eip = 0xe4f56f81; + Context context1(dump, raw_context1); + Thread thread1(dump, 0x657c3f58, stack1, context1, + 0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL); + dump.Add(&stack1); + dump.Add(&context1); + dump.Add(&thread1); + + Memory stack2(dump, 0xc8a92e7c); + stack2.Append("stack for thread two"); + MDRawContextX86 raw_context2; + raw_context2.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context2.eip = 0xb336a438; + Context context2(dump, raw_context2); + Thread thread2(dump, 0xdf4b8a71, stack2, context2, + 0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL); + dump.Add(&stack2); + dump.Add(&context2); + dump.Add(&thread2); + + Memory stack3(dump, 0x36d08e08); + stack3.Append("stack for thread three"); + MDRawContextX86 raw_context3; + raw_context3.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context3.eip = 0xdf99a60c; + Context context3(dump, raw_context3); + Thread thread3(dump, 0x86e6c341, stack3, context3, + 0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL); + dump.Add(&stack3); + dump.Add(&context3); + dump.Add(&thread3); + + Memory stack4(dump, 0x1e0ab4fa); + stack4.Append("stack for thread four"); + MDRawContextX86 raw_context4; + raw_context4.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context4.eip = 0xaa646267; + Context context4(dump, raw_context4); + Thread thread4(dump, 0x261a28d4, stack4, context4, + 0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL); + dump.Add(&stack4); + dump.Add(&context4); + dump.Add(&thread4); + + // Three modules! + String module1_name(dump, "module one"); + Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name); + dump.Add(&module1_name); + dump.Add(&module1); + + String module2_name(dump, "module two"); + Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name); + dump.Add(&module2_name); + dump.Add(&module2); + + String module3_name(dump, "module three"); + Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name); + dump.Add(&module3_name); + dump.Add(&module3); + + // Add one more memory region, on top of the five stacks. + Memory memory5(dump, 0x61979e828040e564ULL); + memory5.Append("contents of memory 5"); + dump.Add(&memory5); + + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(4U, minidump.GetDirectoryEntryCount()); + + // Check the threads. + MinidumpThreadList *thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list != NULL); + ASSERT_EQ(5U, thread_list->thread_count()); + u_int32_t thread_id; + ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id)); + ASSERT_EQ(0xbbef4432U, thread_id); + ASSERT_EQ(0x70b9ebfcU, + thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase()); + ASSERT_EQ(0xaf0709e4U, + thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id)); + ASSERT_EQ(0x657c3f58U, thread_id); + ASSERT_EQ(0xf988cc45U, + thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase()); + ASSERT_EQ(0xe4f56f81U, + thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id)); + ASSERT_EQ(0xdf4b8a71U, thread_id); + ASSERT_EQ(0xc8a92e7cU, + thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase()); + ASSERT_EQ(0xb336a438U, + thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id)); + ASSERT_EQ(0x86e6c341U, thread_id); + ASSERT_EQ(0x36d08e08U, + thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase()); + ASSERT_EQ(0xdf99a60cU, + thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id)); + ASSERT_EQ(0x261a28d4U, thread_id); + ASSERT_EQ(0x1e0ab4faU, + thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase()); + ASSERT_EQ(0xaa646267U, + thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86() + ->eip); + + // Check the modules. + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + ASSERT_EQ(3U, md_module_list->module_count()); + EXPECT_EQ(0xeb77da57b5d4cbdaULL, + md_module_list->GetModuleAtIndex(0)->base_address()); + EXPECT_EQ(0x8675884adfe5ac90ULL, + md_module_list->GetModuleAtIndex(1)->base_address()); + EXPECT_EQ(0x95fc1544da321b6cULL, + md_module_list->GetModuleAtIndex(2)->base_address()); +} + +TEST(Dump, OneMemoryInfo) { + Dump dump(0, kBigEndian); + Stream stream(dump, MD_MEMORY_INFO_LIST_STREAM); + + // Add the MDRawMemoryInfoList header. + const u_int64_t kNumberOfEntries = 1; + stream.D32(sizeof(MDRawMemoryInfoList)) // size_of_header + .D32(sizeof(MDRawMemoryInfo)) // size_of_entry + .D64(kNumberOfEntries); // number_of_entries + + + // Now add a MDRawMemoryInfo entry. + const u_int64_t kBaseAddress = 0x1000; + const u_int64_t kRegionSize = 0x2000; + stream.D64(kBaseAddress) // base_address + .D64(kBaseAddress) // allocation_base + .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // allocation_protection + .D32(0) // __alignment1 + .D64(kRegionSize) // region_size + .D32(MD_MEMORY_STATE_COMMIT) // state + .D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // protection + .D32(MD_MEMORY_TYPE_PRIVATE) // type + .D32(0); // __alignment2 + + dump.Add(&stream); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_MEMORY_INFO_LIST_STREAM, dir->stream_type); + + MinidumpMemoryInfoList *info_list = minidump.GetMemoryInfoList(); + ASSERT_TRUE(info_list != NULL); + ASSERT_EQ(1U, info_list->info_count()); + + const MinidumpMemoryInfo *info1 = info_list->GetMemoryInfoAtIndex(0); + ASSERT_EQ(kBaseAddress, info1->GetBase()); + ASSERT_EQ(kRegionSize, info1->GetSize()); + ASSERT_TRUE(info1->IsExecutable()); + ASSERT_TRUE(info1->IsWritable()); + + // Should get back the same memory region here. + const MinidumpMemoryInfo *info2 = + info_list->GetMemoryInfoForAddress(kBaseAddress + kRegionSize / 2); + ASSERT_EQ(kBaseAddress, info2->GetBase()); + ASSERT_EQ(kRegionSize, info2->GetSize()); +} + +TEST(Dump, OneExceptionX86) { + Dump dump(0, kLittleEndian); + + MDRawContextX86 raw_context; + raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL; + raw_context.edi = 0x3ecba80d; + raw_context.esi = 0x382583b9; + raw_context.ebx = 0x7fccc03f; + raw_context.edx = 0xf62f8ec2; + raw_context.ecx = 0x46a6a6a8; + raw_context.eax = 0x6a5025e2; + raw_context.ebp = 0xd9fabb4a; + raw_context.eip = 0x6913f540; + raw_context.cs = 0xbffe6eda; + raw_context.eflags = 0xb2ce1e2d; + raw_context.esp = 0x659caaa4; + raw_context.ss = 0x2e951ef7; + Context context(dump, raw_context); + + Exception exception(dump, context, + 0x1234abcd, // thread id + 0xdcba4321, // exception code + 0xf0e0d0c0, // exception flags + 0x0919a9b9c9d9e9f9ULL); // exception address + + dump.Add(&context); + dump.Add(&exception); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + MinidumpException *md_exception = minidump.GetException(); + ASSERT_TRUE(md_exception != NULL); + + u_int32_t thread_id; + ASSERT_TRUE(md_exception->GetThreadID(&thread_id)); + ASSERT_EQ(0x1234abcd, thread_id); + + const MDRawExceptionStream* raw_exception = md_exception->exception(); + ASSERT_TRUE(raw_exception != NULL); + EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code); + EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags); + EXPECT_EQ(0x0919a9b9c9d9e9f9ULL, + raw_exception->exception_record.exception_address); + + MinidumpContext *md_context = md_exception->GetContext(); + ASSERT_TRUE(md_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU()); + const MDRawContextX86 *md_raw_context = md_context->GetContextX86(); + ASSERT_TRUE(md_raw_context != NULL); + ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL), + (md_raw_context->context_flags + & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL))); + EXPECT_EQ(0x3ecba80dU, raw_context.edi); + EXPECT_EQ(0x382583b9U, raw_context.esi); + EXPECT_EQ(0x7fccc03fU, raw_context.ebx); + EXPECT_EQ(0xf62f8ec2U, raw_context.edx); + EXPECT_EQ(0x46a6a6a8U, raw_context.ecx); + EXPECT_EQ(0x6a5025e2U, raw_context.eax); + EXPECT_EQ(0xd9fabb4aU, raw_context.ebp); + EXPECT_EQ(0x6913f540U, raw_context.eip); + EXPECT_EQ(0xbffe6edaU, raw_context.cs); + EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags); + EXPECT_EQ(0x659caaa4U, raw_context.esp); + EXPECT_EQ(0x2e951ef7U, raw_context.ss); +} + +TEST(Dump, OneExceptionX86XState) { + Dump dump(0, kLittleEndian); + + MDRawContextX86 raw_context; + raw_context.context_flags = MD_CONTEXT_X86_INTEGER | + MD_CONTEXT_X86_CONTROL | MD_CONTEXT_X86_XSTATE; + raw_context.edi = 0x3ecba80d; + raw_context.esi = 0x382583b9; + raw_context.ebx = 0x7fccc03f; + raw_context.edx = 0xf62f8ec2; + raw_context.ecx = 0x46a6a6a8; + raw_context.eax = 0x6a5025e2; + raw_context.ebp = 0xd9fabb4a; + raw_context.eip = 0x6913f540; + raw_context.cs = 0xbffe6eda; + raw_context.eflags = 0xb2ce1e2d; + raw_context.esp = 0x659caaa4; + raw_context.ss = 0x2e951ef7; + Context context(dump, raw_context); + + Exception exception(dump, context, + 0x1234abcd, // thread id + 0xdcba4321, // exception code + 0xf0e0d0c0, // exception flags + 0x0919a9b9c9d9e9f9ULL); // exception address + + dump.Add(&context); + dump.Add(&exception); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + MinidumpException *md_exception = minidump.GetException(); + ASSERT_TRUE(md_exception != NULL); + + u_int32_t thread_id; + ASSERT_TRUE(md_exception->GetThreadID(&thread_id)); + ASSERT_EQ(0x1234abcd, thread_id); + + const MDRawExceptionStream* raw_exception = md_exception->exception(); + ASSERT_TRUE(raw_exception != NULL); + EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code); + EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags); + EXPECT_EQ(0x0919a9b9c9d9e9f9ULL, + raw_exception->exception_record.exception_address); + + MinidumpContext *md_context = md_exception->GetContext(); + ASSERT_TRUE(md_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU()); + const MDRawContextX86 *md_raw_context = md_context->GetContextX86(); + ASSERT_TRUE(md_raw_context != NULL); + ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL), + (md_raw_context->context_flags + & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL))); + EXPECT_EQ(0x3ecba80dU, raw_context.edi); + EXPECT_EQ(0x382583b9U, raw_context.esi); + EXPECT_EQ(0x7fccc03fU, raw_context.ebx); + EXPECT_EQ(0xf62f8ec2U, raw_context.edx); + EXPECT_EQ(0x46a6a6a8U, raw_context.ecx); + EXPECT_EQ(0x6a5025e2U, raw_context.eax); + EXPECT_EQ(0xd9fabb4aU, raw_context.ebp); + EXPECT_EQ(0x6913f540U, raw_context.eip); + EXPECT_EQ(0xbffe6edaU, raw_context.cs); + EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags); + EXPECT_EQ(0x659caaa4U, raw_context.esp); + EXPECT_EQ(0x2e951ef7U, raw_context.ss); +} + +TEST(Dump, OneExceptionARM) { + Dump dump(0, kLittleEndian); + + MDRawContextARM raw_context; + raw_context.context_flags = MD_CONTEXT_ARM_INTEGER; + raw_context.iregs[0] = 0x3ecba80d; + raw_context.iregs[1] = 0x382583b9; + raw_context.iregs[2] = 0x7fccc03f; + raw_context.iregs[3] = 0xf62f8ec2; + raw_context.iregs[4] = 0x46a6a6a8; + raw_context.iregs[5] = 0x6a5025e2; + raw_context.iregs[6] = 0xd9fabb4a; + raw_context.iregs[7] = 0x6913f540; + raw_context.iregs[8] = 0xbffe6eda; + raw_context.iregs[9] = 0xb2ce1e2d; + raw_context.iregs[10] = 0x659caaa4; + raw_context.iregs[11] = 0xf0e0d0c0; + raw_context.iregs[12] = 0xa9b8c7d6; + raw_context.iregs[13] = 0x12345678; + raw_context.iregs[14] = 0xabcd1234; + raw_context.iregs[15] = 0x10203040; + raw_context.cpsr = 0x2e951ef7; + Context context(dump, raw_context); + + Exception exception(dump, context, + 0x1234abcd, // thread id + 0xdcba4321, // exception code + 0xf0e0d0c0, // exception flags + 0x0919a9b9c9d9e9f9ULL); // exception address + + dump.Add(&context); + dump.Add(&exception); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + MinidumpException *md_exception = minidump.GetException(); + ASSERT_TRUE(md_exception != NULL); + + u_int32_t thread_id; + ASSERT_TRUE(md_exception->GetThreadID(&thread_id)); + ASSERT_EQ(0x1234abcd, thread_id); + + const MDRawExceptionStream* raw_exception = md_exception->exception(); + ASSERT_TRUE(raw_exception != NULL); + EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code); + EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags); + EXPECT_EQ(0x0919a9b9c9d9e9f9ULL, + raw_exception->exception_record.exception_address); + + MinidumpContext *md_context = md_exception->GetContext(); + ASSERT_TRUE(md_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM, md_context->GetContextCPU()); + const MDRawContextARM *md_raw_context = md_context->GetContextARM(); + ASSERT_TRUE(md_raw_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM_INTEGER, + (md_raw_context->context_flags + & MD_CONTEXT_ARM_INTEGER)); + EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]); + EXPECT_EQ(0x382583b9U, raw_context.iregs[1]); + EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]); + EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]); + EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]); + EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]); + EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]); + EXPECT_EQ(0x6913f540U, raw_context.iregs[7]); + EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]); + EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]); + EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]); + EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]); + EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]); + EXPECT_EQ(0x12345678U, raw_context.iregs[13]); + EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]); + EXPECT_EQ(0x10203040U, raw_context.iregs[15]); + EXPECT_EQ(0x2e951ef7U, raw_context.cpsr); +} + +TEST(Dump, OneExceptionARMOldFlags) { + Dump dump(0, kLittleEndian); + + MDRawContextARM raw_context; + // MD_CONTEXT_ARM_INTEGER, but with _OLD + raw_context.context_flags = MD_CONTEXT_ARM_OLD | 0x00000002; + raw_context.iregs[0] = 0x3ecba80d; + raw_context.iregs[1] = 0x382583b9; + raw_context.iregs[2] = 0x7fccc03f; + raw_context.iregs[3] = 0xf62f8ec2; + raw_context.iregs[4] = 0x46a6a6a8; + raw_context.iregs[5] = 0x6a5025e2; + raw_context.iregs[6] = 0xd9fabb4a; + raw_context.iregs[7] = 0x6913f540; + raw_context.iregs[8] = 0xbffe6eda; + raw_context.iregs[9] = 0xb2ce1e2d; + raw_context.iregs[10] = 0x659caaa4; + raw_context.iregs[11] = 0xf0e0d0c0; + raw_context.iregs[12] = 0xa9b8c7d6; + raw_context.iregs[13] = 0x12345678; + raw_context.iregs[14] = 0xabcd1234; + raw_context.iregs[15] = 0x10203040; + raw_context.cpsr = 0x2e951ef7; + Context context(dump, raw_context); + + Exception exception(dump, context, + 0x1234abcd, // thread id + 0xdcba4321, // exception code + 0xf0e0d0c0, // exception flags + 0x0919a9b9c9d9e9f9ULL); // exception address + + dump.Add(&context); + dump.Add(&exception); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + MinidumpException *md_exception = minidump.GetException(); + ASSERT_TRUE(md_exception != NULL); + + u_int32_t thread_id; + ASSERT_TRUE(md_exception->GetThreadID(&thread_id)); + ASSERT_EQ(0x1234abcd, thread_id); + + const MDRawExceptionStream* raw_exception = md_exception->exception(); + ASSERT_TRUE(raw_exception != NULL); + EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code); + EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags); + EXPECT_EQ(0x0919a9b9c9d9e9f9ULL, + raw_exception->exception_record.exception_address); + + MinidumpContext *md_context = md_exception->GetContext(); + ASSERT_TRUE(md_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM, md_context->GetContextCPU()); + const MDRawContextARM *md_raw_context = md_context->GetContextARM(); + ASSERT_TRUE(md_raw_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM_INTEGER, + (md_raw_context->context_flags + & MD_CONTEXT_ARM_INTEGER)); + EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]); + EXPECT_EQ(0x382583b9U, raw_context.iregs[1]); + EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]); + EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]); + EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]); + EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]); + EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]); + EXPECT_EQ(0x6913f540U, raw_context.iregs[7]); + EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]); + EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]); + EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]); + EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]); + EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]); + EXPECT_EQ(0x12345678U, raw_context.iregs[13]); + EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]); + EXPECT_EQ(0x10203040U, raw_context.iregs[15]); + EXPECT_EQ(0x2e951ef7U, raw_context.cpsr); +} + +} // namespace diff --git a/src/lib/crashdump/gbreakpad/processor/module_comparer.cc b/src/lib/crashdump/gbreakpad/processor/module_comparer.cc new file mode 100644 index 0000000..837d854 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/module_comparer.cc @@ -0,0 +1,297 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// module_comparer.cc: ModuleComparer implementation. +// See module_comparer.h for documentation. +// +// Author: lambxsy@google.com (Siyang Xie) + +#include "processor/module_comparer.h" + +#include +#include + +#include "processor/basic_code_module.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + +#define ASSERT_TRUE(condition) \ + if (!(condition)) { \ + BPLOG(ERROR) << "FAIL: " << #condition << " @ " \ + << __FILE__ << ":" << __LINE__; \ + return false; \ + } + +#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition)) + +namespace google_breakpad { + +bool ModuleComparer::Compare(const string &symbol_data) { + scoped_ptr basic_module(new BasicModule("test_module")); + scoped_ptr fast_module(new FastModule("test_module")); + + // Load symbol data into basic_module + scoped_array buffer(new char[symbol_data.size() + 1]); + strcpy(buffer.get(), symbol_data.c_str()); + ASSERT_TRUE(basic_module->LoadMapFromMemory(buffer.get())); + buffer.reset(); + + // Serialize BasicSourceLineResolver::Module. + unsigned int serialized_size = 0; + scoped_array serialized_data( + serializer_.Serialize(*(basic_module.get()), &serialized_size)); + ASSERT_TRUE(serialized_data.get()); + BPLOG(INFO) << "Serialized size = " << serialized_size << " Bytes"; + + // Load FastSourceLineResolver::Module using serialized data. + ASSERT_TRUE(fast_module->LoadMapFromMemory(serialized_data.get())); + + // Compare FastSourceLineResolver::Module with + // BasicSourceLineResolver::Module. + ASSERT_TRUE(CompareModule(basic_module.get(), fast_module.get())); + + return true; +} + +// Traversal the content of module and do comparison +bool ModuleComparer::CompareModule(const BasicModule *basic_module, + const FastModule *fast_module) const { + // Compare name_. + ASSERT_TRUE(basic_module->name_ == fast_module->name_); + + // Compare files_: + { + BasicModule::FileMap::const_iterator iter1 = basic_module->files_.begin(); + FastModule::FileMap::iterator iter2 = fast_module->files_.begin(); + while (iter1 != basic_module->files_.end() + && iter2 != fast_module->files_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + string tmp(iter2.GetValuePtr()); + ASSERT_TRUE(iter1->second == tmp); + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_module->files_.end()); + ASSERT_TRUE(iter2 == fast_module->files_.end()); + } + + // Compare functions_: + { + RangeMap >::MapConstIterator iter1; + StaticRangeMap::MapConstIterator iter2; + iter1 = basic_module->functions_.map_.begin(); + iter2 = fast_module->functions_.map_.begin(); + while (iter1 != basic_module->functions_.map_.end() + && iter2 != fast_module->functions_.map_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base()); + ASSERT_TRUE(CompareFunction( + iter1->second.entry().get(), iter2.GetValuePtr()->entryptr())); + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_module->functions_.map_.end()); + ASSERT_TRUE(iter2 == fast_module->functions_.map_.end()); + } + + // Compare public_symbols_: + { + AddressMap >::MapConstIterator iter1; + StaticAddressMap::MapConstIterator iter2; + iter1 = basic_module->public_symbols_.map_.begin(); + iter2 = fast_module->public_symbols_.map_.begin(); + while (iter1 != basic_module->public_symbols_.map_.end() + && iter2 != fast_module->public_symbols_.map_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + ASSERT_TRUE(ComparePubSymbol( + iter1->second.get(), iter2.GetValuePtr())); + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_module->public_symbols_.map_.end()); + ASSERT_TRUE(iter2 == fast_module->public_symbols_.map_.end()); + } + + // Compare windows_frame_info_[]: + for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i) { + ASSERT_TRUE(CompareCRM(&(basic_module->windows_frame_info_[i]), + &(fast_module->windows_frame_info_[i]))); + } + + // Compare cfi_initial_rules_: + { + RangeMap::MapConstIterator iter1; + StaticRangeMap::MapConstIterator iter2; + iter1 = basic_module->cfi_initial_rules_.map_.begin(); + iter2 = fast_module->cfi_initial_rules_.map_.begin(); + while (iter1 != basic_module->cfi_initial_rules_.map_.end() + && iter2 != fast_module->cfi_initial_rules_.map_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base()); + string tmp(iter2.GetValuePtr()->entryptr()); + ASSERT_TRUE(iter1->second.entry() == tmp); + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_module->cfi_initial_rules_.map_.end()); + ASSERT_TRUE(iter2 == fast_module->cfi_initial_rules_.map_.end()); + } + + // Compare cfi_delta_rules_: + { + map::const_iterator iter1; + StaticMap::iterator iter2; + iter1 = basic_module->cfi_delta_rules_.begin(); + iter2 = fast_module->cfi_delta_rules_.begin(); + while (iter1 != basic_module->cfi_delta_rules_.end() + && iter2 != fast_module->cfi_delta_rules_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + string tmp(iter2.GetValuePtr()); + ASSERT_TRUE(iter1->second == tmp); + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_module->cfi_delta_rules_.end()); + ASSERT_TRUE(iter2 == fast_module->cfi_delta_rules_.end()); + } + + return true; +} + +bool ModuleComparer::CompareFunction(const BasicFunc *basic_func, + const FastFunc *fast_func_raw) const { + FastFunc* fast_func = new FastFunc(); + fast_func->CopyFrom(fast_func_raw); + ASSERT_TRUE(basic_func->name == fast_func->name); + ASSERT_TRUE(basic_func->address == fast_func->address); + ASSERT_TRUE(basic_func->size == fast_func->size); + + // compare range map of lines: + RangeMap >::MapConstIterator iter1; + StaticRangeMap::MapConstIterator iter2; + iter1 = basic_func->lines.map_.begin(); + iter2 = fast_func->lines.map_.begin(); + while (iter1 != basic_func->lines.map_.end() + && iter2 != fast_func->lines.map_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base()); + ASSERT_TRUE(CompareLine(iter1->second.entry().get(), + iter2.GetValuePtr()->entryptr())); + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_func->lines.map_.end()); + ASSERT_TRUE(iter2 == fast_func->lines.map_.end()); + + delete fast_func; + return true; +} + +bool ModuleComparer::CompareLine(const BasicLine *basic_line, + const FastLine *fast_line_raw) const { + FastLine *fast_line = new FastLine; + fast_line->CopyFrom(fast_line_raw); + + ASSERT_TRUE(basic_line->address == fast_line->address); + ASSERT_TRUE(basic_line->size == fast_line->size); + ASSERT_TRUE(basic_line->source_file_id == fast_line->source_file_id); + ASSERT_TRUE(basic_line->line == fast_line->line); + + delete fast_line; + return true; +} + +bool ModuleComparer::ComparePubSymbol(const BasicPubSymbol* basic_ps, + const FastPubSymbol* fastps_raw) const { + FastPubSymbol *fast_ps = new FastPubSymbol; + fast_ps->CopyFrom(fastps_raw); + ASSERT_TRUE(basic_ps->name == fast_ps->name); + ASSERT_TRUE(basic_ps->address == fast_ps->address); + ASSERT_TRUE(basic_ps->parameter_size == fast_ps->parameter_size); + delete fast_ps; + return true; +} + +bool ModuleComparer::CompareWFI(const WindowsFrameInfo& wfi1, + const WindowsFrameInfo& wfi2) const { + ASSERT_TRUE(wfi1.valid == wfi2.valid); + ASSERT_TRUE(wfi1.prolog_size == wfi2.prolog_size); + ASSERT_TRUE(wfi1.epilog_size == wfi2.epilog_size); + ASSERT_TRUE(wfi1.parameter_size == wfi2.parameter_size); + ASSERT_TRUE(wfi1.saved_register_size == wfi2.saved_register_size); + ASSERT_TRUE(wfi1.local_size == wfi2.local_size); + ASSERT_TRUE(wfi1.max_stack_size == wfi2.max_stack_size); + ASSERT_TRUE(wfi1.allocates_base_pointer == wfi2.allocates_base_pointer); + ASSERT_TRUE(wfi1.program_string == wfi2.program_string); + return true; +} + +// Compare ContainedRangeMap +bool ModuleComparer::CompareCRM( + const ContainedRangeMap >* basic_crm, + const StaticContainedRangeMap* fast_crm) const { + ASSERT_TRUE(basic_crm->base_ == fast_crm->base_); + + if (!basic_crm->entry_.get() || !fast_crm->entry_ptr_) { + // empty entry: + ASSERT_TRUE(!basic_crm->entry_.get() && !fast_crm->entry_ptr_); + } else { + WFI newwfi; + newwfi.CopyFrom(fast_resolver_->CopyWFI(fast_crm->entry_ptr_)); + ASSERT_TRUE(CompareWFI(*(basic_crm->entry_.get()), newwfi)); + } + + if ((!basic_crm->map_ || basic_crm->map_->empty()) + || fast_crm->map_.empty()) { + ASSERT_TRUE((!basic_crm->map_ || basic_crm->map_->empty()) + && fast_crm->map_.empty()); + } else { + ContainedRangeMap >::MapConstIterator iter1; + StaticContainedRangeMap::MapConstIterator iter2; + iter1 = basic_crm->map_->begin(); + iter2 = fast_crm->map_.begin(); + while (iter1 != basic_crm->map_->end() + && iter2 != fast_crm->map_.end()) { + ASSERT_TRUE(iter1->first == iter2.GetKey()); + StaticContainedRangeMap *child = + new StaticContainedRangeMap( + reinterpret_cast(iter2.GetValuePtr())); + ASSERT_TRUE(CompareCRM(iter1->second, child)); + delete child; + ++iter1; + ++iter2; + } + ASSERT_TRUE(iter1 == basic_crm->map_->end()); + ASSERT_TRUE(iter2 == fast_crm->map_.end()); + } + + return true; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/module_comparer.h b/src/lib/crashdump/gbreakpad/processor/module_comparer.h new file mode 100644 index 0000000..fcbd517 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/module_comparer.h @@ -0,0 +1,98 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// module_comparer.h: ModuleComparer reads a string format of symbol file, and +// loads the symbol into both BasicSourceLineResolver::Module and +// FastSourceLineResolve::Module. It then traverses both Modules and compare +// the content of data to verify the correctness of new fast module. +// ModuleCompare class is a tool to verify correctness of a loaded +// FastSourceLineResolver::Module instance, i.e., in-memory representation of +// parsed symbol. ModuleComparer class should be used for testing purpose only, +// e.g., in fast_source_line_resolver_unittest. +// +// Author: lambxsy@google.com (Siyang Xie) + +#ifndef PROCESSOR_MODULE_COMPARER_H__ +#define PROCESSOR_MODULE_COMPARER_H__ + +#include + +#include "processor/basic_source_line_resolver_types.h" +#include "processor/fast_source_line_resolver_types.h" +#include "processor/module_serializer.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +class ModuleComparer { + public: + ModuleComparer(): fast_resolver_(new FastSourceLineResolver), + basic_resolver_(new BasicSourceLineResolver) { } + ~ModuleComparer() { + delete fast_resolver_; + delete basic_resolver_; + } + + // BasicSourceLineResolver loads its module using the symbol data, + // ModuleSerializer serialize the loaded module into a memory chunk, + // FastSourceLineResolver loads its module using the serialized memory chunk, + // Then, traverse both modules together and compare underlying data + // return true if both modules contain exactly same data. + bool Compare(const string &symbol_data); + + private: + typedef BasicSourceLineResolver::Module BasicModule; + typedef FastSourceLineResolver::Module FastModule; + typedef BasicSourceLineResolver::Function BasicFunc; + typedef FastSourceLineResolver::Function FastFunc; + typedef BasicSourceLineResolver::Line BasicLine; + typedef FastSourceLineResolver::Line FastLine; + typedef BasicSourceLineResolver::PublicSymbol BasicPubSymbol; + typedef FastSourceLineResolver::PublicSymbol FastPubSymbol; + typedef WindowsFrameInfo WFI; + + bool CompareModule(const BasicModule *oldmodule, + const FastModule *newmodule) const; + bool CompareFunction(const BasicFunc *oldfunc, const FastFunc *newfunc) const; + bool CompareLine(const BasicLine *oldline, const FastLine *newline) const; + bool ComparePubSymbol(const BasicPubSymbol*, const FastPubSymbol*) const; + bool CompareWFI(const WindowsFrameInfo&, const WindowsFrameInfo&) const; + + // Compare ContainedRangeMap + bool CompareCRM(const ContainedRangeMap >*, + const StaticContainedRangeMap*) const; + + FastSourceLineResolver *fast_resolver_; + BasicSourceLineResolver *basic_resolver_; + ModuleSerializer serializer_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_MODULE_COMPARER_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/module_factory.h b/src/lib/crashdump/gbreakpad/processor/module_factory.h new file mode 100644 index 0000000..7aa7caa --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/module_factory.h @@ -0,0 +1,72 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// module_factory.h: ModuleFactory a factory that provides +// an interface for creating a Module and deferring instantiation to subclasses +// BasicModuleFactory and FastModuleFactory. + +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_MODULE_FACTORY_H__ +#define PROCESSOR_MODULE_FACTORY_H__ + +#include "processor/basic_source_line_resolver_types.h" +#include "processor/fast_source_line_resolver_types.h" +#include "processor/source_line_resolver_base_types.h" + +namespace google_breakpad { + +class ModuleFactory { + public: + virtual ~ModuleFactory() { }; + virtual SourceLineResolverBase::Module* CreateModule( + const string &name) const = 0; +}; + +class BasicModuleFactory : public ModuleFactory { + public: + virtual ~BasicModuleFactory() { } + virtual BasicSourceLineResolver::Module* CreateModule( + const string &name) const { + return new BasicSourceLineResolver::Module(name); + } +}; + +class FastModuleFactory : public ModuleFactory { + public: + virtual ~FastModuleFactory() { } + virtual FastSourceLineResolver::Module* CreateModule( + const string &name) const { + return new FastSourceLineResolver::Module(name); + } +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_MODULE_FACTORY_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/module_serializer.cc b/src/lib/crashdump/gbreakpad/processor/module_serializer.cc new file mode 100644 index 0000000..5c5ff77 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/module_serializer.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// module_serializer.cc: ModuleSerializer implementation. +// +// See module_serializer.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include "processor/module_serializer.h" + +#include +#include + +#include "processor/basic_code_module.h" +#include "processor/logging.h" + +namespace google_breakpad { + +// Definition of static member variable in SimplerSerializer, which +// is declared in file "simple_serializer-inl.h" +RangeMapSerializer< MemAddr, linked_ptr > +SimpleSerializer::range_map_serializer_; + +size_t ModuleSerializer::SizeOf(const BasicSourceLineResolver::Module &module) { + size_t total_size_alloc_ = 0; + + // Compute memory size for each map component in Module class. + int map_index = 0; + map_sizes_[map_index++] = files_serializer_.SizeOf(module.files_); + map_sizes_[map_index++] = functions_serializer_.SizeOf(module.functions_); + map_sizes_[map_index++] = pubsym_serializer_.SizeOf(module.public_symbols_); + for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i) + map_sizes_[map_index++] = + wfi_serializer_.SizeOf(&(module.windows_frame_info_[i])); + map_sizes_[map_index++] = cfi_init_rules_serializer_.SizeOf( + module.cfi_initial_rules_); + map_sizes_[map_index++] = cfi_delta_rules_serializer_.SizeOf( + module.cfi_delta_rules_); + + // Header size. + total_size_alloc_ = kNumberMaps_ * sizeof(u_int32_t); + + for (int i = 0; i < kNumberMaps_; ++i) + total_size_alloc_ += map_sizes_[i]; + + // Extra one byte for null terminator for C-string copy safety. + ++total_size_alloc_; + + return total_size_alloc_; +} + +char *ModuleSerializer::Write(const BasicSourceLineResolver::Module &module, + char *dest) { + // Write header. + memcpy(dest, map_sizes_, kNumberMaps_ * sizeof(u_int32_t)); + dest += kNumberMaps_ * sizeof(u_int32_t); + // Write each map. + dest = files_serializer_.Write(module.files_, dest); + dest = functions_serializer_.Write(module.functions_, dest); + dest = pubsym_serializer_.Write(module.public_symbols_, dest); + for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i) + dest = wfi_serializer_.Write(&(module.windows_frame_info_[i]), dest); + dest = cfi_init_rules_serializer_.Write(module.cfi_initial_rules_, dest); + dest = cfi_delta_rules_serializer_.Write(module.cfi_delta_rules_, dest); + // Write a null terminator. + dest = SimpleSerializer::Write(0, dest); + return dest; +} + +char* ModuleSerializer::Serialize( + const BasicSourceLineResolver::Module &module, unsigned int *size) { + // Compute size of memory to allocate. + unsigned int size_to_alloc = SizeOf(module); + + // Allocate memory for serialized data. + char *serialized_data = new char[size_to_alloc]; + if (!serialized_data) { + BPLOG(ERROR) << "ModuleSerializer: memory allocation failed, " + << "size to alloc: " << size_to_alloc; + if (size) *size = 0; + return NULL; + } + + // Write serialized data to allocated memory chunk. + char *end_address = Write(module, serialized_data); + // Verify the allocated memory size is equal to the size of data been written. + unsigned int size_written = + static_cast(end_address - serialized_data); + if (size_to_alloc != size_written) { + BPLOG(ERROR) << "size_to_alloc differs from size_written: " + << size_to_alloc << " vs " << size_written; + } + + // Set size and return the start address of memory chunk. + if (size) + *size = size_to_alloc; + return serialized_data; +} + +bool ModuleSerializer::SerializeModuleAndLoadIntoFastResolver( + const BasicSourceLineResolver::ModuleMap::const_iterator &iter, + FastSourceLineResolver *fast_resolver) { + BPLOG(INFO) << "Converting symbol " << iter->first.c_str(); + + // Cast SourceLineResolverBase::Module* to BasicSourceLineResolver::Module*. + BasicSourceLineResolver::Module* basic_module = + dynamic_cast(iter->second); + + unsigned int size = 0; + scoped_array symbol_data(Serialize(*basic_module, &size)); + if (!symbol_data.get()) { + BPLOG(ERROR) << "Serialization failed for module: " << basic_module->name_; + return false; + } + BPLOG(INFO) << "Serialized Symbol Size " << size; + + // Copy the data into string. + // Must pass string to LoadModuleUsingMapBuffer(), instead of passing char* to + // LoadModuleUsingMemoryBuffer(), becaused of data ownership/lifetime issue. + string symbol_data_string(symbol_data.get(), size); + symbol_data.reset(); + + scoped_ptr code_module( + new BasicCodeModule(0, 0, iter->first, "", "", "", "")); + + return fast_resolver->LoadModuleUsingMapBuffer(code_module.get(), + symbol_data_string); +} + +void ModuleSerializer::ConvertAllModules( + const BasicSourceLineResolver *basic_resolver, + FastSourceLineResolver *fast_resolver) { + // Check for NULL pointer. + if (!basic_resolver || !fast_resolver) + return; + + // Traverse module list in basic resolver. + BasicSourceLineResolver::ModuleMap::const_iterator iter; + iter = basic_resolver->modules_->begin(); + for (; iter != basic_resolver->modules_->end(); ++iter) + SerializeModuleAndLoadIntoFastResolver(iter, fast_resolver); +} + +bool ModuleSerializer::ConvertOneModule( + const string &moduleid, + const BasicSourceLineResolver *basic_resolver, + FastSourceLineResolver *fast_resolver) { + // Check for NULL pointer. + if (!basic_resolver || !fast_resolver) + return false; + + BasicSourceLineResolver::ModuleMap::const_iterator iter; + iter = basic_resolver->modules_->find(moduleid); + if (iter == basic_resolver->modules_->end()) + return false; + + return SerializeModuleAndLoadIntoFastResolver(iter, fast_resolver); +} + +char* ModuleSerializer::SerializeSymbolFileData( + const string &symbol_data, unsigned int *size) { + scoped_ptr module( + new BasicSourceLineResolver::Module("no name")); + scoped_array buffer(new char[symbol_data.size() + 1]); + strcpy(buffer.get(), symbol_data.c_str()); + if (!module->LoadMapFromMemory(buffer.get())) { + return NULL; + } + buffer.reset(NULL); + return Serialize(*(module.get()), size); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/module_serializer.h b/src/lib/crashdump/gbreakpad/processor/module_serializer.h new file mode 100644 index 0000000..3b440a6 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/module_serializer.h @@ -0,0 +1,127 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// module_serializer.h: ModuleSerializer serializes a loaded symbol, +// i.e., a loaded BasicSouceLineResolver::Module instance, into a memory +// chunk of data. The serialized data can be read and loaded by +// FastSourceLineResolver without CPU & memory-intensive parsing. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_MODULE_SERIALIZER_H__ +#define PROCESSOR_MODULE_SERIALIZER_H__ + +#include +#include + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/fast_source_line_resolver.h" +#include "processor/basic_source_line_resolver_types.h" +#include "processor/fast_source_line_resolver_types.h" +#include "processor/linked_ptr.h" +#include "processor/map_serializers-inl.h" +#include "processor/simple_serializer-inl.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +// ModuleSerializer serializes a loaded BasicSourceLineResolver::Module into a +// chunk of memory data. ModuleSerializer also provides interface to compute +// memory size of the serialized data, write serialized data directly into +// memory, convert ASCII format symbol data into serialized binary data, and +// convert loaded BasicSourceLineResolver::Module into +// FastSourceLineResolver::Module. +class ModuleSerializer { + public: + // Compute the size of memory required to serialize a module. Return the + // total size needed for serialization. + size_t SizeOf(const BasicSourceLineResolver::Module &module); + + // Write a module into an allocated memory chunk with required size. + // Return the "end" of data, i.e., the address after the final byte of data. + char* Write(const BasicSourceLineResolver::Module &module, char *dest); + + // Serializes a loaded Module object into a chunk of memory data and returns + // the address of memory chunk. If size != NULL, *size is set to the memory + // size allocated for the serialized data. + // Caller takes the ownership of the memory chunk (allocated on heap), and + // owner should call delete [] to free the memory after use. + char* Serialize(const BasicSourceLineResolver::Module &module, + unsigned int *size = NULL); + + // Given the string format symbol_data, produces a chunk of serialized data. + // Caller takes ownership of the serialized data (on heap), and owner should + // call delete [] to free the memory after use. + char* SerializeSymbolFileData(const string &symbol_data, + unsigned int *size = NULL); + + // Serializes one loaded module with given moduleid in the basic source line + // resolver, and loads the serialized data into the fast source line resolver. + // Return false if the basic source line doesn't have a module with the given + // moduleid. + bool ConvertOneModule(const string &moduleid, + const BasicSourceLineResolver *basic_resolver, + FastSourceLineResolver *fast_resolver); + + // Serializes all the loaded modules in a basic source line resolver, and + // loads the serialized data into a fast source line resolver. + void ConvertAllModules(const BasicSourceLineResolver *basic_resolver, + FastSourceLineResolver *fast_resolver); + + private: + // Convenient type names. + typedef BasicSourceLineResolver::Line Line; + typedef BasicSourceLineResolver::Function Function; + typedef BasicSourceLineResolver::PublicSymbol PublicSymbol; + + // Internal implementation for ConvertOneModule and ConvertAllModules methods. + bool SerializeModuleAndLoadIntoFastResolver( + const BasicSourceLineResolver::ModuleMap::const_iterator &iter, + FastSourceLineResolver *fast_resolver); + + // Number of Maps that Module class contains. + static const u_int32_t kNumberMaps_ = + FastSourceLineResolver::Module::kNumberMaps_; + + // Memory sizes required to serialize map components in Module. + u_int32_t map_sizes_[kNumberMaps_]; + + // Serializers for each individual map component in Module class. + StdMapSerializer files_serializer_; + RangeMapSerializer > functions_serializer_; + AddressMapSerializer > pubsym_serializer_; + ContainedRangeMapSerializer > wfi_serializer_; + RangeMapSerializer cfi_init_rules_serializer_; + StdMapSerializer cfi_delta_rules_serializer_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_MODULE_SERIALIZER_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/pathname_stripper.cc b/src/lib/crashdump/gbreakpad/processor/pathname_stripper.cc new file mode 100644 index 0000000..839287b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/pathname_stripper.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// pathname_stripper.cc: Manipulates pathnames into their component parts. +// +// See pathname_stripper.h for documentation. +// +// Author: Mark Mentovai + +#include "processor/pathname_stripper.h" + +namespace google_breakpad { + +// static +string PathnameStripper::File(const string &path) { + string::size_type slash = path.rfind('/'); + string::size_type backslash = path.rfind('\\'); + + string::size_type file_start = 0; + if (slash != string::npos && + (backslash == string::npos || slash > backslash)) { + file_start = slash + 1; + } else if (backslash != string::npos) { + file_start = backslash + 1; + } + + return path.substr(file_start); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/pathname_stripper.h b/src/lib/crashdump/gbreakpad/processor/pathname_stripper.h new file mode 100644 index 0000000..17db75d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/pathname_stripper.h @@ -0,0 +1,53 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// pathname_stripper.h: Manipulates pathnames into their component parts. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_PATHNAME_STRIPPER_H__ +#define PROCESSOR_PATHNAME_STRIPPER_H__ + +#include + +namespace google_breakpad { + +using std::string; + +class PathnameStripper { + public: + // Given path, a pathname with components separated by slashes (/) or + // backslashes (\), returns the trailing component, without any separator. + // If path ends in a separator character, returns an empty string. + static string File(const string &path); +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_PATHNAME_STRIPPER_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/pathname_stripper_unittest.cc b/src/lib/crashdump/gbreakpad/processor/pathname_stripper_unittest.cc new file mode 100644 index 0000000..1bff4cb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/pathname_stripper_unittest.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 "processor/pathname_stripper.h" +#include "processor/logging.h" + +#define ASSERT_TRUE(condition) \ + if (!(condition)) { \ + fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \ + return false; \ + } + +#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) + +namespace { + +using google_breakpad::PathnameStripper; + +static bool RunTests() { + ASSERT_EQ(PathnameStripper::File("/dir/file"), "file"); + ASSERT_EQ(PathnameStripper::File("\\dir\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("/dir\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("\\dir/file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir/file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir/\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir\\/file"), "file"); + ASSERT_EQ(PathnameStripper::File("file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir/"), ""); + ASSERT_EQ(PathnameStripper::File("dir\\"), ""); + ASSERT_EQ(PathnameStripper::File("dir/dir/"), ""); + ASSERT_EQ(PathnameStripper::File("dir\\dir\\"), ""); + ASSERT_EQ(PathnameStripper::File("dir1/dir2/file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir1\\dir2\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir1/dir2\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir1\\dir2/file"), "file"); + ASSERT_EQ(PathnameStripper::File(""), ""); + ASSERT_EQ(PathnameStripper::File("1"), "1"); + ASSERT_EQ(PathnameStripper::File("1/2"), "2"); + ASSERT_EQ(PathnameStripper::File("1\\2"), "2"); + ASSERT_EQ(PathnameStripper::File("/1/2"), "2"); + ASSERT_EQ(PathnameStripper::File("\\1\\2"), "2"); + ASSERT_EQ(PathnameStripper::File("dir//file"), "file"); + ASSERT_EQ(PathnameStripper::File("dir\\\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("/dir//file"), "file"); + ASSERT_EQ(PathnameStripper::File("\\dir\\\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("c:\\dir\\file"), "file"); + ASSERT_EQ(PathnameStripper::File("c:\\dir\\file.ext"), "file.ext"); + + return true; +} + +} // namespace + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + return RunTests() ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/postfix_evaluator-inl.h b/src/lib/crashdump/gbreakpad/processor/postfix_evaluator-inl.h new file mode 100644 index 0000000..d7dbeac --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/postfix_evaluator-inl.h @@ -0,0 +1,363 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// postfix_evaluator-inl.h: Postfix (reverse Polish) notation expression +// evaluator. +// +// Documentation in postfix_evaluator.h. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_POSTFIX_EVALUATOR_INL_H__ +#define PROCESSOR_POSTFIX_EVALUATOR_INL_H__ + +#include "processor/postfix_evaluator.h" + +#include + +#include + +#include "google_breakpad/processor/memory_region.h" +#include "processor/logging.h" + +namespace google_breakpad { + +using std::istringstream; +using std::ostringstream; + + +// A small class used in Evaluate to make sure to clean up the stack +// before returning failure. +class AutoStackClearer { + public: + explicit AutoStackClearer(vector *stack) : stack_(stack) {} + ~AutoStackClearer() { stack_->clear(); } + + private: + vector *stack_; +}; + + +template +bool PostfixEvaluator::EvaluateToken( + const string &token, + const string &expression, + DictionaryValidityType *assigned) { + // There are enough binary operations that do exactly the same thing + // (other than the specific operation, of course) that it makes sense + // to share as much code as possible. + enum BinaryOperation { + BINARY_OP_NONE = 0, + BINARY_OP_ADD, + BINARY_OP_SUBTRACT, + BINARY_OP_MULTIPLY, + BINARY_OP_DIVIDE_QUOTIENT, + BINARY_OP_DIVIDE_MODULUS, + BINARY_OP_ALIGN + }; + + BinaryOperation operation = BINARY_OP_NONE; + if (token == "+") + operation = BINARY_OP_ADD; + else if (token == "-") + operation = BINARY_OP_SUBTRACT; + else if (token == "*") + operation = BINARY_OP_MULTIPLY; + else if (token == "/") + operation = BINARY_OP_DIVIDE_QUOTIENT; + else if (token == "%") + operation = BINARY_OP_DIVIDE_MODULUS; + else if (token == "@") + operation = BINARY_OP_ALIGN; + + if (operation != BINARY_OP_NONE) { + // Get the operands. + ValueType operand1 = ValueType(); + ValueType operand2 = ValueType(); + if (!PopValues(&operand1, &operand2)) { + BPLOG(ERROR) << "Could not PopValues to get two values for binary " + "operation " << token << ": " << expression; + return false; + } + + // Perform the operation. + ValueType result; + switch (operation) { + case BINARY_OP_ADD: + result = operand1 + operand2; + break; + case BINARY_OP_SUBTRACT: + result = operand1 - operand2; + break; + case BINARY_OP_MULTIPLY: + result = operand1 * operand2; + break; + case BINARY_OP_DIVIDE_QUOTIENT: + result = operand1 / operand2; + break; + case BINARY_OP_DIVIDE_MODULUS: + result = operand1 % operand2; + break; + case BINARY_OP_ALIGN: + result = + operand1 & (static_cast(-1) ^ (operand2 - 1)); + break; + case BINARY_OP_NONE: + // This will not happen, but compilers will want a default or + // BINARY_OP_NONE case. + BPLOG(ERROR) << "Not reached!"; + return false; + break; + } + + // Save the result. + PushValue(result); + } else if (token == "^") { + // ^ for unary dereference. Can't dereference without memory. + if (!memory_) { + BPLOG(ERROR) << "Attempt to dereference without memory: " << + expression; + return false; + } + + ValueType address; + if (!PopValue(&address)) { + BPLOG(ERROR) << "Could not PopValue to get value to derefence: " << + expression; + return false; + } + + ValueType value; + if (!memory_->GetMemoryAtAddress(address, &value)) { + BPLOG(ERROR) << "Could not dereference memory at address " << + HexString(address) << ": " << expression; + return false; + } + + PushValue(value); + } else if (token == "=") { + // = for assignment. + ValueType value; + if (!PopValue(&value)) { + BPLOG(INFO) << "Could not PopValue to get value to assign: " << + expression; + return false; + } + + // Assignment is only meaningful when assigning into an identifier. + // The identifier must name a variable, not a constant. Variables + // begin with '$'. + string identifier; + if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) { + BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an " + "identifier is needed to assign " << + HexString(value) << ": " << expression; + return false; + } + if (identifier.empty() || identifier[0] != '$') { + BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " << + identifier << ": " << expression; + return false; + } + + (*dictionary_)[identifier] = value; + if (assigned) + (*assigned)[identifier] = true; + } else { + // The token is not an operator, it's a literal value or an identifier. + // Push it onto the stack as-is. Use push_back instead of PushValue + // because PushValue pushes ValueType as a string, but token is already + // a string. + stack_.push_back(token); + } + return true; +} + +template +bool PostfixEvaluator::EvaluateInternal( + const string &expression, + DictionaryValidityType *assigned) { + // Tokenize, splitting on whitespace. + istringstream stream(expression); + string token; + while (stream >> token) { + // Normally, tokens are whitespace-separated, but occasionally, the + // assignment operator is smashed up against the next token, i.e. + // $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ = + // This has been observed in program strings produced by MSVS 2010 in LTO + // mode. + if (token.size() > 1 && token[0] == '=') { + if (!EvaluateToken("=", expression, assigned)) { + return false; + } + + if (!EvaluateToken(token.substr(1), expression, assigned)) { + return false; + } + } else if (!EvaluateToken(token, expression, assigned)) { + return false; + } + } + + return true; +} + +template +bool PostfixEvaluator::Evaluate(const string &expression, + DictionaryValidityType *assigned) { + // Ensure that the stack is cleared before returning. + AutoStackClearer clearer(&stack_); + + if (!EvaluateInternal(expression, assigned)) + return false; + + // If there's anything left on the stack, it indicates incomplete execution. + // This is a failure case. If the stack is empty, evalution was complete + // and successful. + if (stack_.empty()) + return true; + + BPLOG(ERROR) << "Incomplete execution: " << expression; + return false; +} + +template +bool PostfixEvaluator::EvaluateForValue(const string &expression, + ValueType *result) { + // Ensure that the stack is cleared before returning. + AutoStackClearer clearer(&stack_); + + if (!EvaluateInternal(expression, NULL)) + return false; + + // A successful execution should leave exactly one value on the stack. + if (stack_.size() != 1) { + BPLOG(ERROR) << "Expression yielded bad number of results: " + << "'" << expression << "'"; + return false; + } + + return PopValue(result); +} + +template +typename PostfixEvaluator::PopResult +PostfixEvaluator::PopValueOrIdentifier( + ValueType *value, string *identifier) { + // There needs to be at least one element on the stack to pop. + if (!stack_.size()) + return POP_RESULT_FAIL; + + string token = stack_.back(); + stack_.pop_back(); + + // First, try to treat the value as a literal. Literals may have leading + // '-' sign, and the entire remaining string must be parseable as + // ValueType. If this isn't possible, it can't be a literal, so treat it + // as an identifier instead. + // + // Some versions of the libstdc++, the GNU standard C++ library, have + // stream extractors for unsigned integer values that permit a leading + // '-' sign (6.0.13); others do not (6.0.9). Since we require it, we + // handle it explicitly here. + istringstream token_stream(token); + ValueType literal = ValueType(); + bool negative; + if (token_stream.peek() == '-') { + negative = true; + token_stream.get(); + } else { + negative = false; + } + if (token_stream >> literal && token_stream.peek() == EOF) { + if (value) { + *value = literal; + } + if (negative) + *value = -*value; + return POP_RESULT_VALUE; + } else { + if (identifier) { + *identifier = token; + } + return POP_RESULT_IDENTIFIER; + } +} + + +template +bool PostfixEvaluator::PopValue(ValueType *value) { + ValueType literal = ValueType(); + string token; + PopResult result; + if ((result = PopValueOrIdentifier(&literal, &token)) == POP_RESULT_FAIL) { + return false; + } else if (result == POP_RESULT_VALUE) { + // This is the easy case. + *value = literal; + } else { // result == POP_RESULT_IDENTIFIER + // There was an identifier at the top of the stack. Resolve it to a + // value by looking it up in the dictionary. + typename DictionaryType::const_iterator iterator = + dictionary_->find(token); + if (iterator == dictionary_->end()) { + // The identifier wasn't found in the dictionary. Don't imply any + // default value, just fail. + BPLOG(INFO) << "Identifier " << token << " not in dictionary"; + return false; + } + + *value = iterator->second; + } + + return true; +} + + +template +bool PostfixEvaluator::PopValues(ValueType *value1, + ValueType *value2) { + return PopValue(value2) && PopValue(value1); +} + + +template +void PostfixEvaluator::PushValue(const ValueType &value) { + ostringstream token_stream; + token_stream << value; + stack_.push_back(token_stream.str()); +} + + +} // namespace google_breakpad + + +#endif // PROCESSOR_POSTFIX_EVALUATOR_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/postfix_evaluator.h b/src/lib/crashdump/gbreakpad/processor/postfix_evaluator.h new file mode 100644 index 0000000..eb53b1e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/postfix_evaluator.h @@ -0,0 +1,178 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// postfix_evaluator.h: Postfix (reverse Polish) notation expression evaluator. +// +// PostfixEvaluator evaluates an expression, using the expression itself +// in postfix (reverse Polish) notation and a dictionary mapping constants +// and variables to their values. The evaluator supports standard +// arithmetic operations, assignment into variables, and when an optional +// MemoryRange is provided, dereferencing. (Any unary key-to-value operation +// may be used with a MemoryRange implementation that returns the appropriate +// values, but PostfixEvaluator was written with dereferencing in mind.) +// +// The expression language is simple. Expressions are supplied as strings, +// with operands and operators delimited by whitespace. Operands may be +// either literal values suitable for ValueType, or constants or variables, +// which reference the dictionary. The supported binary operators are + +// (addition), - (subtraction), * (multiplication), / (quotient of division), +// % (modulus of division), and @ (data alignment). The alignment operator (@) +// accepts a value and an alignment size, and produces a result that is a +// multiple of the alignment size by truncating the input value. +// The unary ^ (dereference) operator is also provided. These operators +// allow any operand to be either a literal value, constant, or variable. +// Assignment (=) of any type of operand into a variable is also supported. +// +// The dictionary is provided as a map with string keys. Keys beginning +// with the '$' character are treated as variables. All other keys are +// treated as constants. Any results must be assigned into variables in the +// dictionary. These variables do not need to exist prior to calling +// Evaluate, unless used in an expression prior to being assigned to. The +// internal stack state is not made available after evaluation, and any +// values remaining on the stack are treated as evidence of incomplete +// execution and cause the evaluator to indicate failure. +// +// PostfixEvaluator is intended to support evaluation of "program strings" +// obtained from MSVC frame data debugging information in pdb files as +// returned by the DIA APIs. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_POSTFIX_EVALUATOR_H__ +#define PROCESSOR_POSTFIX_EVALUATOR_H__ + + +#include +#include +#include + +namespace google_breakpad { + +using std::map; +using std::string; +using std::vector; + +class MemoryRegion; + +template +class PostfixEvaluator { + public: + typedef map DictionaryType; + typedef map DictionaryValidityType; + + // Create a PostfixEvaluator object that may be used (with Evaluate) on + // one or more expressions. PostfixEvaluator does not take ownership of + // either argument. |memory| may be NULL, in which case dereferencing + // (^) will not be supported. |dictionary| may be NULL, but evaluation + // will fail in that case unless set_dictionary is used before calling + // Evaluate. + PostfixEvaluator(DictionaryType *dictionary, const MemoryRegion *memory) + : dictionary_(dictionary), memory_(memory), stack_() {} + + // Evaluate the expression, starting with an empty stack. The results of + // execution will be stored in one (or more) variables in the dictionary. + // Returns false if any failures occur during execution, leaving + // variables in the dictionary in an indeterminate state. If assigned is + // non-NULL, any keys set in the dictionary as a result of evaluation + // will also be set to true in assigned, providing a way to determine if + // an expression modifies any of its input variables. + bool Evaluate(const string &expression, DictionaryValidityType *assigned); + + // Like Evaluate, but provides the value left on the stack to the + // caller. If evaluation succeeds and leaves exactly one value on + // the stack, pop that value, store it in *result, and return true. + // Otherwise, return false. + bool EvaluateForValue(const string &expression, ValueType *result); + + DictionaryType* dictionary() const { return dictionary_; } + + // Reset the dictionary. PostfixEvaluator does not take ownership. + void set_dictionary(DictionaryType *dictionary) {dictionary_ = dictionary; } + + private: + // Return values for PopValueOrIdentifier + enum PopResult { + POP_RESULT_FAIL = 0, + POP_RESULT_VALUE, + POP_RESULT_IDENTIFIER + }; + + // Retrieves the topmost literal value, constant, or variable from the + // stack. Returns POP_RESULT_VALUE if the topmost entry is a literal + // value, and sets |value| accordingly. Returns POP_RESULT_IDENTIFIER + // if the topmost entry is a constant or variable identifier, and sets + // |identifier| accordingly. Returns POP_RESULT_FAIL on failure, such + // as when the stack is empty. + PopResult PopValueOrIdentifier(ValueType *value, string *identifier); + + // Retrieves the topmost value on the stack. If the topmost entry is + // an identifier, the dictionary is queried for the identifier's value. + // Returns false on failure, such as when the stack is empty or when + // a nonexistent identifier is named. + bool PopValue(ValueType *value); + + // Retrieves the top two values on the stack, in the style of PopValue. + // value2 is popped before value1, so that value1 corresponds to the + // entry that was pushed prior to value2. Returns false on failure. + bool PopValues(ValueType *value1, ValueType *value2); + + // Pushes a new value onto the stack. + void PushValue(const ValueType &value); + + // Evaluate expression, updating *assigned if it is non-zero. Return + // true if evaluation completes successfully. Do not clear the stack + // upon successful evaluation. + bool EvaluateInternal(const string &expression, + DictionaryValidityType *assigned); + + bool EvaluateToken(const string &token, + const string &expression, + DictionaryValidityType *assigned); + + // The dictionary mapping constant and variable identifiers (strings) to + // values. Keys beginning with '$' are treated as variable names, and + // PostfixEvaluator is free to create and modify these keys. Weak pointer. + DictionaryType *dictionary_; + + // If non-NULL, the MemoryRegion used for dereference (^) operations. + // If NULL, dereferencing is unsupported and will fail. Weak pointer. + const MemoryRegion *memory_; + + // The stack contains state information as execution progresses. Values + // are pushed on to it as the expression string is read and as operations + // yield values; values are popped when used as operands to operators. + vector stack_; +}; + +} // namespace google_breakpad + + +#endif // PROCESSOR_POSTFIX_EVALUATOR_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/postfix_evaluator_unittest.cc b/src/lib/crashdump/gbreakpad/processor/postfix_evaluator_unittest.cc new file mode 100644 index 0000000..f6cbcf0 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/postfix_evaluator_unittest.cc @@ -0,0 +1,399 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator. +// +// Author: Mark Mentovai + +#include + +#include +#include + +#include "processor/postfix_evaluator-inl.h" + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/memory_region.h" +#include "processor/logging.h" + + +namespace { + + +using std::map; +using std::string; +using google_breakpad::MemoryRegion; +using google_breakpad::PostfixEvaluator; + + +// FakeMemoryRegion is used to test PostfixEvaluator's dereference (^) +// operator. The result of dereferencing a value is one greater than +// the value. +class FakeMemoryRegion : public MemoryRegion { + public: + virtual u_int64_t GetBase() const { return 0; } + virtual u_int32_t GetSize() const { return 0; } + virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { + *value = address + 1; + return true; + } + virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { + *value = address + 1; + return true; + } + virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { + *value = address + 1; + return true; + } + virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { + *value = address + 1; + return true; + } +}; + + +struct EvaluateTest { + // Expression passed to PostfixEvaluator::Evaluate. + const string expression; + + // True if the expression is expected to be evaluable, false if evaluation + // is expected to fail. + bool evaluable; +}; + + +struct EvaluateTestSet { + // The dictionary used for all tests in the set. + PostfixEvaluator::DictionaryType *dictionary; + + // The list of tests. + const EvaluateTest *evaluate_tests; + + // The number of tests. + unsigned int evaluate_test_count; + + // Identifiers and their expected values upon completion of the Evaluate + // tests in the set. + map *validate_data; +}; + + +struct EvaluateForValueTest { + // Expression passed to PostfixEvaluator::Evaluate. + const string expression; + + // True if the expression is expected to be evaluable, false if evaluation + // is expected to fail. + bool evaluable; + + // If evaluable, the value we expect it to yield. + unsigned int value; +}; + +static bool RunTests() { + // The first test set checks the basic operations and failure modes. + PostfixEvaluator::DictionaryType dictionary_0; + const EvaluateTest evaluate_tests_0[] = { + { "$rAdd 2 2 + =", true }, // $rAdd = 2 + 2 = 4 + { "$rAdd $rAdd 2 + =", true }, // $rAdd = $rAdd + 2 = 6 + { "$rAdd 2 $rAdd + =", true }, // $rAdd = 2 + $rAdd = 8 + { "99", false }, // put some junk on the stack... + { "$rAdd2 2 2 + =", true }, // ...and make sure things still work + { "$rAdd2\t2\n2 + =", true }, // same but with different whitespace + { "$rAdd2 2 2 + = ", true }, // trailing whitespace + { " $rAdd2 2 2 + =", true }, // leading whitespace + { "$rAdd2 2 2 + =", true }, // extra whitespace + { "$T0 2 = +", false }, // too few operands for add + { "2 + =", false }, // too few operands for add + { "2 +", false }, // too few operands for add + { "+", false }, // too few operands for add + { "^", false }, // too few operands for dereference + { "=", false }, // too few operands for assignment + { "2 =", false }, // too few operands for assignment + { "2 2 + =", false }, // too few operands for assignment + { "2 2 =", false }, // can't assign into a literal + { "k 2 =", false }, // can't assign into a constant + { "2", false }, // leftover data on stack + { "2 2 +", false }, // leftover data on stack + { "$rAdd", false }, // leftover data on stack + { "0 $T1 0 0 + =", false }, // leftover data on stack + { "$T2 $T2 2 + =", false }, // can't operate on an undefined value + { "$rMul 9 6 * =", true }, // $rMul = 9 * 6 = 54 + { "$rSub 9 6 - =", true }, // $rSub = 9 - 6 = 3 + { "$rDivQ 9 6 / =", true }, // $rDivQ = 9 / 6 = 1 + { "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3 + { "$rDeref 9 ^ =", true }, // $rDeref = ^9 = 10 (FakeMemoryRegion) + { "$rAlign 36 8 @ =", true }, // $rAlign = 36 @ 8 + { "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization + }; + map validate_data_0; + validate_data_0["$rAdd"] = 8; + validate_data_0["$rAdd2"] = 4; + validate_data_0["$rSub"] = 3; + validate_data_0["$rMul"] = 54; + validate_data_0["$rDivQ"] = 1; + validate_data_0["$rDivM"] = 3; + validate_data_0["$rDeref"] = 10; + validate_data_0["$rAlign"] = 32; + validate_data_0["$rAdd3"] = 4; + validate_data_0["$rMul2"] = 54; + + // The second test set simulates a couple of MSVC program strings. + // The data is fudged a little bit because the tests use FakeMemoryRegion + // instead of a real stack snapshot, but the program strings are real and + // the implementation doesn't know or care that the data is not real. + PostfixEvaluator::DictionaryType dictionary_1; + dictionary_1["$ebp"] = 0xbfff0010; + dictionary_1["$eip"] = 0x10000000; + dictionary_1["$esp"] = 0xbfff0000; + dictionary_1[".cbSavedRegs"] = 4; + dictionary_1[".cbParams"] = 4; + dictionary_1[".raSearchStart"] = 0xbfff0020; + const EvaluateTest evaluate_tests_1[] = { + { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = " + "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", true }, + // Intermediate state: $T0 = 0xbfff0010, $eip = 0xbfff0015, + // $ebp = 0xbfff0011, $esp = 0xbfff0018, + // $L = 0xbfff000c, $P = 0xbfff001c + { "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = " + "$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =", + true }, + // Intermediate state: $T0 = 0xbfff0011, $eip = 0xbfff0016, + // $ebp = 0xbfff0012, $esp = 0xbfff0019, + // $L = 0xbfff000d, $P = 0xbfff001d, + // $ebx = 0xbffefff6 + { "$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = " + "$esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = " + "$ebx $T0 28 - ^ =", + true } + }; + map validate_data_1; + validate_data_1["$T0"] = 0xbfff0012; + validate_data_1["$T1"] = 0xbfff0020; + validate_data_1["$T2"] = 0xbfff0019; + validate_data_1["$eip"] = 0xbfff0021; + validate_data_1["$ebp"] = 0xbfff0012; + validate_data_1["$esp"] = 0xbfff0024; + validate_data_1["$L"] = 0xbfff000e; + validate_data_1["$P"] = 0xbfff0028; + validate_data_1["$ebx"] = 0xbffefff7; + validate_data_1[".cbSavedRegs"] = 4; + validate_data_1[".cbParams"] = 4; + + EvaluateTestSet evaluate_test_sets[] = { + { &dictionary_0, evaluate_tests_0, + sizeof(evaluate_tests_0) / sizeof(EvaluateTest), &validate_data_0 }, + { &dictionary_1, evaluate_tests_1, + sizeof(evaluate_tests_1) / sizeof(EvaluateTest), &validate_data_1 }, + }; + + unsigned int evaluate_test_set_count = sizeof(evaluate_test_sets) / + sizeof(EvaluateTestSet); + + FakeMemoryRegion fake_memory; + PostfixEvaluator postfix_evaluator = + PostfixEvaluator(NULL, &fake_memory); + + for (unsigned int evaluate_test_set_index = 0; + evaluate_test_set_index < evaluate_test_set_count; + ++evaluate_test_set_index) { + EvaluateTestSet *evaluate_test_set = + &evaluate_test_sets[evaluate_test_set_index]; + const EvaluateTest *evaluate_tests = evaluate_test_set->evaluate_tests; + unsigned int evaluate_test_count = evaluate_test_set->evaluate_test_count; + + // The same dictionary will be used for each test in the set. Earlier + // tests can affect the state of the dictionary for later tests. + postfix_evaluator.set_dictionary(evaluate_test_set->dictionary); + + // Use a new validity dictionary for each test set. + PostfixEvaluator::DictionaryValidityType assigned; + + for (unsigned int evaluate_test_index = 0; + evaluate_test_index < evaluate_test_count; + ++evaluate_test_index) { + const EvaluateTest *evaluate_test = &evaluate_tests[evaluate_test_index]; + + // Do the test. + bool result = postfix_evaluator.Evaluate(evaluate_test->expression, + &assigned); + if (result != evaluate_test->evaluable) { + fprintf(stderr, "FAIL: evaluate set %d/%d, test %d/%d, " + "expression \"%s\", expected %s, observed %s\n", + evaluate_test_set_index, evaluate_test_set_count, + evaluate_test_index, evaluate_test_count, + evaluate_test->expression.c_str(), + evaluate_test->evaluable ? "evaluable" : "not evaluable", + result ? "evaluted" : "not evaluated"); + return false; + } + } + + // Validate the results. + for (map::const_iterator validate_iterator = + evaluate_test_set->validate_data->begin(); + validate_iterator != evaluate_test_set->validate_data->end(); + ++validate_iterator) { + const string identifier = validate_iterator->first; + unsigned int expected_value = validate_iterator->second; + + map::const_iterator dictionary_iterator = + evaluate_test_set->dictionary->find(identifier); + + // The identifier must exist in the dictionary. + if (dictionary_iterator == evaluate_test_set->dictionary->end()) { + fprintf(stderr, "FAIL: evaluate test set %d/%d, " + "validate identifier \"%s\", " + "expected %d, observed not found\n", + evaluate_test_set_index, evaluate_test_set_count, + identifier.c_str(), expected_value); + return false; + } + + // The value in the dictionary must be the same as the expected value. + unsigned int observed_value = dictionary_iterator->second; + if (expected_value != observed_value) { + fprintf(stderr, "FAIL: evaluate test set %d/%d, " + "validate identifier \"%s\", " + "expected %d, observed %d\n", + evaluate_test_set_index, evaluate_test_set_count, + identifier.c_str(), expected_value, observed_value); + return false; + } + + // The value must be set in the "assigned" dictionary if it was a + // variable. It must not have been assigned if it was a constant. + bool expected_assigned = identifier[0] == '$'; + bool observed_assigned = false; + PostfixEvaluator::DictionaryValidityType::const_iterator + iterator_assigned = assigned.find(identifier); + if (iterator_assigned != assigned.end()) { + observed_assigned = iterator_assigned->second; + } + if (expected_assigned != observed_assigned) { + fprintf(stderr, "FAIL: evaluate test set %d/%d, " + "validate assignment of \"%s\", " + "expected %d, observed %d\n", + evaluate_test_set_index, evaluate_test_set_count, + identifier.c_str(), expected_assigned, observed_assigned); + return false; + } + } + } + + // EvaluateForValue tests. + PostfixEvaluator::DictionaryType dictionary_2; + dictionary_2["$ebp"] = 0xbfff0010; + dictionary_2["$eip"] = 0x10000000; + dictionary_2["$esp"] = 0xbfff0000; + dictionary_2[".cbSavedRegs"] = 4; + dictionary_2[".cbParams"] = 4; + dictionary_2[".raSearchStart"] = 0xbfff0020; + const EvaluateForValueTest evaluate_for_value_tests_2[] = { + { "28907223", true, 28907223 }, // simple constant + { "89854293 40010015 +", true, 89854293 + 40010015 }, // arithmetic + { "-870245 8769343 +", true, 7899098 }, // negative constants + { "$ebp $esp - $eip +", true, 0x10000010 }, // variable references + { "18929794 34015074", false, 0 }, // too many values + { "$ebp $ebp 4 - =", false, 0 }, // too few values + { "$new $eip = $new", true, 0x10000000 }, // make new variable + { "$new 4 +", true, 0x10000004 }, // see prior assignments + { ".cfa 42 = 10", false, 0 } // can't set constants + }; + const int evaluate_for_value_tests_2_size + = (sizeof (evaluate_for_value_tests_2) + / sizeof (evaluate_for_value_tests_2[0])); + map validate_data_2; + validate_data_2["$eip"] = 0x10000000; + validate_data_2["$ebp"] = 0xbfff000c; + validate_data_2["$esp"] = 0xbfff0000; + validate_data_2["$new"] = 0x10000000; + validate_data_2[".cbSavedRegs"] = 4; + validate_data_2[".cbParams"] = 4; + validate_data_2[".raSearchStart"] = 0xbfff0020; + + postfix_evaluator.set_dictionary(&dictionary_2); + for (int i = 0; i < evaluate_for_value_tests_2_size; i++) { + const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i]; + unsigned int result; + if (postfix_evaluator.EvaluateForValue(test->expression, &result) + != test->evaluable) { + fprintf(stderr, "FAIL: evaluate for value test %d, " + "expected evaluation to %s, but it %s\n", + i, test->evaluable ? "succeed" : "fail", + test->evaluable ? "failed" : "succeeded"); + return false; + } + if (test->evaluable && result != test->value) { + fprintf(stderr, "FAIL: evaluate for value test %d, " + "expected value to be 0x%x, but it was 0x%x\n", + i, test->value, result); + return false; + } + } + + for (map::iterator v = validate_data_2.begin(); + v != validate_data_2.end(); v++) { + map::iterator a = dictionary_2.find(v->first); + if (a == dictionary_2.end()) { + fprintf(stderr, "FAIL: evaluate for value dictionary check: " + "expected dict[\"%s\"] to be 0x%x, but it was unset\n", + v->first.c_str(), v->second); + return false; + } else if (a->second != v->second) { + fprintf(stderr, "FAIL: evaluate for value dictionary check: " + "expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n", + v->first.c_str(), v->second, a->second); + return false; + } + dictionary_2.erase(a); + } + + map::iterator remaining = dictionary_2.begin(); + if (remaining != dictionary_2.end()) { + fprintf(stderr, "FAIL: evaluation of test expressions put unexpected " + "values in dictionary:\n"); + for (; remaining != dictionary_2.end(); remaining++) + fprintf(stderr, " dict[\"%s\"] == 0x%x\n", + remaining->first.c_str(), remaining->second); + return false; + } + + return true; +} + + +} // namespace + + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + return RunTests() ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/process_state.cc b/src/lib/crashdump/gbreakpad/processor/process_state.cc new file mode 100644 index 0000000..1d970ba --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/process_state.cc @@ -0,0 +1,64 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// process_state.cc: A snapshot of a process, in a fully-digested state. +// +// See process_state.h for documentation. +// +// Author: Mark Mentovai + +#include "google_breakpad/processor/process_state.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_modules.h" + +namespace google_breakpad { + +ProcessState::~ProcessState() { + Clear(); +} + +void ProcessState::Clear() { + time_date_stamp_ = 0; + crashed_ = false; + crash_reason_.clear(); + crash_address_ = 0; + assertion_.clear(); + requesting_thread_ = -1; + for (vector::const_iterator iterator = threads_.begin(); + iterator != threads_.end(); + ++iterator) { + delete *iterator; + } + threads_.clear(); + system_info_.Clear(); + delete modules_; + modules_ = NULL; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/proto/README b/src/lib/crashdump/gbreakpad/processor/proto/README new file mode 100644 index 0000000..df37b6f --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/proto/README @@ -0,0 +1,20 @@ +If you wish to use these protobufs, you must generate their source files +using protoc from the protobuf project (http://code.google.com/p/protobuf/). + +----- +Troubleshooting for Protobuf: + +Install: +If you are getting permission errors install, make sure you are not trying to +install from an NFS. + + +Running protoc: +protoc: error while loading shared libraries: libprotobuf.so.0: cannot open +shared object file: No such file or directory + +The issue is that Ubuntu 8.04 doesn't include /usr/local/lib in +library paths. + +To fix it for your current terminal session, just type in +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib diff --git a/src/lib/crashdump/gbreakpad/processor/proto/process_state.proto b/src/lib/crashdump/gbreakpad/processor/proto/process_state.proto new file mode 100644 index 0000000..5ff6194 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/proto/process_state.proto @@ -0,0 +1,207 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// process_state_proto.proto: A client proto representation of a process, +// in a fully-digested state. +// +// Derived from earlier struct and class based models of a client-side +// processed minidump found under src/google_breakpad/processor. The +// file process_state.h holds the top level representation of this model, +// supported by additional classes. We've added a proto representation +// to ease serialization and parsing for server-side storage of crash +// reports processed on the client. +// +// Author: Jess Gray + +syntax = "proto2"; + +package google_breakpad; + +// A proto representation of a process, in a fully-digested state. +// See src/google_breakpad/processor/process_state.h +message ProcessStateProto { + // Next value: 13 + + // The time-date stamp of the original minidump (time_t format) + optional int64 time_date_stamp = 1; + + message Crash { + // The type of crash. OS- and possibly CPU- specific. For example, + // "EXCEPTION_ACCESS_VIOLATION" (Windows), "EXC_BAD_ACCESS / + // KERN_INVALID_ADDRESS" (Mac OS X), "SIGSEGV" (other Unix). + required string reason = 1; + + // If crash_reason implicates memory, the memory address that caused the + // crash. For data access errors, this will be the data address that + // caused the fault. For code errors, this will be the address of the + // instruction that caused the fault. + required int64 address = 2; + } + optional Crash crash = 2; + + + // If there was an assertion that was hit, a textual representation + // of that assertion, possibly including the file and line at which + // it occurred. + optional string assertion = 3; + + // The index of the thread that requested a dump be written in the + // threads vector. If a dump was produced as a result of a crash, this + // will point to the thread that crashed. If the dump was produced as + // by user code without crashing, and the dump contains extended Breakpad + // information, this will point to the thread that requested the dump. + optional int32 requesting_thread = 4; + + message Thread { + // Stack for the given thread + repeated StackFrame frames = 1; + } + + // Stacks for each thread (except possibly the exception handler + // thread) at the time of the crash. + repeated Thread threads = 5; + + // The modules that were loaded into the process represented by the + // ProcessState. + repeated CodeModule modules = 6; + + // System Info: OS and CPU + + // A string identifying the operating system, such as "Windows NT", + // "Mac OS X", or "Linux". If the information is present in the dump but + // its value is unknown, this field will contain a numeric value. If + // the information is not present in the dump, this field will be empty. + optional string os = 7; + + // A short form of the os string, using lowercase letters and no spaces, + // suitable for use in a filesystem. Possible values are "windows", + // "mac", and "linux". Empty if the information is not present in the dump + // or if the OS given by the dump is unknown. The values stored in this + // field should match those used by MinidumpSystemInfo::GetOS. + optional string os_short = 8; + + // A string identifying the version of the operating system, such as + // "5.1.2600 Service Pack 2" or "10.4.8 8L2127". If the dump does not + // contain this information, this field will be empty. + optional string os_version = 9; + + // A string identifying the basic CPU family, such as "x86" or "ppc". + // If this information is present in the dump but its value is unknown, + // this field will contain a numeric value. If the information is not + // present in the dump, this field will be empty. The values stored in + // this field should match those used by MinidumpSystemInfo::GetCPU. + optional string cpu = 10; + + // A string further identifying the specific CPU, such as + // "GenuineIntel level 6 model 13 stepping 8". If the information is not + // present in the dump, or additional identifying information is not + // defined for the CPU family, this field will be empty. + optional string cpu_info = 11; + + // The number of processors in the system. Will be greater than one for + // multi-core systems. + optional int32 cpu_count = 12; + + // Leave the ability to add the raw minidump to this representation +} + + +// Represents a single frame in a stack +// See src/google_breakpad/processor/code_module.h +message StackFrame { + // Next value: 8 + + // The program counter location as an absolute virtual address. For the + // innermost called frame in a stack, this will be an exact program counter + // or instruction pointer value. For all other frames, this will be within + // the instruction that caused execution to branch to a called function, + // but may not necessarily point to the exact beginning of that instruction. + required int64 instruction = 1; + + // The module in which the instruction resides. + optional CodeModule module = 2; + + // The function name, may be omitted if debug symbols are not available. + optional string function_name = 3; + + // The start address of the function, may be omitted if debug symbols + // are not available. + optional int64 function_base = 4; + + // The source file name, may be omitted if debug symbols are not available. + optional string source_file_name = 5; + + // The (1-based) source line number, may be omitted if debug symbols are + // not available. + optional int32 source_line = 6; + + // The start address of the source line, may be omitted if debug symbols + // are not available. + optional int64 source_line_base = 7; +} + + +// Carries information about code modules that are loaded into a process. +// See src/google_breakpad/processor/code_module.h +message CodeModule { + // Next value: 8 + + // The base address of this code module as it was loaded by the process. + optional int64 base_address = 1; + + // The size of the code module. + optional int64 size = 2; + + // The path or file name that the code module was loaded from. + optional string code_file = 3; + + // An identifying string used to discriminate between multiple versions and + // builds of the same code module. This may contain a uuid, timestamp, + // version number, or any combination of this or other information, in an + // implementation-defined format. + optional string code_identifier = 4; + + // The filename containing debugging information associated with the code + // module. If debugging information is stored in a file separate from the + // code module itself (as is the case when .pdb or .dSYM files are used), + // this will be different from code_file. If debugging information is + // stored in the code module itself (possibly prior to stripping), this + // will be the same as code_file. + optional string debug_file = 5; + + // An identifying string similar to code_identifier, but identifies a + // specific version and build of the associated debug file. This may be + // the same as code_identifier when the debug_file and code_file are + // identical or when the same identifier is used to identify distinct + // debug and code files. + optional string debug_identifier = 6; + + // A human-readable representation of the code module's version. + optional string version = 7; +} diff --git a/src/lib/crashdump/gbreakpad/processor/range_map-inl.h b/src/lib/crashdump/gbreakpad/processor/range_map-inl.h new file mode 100644 index 0000000..3aa2603 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/range_map-inl.h @@ -0,0 +1,210 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// range_map-inl.h: Range map implementation. +// +// See range_map.h for documentation. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_RANGE_MAP_INL_H__ +#define PROCESSOR_RANGE_MAP_INL_H__ + + +#include + +#include "processor/range_map.h" +#include "processor/logging.h" + + +namespace google_breakpad { + + +template +bool RangeMap::StoreRange(const AddressType &base, + const AddressType &size, + const EntryType &entry) { + AddressType high = base + size - 1; + + // Check for undersize or overflow. + if (size <= 0 || high < base) { + // The processor will hit this case too frequently with common symbol + // files in the size == 0 case, which is more suited to a DEBUG channel. + // Filter those out since there's no DEBUG channel at the moment. + BPLOG_IF(INFO, size != 0) << "StoreRange failed, " << HexString(base) << + "+" << HexString(size) << ", " << + HexString(high); + return false; + } + + // Ensure that this range does not overlap with another one already in the + // map. + MapConstIterator iterator_base = map_.lower_bound(base); + MapConstIterator iterator_high = map_.lower_bound(high); + + if (iterator_base != iterator_high) { + // Some other range begins in the space used by this range. It may be + // contained within the space used by this range, or it may extend lower. + // Regardless, it is an error. + AddressType other_base = iterator_base->second.base(); + AddressType other_size = iterator_base->first - other_base + 1; + BPLOG(INFO) << "StoreRange failed, an existing range is contained by or " + "extends lower than the new range: new " << + HexString(base) << "+" << HexString(size) << ", existing " << + HexString(other_base) << "+" << HexString(other_size); + return false; + } + + if (iterator_high != map_.end()) { + if (iterator_high->second.base() <= high) { + // The range above this one overlaps with this one. It may fully + // contain this range, or it may begin within this range and extend + // higher. Regardless, it's an error. + AddressType other_base = iterator_high->second.base(); + AddressType other_size = iterator_high->first - other_base + 1; + BPLOG(INFO) << "StoreRange failed, an existing range contains or " + "extends higher than the new range: new " << + HexString(base) << "+" << HexString(size) << + ", existing " << + HexString(other_base) << "+" << HexString(other_size); + return false; + } + } + + // Store the range in the map by its high address, so that lower_bound can + // be used to quickly locate a range by address. + map_.insert(MapValue(high, Range(base, entry))); + return true; +} + + +template +bool RangeMap::RetrieveRange( + const AddressType &address, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) const { + BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveRange requires |entry|"; + assert(entry); + + MapConstIterator iterator = map_.lower_bound(address); + if (iterator == map_.end()) + return false; + + // The map is keyed by the high address of each range, so |address| is + // guaranteed to be lower than the range's high address. If |range| is + // not directly preceded by another range, it's possible for address to + // be below the range's low address, though. When that happens, address + // references something not within any range, so return false. + if (address < iterator->second.base()) + return false; + + *entry = iterator->second.entry(); + if (entry_base) + *entry_base = iterator->second.base(); + if (entry_size) + *entry_size = iterator->first - iterator->second.base() + 1; + + return true; +} + + +template +bool RangeMap::RetrieveNearestRange( + const AddressType &address, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) const { + BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveNearestRange requires |entry|"; + assert(entry); + + // If address is within a range, RetrieveRange can handle it. + if (RetrieveRange(address, entry, entry_base, entry_size)) + return true; + + // upper_bound gives the first element whose key is greater than address, + // but we want the first element whose key is less than or equal to address. + // Decrement the iterator to get there, but not if the upper_bound already + // points to the beginning of the map - in that case, address is lower than + // the lowest stored key, so return false. + MapConstIterator iterator = map_.upper_bound(address); + if (iterator == map_.begin()) + return false; + --iterator; + + *entry = iterator->second.entry(); + if (entry_base) + *entry_base = iterator->second.base(); + if (entry_size) + *entry_size = iterator->first - iterator->second.base() + 1; + + return true; +} + + +template +bool RangeMap::RetrieveRangeAtIndex( + int index, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) const { + BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveRangeAtIndex requires |entry|"; + assert(entry); + + if (index >= GetCount()) { + BPLOG(ERROR) << "Index out of range: " << index << "/" << GetCount(); + return false; + } + + // Walk through the map. Although it's ordered, it's not a vector, so it + // can't be addressed directly by index. + MapConstIterator iterator = map_.begin(); + for (int this_index = 0; this_index < index; ++this_index) + ++iterator; + + *entry = iterator->second.entry(); + if (entry_base) + *entry_base = iterator->second.base(); + if (entry_size) + *entry_size = iterator->first - iterator->second.base() + 1; + + return true; +} + + +template +int RangeMap::GetCount() const { + return map_.size(); +} + + +template +void RangeMap::Clear() { + map_.clear(); +} + + +} // namespace google_breakpad + + +#endif // PROCESSOR_RANGE_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/range_map.h b/src/lib/crashdump/gbreakpad/processor/range_map.h new file mode 100644 index 0000000..2572e49 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/range_map.h @@ -0,0 +1,132 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// range_map.h: Range maps. +// +// A range map associates a range of addresses with a specific object. This +// is useful when certain objects of variable size are located within an +// address space. The range map makes it simple to determine which object is +// associated with a specific address, which may be any address within the +// range associated with an object. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_RANGE_MAP_H__ +#define PROCESSOR_RANGE_MAP_H__ + + +#include + + +namespace google_breakpad { + +// Forward declarations (for later friend declarations of specialized template). +template class RangeMapSerializer; + +template +class RangeMap { + public: + RangeMap() : map_() {} + + // Inserts a range into the map. Returns false for a parameter error, + // or if the location of the range would conflict with a range already + // stored in the map. + bool StoreRange(const AddressType &base, + const AddressType &size, + const EntryType &entry); + + // Locates the range encompassing the supplied address. If there is + // no such range, returns false. entry_base and entry_size, if non-NULL, + // are set to the base and size of the entry's range. + bool RetrieveRange(const AddressType &address, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) const; + + // Locates the range encompassing the supplied address, if one exists. + // If no range encompasses the supplied address, locates the nearest range + // to the supplied address that is lower than the address. Returns false + // if no range meets these criteria. entry_base and entry_size, if + // non-NULL, are set to the base and size of the entry's range. + bool RetrieveNearestRange(const AddressType &address, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) + const; + + // Treating all ranges as a list ordered by the address spaces that they + // occupy, locates the range at the index specified by index. Returns + // false if index is larger than the number of ranges stored. entry_base + // and entry_size, if non-NULL, are set to the base and size of the entry's + // range. + // + // RetrieveRangeAtIndex is not optimized for speedy operation. + bool RetrieveRangeAtIndex(int index, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) + const; + + // Returns the number of ranges stored in the RangeMap. + int GetCount() const; + + // Empties the range map, restoring it to the state it was when it was + // initially created. + void Clear(); + + private: + // Friend declarations. + friend class ModuleComparer; + friend class RangeMapSerializer; + + class Range { + public: + Range(const AddressType &base, const EntryType &entry) + : base_(base), entry_(entry) {} + + AddressType base() const { return base_; } + EntryType entry() const { return entry_; } + + private: + // The base address of the range. The high address does not need to + // be stored, because RangeMap uses it as the key to the map. + const AddressType base_; + + // The entry corresponding to a range. + const EntryType entry_; + }; + + // Convenience types. + typedef std::map AddressToRangeMap; + typedef typename AddressToRangeMap::const_iterator MapConstIterator; + typedef typename AddressToRangeMap::value_type MapValue; + + // Maps the high address of each range to a EntryType. + AddressToRangeMap map_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_RANGE_MAP_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/range_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/range_map_unittest.cc new file mode 100644 index 0000000..996ae6d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/range_map_unittest.cc @@ -0,0 +1,553 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// range_map_unittest.cc: Unit tests for RangeMap +// +// Author: Mark Mentovai + + +#include +#include + +#include "processor/range_map-inl.h" + +#include "processor/linked_ptr.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + + +namespace { + + +using google_breakpad::linked_ptr; +using google_breakpad::scoped_ptr; +using google_breakpad::RangeMap; + + +// A CountedObject holds an int. A global (not thread safe!) count of +// allocated CountedObjects is maintained to help test memory management. +class CountedObject { + public: + explicit CountedObject(int id) : id_(id) { ++count_; } + ~CountedObject() { --count_; } + + static int count() { return count_; } + int id() const { return id_; } + + private: + static int count_; + int id_; +}; + +int CountedObject::count_; + + +typedef int AddressType; +typedef RangeMap< AddressType, linked_ptr > TestMap; + + +// RangeTest contains data to use for store and retrieve tests. See +// RunTests for descriptions of the tests. +struct RangeTest { + // Base address to use for test + AddressType address; + + // Size of range to use for test + AddressType size; + + // Unique ID of range - unstorable ranges must have unique IDs too + int id; + + // Whether this range is expected to be stored successfully or not + bool expect_storable; +}; + + +// A RangeTestSet encompasses multiple RangeTests, which are run in +// sequence on the same RangeMap. +struct RangeTestSet { + // An array of RangeTests + const RangeTest *range_tests; + + // The number of tests in the set + unsigned int range_test_count; +}; + + +// StoreTest uses the data in a RangeTest and calls StoreRange on the +// test RangeMap. It returns true if the expected result occurred, and +// false if something else happened. +static bool StoreTest(TestMap *range_map, const RangeTest *range_test) { + linked_ptr object(new CountedObject(range_test->id)); + bool stored = range_map->StoreRange(range_test->address, + range_test->size, + object); + + if (stored != range_test->expect_storable) { + fprintf(stderr, "FAILED: " + "StoreRange id %d, expected %s, observed %s\n", + range_test->id, + range_test->expect_storable ? "storable" : "not storable", + stored ? "stored" : "not stored"); + return false; + } + + return true; +} + + +// RetrieveTest uses the data in RangeTest and calls RetrieveRange on the +// test RangeMap. If it retrieves the expected value (which can be no +// map entry at the specified range,) it returns true, otherwise, it returns +// false. RetrieveTest will check the values around the base address and +// the high address of a range to guard against off-by-one errors. +static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) { + for (unsigned int side = 0; side <= 1; ++side) { + // When side == 0, check the low side (base address) of each range. + // When side == 1, check the high side (base + size) of each range. + + // Check one-less and one-greater than the target address in addition + // to the target address itself. + + // If the size of the range is only 1, don't check one greater than + // the base or one less than the high - for a successfully stored + // range, these tests would erroneously fail because the range is too + // small. + AddressType low_offset = -1; + AddressType high_offset = 1; + if (range_test->size == 1) { + if (!side) // When checking the low side, + high_offset = 0; // don't check one over the target. + else // When checking the high side, + low_offset = 0; // don't check one under the target. + } + + for (AddressType offset = low_offset; offset <= high_offset; ++offset) { + AddressType address = + offset + + (!side ? range_test->address : + range_test->address + range_test->size - 1); + + bool expected_result = false; // This is correct for tests not stored. + if (range_test->expect_storable) { + if (offset == 0) // When checking the target address, + expected_result = true; // test should always succeed. + else if (offset == -1) // When checking one below the target, + expected_result = side; // should fail low and succeed high. + else // When checking one above the target, + expected_result = !side; // should succeed low and fail high. + } + + linked_ptr object; + AddressType retrieved_base = AddressType(); + AddressType retrieved_size = AddressType(); + bool retrieved = range_map->RetrieveRange(address, &object, + &retrieved_base, + &retrieved_size); + + bool observed_result = retrieved && object->id() == range_test->id; + + if (observed_result != expected_result) { + fprintf(stderr, "FAILED: " + "RetrieveRange id %d, side %d, offset %d, " + "expected %s, observed %s\n", + range_test->id, + side, + offset, + expected_result ? "true" : "false", + observed_result ? "true" : "false"); + return false; + } + + // If a range was successfully retrieved, check that the returned + // bounds match the range as stored. + if (observed_result == true && + (retrieved_base != range_test->address || + retrieved_size != range_test->size)) { + fprintf(stderr, "FAILED: " + "RetrieveRange id %d, side %d, offset %d, " + "expected base/size %d/%d, observed %d/%d\n", + range_test->id, + side, + offset, + range_test->address, range_test->size, + retrieved_base, retrieved_size); + return false; + } + + // Now, check RetrieveNearestRange. The nearest range is always + // expected to be different from the test range when checking one + // less than the low side. + bool expected_nearest = range_test->expect_storable; + if (!side && offset < 0) + expected_nearest = false; + + linked_ptr nearest_object; + AddressType nearest_base = AddressType(); + AddressType nearest_size = AddressType(); + bool retrieved_nearest = range_map->RetrieveNearestRange(address, + &nearest_object, + &nearest_base, + &nearest_size); + + // When checking one greater than the high side, RetrieveNearestRange + // should usually return the test range. When a different range begins + // at that address, though, then RetrieveNearestRange should return the + // range at the address instead of the test range. + if (side && offset > 0 && nearest_base == address) { + expected_nearest = false; + } + + bool observed_nearest = retrieved_nearest && + nearest_object->id() == range_test->id; + + if (observed_nearest != expected_nearest) { + fprintf(stderr, "FAILED: " + "RetrieveNearestRange id %d, side %d, offset %d, " + "expected %s, observed %s\n", + range_test->id, + side, + offset, + expected_nearest ? "true" : "false", + observed_nearest ? "true" : "false"); + return false; + } + + // If a range was successfully retrieved, check that the returned + // bounds match the range as stored. + if (expected_nearest && + (nearest_base != range_test->address || + nearest_size != range_test->size)) { + fprintf(stderr, "FAILED: " + "RetrieveNearestRange id %d, side %d, offset %d, " + "expected base/size %d/%d, observed %d/%d\n", + range_test->id, + side, + offset, + range_test->address, range_test->size, + nearest_base, nearest_size); + return false; + } + } + } + + return true; +} + + +// Test RetrieveRangeAtIndex, which is supposed to return objects in order +// according to their addresses. This test is performed by looping through +// the map, calling RetrieveRangeAtIndex for all possible indices in sequence, +// and verifying that each call returns a different object than the previous +// call, and that ranges are returned with increasing base addresses. Returns +// false if the test fails. +static bool RetrieveIndexTest(TestMap *range_map, int set) { + linked_ptr object; + CountedObject *last_object = NULL; + AddressType last_base = 0; + + int object_count = range_map->GetCount(); + for (int object_index = 0; object_index < object_count; ++object_index) { + AddressType base; + if (!range_map->RetrieveRangeAtIndex(object_index, &object, &base, NULL)) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected success, observed failure\n", + set, object_index); + return false; + } + + if (!object.get()) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected object, observed NULL\n", + set, object_index); + return false; + } + + // It's impossible to do these comparisons unless there's a previous + // object to compare against. + if (last_object) { + // The object must be different from the last one. + if (object->id() == last_object->id()) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected different objects, observed same objects (%d)\n", + set, object_index, object->id()); + return false; + } + + // Each object must have a base greater than the previous object's base. + if (base <= last_base) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected different bases, observed same bases (%d)\n", + set, object_index, base); + return false; + } + } + + last_object = object.get(); + last_base = base; + } + + // Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that + // are too high. + if (range_map->RetrieveRangeAtIndex(object_count, &object, NULL, NULL)) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d (too large), " + "expected failure, observed success\n", + set, object_count); + return false; + } + + return true; +} + +// Additional RetriveAtIndex test to expose the bug in RetrieveRangeAtIndex(). +// Bug info: RetrieveRangeAtIndex() previously retrieves the high address of +// entry, however, it is supposed to retrieve the base address of entry as +// stated in the comment in range_map.h. +static bool RetriveAtIndexTest2() { + scoped_ptr range_map(new TestMap()); + + // Store ranges with base address = 2 * object_id: + const int range_size = 2; + for (int object_id = 0; object_id < 100; ++object_id) { + linked_ptr object(new CountedObject(object_id)); + int base_address = 2 * object_id; + range_map->StoreRange(base_address, range_size, object); + } + + linked_ptr object; + int object_count = range_map->GetCount(); + for (int object_index = 0; object_index < object_count; ++object_index) { + AddressType base; + if (!range_map->RetrieveRangeAtIndex(object_index, &object, &base, NULL)) { + fprintf(stderr, "FAILED: RetrieveAtIndexTest2 index %d, " + "expected success, observed failure\n", object_index); + return false; + } + + int expected_base = 2 * object->id(); + if (base != expected_base) { + fprintf(stderr, "FAILED: RetriveAtIndexTest2 index %d, " + "expected base %d, observed base %d", + object_index, expected_base, base); + return false; + } + } + + return true; +} + + +// RunTests runs a series of test sets. +static bool RunTests() { + // These tests will be run sequentially. The first set of tests exercises + // most functions of RangeTest, and verifies all of the bounds-checking. + const RangeTest range_tests_0[] = { + { INT_MIN, 16, 1, true }, // lowest possible range + { -2, 5, 2, true }, // a range through zero + { INT_MAX - 9, 11, 3, false }, // tests anti-overflow + { INT_MAX - 9, 10, 4, true }, // highest possible range + { 5, 0, 5, false }, // tests anti-zero-size + { 5, 1, 6, true }, // smallest possible range + { -20, 15, 7, true }, // entirely negative + + { 10, 10, 10, true }, // causes the following tests to fail + { 9, 10, 11, false }, // one-less base, one-less high + { 9, 11, 12, false }, // one-less base, identical high + { 9, 12, 13, false }, // completely contains existing + { 10, 9, 14, false }, // identical base, one-less high + { 10, 10, 15, false }, // exactly identical to existing range + { 10, 11, 16, false }, // identical base, one-greater high + { 11, 8, 17, false }, // contained completely within + { 11, 9, 18, false }, // one-greater base, identical high + { 11, 10, 19, false }, // one-greater base, one-greater high + { 9, 2, 20, false }, // overlaps bottom by one + { 10, 1, 21, false }, // overlaps bottom by one, contained + { 19, 1, 22, false }, // overlaps top by one, contained + { 19, 2, 23, false }, // overlaps top by one + + { 9, 1, 24, true }, // directly below without overlap + { 20, 1, 25, true }, // directly above without overlap + + { 6, 3, 26, true }, // exactly between two ranges, gapless + { 7, 3, 27, false }, // tries to span two ranges + { 7, 5, 28, false }, // tries to span three ranges + { 4, 20, 29, false }, // tries to contain several ranges + + { 30, 50, 30, true }, + { 90, 25, 31, true }, + { 35, 65, 32, false }, // tries to span two noncontiguous + { 120, 10000, 33, true }, // > 8-bit + { 20000, 20000, 34, true }, // > 8-bit + { 0x10001, 0x10001, 35, true }, // > 16-bit + + { 27, -1, 36, false } // tests high < base + }; + + // Attempt to fill the entire space. The entire space must be filled with + // three stores because AddressType is signed for these tests, so RangeMap + // treats the size as signed and rejects sizes that appear to be negative. + // Even if these tests were run as unsigned, two stores would be needed + // to fill the space because the entire size of the space could only be + // described by using one more bit than would be present in AddressType. + const RangeTest range_tests_1[] = { + { INT_MIN, INT_MAX, 50, true }, // From INT_MIN to -2, inclusive + { -1, 2, 51, true }, // From -1 to 0, inclusive + { 1, INT_MAX, 52, true }, // From 1 to INT_MAX, inclusive + { INT_MIN, INT_MAX, 53, false }, // Can't fill the space twice + { -1, 2, 54, false }, + { 1, INT_MAX, 55, false }, + { -3, 6, 56, false }, // -3 to 2, inclusive - spans 3 ranges + }; + + // A light round of testing to verify that RetrieveRange does the right + // the right thing at the extremities of the range when nothing is stored + // there. Checks are forced without storing anything at the extremities + // by setting size = 0. + const RangeTest range_tests_2[] = { + { INT_MIN, 0, 100, false }, // makes RetrieveRange check low end + { -1, 3, 101, true }, + { INT_MAX, 0, 102, false }, // makes RetrieveRange check high end + }; + + // Similar to the previous test set, but with a couple of ranges closer + // to the extremities. + const RangeTest range_tests_3[] = { + { INT_MIN + 1, 1, 110, true }, + { INT_MAX - 1, 1, 111, true }, + { INT_MIN, 0, 112, false }, // makes RetrieveRange check low end + { INT_MAX, 0, 113, false } // makes RetrieveRange check high end + }; + + // The range map is cleared between sets of tests listed here. + const RangeTestSet range_test_sets[] = { + { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) }, + { range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) }, + { range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) }, + { range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) }, + { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) } // Run again + }; + + // Maintain the range map in a pointer so that deletion can be meaningfully + // tested. + scoped_ptr range_map(new TestMap()); + + // Run all of the test sets in sequence. + unsigned int range_test_set_count = sizeof(range_test_sets) / + sizeof(RangeTestSet); + for (unsigned int range_test_set_index = 0; + range_test_set_index < range_test_set_count; + ++range_test_set_index) { + const RangeTest *range_tests = + range_test_sets[range_test_set_index].range_tests; + unsigned int range_test_count = + range_test_sets[range_test_set_index].range_test_count; + + // Run the StoreRange test, which validates StoreRange and initializes + // the RangeMap with data for the RetrieveRange test. + int stored_count = 0; // The number of ranges successfully stored + for (unsigned int range_test_index = 0; + range_test_index < range_test_count; + ++range_test_index) { + const RangeTest *range_test = &range_tests[range_test_index]; + if (!StoreTest(range_map.get(), range_test)) + return false; + + if (range_test->expect_storable) + ++stored_count; + } + + // There should be exactly one CountedObject for everything successfully + // stored in the RangeMap. + if (CountedObject::count() != stored_count) { + fprintf(stderr, "FAILED: " + "stored object counts don't match, expected %d, observed %d\n", + stored_count, + CountedObject::count()); + + return false; + } + + // The RangeMap's own count of objects should also match. + if (range_map->GetCount() != stored_count) { + fprintf(stderr, "FAILED: stored object count doesn't match GetCount, " + "expected %d, observed %d\n", + stored_count, range_map->GetCount()); + + return false; + } + + // Run the RetrieveRange test + for (unsigned int range_test_index = 0; + range_test_index < range_test_count; + ++range_test_index) { + const RangeTest *range_test = &range_tests[range_test_index]; + if (!RetrieveTest(range_map.get(), range_test)) + return false; + } + + if (!RetrieveIndexTest(range_map.get(), range_test_set_index)) + return false; + + // Clear the map between test sets. If this is the final test set, + // delete the map instead to test destruction. + if (range_test_set_index < range_test_set_count - 1) + range_map->Clear(); + else + range_map.reset(); + + // Test that all stored objects are freed when the RangeMap is cleared + // or deleted. + if (CountedObject::count() != 0) { + fprintf(stderr, "FAILED: " + "did not free all objects after %s, %d still allocated\n", + range_test_set_index < range_test_set_count - 1 ? "clear" + : "delete", + CountedObject::count()); + + return false; + } + } + + if (!RetriveAtIndexTest2()) { + fprintf(stderr, "FAILED: did not pass RetrieveAtIndexTest2()\n"); + return false; + } + + return true; +} + + +} // namespace + + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + return RunTests() ? 0 : 1; +} diff --git a/src/lib/crashdump/gbreakpad/processor/scoped_ptr.h b/src/lib/crashdump/gbreakpad/processor/scoped_ptr.h new file mode 100644 index 0000000..0d4f7fd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/scoped_ptr.h @@ -0,0 +1,335 @@ +// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999. +// Copyright (c) 2001, 2002 Peter Dimov +// +// Permission to copy, use, modify, sell and distribute this software +// is granted provided this copyright notice appears in all copies. +// This software is provided "as is" without express or implied +// warranty, and with no claim as to its suitability for any purpose. +// +// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation. +// + +// scoped_ptr mimics a built-in pointer except that it guarantees deletion +// of the object pointed to, either on destruction of the scoped_ptr or via +// an explicit reset(). scoped_ptr is a simple solution for simple needs; +// use shared_ptr or std::auto_ptr if your needs are more complex. + +// *** NOTE *** +// If your scoped_ptr is a class member of class FOO pointing to a +// forward declared type BAR (as shown below), then you MUST use a non-inlined +// version of the destructor. The destructor of a scoped_ptr (called from +// FOO's destructor) must have a complete definition of BAR in order to +// destroy it. Example: +// +// -- foo.h -- +// class BAR; +// +// class FOO { +// public: +// FOO(); +// ~FOO(); // Required for sources that instantiate class FOO to compile! +// +// private: +// scoped_ptr bar_; +// }; +// +// -- foo.cc -- +// #include "foo.h" +// FOO::~FOO() {} // Empty, but must be non-inlined to FOO's class definition. + +// scoped_ptr_malloc added by Google +// When one of these goes out of scope, instead of doing a delete or +// delete[], it calls free(). scoped_ptr_malloc is likely to see +// much more use than any other specializations. + +// release() added by Google +// Use this to conditionally transfer ownership of a heap-allocated object +// to the caller, usually on method success. + +#ifndef PROCESSOR_SCOPED_PTR_H__ +#define PROCESSOR_SCOPED_PTR_H__ + +#include // for std::ptrdiff_t +#include // for assert +#include // for free() decl + +namespace google_breakpad { + +template +class scoped_ptr { + private: + + T* ptr; + + scoped_ptr(scoped_ptr const &); + scoped_ptr & operator=(scoped_ptr const &); + + public: + + typedef T element_type; + + explicit scoped_ptr(T* p = 0): ptr(p) {} + + ~scoped_ptr() { + typedef char type_must_be_complete[sizeof(T)]; + delete ptr; + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + delete ptr; + ptr = p; + } + } + + T& operator*() const { + assert(ptr != 0); + return *ptr; + } + + T* operator->() const { + assert(ptr != 0); + return ptr; + } + + bool operator==(T* p) const { + return ptr == p; + } + + bool operator!=(T* p) const { + return ptr != p; + } + + T* get() const { + return ptr; + } + + void swap(scoped_ptr & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + private: + + // no reason to use these: each scoped_ptr should have its own object + template bool operator==(scoped_ptr const& p) const; + template bool operator!=(scoped_ptr const& p) const; +}; + +template inline +void swap(scoped_ptr& a, scoped_ptr& b) { + a.swap(b); +} + +template inline +bool operator==(T* p, const scoped_ptr& b) { + return p == b.get(); +} + +template inline +bool operator!=(T* p, const scoped_ptr& b) { + return p != b.get(); +} + +// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to +// is guaranteed, either on destruction of the scoped_array or via an explicit +// reset(). Use shared_array or std::vector if your needs are more complex. + +template +class scoped_array { + private: + + T* ptr; + + scoped_array(scoped_array const &); + scoped_array & operator=(scoped_array const &); + + public: + + typedef T element_type; + + explicit scoped_array(T* p = 0) : ptr(p) {} + + ~scoped_array() { + typedef char type_must_be_complete[sizeof(T)]; + delete[] ptr; + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + delete [] ptr; + ptr = p; + } + } + + T& operator[](std::ptrdiff_t i) const { + assert(ptr != 0); + assert(i >= 0); + return ptr[i]; + } + + bool operator==(T* p) const { + return ptr == p; + } + + bool operator!=(T* p) const { + return ptr != p; + } + + T* get() const { + return ptr; + } + + void swap(scoped_array & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + private: + + // no reason to use these: each scoped_array should have its own object + template bool operator==(scoped_array const& p) const; + template bool operator!=(scoped_array const& p) const; +}; + +template inline +void swap(scoped_array& a, scoped_array& b) { + a.swap(b); +} + +template inline +bool operator==(T* p, const scoped_array& b) { + return p == b.get(); +} + +template inline +bool operator!=(T* p, const scoped_array& b) { + return p != b.get(); +} + + +// This class wraps the c library function free() in a class that can be +// passed as a template argument to scoped_ptr_malloc below. +class ScopedPtrMallocFree { + public: + inline void operator()(void* x) const { + free(x); + } +}; + +// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a +// second template argument, the functor used to free the object. + +template +class scoped_ptr_malloc { + private: + + T* ptr; + + scoped_ptr_malloc(scoped_ptr_malloc const &); + scoped_ptr_malloc & operator=(scoped_ptr_malloc const &); + + public: + + typedef T element_type; + + explicit scoped_ptr_malloc(T* p = 0): ptr(p) {} + + ~scoped_ptr_malloc() { + typedef char type_must_be_complete[sizeof(T)]; + free_((void*) ptr); + } + + void reset(T* p = 0) { + typedef char type_must_be_complete[sizeof(T)]; + + if (ptr != p) { + free_((void*) ptr); + ptr = p; + } + } + + T& operator*() const { + assert(ptr != 0); + return *ptr; + } + + T* operator->() const { + assert(ptr != 0); + return ptr; + } + + bool operator==(T* p) const { + return ptr == p; + } + + bool operator!=(T* p) const { + return ptr != p; + } + + T* get() const { + return ptr; + } + + void swap(scoped_ptr_malloc & b) { + T* tmp = b.ptr; + b.ptr = ptr; + ptr = tmp; + } + + T* release() { + T* tmp = ptr; + ptr = 0; + return tmp; + } + + private: + + // no reason to use these: each scoped_ptr_malloc should have its own object + template + bool operator==(scoped_ptr_malloc const& p) const; + template + bool operator!=(scoped_ptr_malloc const& p) const; + + static FreeProc const free_; +}; + +template +FP const scoped_ptr_malloc::free_ = FP(); + +template inline +void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) { + a.swap(b); +} + +template inline +bool operator==(T* p, const scoped_ptr_malloc& b) { + return p == b.get(); +} + +template inline +bool operator!=(T* p, const scoped_ptr_malloc& b) { + return p != b.get(); +} + +} // namespace google_breakpad + +#endif // PROCESSOR_SCOPED_PTR_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/simple_serializer-inl.h b/src/lib/crashdump/gbreakpad/processor/simple_serializer-inl.h new file mode 100644 index 0000000..a6f5496 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/simple_serializer-inl.h @@ -0,0 +1,252 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// simple_serializer-inl.h: template specializations for following types: +// bool, const char *(C-string), string, +// Line, Function, PublicSymbol, WindowsFrameInfo and their linked pointers. +// +// See simple_serializer.h for moredocumentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_SIMPLE_SERIALIZER_INL_H__ +#define PROCESSOR_SIMPLE_SERIALIZER_INL_H__ + +#include + +#include "processor/simple_serializer.h" +#include "map_serializers-inl.h" + +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "processor/basic_source_line_resolver_types.h" +#include "processor/linked_ptr.h" +#include "processor/windows_frame_info.h" + +namespace google_breakpad { + +// Specializations of SimpleSerializer: bool +template<> +class SimpleSerializer { + public: + static size_t SizeOf(bool boolean) { return 1; } + + static char *Write(bool boolean, char *dest) { + *dest = static_cast(boolean? 255 : 0); + return ++dest; + } +}; + +// Specializations of SimpleSerializer: string +template<> +class SimpleSerializer { + public: + static size_t SizeOf(const string &str) { return str.size() + 1; } + + static char *Write(const string &str, char *dest) { + strcpy(dest, str.c_str()); + return dest + SizeOf(str); + } +}; + +// Specializations of SimpleSerializer: C-string +template<> +class SimpleSerializer { + public: + static size_t SizeOf(const char *cstring) { + return strlen(cstring) + 1; + } + + static char *Write(const char *cstring, char *dest) { + strcpy(dest, cstring); + return dest + SizeOf(cstring); + } +}; + +// Specializations of SimpleSerializer: Line +template<> +class SimpleSerializer { + typedef BasicSourceLineResolver::Line Line; + public: + static size_t SizeOf(const Line &line) { + return SimpleSerializer::SizeOf(line.address) + + SimpleSerializer::SizeOf(line.size) + + SimpleSerializer::SizeOf(line.source_file_id) + + SimpleSerializer::SizeOf(line.line); + } + static char *Write(const Line &line, char *dest) { + dest = SimpleSerializer::Write(line.address, dest); + dest = SimpleSerializer::Write(line.size, dest); + dest = SimpleSerializer::Write(line.source_file_id, dest); + dest = SimpleSerializer::Write(line.line, dest); + return dest; + } +}; + +// Specializations of SimpleSerializer: PublicSymbol +template<> +class SimpleSerializer { + typedef BasicSourceLineResolver::PublicSymbol PublicSymbol; + public: + static size_t SizeOf(const PublicSymbol &pubsymbol) { + return SimpleSerializer::SizeOf(pubsymbol.name) + + SimpleSerializer::SizeOf(pubsymbol.address) + + SimpleSerializer::SizeOf(pubsymbol.parameter_size); + } + static char *Write(const PublicSymbol &pubsymbol, char *dest) { + dest = SimpleSerializer::Write(pubsymbol.name, dest); + dest = SimpleSerializer::Write(pubsymbol.address, dest); + dest = SimpleSerializer::Write(pubsymbol.parameter_size, dest); + return dest; + } +}; + +// Specializations of SimpleSerializer: WindowsFrameInfo +template<> +class SimpleSerializer { + public: + static size_t SizeOf(const WindowsFrameInfo &wfi) { + unsigned int size = 0; + size += SimpleSerializer::SizeOf(wfi.valid); + size += SimpleSerializer::SizeOf(wfi.prolog_size); + size += SimpleSerializer::SizeOf(wfi.epilog_size); + size += SimpleSerializer::SizeOf(wfi.parameter_size); + size += SimpleSerializer::SizeOf(wfi.saved_register_size); + size += SimpleSerializer::SizeOf(wfi.local_size); + size += SimpleSerializer::SizeOf(wfi.max_stack_size); + size += SimpleSerializer::SizeOf(wfi.allocates_base_pointer); + size += SimpleSerializer::SizeOf(wfi.program_string); + return size; + } + static char *Write(const WindowsFrameInfo &wfi, char *dest) { + dest = SimpleSerializer::Write(wfi.valid, dest); + dest = SimpleSerializer::Write(wfi.prolog_size, dest); + dest = SimpleSerializer::Write(wfi.epilog_size, dest); + dest = SimpleSerializer::Write(wfi.parameter_size, dest); + dest = SimpleSerializer::Write(wfi.saved_register_size, dest); + dest = SimpleSerializer::Write(wfi.local_size, dest); + dest = SimpleSerializer::Write(wfi.max_stack_size, dest); + dest = SimpleSerializer::Write(wfi.allocates_base_pointer, dest); + return SimpleSerializer::Write(wfi.program_string, dest); + } +}; + +// Specializations of SimpleSerializer: Linked_ptr version of +// Line, Function, PublicSymbol, WindowsFrameInfo. +template<> +class SimpleSerializer< linked_ptr > { + typedef BasicSourceLineResolver::Line Line; + public: + static size_t SizeOf(const linked_ptr &lineptr) { + if (lineptr.get() == NULL) return 0; + return SimpleSerializer::SizeOf(*(lineptr.get())); + } + static char *Write(const linked_ptr &lineptr, char *dest) { + if (lineptr.get()) + dest = SimpleSerializer::Write(*(lineptr.get()), dest); + return dest; + } +}; + +template<> +class SimpleSerializer { + // Convenient type names. + typedef BasicSourceLineResolver::Function Function; + typedef BasicSourceLineResolver::Line Line; + public: + static size_t SizeOf(const Function &func) { + unsigned int size = 0; + size += SimpleSerializer::SizeOf(func.name); + size += SimpleSerializer::SizeOf(func.address); + size += SimpleSerializer::SizeOf(func.size); + size += SimpleSerializer::SizeOf(func.parameter_size); + size += range_map_serializer_.SizeOf(func.lines); + return size; + } + + static char *Write(const Function &func, char *dest) { + dest = SimpleSerializer::Write(func.name, dest); + dest = SimpleSerializer::Write(func.address, dest); + dest = SimpleSerializer::Write(func.size, dest); + dest = SimpleSerializer::Write(func.parameter_size, dest); + dest = range_map_serializer_.Write(func.lines, dest); + return dest; + } + private: + // This static member is defined in module_serializer.cc. + static RangeMapSerializer< MemAddr, linked_ptr > range_map_serializer_; +}; + +template<> +class SimpleSerializer< linked_ptr > { + typedef BasicSourceLineResolver::Function Function; + public: + static size_t SizeOf(const linked_ptr &func) { + if (!func.get()) return 0; + return SimpleSerializer::SizeOf(*(func.get())); + } + + static char *Write(const linked_ptr &func, char *dest) { + if (func.get()) + dest = SimpleSerializer::Write(*(func.get()), dest); + return dest; + } +}; + +template<> +class SimpleSerializer< linked_ptr > { + typedef BasicSourceLineResolver::PublicSymbol PublicSymbol; + public: + static size_t SizeOf(const linked_ptr &pubsymbol) { + if (pubsymbol.get() == NULL) return 0; + return SimpleSerializer::SizeOf(*(pubsymbol.get())); + } + static char *Write(const linked_ptr &pubsymbol, char *dest) { + if (pubsymbol.get()) + dest = SimpleSerializer::Write(*(pubsymbol.get()), dest); + return dest; + } +}; + +template<> +class SimpleSerializer< linked_ptr > { + public: + static size_t SizeOf(const linked_ptr &wfi) { + if (wfi.get() == NULL) return 0; + return SimpleSerializer::SizeOf(*(wfi.get())); + } + static char *Write(const linked_ptr &wfi, char *dest) { + if (wfi.get()) + dest = SimpleSerializer::Write(*(wfi.get()), dest); + return dest; + } +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_SIMPLE_SERIALIZER_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/simple_serializer.h b/src/lib/crashdump/gbreakpad/processor/simple_serializer.h new file mode 100644 index 0000000..a1ca4f3 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/simple_serializer.h @@ -0,0 +1,63 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// simple_serializer.h: SimpleSerializer is a template for calculating size and +// writing to specific memory location for objects of primitive types, C-style +// string, string, breakpad types/structs etc. +// All specializations of SimpleSerializer template are defined in the +// "simple_serializer-inl.h" file. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_SIMPLE_SERIALIZER_H__ +#define PROCESSOR_SIMPLE_SERIALIZER_H__ + +#include + +namespace google_breakpad { + +typedef u_int64_t MemAddr; + +// Default implementation of SimpleSerializer template. +// Specializations are defined in "simple_serializer-inl.h". +template class SimpleSerializer { + public: + // Calculate and return the size of the 'item'. + static size_t SizeOf(const Type &item) { return sizeof(item); } + // Write 'item' to memory location 'dest', and return to the "end" address of + // data written, i.e., the address after the final byte written. + static char *Write(const Type &item, char *dest) { + new (dest) Type(item); + return dest + SizeOf(item); + } +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_SIMPLE_SERIALIZER_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.cc b/src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.cc new file mode 100644 index 0000000..76820e1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.cc @@ -0,0 +1,200 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// simple_symbol_supplier.cc: A simple SymbolSupplier implementation +// +// See simple_symbol_supplier.h for documentation. +// +// Author: Mark Mentovai + +#include "processor/simple_symbol_supplier.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/logging.h" +#include "processor/pathname_stripper.h" + +namespace google_breakpad { + +static bool file_exists(const string &file_name) { + struct stat sb; + return stat(file_name.c_str(), &sb) == 0; +} + +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile( + const CodeModule *module, const SystemInfo *system_info, + string *symbol_file) { + BPLOG_IF(ERROR, !symbol_file) << "SimpleSymbolSupplier::GetSymbolFile " + "requires |symbol_file|"; + assert(symbol_file); + symbol_file->clear(); + + for (unsigned int path_index = 0; path_index < paths_.size(); ++path_index) { + SymbolResult result; + if ((result = GetSymbolFileAtPathFromRoot(module, system_info, + paths_[path_index], + symbol_file)) != NOT_FOUND) { + return result; + } + } + return NOT_FOUND; +} + +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + assert(symbol_data); + symbol_data->clear(); + + SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info, symbol_file); + + if (s == FOUND) { + std::ifstream in(symbol_file->c_str()); + std::getline(in, *symbol_data, std::string::traits_type::to_char_type( + std::string::traits_type::eof())); + in.close(); + } + return s; +} + +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetCStringSymbolData( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data) { + assert(symbol_data); + + string symbol_data_string; + SymbolSupplier::SymbolResult s = + GetSymbolFile(module, system_info, symbol_file, &symbol_data_string); + + if (s == FOUND) { + unsigned int size = symbol_data_string.size() + 1; + *symbol_data = new char[size]; + if (*symbol_data == NULL) { + BPLOG(ERROR) << "Memory allocation for size " << size << " failed"; + return INTERRUPT; + } + memcpy(*symbol_data, symbol_data_string.c_str(), size - 1); + (*symbol_data)[size - 1] = '\0'; + memory_buffers_.insert(make_pair(module->code_file(), *symbol_data)); + } + return s; +} + +void SimpleSymbolSupplier::FreeSymbolData(const CodeModule *module) { + if (!module) { + BPLOG(INFO) << "Cannot free symbol data buffer for NULL module"; + return; + } + + map::iterator it = memory_buffers_.find(module->code_file()); + if (it == memory_buffers_.end()) { + BPLOG(INFO) << "Cannot find symbol data buffer for module " + << module->code_file(); + return; + } + delete [] it->second; + memory_buffers_.erase(it); +} + +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPathFromRoot( + const CodeModule *module, const SystemInfo *system_info, + const string &root_path, string *symbol_file) { + BPLOG_IF(ERROR, !symbol_file) << "SimpleSymbolSupplier::GetSymbolFileAtPath " + "requires |symbol_file|"; + assert(symbol_file); + symbol_file->clear(); + + if (!module) + return NOT_FOUND; + + // Start with the base path. + string path = root_path; + + // Append the debug (pdb) file name as a directory name. + path.append("/"); + string debug_file_name = PathnameStripper::File(module->debug_file()); + if (debug_file_name.empty()) { + BPLOG(ERROR) << "Can't construct symbol file path without debug_file " + "(code_file = " << + PathnameStripper::File(module->code_file()) << ")"; + return NOT_FOUND; + } + path.append(debug_file_name); + + // Append the identifier as a directory name. + path.append("/"); + string identifier = module->debug_identifier(); + if (identifier.empty()) { + BPLOG(ERROR) << "Can't construct symbol file path without debug_identifier " + "(code_file = " << + PathnameStripper::File(module->code_file()) << + ", debug_file = " << debug_file_name << ")"; + return NOT_FOUND; + } + path.append(identifier); + + // Transform the debug file name into one ending in .sym. If the existing + // name ends in .pdb, strip the .pdb. Otherwise, add .sym to the non-.pdb + // name. + path.append("/"); + string debug_file_extension; + if (debug_file_name.size() > 4) + debug_file_extension = debug_file_name.substr(debug_file_name.size() - 4); + std::transform(debug_file_extension.begin(), debug_file_extension.end(), + debug_file_extension.begin(), tolower); + if (debug_file_extension == ".pdb") { + path.append(debug_file_name.substr(0, debug_file_name.size() - 4)); + } else { + path.append(debug_file_name); + } + path.append(".sym"); + + if (!file_exists(path)) { + BPLOG(INFO) << "No symbol file at " << path; + return NOT_FOUND; + } + + *symbol_file = path; + return FOUND; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.h b/src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.h new file mode 100644 index 0000000..e1c1619 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/simple_symbol_supplier.h @@ -0,0 +1,139 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// simple_symbol_supplier.h: A simple SymbolSupplier implementation +// +// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier +// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is +// created with one or more base directories, which are the root paths for all +// symbol files. Each symbol file contained therein has a directory entry in +// the base directory with a name identical to the corresponding debugging +// file (pdb). Within each of these directories, there are subdirectories +// named for the debugging file's identifier. For recent pdb files, this is +// a concatenation of the pdb's uuid and age, presented in hexadecimal form, +// without any dashes or separators. The uuid is in uppercase hexadecimal +// and the age is in lowercase hexadecimal. Within that subdirectory, +// SimpleSymbolSupplier expects to find the symbol file, which is named +// identically to the debug file, but with a .sym extension. If the original +// debug file had a name ending in .pdb, the .pdb extension will be replaced +// with .sym. This sample hierarchy is rooted at the "symbols" base +// directory: +// +// symbols +// symbols/test_app.pdb +// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1 +// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1/test_app.sym +// symbols/kernel32.pdb +// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542 +// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym +// +// In this case, the uuid of test_app.pdb is +// 63fe4780-728d-4937-9b9d-7bb6460cb42a and its age is 1. +// +// This scheme was chosen to be roughly analogous to the way that +// symbol files may be accessed from Microsoft Symbol Server. A hierarchy +// used for Microsoft Symbol Server storage is usable as a hierarchy for +// SimpleSymbolServer, provided that the pdb files are transformed to dumped +// format using a tool such as dump_syms, and given a .sym extension. +// +// SimpleSymbolSupplier will iterate over all root paths searching for +// a symbol file existing in that path. +// +// SimpleSymbolSupplier supports any debugging file which can be identified +// by a CodeModule object's debug_file and debug_identifier accessors. The +// expected ultimate source of these CodeModule objects are MinidumpModule +// objects; it is this class that is responsible for assigning appropriate +// values for debug_file and debug_identifier. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__ +#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__ + +#include +#include +#include + +#include "google_breakpad/processor/symbol_supplier.h" + +namespace google_breakpad { + +using std::map; +using std::string; +using std::vector; + +class CodeModule; + +class SimpleSymbolSupplier : public SymbolSupplier { + public: + // Creates a new SimpleSymbolSupplier, using path as the root path where + // symbols are stored. + explicit SimpleSymbolSupplier(const string &path) : paths_(1, path) {} + + // Creates a new SimpleSymbolSupplier, using paths as a list of root + // paths where symbols may be stored. + explicit SimpleSymbolSupplier(const vector &paths) : paths_(paths) {} + + virtual ~SimpleSymbolSupplier() {} + + // Returns the path to the symbol file for the given module. See the + // description above. + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file); + + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); + + // Allocates data buffer on heap and writes symbol data into buffer. + // Symbol supplier ALWAYS takes ownership of the data buffer. + virtual SymbolResult GetCStringSymbolData(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + char **symbol_data); + + // Free the data buffer allocated in the above GetCStringSymbolData(); + virtual void FreeSymbolData(const CodeModule *module); + + protected: + SymbolResult GetSymbolFileAtPathFromRoot(const CodeModule *module, + const SystemInfo *system_info, + const string &root_path, + string *symbol_file); + + private: + map memory_buffers_; + vector paths_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/source_line_resolver_base.cc b/src/lib/crashdump/gbreakpad/processor/source_line_resolver_base.cc new file mode 100644 index 0000000..49088c8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/source_line_resolver_base.cc @@ -0,0 +1,311 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// source_line_resolver_base.cc: Implementation of SourceLineResolverBase. +// +// See source_line_resolver_base.h and source_line_resolver_base_types.h for +// more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include +#include +#include + +#include +#include + +#include "google_breakpad/processor/source_line_resolver_base.h" +#include "processor/source_line_resolver_base_types.h" +#include "processor/module_factory.h" + +using std::map; +using std::make_pair; + +namespace google_breakpad { + +SourceLineResolverBase::SourceLineResolverBase( + ModuleFactory *module_factory) + : modules_(new ModuleMap), + memory_buffers_(new MemoryMap), + module_factory_(module_factory) { +} + +SourceLineResolverBase::~SourceLineResolverBase() { + ModuleMap::iterator it; + // Iterate through ModuleMap and delete all loaded modules. + for (it = modules_->begin(); it != modules_->end(); ++it) { + // Delete individual module. + delete it->second; + } + // Delete the map of modules. + delete modules_; + + MemoryMap::iterator iter = memory_buffers_->begin(); + for (; iter != memory_buffers_->end(); ++iter) { + delete [] iter->second; + } + // Delete the map of memory buffers. + delete memory_buffers_; + + delete module_factory_; +} + +bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data, + const string &map_file) { + if (symbol_data == NULL) { + BPLOG(ERROR) << "Could not Read file into Null memory pointer"; + return false; + } + + struct stat buf; + int error_code = stat(map_file.c_str(), &buf); + if (error_code == -1) { + string error_string; + error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not open " << map_file << + ", error " << error_code << ": " << error_string; + return false; + } + + off_t file_size = buf.st_size; + + // Allocate memory for file contents, plus a null terminator + // since we may use strtok() on the contents. + *symbol_data = new char[file_size + 1]; + + if (*symbol_data == NULL) { + BPLOG(ERROR) << "Could not allocate memory for " << map_file; + return false; + } + + BPLOG(INFO) << "Opening " << map_file; + + FILE *f = fopen(map_file.c_str(), "rt"); + if (!f) { + string error_string; + error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not open " << map_file << + ", error " << error_code << ": " << error_string; + delete [] (*symbol_data); + *symbol_data = NULL; + return false; + } + + AutoFileCloser closer(f); + + int items_read = 0; + + items_read = fread(*symbol_data, 1, file_size, f); + + if (items_read != file_size) { + string error_string; + error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not slurp " << map_file << + ", error " << error_code << ": " << error_string; + delete [] (*symbol_data); + *symbol_data = NULL; + return false; + } + + (*symbol_data)[file_size] = '\0'; + return true; +} + +bool SourceLineResolverBase::LoadModule(const CodeModule *module, + const string &map_file) { + if (module == NULL) + return false; + + // Make sure we don't already have a module with the given name. + if (modules_->find(module->code_file()) != modules_->end()) { + BPLOG(INFO) << "Symbols for module " << module->code_file() + << " already loaded"; + return false; + } + + BPLOG(INFO) << "Loading symbols for module " << module->code_file() + << " from " << map_file; + + char *memory_buffer; + if (!ReadSymbolFile(&memory_buffer, map_file)) + return false; + + BPLOG(INFO) << "Read symbol file " << map_file << " succeeded"; + + bool load_result = LoadModuleUsingMemoryBuffer(module, memory_buffer); + + if (load_result && !ShouldDeleteMemoryBufferAfterLoadModule()) { + // memory_buffer has to stay alive as long as the module. + memory_buffers_->insert(make_pair(module->code_file(), memory_buffer)); + } else { + delete [] memory_buffer; + } + + return load_result; +} + +bool SourceLineResolverBase::LoadModuleUsingMapBuffer( + const CodeModule *module, const string &map_buffer) { + if (module == NULL) + return false; + + // Make sure we don't already have a module with the given name. + if (modules_->find(module->code_file()) != modules_->end()) { + BPLOG(INFO) << "Symbols for module " << module->code_file() + << " already loaded"; + return false; + } + + char *memory_buffer = new char[map_buffer.size() + 1]; + if (memory_buffer == NULL) { + BPLOG(ERROR) << "Could not allocate memory for " << module->code_file(); + return false; + } + + // Can't use strcpy, as the data may contain '\0's before the end. + memcpy(memory_buffer, map_buffer.c_str(), map_buffer.size()); + memory_buffer[map_buffer.size()] = '\0'; + + bool load_result = LoadModuleUsingMemoryBuffer(module, memory_buffer); + + if (load_result && !ShouldDeleteMemoryBufferAfterLoadModule()) { + // memory_buffer has to stay alive as long as the module. + memory_buffers_->insert(make_pair(module->code_file(), memory_buffer)); + } else { + delete [] memory_buffer; + } + + return load_result; +} + +bool SourceLineResolverBase::LoadModuleUsingMemoryBuffer( + const CodeModule *module, char *memory_buffer) { + if (!module) + return false; + + // Make sure we don't already have a module with the given name. + if (modules_->find(module->code_file()) != modules_->end()) { + BPLOG(INFO) << "Symbols for module " << module->code_file() + << " already loaded"; + return false; + } + + BPLOG(INFO) << "Loading symbols for module " << module->code_file() + << " from memory buffer"; + + Module *basic_module = module_factory_->CreateModule(module->code_file()); + + // Ownership of memory is NOT transfered to Module::LoadMapFromMemory(). + if (!basic_module->LoadMapFromMemory(memory_buffer)) { + delete basic_module; + return false; + } + + modules_->insert(make_pair(module->code_file(), basic_module)); + return true; +} + +bool SourceLineResolverBase::ShouldDeleteMemoryBufferAfterLoadModule() { + return true; +} + +void SourceLineResolverBase::UnloadModule(const CodeModule *code_module) { + if (!code_module) + return; + + ModuleMap::iterator mod_iter = modules_->find(code_module->code_file()); + if (mod_iter != modules_->end()) { + Module *symbol_module = mod_iter->second; + delete symbol_module; + modules_->erase(mod_iter); + } + + if (ShouldDeleteMemoryBufferAfterLoadModule()) { + // No-op. Because we never store any memory buffers. + } else { + // There may be a buffer stored locally, we need to find and delete it. + MemoryMap::iterator iter = memory_buffers_->find(code_module->code_file()); + if (iter != memory_buffers_->end()) { + delete [] iter->second; + memory_buffers_->erase(iter); + } + } +} + +bool SourceLineResolverBase::HasModule(const CodeModule *module) { + if (!module) + return false; + return modules_->find(module->code_file()) != modules_->end(); +} + +void SourceLineResolverBase::FillSourceLineInfo(StackFrame *frame) { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + it->second->LookupAddress(frame); + } + } +} + +WindowsFrameInfo *SourceLineResolverBase::FindWindowsFrameInfo( + const StackFrame *frame) { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + return it->second->FindWindowsFrameInfo(frame); + } + } + return NULL; +} + +CFIFrameInfo *SourceLineResolverBase::FindCFIFrameInfo( + const StackFrame *frame) { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + return it->second->FindCFIFrameInfo(frame); + } + } + return NULL; +} + +bool SourceLineResolverBase::CompareString::operator()( + const string &s1, const string &s2) const { + return strcmp(s1.c_str(), s2.c_str()) < 0; +} + +bool SourceLineResolverBase::Module::ParseCFIRuleSet( + const string &rule_set, CFIFrameInfo *frame_info) const { + CFIFrameInfoParseHandler handler(frame_info); + CFIRuleParser parser(&handler); + return parser.Parse(rule_set); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/source_line_resolver_base_types.h b/src/lib/crashdump/gbreakpad/processor/source_line_resolver_base_types.h new file mode 100644 index 0000000..5b099f1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/source_line_resolver_base_types.h @@ -0,0 +1,149 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// source_line_resolver_base_types.h: definition of nested classes/structs in +// SourceLineResolverBase. It moves the definitions out of +// source_line_resolver_base.cc, so that other classes may have access +// to these private nested types without including source_line_resolver_base.cc +// In addition, Module is defined as a pure abstract class to be implemented by +// each concrete source line resolver class. +// +// See source_line_resolver_base.h for more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include + +#include +#include + +#include "google_breakpad/processor/source_line_resolver_base.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/cfi_frame_info.h" +#include "processor/windows_frame_info.h" + +#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ +#define PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ + +namespace google_breakpad { + +class SourceLineResolverBase::AutoFileCloser { + public: + explicit AutoFileCloser(FILE *file) : file_(file) {} + ~AutoFileCloser() { + if (file_) + fclose(file_); + } + + private: + FILE *file_; +}; + +struct SourceLineResolverBase::Line { + Line() { } + Line(MemAddr addr, MemAddr code_size, int file_id, int source_line) + : address(addr) + , size(code_size) + , source_file_id(file_id) + , line(source_line) { } + + MemAddr address; + MemAddr size; + int32_t source_file_id; + int32_t line; +}; + +struct SourceLineResolverBase::Function { + Function() { } + Function(const string &function_name, + MemAddr function_address, + MemAddr code_size, + int set_parameter_size) + : name(function_name), address(function_address), size(code_size), + parameter_size(set_parameter_size) { } + + string name; + MemAddr address; + MemAddr size; + + // The size of parameters passed to this function on the stack. + int32_t parameter_size; +}; + +struct SourceLineResolverBase::PublicSymbol { + PublicSymbol() { } + PublicSymbol(const string& set_name, + MemAddr set_address, + int set_parameter_size) + : name(set_name), + address(set_address), + parameter_size(set_parameter_size) {} + + string name; + MemAddr address; + + // If the public symbol is used as a function entry point, parameter_size + // is set to the size of the parameters passed to the funciton on the + // stack, if known. + int32_t parameter_size; +}; + +class SourceLineResolverBase::Module { + public: + virtual ~Module() { }; + // Loads a map from the given buffer in char* type. + // Does NOT take ownership of memory_buffer (the caller, source line resolver, + // is the owner of memory_buffer). + virtual bool LoadMapFromMemory(char *memory_buffer) = 0; + + // Looks up the given relative address, and fills the StackFrame struct + // with the result. + virtual void LookupAddress(StackFrame *frame) const = 0; + + // If Windows stack walking information is available covering ADDRESS, + // return a WindowsFrameInfo structure describing it. If the information + // is not available, returns NULL. A NULL return value does not indicate + // an error. The caller takes ownership of any returned WindowsFrameInfo + // object. + virtual WindowsFrameInfo * + FindWindowsFrameInfo(const StackFrame *frame) const = 0; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const = 0; + protected: + virtual bool ParseCFIRuleSet(const string &rule_set, + CFIFrameInfo *frame_info) const; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker.cc new file mode 100644 index 0000000..2bd333a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker.cc @@ -0,0 +1,243 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker.cc: Generic stackwalker. +// +// See stackwalker.h for documentation. +// +// Author: Mark Mentovai + +#include "google_breakpad/processor/stackwalker.h" + +#include + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/symbol_supplier.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/linked_ptr.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_ppc.h" +#include "processor/stackwalker_sparc.h" +#include "processor/stackwalker_x86.h" +#include "processor/stackwalker_amd64.h" +#include "processor/stackwalker_arm.h" + +namespace google_breakpad { + +u_int32_t Stackwalker::max_frames_ = 1024; + +Stackwalker::Stackwalker(const SystemInfo *system_info, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : system_info_(system_info), + memory_(memory), + modules_(modules), + resolver_(resolver), + supplier_(supplier) { +} + + +bool Stackwalker::Walk(CallStack *stack) { + BPLOG_IF(ERROR, !stack) << "Stackwalker::Walk requires |stack|"; + assert(stack); + stack->Clear(); + + // Begin with the context frame, and keep getting callers until there are + // no more. + + // Take ownership of the pointer returned by GetContextFrame. + scoped_ptr frame(GetContextFrame()); + + while (frame.get()) { + // frame already contains a good frame with properly set instruction and + // frame_pointer fields. The frame structure comes from either the + // context frame (above) or a caller frame (below). + + // Resolve the module information, if a module map was provided. + if (modules_) { + const CodeModule *module = + modules_->GetModuleForAddress(frame->instruction); + if (module) { + frame->module = module; + if (resolver_ && + !resolver_->HasModule(frame->module) && + no_symbol_modules_.find( + module->code_file()) == no_symbol_modules_.end() && + supplier_) { + string symbol_file; + char *symbol_data = NULL; + SymbolSupplier::SymbolResult symbol_result = + supplier_->GetCStringSymbolData(module, + system_info_, + &symbol_file, + &symbol_data); + + switch (symbol_result) { + case SymbolSupplier::FOUND: + resolver_->LoadModuleUsingMemoryBuffer(frame->module, + symbol_data); + break; + case SymbolSupplier::NOT_FOUND: + no_symbol_modules_.insert(module->code_file()); + break; // nothing to do + case SymbolSupplier::INTERRUPT: + return false; + } + // Inform symbol supplier to free the unused data memory buffer. + if (resolver_->ShouldDeleteMemoryBufferAfterLoadModule()) + supplier_->FreeSymbolData(module); + } + if (resolver_) + resolver_->FillSourceLineInfo(frame.get()); + } + } + + // Add the frame to the call stack. Relinquish the ownership claim + // over the frame, because the stack now owns it. + stack->frames_.push_back(frame.release()); + if (stack->frames_.size() > max_frames_) { + BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames."; + break; + } + + // Get the next frame and take ownership. + frame.reset(GetCallerFrame(stack)); + } + + return true; +} + + +// static +Stackwalker* Stackwalker::StackwalkerForCPU( + const SystemInfo *system_info, + MinidumpContext *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) { + if (!context) { + BPLOG(ERROR) << "Can't choose a stackwalker implementation without context"; + return NULL; + } + + Stackwalker *cpu_stackwalker = NULL; + + u_int32_t cpu = context->GetContextCPU(); + switch (cpu) { + case MD_CONTEXT_X86: + cpu_stackwalker = new StackwalkerX86(system_info, + context->GetContextX86(), + memory, modules, supplier, + resolver); + break; + + case MD_CONTEXT_PPC: + cpu_stackwalker = new StackwalkerPPC(system_info, + context->GetContextPPC(), + memory, modules, supplier, + resolver); + break; + + case MD_CONTEXT_AMD64: + cpu_stackwalker = new StackwalkerAMD64(system_info, + context->GetContextAMD64(), + memory, modules, supplier, + resolver); + break; + + case MD_CONTEXT_SPARC: + cpu_stackwalker = new StackwalkerSPARC(system_info, + context->GetContextSPARC(), + memory, modules, supplier, + resolver); + break; + + case MD_CONTEXT_ARM: + int fp_register = -1; + if (system_info->os_short == "ios") + fp_register = MD_CONTEXT_ARM_REG_IOS_FP; + cpu_stackwalker = new StackwalkerARM(system_info, + context->GetContextARM(), + fp_register, memory, modules, + supplier, resolver); + break; + } + + BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) << + ", can't choose a stackwalker " + "implementation"; + return cpu_stackwalker; +} + +bool Stackwalker::InstructionAddressSeemsValid(u_int64_t address) { + const CodeModule *module = modules_->GetModuleForAddress(address); + if (!module) { + // not inside any loaded module + return false; + } + + if (!resolver_ || !supplier_) { + // we don't have a resolver and or symbol supplier, + // but we're inside a known module + return true; + } + + if (!resolver_->HasModule(module)) { + string symbol_file; + char *symbol_data = NULL; + SymbolSupplier::SymbolResult symbol_result = + supplier_->GetCStringSymbolData(module, system_info_, + &symbol_file, &symbol_data); + + if (symbol_result != SymbolSupplier::FOUND || + !resolver_->LoadModuleUsingMemoryBuffer(module, + symbol_data)) { + // we don't have symbols, but we're inside a loaded module + return true; + } + } + + StackFrame frame; + frame.module = module; + frame.instruction = address; + resolver_->FillSourceLineInfo(&frame); + // we have symbols, so return true if inside a function + return !frame.function_name.empty(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.cc new file mode 100644 index 0000000..5a9ddb1 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.cc @@ -0,0 +1,243 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_amd64.cc: amd64-specific stackwalker. +// +// See stackwalker_amd64.h for documentation. +// +// Author: Mark Mentovai, Ted Mielczarek + + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_amd64.h" + +namespace google_breakpad { + + +const StackwalkerAMD64::CFIWalker::RegisterSet +StackwalkerAMD64::cfi_register_map_[] = { + // It may seem like $rip and $rsp are callee-saves, because the callee is + // responsible for having them restored upon return. But the callee_saves + // flags here really means that the walker should assume they're + // unchanged if the CFI doesn't mention them --- clearly wrong for $rip + // and $rsp. + { "$rax", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RAX, &MDRawContextAMD64::rax }, + { "$rdx", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RDX, &MDRawContextAMD64::rdx }, + { "$rcx", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RCX, &MDRawContextAMD64::rcx }, + { "$rbx", NULL, true, + StackFrameAMD64::CONTEXT_VALID_RBX, &MDRawContextAMD64::rbx }, + { "$rsi", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RSI, &MDRawContextAMD64::rsi }, + { "$rdi", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RDI, &MDRawContextAMD64::rdi }, + { "$rbp", NULL, true, + StackFrameAMD64::CONTEXT_VALID_RBP, &MDRawContextAMD64::rbp }, + { "$rsp", ".cfa", false, + StackFrameAMD64::CONTEXT_VALID_RSP, &MDRawContextAMD64::rsp }, + { "$r8", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R8, &MDRawContextAMD64::r8 }, + { "$r9", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R9, &MDRawContextAMD64::r9 }, + { "$r10", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R10, &MDRawContextAMD64::r10 }, + { "$r11", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R11, &MDRawContextAMD64::r11 }, + { "$r12", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R12, &MDRawContextAMD64::r12 }, + { "$r13", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R13, &MDRawContextAMD64::r13 }, + { "$r14", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R14, &MDRawContextAMD64::r14 }, + { "$r15", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R15, &MDRawContextAMD64::r15 }, + { "$rip", ".ra", false, + StackFrameAMD64::CONTEXT_VALID_RIP, &MDRawContextAMD64::rip }, +}; + +StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info, + const MDRawContextAMD64 *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context), + cfi_walker_(cfi_register_map_, + (sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) { +} + + +StackFrame* StackwalkerAMD64::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameAMD64 *frame = new StackFrameAMD64(); + + // The instruction pointer is stored directly in a register, so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = StackFrameAMD64::CONTEXT_VALID_ALL; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.rip; + + return frame; +} + +StackFrameAMD64 *StackwalkerAMD64::GetCallerByCFIFrameInfo( + const vector &frames, + CFIFrameInfo *cfi_frame_info) { + StackFrameAMD64 *last_frame = static_cast(frames.back()); + + scoped_ptr frame(new StackFrameAMD64()); + if (!cfi_walker_ + .FindCallerRegisters(*memory_, *cfi_frame_info, + last_frame->context, last_frame->context_validity, + &frame->context, &frame->context_validity)) + return NULL; + + // Make sure we recovered all the essentials. + static const int essentials = (StackFrameAMD64::CONTEXT_VALID_RIP + | StackFrameAMD64::CONTEXT_VALID_RSP); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + frame->trust = StackFrame::FRAME_TRUST_CFI; + return frame.release(); +} + +StackFrameAMD64 *StackwalkerAMD64::GetCallerByStackScan( + const vector &frames) { + StackFrameAMD64 *last_frame = static_cast(frames.back()); + u_int64_t last_rsp = last_frame->context.rsp; + u_int64_t caller_rip_address, caller_rip; + + if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip)) { + // No plausible return address was found. + return NULL; + } + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameAMD64 *frame = new StackFrameAMD64(); + + frame->trust = StackFrame::FRAME_TRUST_SCAN; + frame->context = last_frame->context; + frame->context.rip = caller_rip; + // The caller's %rsp is directly underneath the return address pushed by + // the call. + frame->context.rsp = caller_rip_address + 8; + frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP; + + // Other unwinders give up if they don't have an %rbp value, so see if we + // can pass some plausible value on. + if (last_frame->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) { + // Functions typically push their caller's %rbp immediately upon entry, + // and then set %rbp to point to that. So if the callee's %rbp is + // pointing to the first word below the alleged return address, presume + // that the caller's %rbp is saved there. + if (caller_rip_address - 8 == last_frame->context.rbp) { + u_int64_t caller_rbp = 0; + if (memory_->GetMemoryAtAddress(last_frame->context.rbp, &caller_rbp) && + caller_rbp > caller_rip_address) { + frame->context.rbp = caller_rbp; + frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP; + } + } else if (last_frame->context.rbp >= caller_rip_address + 8) { + // If the callee's %rbp is plausible as a value for the caller's + // %rbp, presume that the callee left it unchanged. + frame->context.rbp = last_frame->context.rbp; + frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP; + } + } + + return frame; +} + +StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + const vector &frames = *stack->frames(); + StackFrameAMD64 *last_frame = static_cast(frames.back()); + scoped_ptr new_frame; + + // If we have DWARF CFI information, use it. + scoped_ptr cfi_frame_info( + resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL); + if (cfi_frame_info.get()) + new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get())); + + // If CFI failed, or there wasn't CFI available, fall back + // to stack scanning. + if (!new_frame.get()) { + new_frame.reset(GetCallerByStackScan(frames)); + } + + // If nothing worked, tell the caller. + if (!new_frame.get()) + return NULL; + + // Treat an instruction address of 0 as end-of-stack. + if (new_frame->context.rip == 0) + return NULL; + + // If the new stack pointer is at a lower address than the old, then + // that's clearly incorrect. Treat this as end-of-stack to enforce + // progress and avoid infinite loops. + if (new_frame->context.rsp <= last_frame->context.rsp) + return NULL; + + // new_frame->context.rip is the return address, which is one instruction + // past the CALL that caused us to arrive at the callee. Set + // new_frame->instruction to one less than that. This won't reference the + // beginning of the CALL instruction, but it's guaranteed to be within + // the CALL, which is sufficient to get the source line information to + // match up with the line that contains a function call. Callers that + // require the exact return address value may access the context.rip + // field of StackFrameAMD64. + new_frame->instruction = new_frame->context.rip - 1; + + return new_frame.release(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.h b/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.h new file mode 100644 index 0000000..cde9520 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64.h @@ -0,0 +1,99 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_amd64.h: amd64-specific stackwalker. +// +// Provides stack frames given amd64 register context and a memory region +// corresponding to a amd64 stack. +// +// Author: Mark Mentovai, Ted Mielczarek + + +#ifndef PROCESSOR_STACKWALKER_AMD64_H__ +#define PROCESSOR_STACKWALKER_AMD64_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerAMD64 : public Stackwalker { + public: + // context is a amd64 context object that gives access to amd64-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerAMD64(const SystemInfo *system_info, + const MDRawContextAMD64 *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + private: + // A STACK CFI-driven frame walker for the AMD64 + typedef SimpleCFIWalker CFIWalker; + + // Implementation of Stackwalker, using amd64 context (stack pointer in %rsp, + // stack base in %rbp) and stack conventions (saved stack pointer at 0(%rbp)) + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame(const CallStack *stack); + + // Use cfi_frame_info (derived from STACK CFI records) to construct + // the frame that called frames.back(). The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameAMD64 *GetCallerByCFIFrameInfo(const vector &frames, + CFIFrameInfo *cfi_frame_info); + + // Scan the stack for plausible return addresses. The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameAMD64 *GetCallerByStackScan(const vector &frames); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextAMD64 *context_; + + // Our register map, for cfi_walker_. + static const CFIWalker::RegisterSet cfi_register_map_[]; + + // Our CFI frame walker. + const CFIWalker cfi_walker_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_AMD64_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64_unittest.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64_unittest.cc new file mode 100644 index 0000000..a107bee --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_amd64_unittest.cc @@ -0,0 +1,561 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// stackwalker_amd64_unittest.cc: Unit tests for StackwalkerAMD64 class. + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/test_assembler.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_amd64.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameAMD64; +using google_breakpad::StackwalkerAMD64; +using google_breakpad::SystemInfo; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using std::string; +using std::vector; +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerAMD64Fixture { + public: + StackwalkerAMD64Fixture() + : stack_section(kLittleEndian), + // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(0x40000000c0000000ULL, 0x10000, "module1", "version1"), + module2(0x50000000b0000000ULL, 0x10000, "module2", "version2") { + // Identify the system as a Linux system. + system_info.os = "Linux"; + system_info.os_short = "linux"; + system_info.os_version = "Horrendous Hippo"; + system_info.cpu = "x86"; + system_info.cpu_info = ""; + + // Put distinctive values in the raw CPU context. + BrandContext(&raw_context); + + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + unsigned int buffer_size = info.size() + 1; + char *buffer = reinterpret_cast(operator new(buffer_size)); + strcpy(buffer, info.c_str()); + EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), + Return(MockSymbolSupplier::FOUND))); + } + + // Populate stack_region with the contents of stack_section. Use + // stack_section.start() as the region's starting address. + void RegionFromSection() { + string contents; + ASSERT_TRUE(stack_section.GetContents(&contents)); + stack_region.Init(stack_section.start().Value(), contents); + } + + // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. + void BrandContext(MDRawContextAMD64 *raw_context) { + u_int8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextAMD64 raw_context; + Section stack_section; + MockMemoryRegion stack_region; + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + CallStack call_stack; + const vector *frames; +}; + +class GetContextFrame: public StackwalkerAMD64Fixture, public Test { }; + +class SanityCheck: public StackwalkerAMD64Fixture, public Test { }; + +TEST_F(SanityCheck, NoResolver) { + // There should be no references to the stack in this walk: we don't + // provide any call frame information, so trying to reconstruct the + // context frame's caller should fail. So there's no need for us to + // provide stack contents. + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = 0x8000000080000000ULL; + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + NULL, NULL); + // This should succeed even without a resolver or supplier. + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_GE(1U, frames->size()); + StackFrameAMD64 *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +TEST_F(GetContextFrame, Simple) { + // There should be no references to the stack in this walk: we don't + // provide any call frame information, so trying to reconstruct the + // context frame's caller should fail. So there's no need for us to + // provide stack contents. + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = 0x8000000080000000ULL; + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_GE(1U, frames->size()); + StackFrameAMD64 *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetCallerFrame: public StackwalkerAMD64Fixture, public Test { }; + +TEST_F(GetCallerFrame, ScanWithoutSymbols) { + // When the stack walker resorts to scanning the stack, + // only addresses located within loaded modules are + // considered valid return addresses. + // Force scanning through three frames to ensure that the + // stack pointer is set properly in scan-recovered frames. + stack_section.start() = 0x8000000080000000ULL; + u_int64_t return_address1 = 0x50000000b0000100ULL; + u_int64_t return_address2 = 0x50000000b0000900ULL; + Label frame1_sp, frame2_sp, frame1_rbp; + stack_section + // frame 0 + .Append(16, 0) // space + + .D64(0x40000000b0000000ULL) // junk that's not + .D64(0x50000000d0000000ULL) // a return address + + .D64(return_address1) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(16, 0) // space + + .D64(0x40000000b0000000ULL) // more junk + .D64(0x50000000d0000000ULL) + + .Mark(&frame1_rbp) + .D64(stack_section.start()) // This is in the right place to be + // a saved rbp, but it's bogus, so + // we shouldn't report it. + + .D64(return_address2) // actual return address + // frame 2 + .Mark(&frame2_sp) + .Append(32, 0); // end of stack + + RegionFromSection(); + + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = frame1_rbp.Value(); + raw_context.rsp = stack_section.start().Value(); + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameAMD64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameAMD64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.rip); + EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp); + EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp); + + StackFrameAMD64 *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.rip); + EXPECT_EQ(frame2_sp.Value(), frame2->context.rsp); +} + +TEST_F(GetCallerFrame, ScanWithFunctionSymbols) { + // During stack scanning, if a potential return address + // is located within a loaded module that has symbols, + // it is only considered a valid return address if it + // lies within a function's bounds. + stack_section.start() = 0x8000000080000000ULL; + u_int64_t return_address = 0x50000000b0000110ULL; + Label frame1_sp, frame1_rbp; + + stack_section + // frame 0 + .Append(16, 0) // space + + .D64(0x40000000b0000000ULL) // junk that's not + .D64(0x50000000b0000000ULL) // a return address + + .D64(0x40000000c0001000ULL) // a couple of plausible addresses + .D64(0x50000000b000aaaaULL) // that are not within functions + + .D64(return_address) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(32, 0) // end of stack + .Mark(&frame1_rbp); + RegionFromSection(); + + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = frame1_rbp.Value(); + raw_context.rsp = stack_section.start().Value(); + + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 100 400 10 platypus\n"); + SetModuleSymbols(&module2, + // The calling frame's function. + "FUNC 100 400 10 echidna\n"); + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameAMD64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ("platypus", frame0->function_name); + EXPECT_EQ(0x40000000c0000100ULL, frame0->function_base); + + StackFrameAMD64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP), + frame1->context_validity); + EXPECT_EQ(return_address, frame1->context.rip); + EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp); + EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp); + EXPECT_EQ("echidna", frame1->function_name); + EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base); +} + +TEST_F(GetCallerFrame, CallerPushedRBP) { + // Functions typically push their %rbp upon entry and set %rbp pointing + // there. If stackwalking finds a plausible address for the next frame's + // %rbp directly below the return address, assume that it is indeed the + // next frame's %rbp. + stack_section.start() = 0x8000000080000000ULL; + u_int64_t return_address = 0x50000000b0000110ULL; + Label frame0_rbp, frame1_sp, frame1_rbp; + + stack_section + // frame 0 + .Append(16, 0) // space + + .D64(0x40000000b0000000ULL) // junk that's not + .D64(0x50000000b0000000ULL) // a return address + + .D64(0x40000000c0001000ULL) // a couple of plausible addresses + .D64(0x50000000b000aaaaULL) // that are not within functions + + .Mark(&frame0_rbp) + .D64(frame1_rbp) // caller-pushed %rbp + .D64(return_address) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(32, 0) // body of frame1 + .Mark(&frame1_rbp); // end of stack + RegionFromSection(); + + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = frame0_rbp.Value(); + raw_context.rsp = stack_section.start().Value(); + + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 100 400 10 sasquatch\n"); + SetModuleSymbols(&module2, + // The calling frame's function. + "FUNC 100 400 10 yeti\n"); + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameAMD64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(frame0_rbp.Value(), frame0->context.rbp); + EXPECT_EQ("sasquatch", frame0->function_name); + EXPECT_EQ(0x40000000c0000100ULL, frame0->function_base); + + StackFrameAMD64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP), + frame1->context_validity); + EXPECT_EQ(return_address, frame1->context.rip); + EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp); + EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp); + EXPECT_EQ("yeti", frame1->function_name); + EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base); +} + +struct CFIFixture: public StackwalkerAMD64Fixture { + CFIFixture() { + // Provide a bunch of STACK CFI records; we'll walk to the caller + // from every point in this series, expecting to find the same set + // of register values. + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 4000 1000 10 enchiridion\n" + // Initially, just a return address. + "STACK CFI INIT 4000 100 .cfa: $rsp 8 + .ra: .cfa 8 - ^\n" + // Push %rbx. + "STACK CFI 4001 .cfa: $rsp 16 + $rbx: .cfa 16 - ^\n" + // Save %r12 in %rbx. Weird, but permitted. + "STACK CFI 4002 $r12: $rbx\n" + // Allocate frame space, and save %r13. + "STACK CFI 4003 .cfa: $rsp 40 + $r13: .cfa 32 - ^\n" + // Put the return address in %r13. + "STACK CFI 4005 .ra: $r13\n" + // Save %rbp, and use it as a frame pointer. + "STACK CFI 4006 .cfa: $rbp 16 + $rbp: .cfa 24 - ^\n" + + // The calling function. + "FUNC 5000 1000 10 epictetus\n" + // Mark it as end of stack. + "STACK CFI INIT 5000 1000 .cfa: $rsp .ra 0\n"); + + // Provide some distinctive values for the caller's registers. + expected.rsp = 0x8000000080000000ULL; + expected.rip = 0x40000000c0005510ULL; + expected.rbp = 0x68995b1de4700266ULL; + expected.rbx = 0x5a5beeb38de23be8ULL; + expected.r12 = 0xed1b02e8cc0fc79cULL; + expected.r13 = 0x1d20ad8acacbe930ULL; + expected.r14 = 0xe94cffc2f7adaa28ULL; + expected.r15 = 0xb638d17d8da413b5ULL; + + // By default, registers are unchanged. + raw_context = expected; + } + + // Walk the stack, using stack_section as the contents of the stack + // and raw_context as the current register values. (Set + // raw_context.rsp to the stack's starting address.) Expect two + // stack frames; in the older frame, expect the callee-saves + // registers to have values matching those in 'expected'. + void CheckWalk() { + RegionFromSection(); + raw_context.rsp = stack_section.start().Value(); + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameAMD64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40000000c0004000ULL, frame0->function_base); + + StackFrameAMD64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP | + StackFrameAMD64::CONTEXT_VALID_RBX | + StackFrameAMD64::CONTEXT_VALID_R12 | + StackFrameAMD64::CONTEXT_VALID_R13 | + StackFrameAMD64::CONTEXT_VALID_R14 | + StackFrameAMD64::CONTEXT_VALID_R15), + frame1->context_validity); + EXPECT_EQ(expected.rip, frame1->context.rip); + EXPECT_EQ(expected.rsp, frame1->context.rsp); + EXPECT_EQ(expected.rbp, frame1->context.rbp); + EXPECT_EQ(expected.rbx, frame1->context.rbx); + EXPECT_EQ(expected.r12, frame1->context.r12); + EXPECT_EQ(expected.r13, frame1->context.r13); + EXPECT_EQ(expected.r14, frame1->context.r14); + EXPECT_EQ(expected.r15, frame1->context.r15); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values we expect to find for the caller's registers. + MDRawContextAMD64 expected; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, At4000) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004000ULL; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004001ULL; + raw_context.rbx = 0xbe0487d2f9eafe29ULL; // callee's (distinct) %rbx value + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004002ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0xb0118de918a4bceaULL; // callee's (distinct) %r12 value + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x0e023828dffd4d81ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0x319e68b49e3ace0fULL) // garbage + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004003ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12 + raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13 + CheckWalk(); +} + +// The results here should be the same as those at module offset 0x4003. +TEST_F(CFI, At4004) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x0e023828dffd4d81ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0x319e68b49e3ace0fULL) // garbage + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004004ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12 + raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13 + CheckWalk(); +} + +TEST_F(CFI, At4005) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x4b516dd035745953ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0xa6d445e16ae3d872ULL) // garbage + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0xaa95fa054aedfbaeULL) // garbage + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004005ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x46b1b8868891b34aULL; // callee's %r12 + raw_context.r13 = 0x40000000c0005510ULL; // return address + CheckWalk(); +} + +TEST_F(CFI, At4006) { + Label frame0_rbp; + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x043c6dfceb91aa34ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0x68995b1de4700266ULL) // saved %rbp + .Mark(&frame0_rbp) // frame pointer points here + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0xf015ee516ad89eabULL) // garbage + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004006ULL; + raw_context.rbp = frame0_rbp.Value(); + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x26e007b341acfebdULL; // callee's %r12 + raw_context.r13 = 0x40000000c0005510ULL; // return address + CheckWalk(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_arm.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_arm.cc new file mode 100644 index 0000000..0a3c522 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_arm.cc @@ -0,0 +1,293 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_arm.cc: arm-specific stackwalker. +// +// See stackwalker_arm.h for documentation. +// +// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_arm.h" + +namespace google_breakpad { + + +StackwalkerARM::StackwalkerARM(const SystemInfo *system_info, + const MDRawContextARM *context, + int fp_register, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context), fp_register_(fp_register), + context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { } + + +StackFrame* StackwalkerARM::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameARM *frame = new StackFrameARM(); + + // The instruction pointer is stored directly in a register (r15), so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = context_frame_validity_; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC]; + + return frame; +} + +StackFrameARM *StackwalkerARM::GetCallerByCFIFrameInfo( + const vector &frames, + CFIFrameInfo *cfi_frame_info) { + StackFrameARM *last_frame = static_cast(frames.back()); + + static const char *register_names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "fps", "cpsr", + NULL + }; + + // Populate a dictionary with the valid register values in last_frame. + CFIFrameInfo::RegisterValueMap callee_registers; + for (int i = 0; register_names[i]; i++) + if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i)) + callee_registers[register_names[i]] = last_frame->context.iregs[i]; + + // Use the STACK CFI data to recover the caller's register values. + CFIFrameInfo::RegisterValueMap caller_registers; + if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_, + &caller_registers)) + return NULL; + + // Construct a new stack frame given the values the CFI recovered. + scoped_ptr frame(new StackFrameARM()); + for (int i = 0; register_names[i]; i++) { + CFIFrameInfo::RegisterValueMap::iterator entry = + caller_registers.find(register_names[i]); + if (entry != caller_registers.end()) { + // We recovered the value of this register; fill the context with the + // value from caller_registers. + frame->context_validity |= StackFrameARM::RegisterValidFlag(i); + frame->context.iregs[i] = entry->second; + } else if (4 <= i && i <= 11 && (last_frame->context_validity & + StackFrameARM::RegisterValidFlag(i))) { + // If the STACK CFI data doesn't mention some callee-saves register, and + // it is valid in the callee, assume the callee has not yet changed it. + // Registers r4 through r11 are callee-saves, according to the Procedure + // Call Standard for the ARM Architecture, which the Linux ABI follows. + frame->context_validity |= StackFrameARM::RegisterValidFlag(i); + frame->context.iregs[i] = last_frame->context.iregs[i]; + } + } + // If the CFI doesn't recover the PC explicitly, then use .ra. + if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) { + CFIFrameInfo::RegisterValueMap::iterator entry = + caller_registers.find(".ra"); + if (entry != caller_registers.end()) { + if (fp_register_ == -1) { + frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second; + } else { + // The CFI updated the link register and not the program counter. + // Handle getting the program counter from the link register. + frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC; + frame->context_validity |= StackFrameARM::CONTEXT_VALID_LR; + frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = entry->second; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = + last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR]; + } + } + } + // If the CFI doesn't recover the SP explicitly, then use .cfa. + if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) { + CFIFrameInfo::RegisterValueMap::iterator entry = + caller_registers.find(".cfa"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP; + frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second; + } + } + + // If we didn't recover the PC and the SP, then the frame isn't very useful. + static const int essentials = (StackFrameARM::CONTEXT_VALID_SP + | StackFrameARM::CONTEXT_VALID_PC); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + frame->trust = StackFrame::FRAME_TRUST_CFI; + return frame.release(); +} + +StackFrameARM *StackwalkerARM::GetCallerByStackScan( + const vector &frames) { + StackFrameARM *last_frame = static_cast(frames.back()); + u_int32_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP]; + u_int32_t caller_sp, caller_pc; + + if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc)) { + // No plausible return address was found. + return NULL; + } + + // ScanForReturnAddress found a reasonable return address. Advance + // %sp to the location above the one where the return address was + // found. + caller_sp += 4; + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameARM *frame = new StackFrameARM(); + + frame->trust = StackFrame::FRAME_TRUST_SCAN; + frame->context = last_frame->context; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = caller_pc; + frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = caller_sp; + frame->context_validity = StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_SP; + + return frame; +} + +StackFrameARM *StackwalkerARM::GetCallerByFramePointer( + const vector &frames) { + StackFrameARM *last_frame = static_cast(frames.back()); + + if (!(last_frame->context_validity & + StackFrameARM::RegisterValidFlag(fp_register_))) { + return NULL; + } + + u_int32_t last_fp = last_frame->context.iregs[fp_register_]; + + u_int32_t caller_fp = 0; + if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) { + BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x" + << std::hex << last_fp; + return NULL; + } + + u_int32_t caller_lr = 0; + if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 4, &caller_lr)) { + BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 4: 0x" + << std::hex << (last_fp + 4); + return NULL; + } + + u_int32_t caller_sp = last_fp ? last_fp + 8 : + last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP]; + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameARM *frame = new StackFrameARM(); + + frame->trust = StackFrame::FRAME_TRUST_FP; + frame->context = last_frame->context; + frame->context.iregs[fp_register_] = caller_fp; + frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = caller_sp; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = + last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR]; + frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = caller_lr; + frame->context_validity = StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(fp_register_) | + StackFrameARM::CONTEXT_VALID_SP; + return frame; +} + +StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + const vector &frames = *stack->frames(); + StackFrameARM *last_frame = static_cast(frames.back()); + scoped_ptr frame; + + // See if there is DWARF call frame information covering this address. + scoped_ptr cfi_frame_info( + resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL); + if (cfi_frame_info.get()) + frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get())); + + // If CFI failed, or there wasn't CFI available, fall back + // to frame pointer, if this is configured. + if (fp_register_ >= 0 && !frame.get()) + frame.reset(GetCallerByFramePointer(frames)); + + // If everuthing failed, fall back to stack scanning. + if (!frame.get()) + frame.reset(GetCallerByStackScan(frames)); + + // If nothing worked, tell the caller. + if (!frame.get()) + return NULL; + + + // An instruction address of zero marks the end of the stack. + if (frame->context.iregs[MD_CONTEXT_ARM_REG_PC] == 0) + return NULL; + + // If the new stack pointer is at a lower address than the old, then + // that's clearly incorrect. Treat this as end-of-stack to enforce + // progress and avoid infinite loops. + if (frame->context.iregs[MD_CONTEXT_ARM_REG_SP] + < last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP]) + return NULL; + + // The new frame's context's PC is the return address, which is one + // instruction past the instruction that caused us to arrive at the + // callee. Set new_frame->instruction to one less than the PC. This won't + // reference the beginning of the call instruction, but it's at least + // within it, which is sufficient to get the source line information to + // match up with the line that contains the function call. Callers that + // require the exact return address value may access + // frame->context.iregs[MD_CONTEXT_ARM_REG_PC]. + frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 2; + + return frame.release(); +} + + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_arm.h b/src/lib/crashdump/gbreakpad/processor/stackwalker_arm.h new file mode 100644 index 0000000..24fc60d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_arm.h @@ -0,0 +1,107 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_arm.h: arm-specific stackwalker. +// +// Provides stack frames given arm register context and a memory region +// corresponding to an arm stack. +// +// Author: Mark Mentovai, Ted Mielczarek + + +#ifndef PROCESSOR_STACKWALKER_ARM_H__ +#define PROCESSOR_STACKWALKER_ARM_H__ + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerARM : public Stackwalker { + public: + // context is an arm context object that gives access to arm-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerARM(const SystemInfo *system_info, + const MDRawContextARM *context, + int fp_register, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + // Change the context validity mask of the frame returned by + // GetContextFrame to VALID. This is only for use by unit tests; the + // default behavior is correct for all application code. + void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; } + + private: + // Implementation of Stackwalker, using arm context and stack conventions. + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame(const CallStack *stack); + + // Use cfi_frame_info (derived from STACK CFI records) to construct + // the frame that called frames.back(). The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameARM *GetCallerByCFIFrameInfo(const vector &frames, + CFIFrameInfo *cfi_frame_info); + + // Use the frame pointer. The caller takes ownership of the returned frame. + // Return NULL on failure. + StackFrameARM *GetCallerByFramePointer(const vector &frames); + + // Scan the stack for plausible return addresses. The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameARM *GetCallerByStackScan(const vector &frames); + + // Stores the CPU context corresponding to the youngest stack frame, to + // be returned by GetContextFrame. + const MDRawContextARM *context_; + + // The register to use a as frame pointer. The value is -1 if frame pointer + // cannot be used. + int fp_register_; + + // Validity mask for youngest stack frame. This is always + // CONTEXT_VALID_ALL in real use; it is only changeable for the sake of + // unit tests. + int context_frame_validity_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_ARM_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_arm_unittest.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_arm_unittest.cc new file mode 100644 index 0000000..6a623c2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_arm_unittest.cc @@ -0,0 +1,764 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class. + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/test_assembler.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_arm.h" +#include "processor/windows_frame_info.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameARM; +using google_breakpad::StackwalkerARM; +using google_breakpad::SystemInfo; +using google_breakpad::WindowsFrameInfo; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using std::string; +using std::vector; +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerARMFixture { + public: + StackwalkerARMFixture() + : stack_section(kLittleEndian), + // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(0x40000000, 0x10000, "module1", "version1"), + module2(0x50000000, 0x10000, "module2", "version2") { + // Identify the system as a Linux system. + system_info.os = "Linux"; + system_info.os_short = "linux"; + system_info.os_version = "Lugubrious Labrador"; + system_info.cpu = "arm"; + system_info.cpu_info = ""; + + // Put distinctive values in the raw CPU context. + BrandContext(&raw_context); + + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + unsigned int buffer_size = info.size() + 1; + char *buffer = reinterpret_cast(operator new(buffer_size)); + strcpy(buffer, info.c_str()); + EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), + Return(MockSymbolSupplier::FOUND))); + } + + // Populate stack_region with the contents of stack_section. Use + // stack_section.start() as the region's starting address. + void RegionFromSection() { + string contents; + ASSERT_TRUE(stack_section.GetContents(&contents)); + stack_region.Init(stack_section.start().Value(), contents); + } + + // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. + void BrandContext(MDRawContextARM *raw_context) { + u_int8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextARM raw_context; + Section stack_section; + MockMemoryRegion stack_region; + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + CallStack call_stack; + const vector *frames; +}; + +class SanityCheck: public StackwalkerARMFixture, public Test { }; + +TEST_F(SanityCheck, NoResolver) { + // Since we have no call frame information, and all unwinding + // requires call frame information, the stack walk will end after + // the first frame. + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, + NULL, NULL); + // This should succeed even without a resolver or supplier. + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); + StackFrameARM *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetContextFrame: public StackwalkerARMFixture, public Test { }; + +TEST_F(GetContextFrame, Simple) { + // Since we have no call frame information, and all unwinding + // requires call frame information, the stack walk will end after + // the first frame. + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); + StackFrameARM *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetCallerFrame: public StackwalkerARMFixture, public Test { }; + +TEST_F(GetCallerFrame, ScanWithoutSymbols) { + // When the stack walker resorts to scanning the stack, + // only addresses located within loaded modules are + // considered valid return addresses. + // Force scanning through three frames to ensure that the + // stack pointer is set properly in scan-recovered frames. + stack_section.start() = 0x80000000; + u_int32_t return_address1 = 0x50000100; + u_int32_t return_address2 = 0x50000900; + Label frame1_sp, frame2_sp; + stack_section + // frame 0 + .Append(16, 0) // space + + .D32(0x40090000) // junk that's not + .D32(0x60000000) // a return address + + .D32(return_address1) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(16, 0) // space + + .D32(0xF0000000) // more junk + .D32(0x0000000D) + + .D32(return_address2) // actual return address + // frame 2 + .Mark(&frame2_sp) + .Append(32, 0); // end of stack + RegionFromSection(); + + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + + StackFrameARM *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]); +} + +TEST_F(GetCallerFrame, ScanWithFunctionSymbols) { + // During stack scanning, if a potential return address + // is located within a loaded module that has symbols, + // it is only considered a valid return address if it + // lies within a function's bounds. + stack_section.start() = 0x80000000; + u_int32_t return_address = 0x50000200; + Label frame1_sp; + + stack_section + // frame 0 + .Append(16, 0) // space + + .D32(0x40090000) // junk that's not + .D32(0x60000000) // a return address + + .D32(0x40001000) // a couple of plausible addresses + .D32(0x5000F000) // that are not within functions + + .D32(return_address) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(32, 0); // end of stack + RegionFromSection(); + + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40000200; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 100 400 10 monotreme\n"); + SetModuleSymbols(&module2, + // The calling frame's function. + "FUNC 100 400 10 marsupial\n"); + + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + EXPECT_EQ("monotreme", frame0->function_name); + EXPECT_EQ(0x40000100, frame0->function_base); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ("marsupial", frame1->function_name); + EXPECT_EQ(0x50000100, frame1->function_base); +} + +struct CFIFixture: public StackwalkerARMFixture { + CFIFixture() { + // Provide a bunch of STACK CFI records; we'll walk to the caller + // from every point in this series, expecting to find the same set + // of register values. + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 4000 1000 10 enchiridion\n" + // Initially, nothing has been pushed on the stack, + // and the return address is still in the link register. + "STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n" + // Push r4, the frame pointer, and the link register. + "STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^" + " r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n" + // Save r4..r7 in r0..r3: verify that we populate + // the youngest frame with all the values we have. + "STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n" + // Restore r4..r7. Save the non-callee-saves register r1. + "STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^" + " r4: r4 r5: r5 r6: r6 r7: r7\n" + // Move the .cfa back four bytes, to point at the return + // address, and restore the sp explicitly. + "STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^" + " r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n" + // Recover the PC explicitly from a new stack slot; + // provide garbage for the .ra. + "STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n" + + // The calling function. + "FUNC 5000 1000 10 epictetus\n" + // Mark it as end of stack. + "STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n" + + // A function whose CFI makes the stack pointer + // go backwards. + "FUNC 6000 1000 20 palinal\n" + "STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n" + + // A function with CFI expressions that can't be + // evaluated. + "FUNC 7000 1000 20 rhetorical\n" + "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n"); + + // Provide some distinctive values for the caller's registers. + expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510; + expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + expected.iregs[4] = 0xb5d55e68; + expected.iregs[5] = 0xebd134f3; + expected.iregs[6] = 0xa31e74bc; + expected.iregs[7] = 0x2dcb16b3; + expected.iregs[8] = 0x2ada2137; + expected.iregs[9] = 0xbbbb557d; + expected.iregs[10] = 0x48bf8ca7; + expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110; + + // Expect CFI to recover all callee-saves registers. Since CFI is the + // only stack frame construction technique we have, aside from the + // context frame itself, there's no way for us to have a set of valid + // registers smaller than this. + expected_validity = (StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_SP | + StackFrameARM::CONTEXT_VALID_R4 | + StackFrameARM::CONTEXT_VALID_R5 | + StackFrameARM::CONTEXT_VALID_R6 | + StackFrameARM::CONTEXT_VALID_R7 | + StackFrameARM::CONTEXT_VALID_R8 | + StackFrameARM::CONTEXT_VALID_R9 | + StackFrameARM::CONTEXT_VALID_R10 | + StackFrameARM::CONTEXT_VALID_FP); + + // By default, context frames provide all registers, as normal. + context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL; + + // By default, registers are unchanged. + raw_context = expected; + } + + // Walk the stack, using stack_section as the contents of the stack + // and raw_context as the current register values. (Set the stack + // pointer to the stack's starting address.) Expect two stack + // frames; in the older frame, expect the callee-saves registers to + // have values matching those in 'expected'. + void CheckWalk() { + RegionFromSection(); + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, + &modules, &supplier, &resolver); + walker.SetContextFrameValidity(context_frame_validity); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(context_frame_validity, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40004000U, frame0->function_base); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ(expected_validity, frame1->context_validity); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R1) + EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R4) + EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R5) + EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R6) + EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R7) + EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R8) + EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R9) + EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R10) + EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_FP) + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP], + frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]); + + // We would never have gotten a frame in the first place if the SP + // and PC weren't valid or ->instruction weren't set. + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP], + frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC], + frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC], + frame1->instruction + 2); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values we expect to find for the caller's registers. + MDRawContextARM expected; + + // The validity mask for expected. + int expected_validity; + + // The validity mask to impose on the context frame. + int context_frame_validity; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, At4000) { + stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0xb5d55e68) // saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001; + raw_context.iregs[4] = 0x635adc9f; // distinct callee r4 + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp + CheckWalk(); +} + +// As above, but unwind from a context that has only the PC and SP. +TEST_F(CFI, At4001LimitedValidity) { + context_frame_validity = + StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP; + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001; + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0xb5d55e68) // saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + expected_validity = (StackFrameARM::CONTEXT_VALID_PC + | StackFrameARM::CONTEXT_VALID_SP + | StackFrameARM::CONTEXT_VALID_FP + | StackFrameARM::CONTEXT_VALID_R4); + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0xfb81ff3d) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002; + raw_context.iregs[0] = 0xb5d55e68; // saved r4 + raw_context.iregs[1] = 0xebd134f3; // saved r5 + raw_context.iregs[2] = 0xa31e74bc; // saved r6 + raw_context.iregs[3] = 0x2dcb16b3; // saved r7 + raw_context.iregs[4] = 0xfdd35466; // distinct callee r4 + raw_context.iregs[5] = 0xf18c946c; // distinct callee r5 + raw_context.iregs[6] = 0xac2079e8; // distinct callee r6 + raw_context.iregs[7] = 0xa449829f; // distinct callee r7 + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xcb78040e) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003; + raw_context.iregs[1] = 0xfb756319; // distinct callee r1 + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// We have no new rule at module offset 0x4004, so the results here should +// be the same as those at module offset 0x4003. +TEST_F(CFI, At4004) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xcb78040e) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004; + raw_context.iregs[1] = 0xfb756319; // distinct callee r1 + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// Here we move the .cfa, but provide an explicit rule to recover the SP, +// so again there should be no change in the registers recovered. +TEST_F(CFI, At4005) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xf013f841) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005; + raw_context.iregs[1] = 0xfb756319; // distinct callee r1 + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// Here we provide an explicit rule for the PC, and have the saved .ra be +// bogus. +TEST_F(CFI, At4006) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x40005510) // saved pc + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xf013f841) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0xf8d15783) // .ra rule recovers this, which is garbage + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006; + raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// Check that we reject rules that would cause the stack pointer to +// move in the wrong direction. +TEST_F(CFI, RejectBackwards) { + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510; + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); +} + +// Check that we reject rules whose expressions' evaluation fails. +TEST_F(CFI, RejectBadExpressions) { + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + StackwalkerARM walker(&system_info, &raw_context, -1, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); +} + +class StackwalkerARMFixtureIOS : public StackwalkerARMFixture { + public: + StackwalkerARMFixtureIOS() { + system_info.os = "iOS"; + system_info.os_short = "ios"; + } +}; + +class GetFramesByFramePointer: public StackwalkerARMFixtureIOS, public Test { }; + +TEST_F(GetFramesByFramePointer, OnlyFramePointer) { + stack_section.start() = 0x80000000; + u_int32_t return_address1 = 0x50000100; + u_int32_t return_address2 = 0x50000900; + Label frame1_sp, frame2_sp; + Label frame1_fp, frame2_fp; + stack_section + // frame 0 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame1_fp) // Next fp will point to the next value. + .D32(frame2_fp) // Save current frame pointer. + .D32(return_address2) // Save current link register. + .Mark(&frame1_sp) + + // frame 1 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame2_fp) + .D32(0) + .D32(0) + .Mark(&frame2_sp) + + // frame 2 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000); // a return address. + RegionFromSection(); + + + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1; + raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value(); + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP, + &stack_region, &modules, &supplier, &resolver); + + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(frame2_fp.Value(), + frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); + + StackFrameARM *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); +} + +TEST_F(GetFramesByFramePointer, FramePointerAndCFI) { + // Provide the standatd STACK CFI records that is obtained when exmining an + // executable produced by XCode. + SetModuleSymbols(&module1, + // Adding a function in CFI. + "FUNC 4000 1000 10 enchiridion\n" + + "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: lr\n" + "STACK CFI 4001 .cfa: sp 8 + .ra: .cfa -4 + ^" + " r7: .cfa -8 + ^\n" + "STACK CFI 4002 .cfa: r7 8 +\n" + ); + + stack_section.start() = 0x80000000; + u_int32_t return_address1 = 0x40004010; + u_int32_t return_address2 = 0x50000900; + Label frame1_sp, frame2_sp; + Label frame1_fp, frame2_fp; + stack_section + // frame 0 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame1_fp) // Next fp will point to the next value. + .D32(frame2_fp) // Save current frame pointer. + .D32(return_address2) // Save current link register. + .Mark(&frame1_sp) + + // frame 1 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame2_fp) + .D32(0) + .D32(0) + .Mark(&frame2_sp) + + // frame 2 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000); // a return address. + RegionFromSection(); + + + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x50000400; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = return_address1; + raw_context.iregs[MD_CONTEXT_ARM_REG_IOS_FP] = frame1_fp.Value(); + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, MD_CONTEXT_ARM_REG_IOS_FP, + &stack_region, &modules, &supplier, &resolver); + + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(frame2_fp.Value(), + frame1->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); + EXPECT_EQ("enchiridion", frame1->function_name); + EXPECT_EQ(0x40004000U, frame1->function_base); + + + StackFrameARM *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); + ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_LR | + StackFrameARM::RegisterValidFlag(MD_CONTEXT_ARM_REG_IOS_FP) | + StackFrameARM::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_LR]); + EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(0, frame2->context.iregs[MD_CONTEXT_ARM_REG_IOS_FP]); +} diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.cc new file mode 100644 index 0000000..4d9a628 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_ppc.cc: ppc-specific stackwalker. +// +// See stackwalker_ppc.h for documentation. +// +// Author: Mark Mentovai + + +#include "processor/stackwalker_ppc.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/logging.h" + +namespace google_breakpad { + + +StackwalkerPPC::StackwalkerPPC(const SystemInfo *system_info, + const MDRawContextPPC *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context) { + if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) { + // This implementation only covers 32-bit ppc CPUs. The limits of the + // supplied stack are invalid. Mark memory_ = NULL, which will cause + // stackwalking to fail. + BPLOG(ERROR) << "Memory out of range for stackwalking: " << + HexString(memory_->GetBase()) << "+" << + HexString(memory_->GetSize()); + memory_ = NULL; + } +} + + +StackFrame* StackwalkerPPC::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFramePPC *frame = new StackFramePPC(); + + // The instruction pointer is stored directly in a register, so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = StackFramePPC::CONTEXT_VALID_ALL; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.srr0; + + return frame; +} + + +StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + // The instruction pointers for previous frames are saved on the stack. + // The typical ppc calling convention is for the called procedure to store + // its return address in the calling procedure's stack frame at 8(%r1), + // and to allocate its own stack frame by decrementing %r1 (the stack + // pointer) and saving the old value of %r1 at 0(%r1). Because the ppc has + // no hardware stack, there is no distinction between the stack pointer and + // frame pointer, and what is typically thought of as the frame pointer on + // an x86 is usually referred to as the stack pointer on a ppc. + + StackFramePPC *last_frame = static_cast( + stack->frames()->back()); + + // A caller frame must reside higher in memory than its callee frames. + // Anything else is an error, or an indication that we've reached the + // end of the stack. + u_int32_t stack_pointer; + if (!memory_->GetMemoryAtAddress(last_frame->context.gpr[1], + &stack_pointer) || + stack_pointer <= last_frame->context.gpr[1]) { + return NULL; + } + + // Mac OS X/Darwin gives 1 as the return address from the bottom-most + // frame in a stack (a thread's entry point). I haven't found any + // documentation on this, but 0 or 1 would be bogus return addresses, + // so check for them here and return false (end of stack) when they're + // hit to avoid having a phantom frame. + u_int32_t instruction; + if (!memory_->GetMemoryAtAddress(stack_pointer + 8, &instruction) || + instruction <= 1) { + return NULL; + } + + StackFramePPC *frame = new StackFramePPC(); + + frame->context = last_frame->context; + frame->context.srr0 = instruction; + frame->context.gpr[1] = stack_pointer; + frame->context_validity = StackFramePPC::CONTEXT_VALID_SRR0 | + StackFramePPC::CONTEXT_VALID_GPR1; + frame->trust = StackFrame::FRAME_TRUST_FP; + + // frame->context.srr0 is the return address, which is one instruction + // past the branch that caused us to arrive at the callee. Set + // frame_ppc->instruction to four less than that. Since all ppc + // instructions are 4 bytes wide, this is the address of the branch + // instruction. This allows source line information to match up with the + // line that contains a function call. Callers that require the exact + // return address value may access the context.srr0 field of StackFramePPC. + frame->instruction = frame->context.srr0 - 4; + + return frame; +} + + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.h b/src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.h new file mode 100644 index 0000000..bfbb4f8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_ppc.h @@ -0,0 +1,79 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_ppc.h: ppc-specific stackwalker. +// +// Provides stack frames given ppc register context and a memory region +// corresponding to a ppc stack. +// +// Author: Mark Mentovai + + +#ifndef PROCESSOR_STACKWALKER_PPC_H__ +#define PROCESSOR_STACKWALKER_PPC_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerPPC : public Stackwalker { + public: + // context is a ppc context object that gives access to ppc-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerPPC(const SystemInfo *system_info, + const MDRawContextPPC *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + private: + // Implementation of Stackwalker, using ppc context (stack pointer in %r1, + // saved program counter in %srr0) and stack conventions (saved stack + // pointer at 0(%r1), return address at 8(0(%r1)). + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame(const CallStack *stack); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextPPC *context_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_PPC_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_selftest.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_selftest.cc new file mode 100644 index 0000000..fdd1527 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_selftest.cc @@ -0,0 +1,425 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the +// running process' stack as test data, if running on an x86 or ppc and +// compiled with gcc. This test is not enabled in the "make check" suite +// by default, because certain optimizations interfere with its proper +// operation. To turn it on, configure with --enable-selftest. +// +// Optimizations that cause problems: +// - stack frame reuse. The Recursor function here calls itself with +// |return Recursor|. When the caller's frame is reused, it will cause +// CountCallerFrames to correctly return the same number of frames +// in both the caller and callee. This is considered an unexpected +// condition in the test, which expects a callee to have one more +// caller frame in the stack than its caller. +// - frame pointer omission. Even with a stackwalker that understands +// this optimization, the code to harness debug information currently +// only exists to retrieve it from minidumps, not the current process. +// +// This test can also serve as a developmental and debugging aid if +// PRINT_STACKS is defined. +// +// Author: Mark Mentovai + +#include "processor/logging.h" + +#if defined(__i386) && !defined(__i386__) +#define __i386__ +#endif +#if defined(__sparc) && !defined(__sparc__) +#define __sparc__ +#endif + +#if (defined(__SUNPRO_CC) || defined(__GNUC__)) && \ + (defined(__i386__) || defined(__ppc__) || defined(__sparc__)) + + +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/scoped_ptr.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::MemoryRegion; +using google_breakpad::scoped_ptr; +using google_breakpad::StackFrame; +using google_breakpad::StackFramePPC; +using google_breakpad::StackFrameX86; +using google_breakpad::StackFrameSPARC; + +#if defined(__i386__) +#include "processor/stackwalker_x86.h" +using google_breakpad::StackwalkerX86; +#elif defined(__ppc__) +#include "processor/stackwalker_ppc.h" +using google_breakpad::StackwalkerPPC; +#elif defined(__sparc__) +#include "processor/stackwalker_sparc.h" +using google_breakpad::StackwalkerSPARC; +#endif // __i386__ || __ppc__ || __sparc__ + +#define RECURSION_DEPTH 100 + + +// A simple MemoryRegion subclass that provides direct access to this +// process' memory space by pointer. +class SelfMemoryRegion : public MemoryRegion { + public: + virtual u_int64_t GetBase() { return 0; } + virtual u_int32_t GetSize() { return 0xffffffff; } + + bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) { + return GetMemoryAtAddressInternal(address, value); } + bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) { + return GetMemoryAtAddressInternal(address, value); } + bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) { + return GetMemoryAtAddressInternal(address, value); } + bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) { + return GetMemoryAtAddressInternal(address, value); } + + private: + template bool GetMemoryAtAddressInternal(u_int64_t address, + T* value) { + // Without knowing what addresses are actually mapped, just assume that + // everything low is not mapped. This helps the stackwalker catch the + // end of a stack when it tries to dereference a null or low pointer + // in an attempt to find the caller frame. Other unmapped accesses will + // cause the program to crash, but that would properly be a test failure. + if (address < 0x100) + return false; + + u_int8_t* memory = 0; + *value = *reinterpret_cast(&memory[address]); + return true; + } +}; + + +#if defined(__GNUC__) + + +#if defined(__i386__) + +// GetEBP returns the current value of the %ebp register. Because it's +// implemented as a function, %ebp itself contains GetEBP's frame pointer +// and not the caller's frame pointer. Dereference %ebp to obtain the +// caller's frame pointer, which the compiler-generated preamble stored +// on the stack (provided frame pointers are not being omitted.) Because +// this function depends on the compiler-generated preamble, inlining is +// disabled. +static u_int32_t GetEBP() __attribute__((noinline)); +static u_int32_t GetEBP() { + u_int32_t ebp; + __asm__ __volatile__( + "movl (%%ebp), %0" + : "=a" (ebp) + ); + return ebp; +} + + +// The caller's %esp is 8 higher than the value of %ebp in this function, +// assuming that it's not inlined and that the standard prolog is used. +// The CALL instruction places a 4-byte return address on the stack above +// the caller's %esp, and this function's prolog will save the caller's %ebp +// on the stack as well, for another 4 bytes, before storing %esp in %ebp. +static u_int32_t GetESP() __attribute__((noinline)); +static u_int32_t GetESP() { + u_int32_t ebp; + __asm__ __volatile__( + "movl %%ebp, %0" + : "=a" (ebp) + ); + return ebp + 8; +} + + +// GetEIP returns the instruction pointer identifying the next instruction +// to execute after GetEIP returns. It obtains this information from the +// stack, where it was placed by the call instruction that called GetEIP. +// This function depends on frame pointers not being omitted. It is possible +// to write a pure asm version of this routine that has no compiler-generated +// preamble and uses %esp instead of %ebp; that would function in the +// absence of frame pointers. However, the simpler approach is used here +// because GetEBP and stackwalking necessarily depends on access to frame +// pointers. Because this function depends on a call instruction and the +// compiler-generated preamble, inlining is disabled. +static u_int32_t GetEIP() __attribute__((noinline)); +static u_int32_t GetEIP() { + u_int32_t eip; + __asm__ __volatile__( + "movl 4(%%ebp), %0" + : "=a" (eip) + ); + return eip; +} + + +#elif defined(__ppc__) + + +// GetSP returns the current value of the %r1 register, which by convention, +// is the stack pointer on ppc. Because it's implemented as a function, +// %r1 itself contains GetSP's own stack pointer and not the caller's stack +// pointer. Dereference %r1 to obtain the caller's stack pointer, which the +// compiler-generated prolog stored on the stack. Because this function +// depends on the compiler-generated prolog, inlining is disabled. +static u_int32_t GetSP() __attribute__((noinline)); +static u_int32_t GetSP() { + u_int32_t sp; + __asm__ __volatile__( + "lwz %0, 0(r1)" + : "=r" (sp) + ); + return sp; +} + + +// GetPC returns the program counter identifying the next instruction to +// execute after GetPC returns. It obtains this information from the +// link register, where it was placed by the branch instruction that called +// GetPC. Because this function depends on the caller's use of a branch +// instruction, inlining is disabled. +static u_int32_t GetPC() __attribute__((noinline)); +static u_int32_t GetPC() { + u_int32_t lr; + __asm__ __volatile__( + "mflr %0" + : "=r" (lr) + ); + return lr; +} + + +#elif defined(__sparc__) + + +// GetSP returns the current value of the %sp/%o6/%g_r[14] register, which +// by convention, is the stack pointer on sparc. Because it's implemented +// as a function, %sp itself contains GetSP's own stack pointer and not +// the caller's stack pointer. Dereference to obtain the caller's stack +// pointer, which the compiler-generated prolog stored on the stack. +// Because this function depends on the compiler-generated prolog, inlining +// is disabled. +static u_int32_t GetSP() __attribute__((noinline)); +static u_int32_t GetSP() { + u_int32_t sp; + __asm__ __volatile__( + "mov %%fp, %0" + : "=r" (sp) + ); + return sp; +} + +// GetFP returns the current value of the %fp register. Because it's +// implemented as a function, %fp itself contains GetFP's frame pointer +// and not the caller's frame pointer. Dereference %fp to obtain the +// caller's frame pointer, which the compiler-generated preamble stored +// on the stack (provided frame pointers are not being omitted.) Because +// this function depends on the compiler-generated preamble, inlining is +// disabled. +static u_int32_t GetFP() __attribute__((noinline)); +static u_int32_t GetFP() { + u_int32_t fp; + __asm__ __volatile__( + "ld [%%fp+56], %0" + : "=r" (fp) + ); + return fp; +} + +// GetPC returns the program counter identifying the next instruction to +// execute after GetPC returns. It obtains this information from the +// link register, where it was placed by the branch instruction that called +// GetPC. Because this function depends on the caller's use of a branch +// instruction, inlining is disabled. +static u_int32_t GetPC() __attribute__((noinline)); +static u_int32_t GetPC() { + u_int32_t pc; + __asm__ __volatile__( + "mov %%i7, %0" + : "=r" (pc) + ); + return pc + 8; +} + +#endif // __i386__ || __ppc__ || __sparc__ + +#elif defined(__SUNPRO_CC) + +#if defined(__i386__) +extern "C" { +extern u_int32_t GetEIP(); +extern u_int32_t GetEBP(); +extern u_int32_t GetESP(); +} +#elif defined(__sparc__) +extern "C" { +extern u_int32_t GetPC(); +extern u_int32_t GetFP(); +extern u_int32_t GetSP(); +} +#endif // __i386__ || __sparc__ + +#endif // __GNUC__ || __SUNPRO_CC + +// CountCallerFrames returns the number of stack frames beneath the function +// that called CountCallerFrames. Because this function's return value +// is dependent on the size of the stack beneath it, inlining is disabled, +// and any function that calls this should not be inlined either. +#if defined(__GNUC__) +static unsigned int CountCallerFrames() __attribute__((noinline)); +#elif defined(__SUNPRO_CC) +static unsigned int CountCallerFrames(); +#endif +static unsigned int CountCallerFrames() { + SelfMemoryRegion memory; + BasicSourceLineResolver resolver; + +#if defined(__i386__) + MDRawContextX86 context = MDRawContextX86(); + context.eip = GetEIP(); + context.ebp = GetEBP(); + context.esp = GetESP(); + + StackwalkerX86 stackwalker = StackwalkerX86(NULL, &context, &memory, NULL, + NULL, &resolver); +#elif defined(__ppc__) + MDRawContextPPC context = MDRawContextPPC(); + context.srr0 = GetPC(); + context.gpr[1] = GetSP(); + + StackwalkerPPC stackwalker = StackwalkerPPC(NULL, &context, &memory, NULL, + NULL, &resolver); +#elif defined(__sparc__) + MDRawContextSPARC context = MDRawContextSPARC(); + context.pc = GetPC(); + context.g_r[14] = GetSP(); + context.g_r[30] = GetFP(); + + StackwalkerSPARC stackwalker = StackwalkerSPARC(NULL, &context, &memory, + NULL, NULL, &resolver); +#endif // __i386__ || __ppc__ || __sparc__ + + CallStack stack; + stackwalker.Walk(&stack); + +#ifdef PRINT_STACKS + printf("\n"); + for (unsigned int frame_index = 0; + frame_index < stack.frames()->size(); + ++frame_index) { + StackFrame *frame = stack.frames()->at(frame_index); + printf("frame %-3d instruction = 0x%08" PRIx64, + frame_index, frame->instruction); +#if defined(__i386__) + StackFrameX86 *frame_x86 = reinterpret_cast(frame); + printf(" esp = 0x%08x ebp = 0x%08x\n", + frame_x86->context.esp, frame_x86->context.ebp); +#elif defined(__ppc__) + StackFramePPC *frame_ppc = reinterpret_cast(frame); + printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]); +#elif defined(__sparc__) + StackFrameSPARC *frame_sparc = reinterpret_cast(frame); + printf(" sp = 0x%08x fp = 0x%08x\n", + frame_sparc->context.g_r[14], frame_sparc->context.g_r[30]); +#endif // __i386__ || __ppc__ || __sparc__ + } +#endif // PRINT_STACKS + + // Subtract 1 because the caller wants the number of frames beneath + // itself. Because the caller called us, subract two for our frame and its + // frame, which are included in stack.size(). + return stack.frames()->size() - 2; +} + + +// Recursor verifies that the number stack frames beneath itself is one more +// than the number of stack frames beneath its parent. When depth frames +// have been reached, Recursor stops checking and returns success. If the +// frame count check fails at any depth, Recursor will stop and return false. +// Because this calls CountCallerFrames, inlining is disabled. +#if defined(__GNUC__) +static bool Recursor(unsigned int depth, unsigned int parent_callers) + __attribute__((noinline)); +#elif defined(__SUNPRO_CC) +static bool Recursor(unsigned int depth, unsigned int parent_callers); +#endif +static bool Recursor(unsigned int depth, unsigned int parent_callers) { + unsigned int callers = CountCallerFrames(); + if (callers != parent_callers + 1) + return false; + + if (depth) + return Recursor(depth - 1, callers); + + // depth == 0 + return true; +} + + +// Because this calls CountCallerFrames, inlining is disabled - but because +// it's main (and nobody calls it other than the entry point), it wouldn't +// be inlined anyway. +#if defined(__GNUC__) +int main(int argc, char** argv) __attribute__((noinline)); +#elif defined(__SUNPRO_CC) +int main(int argc, char** argv); +#endif +int main(int argc, char** argv) { + BPLOG_INIT(&argc, &argv); + + return Recursor(RECURSION_DEPTH, CountCallerFrames()) ? 0 : 1; +} + + +#else +// Not i386 or ppc or sparc? We can only test stacks we know how to walk. + + +int main(int argc, char **argv) { + BPLOG_INIT(&argc, &argv); + + // "make check" interprets an exit status of 77 to mean that the test is + // not supported. + BPLOG(ERROR) << "Selftest not supported here"; + return 77; +} + + +#endif // (__GNUC__ || __SUNPRO_CC) && (__i386__ || __ppc__ || __sparc__) diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_selftest_sol.s b/src/lib/crashdump/gbreakpad/processor/stackwalker_selftest_sol.s new file mode 100644 index 0000000..648b049 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_selftest_sol.s @@ -0,0 +1,111 @@ +/* Copyright (c) 2007, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. + */ + +/* stackwalker_selftest_sol.s + * On Solaris, the recommeded compiler is CC, so we can not use gcc inline + * asm, use this method instead. + * + * How to compile: as -P -L -D_ASM -D_STDC -K PIC -o \ + * src/processor/stackwalker_selftest_sol.o \ + * src/processor/stackwalker_selftest_sol.s + * + * Author: Michael Shang + */ + +#include + +#if defined(__i386) + + +ENTRY(GetEBP) + pushl %ebp + movl %esp,%ebp + subl $0x00000004,%esp + movl 0x00000000(%ebp),%eax + movl %eax,0xfffffffc(%ebp) + movl 0xfffffffc(%ebp),%eax + leave + ret +SET_SIZE(GetEBP) + +ENTRY(GetEIP) + pushl %ebp + movl %esp,%ebp + subl $0x00000004,%esp + movl 0x00000004(%ebp),%eax + movl %eax,0xfffffffc(%ebp) + movl 0xfffffffc(%ebp),%eax + leave + ret +SET_SIZE(GetEIP) + +ENTRY(GetESP) + pushl %ebp + movl %esp,%ebp + subl $0x00000004,%esp + movl %ebp,%eax + movl %eax,0xfffffffc(%ebp) + movl 0xfffffffc(%ebp),%eax + addl $0x00000008,%eax + leave + ret +SET_SIZE(GetESP) + + +#elif defined(__sparc) + + +ENTRY(GetPC) + save %sp, -120, %sp + mov %i7, %i4 + inccc 8, %i4 + mov %i4, %i0 + ret + restore +SET_SIZE(GetPC) + +ENTRY(GetSP) + save %sp, -120, %sp + mov %fp, %i4 + mov %i4, %i0 + ret + restore +SET_SIZE(GetSP) + +ENTRY(GetFP) + save %sp, -120, %sp + ld [%fp + 56], %g1 + mov %g1, %i0 + ret + restore +SET_SIZE(GetFP) + + +#endif // __i386 || __sparc diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.cc new file mode 100644 index 0000000..2e819a6 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.cc @@ -0,0 +1,139 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_sparc.cc: sparc-specific stackwalker. +// +// See stackwalker_sparc.h for documentation. +// +// Author: Michael Shang + + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/logging.h" +#include "processor/stackwalker_sparc.h" + +namespace google_breakpad { + + +StackwalkerSPARC::StackwalkerSPARC(const SystemInfo *system_info, + const MDRawContextSPARC *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context) { +} + + +StackFrame* StackwalkerSPARC::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameSPARC *frame = new StackFrameSPARC(); + + // The instruction pointer is stored directly in a register, so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = StackFrameSPARC::CONTEXT_VALID_ALL; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.pc; + + return frame; +} + + +StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + StackFrameSPARC *last_frame = static_cast( + stack->frames()->back()); + + // new: caller + // old: callee + // %fp, %i6 and g_r[30] is the same, see minidump_format.h + // %sp, %o6 and g_r[14] is the same, see minidump_format.h + // %sp_new = %fp_old + // %fp_new = *(%fp_old + 32 + 32 - 8), where the callee's %i6 + // %pc_new = *(%fp_old + 32 + 32 - 4) + 8 + // which is callee's %i7 plus 8 + + // A caller frame must reside higher in memory than its callee frames. + // Anything else is an error, or an indication that we've reached the + // end of the stack. + u_int64_t stack_pointer = last_frame->context.g_r[30]; + if (stack_pointer <= last_frame->context.g_r[14]) { + return NULL; + } + + u_int32_t instruction; + if (!memory_->GetMemoryAtAddress(stack_pointer + 60, + &instruction) || instruction <= 1) { + return NULL; + } + + u_int32_t stack_base; + if (!memory_->GetMemoryAtAddress(stack_pointer + 56, + &stack_base) || stack_base <= 1) { + return NULL; + } + + StackFrameSPARC *frame = new StackFrameSPARC(); + + frame->context = last_frame->context; + frame->context.g_r[14] = stack_pointer; + frame->context.g_r[30] = stack_base; + + // frame->context.pc is the return address, which is 2 instruction + // past the branch that caused us to arrive at the callee, which are + // a CALL instruction then a NOP instruction. + // frame_ppc->instruction to 8 less than that. Since all sparc + // instructions are 4 bytes wide, this is the address of the branch + // instruction. This allows source line information to match up with the + // line that contains a function call. Callers that require the exact + // return address value may access the %i7/g_r[31] field of StackFrameSPARC. + frame->context.pc = instruction + 8; + frame->instruction = instruction; + frame->context_validity = StackFrameSPARC::CONTEXT_VALID_PC | + StackFrameSPARC::CONTEXT_VALID_SP | + StackFrameSPARC::CONTEXT_VALID_FP; + frame->trust = StackFrame::FRAME_TRUST_FP; + + return frame; +} + + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.h b/src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.h new file mode 100644 index 0000000..ad41aea --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_sparc.h @@ -0,0 +1,78 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_sparc.h: sparc-specific stackwalker. +// +// Provides stack frames given sparc register context and a memory region +// corresponding to an sparc stack. +// +// Author: Michael Shang + + +#ifndef PROCESSOR_STACKWALKER_SPARC_H__ +#define PROCESSOR_STACKWALKER_SPARC_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerSPARC : public Stackwalker { + public: + // context is a sparc context object that gives access to sparc-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerSPARC(const SystemInfo *system_info, + const MDRawContextSPARC *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + private: + // Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and + // stack conventions + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame(const CallStack *stack); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextSPARC *context_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_SPARC_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_unittest_utils.h b/src/lib/crashdump/gbreakpad/processor/stackwalker_unittest_utils.h new file mode 100644 index 0000000..d2e29f7 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_unittest_utils.h @@ -0,0 +1,180 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// Mock classes for writing stackwalker tests, shared amongst architectures. + +#ifndef PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ +#define PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ + +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/symbol_supplier.h" +#include "google_breakpad/processor/system_info.h" + +class MockMemoryRegion: public google_breakpad::MemoryRegion { + public: + MockMemoryRegion(): base_address_(0) { } + + // Set this region's address and contents. If we have placed an + // instance of this class in a test fixture class, individual tests + // can use this to provide the region's contents. + void Init(u_int64_t base_address, const std::string &contents) { + base_address_ = base_address; + contents_ = contents; + } + + u_int64_t GetBase() const { return base_address_; } + u_int32_t GetSize() const { return contents_.size(); } + + bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { + return GetMemoryLittleEndian(address, value); + } + bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { + return GetMemoryLittleEndian(address, value); + } + bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { + return GetMemoryLittleEndian(address, value); + } + bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { + return GetMemoryLittleEndian(address, value); + } + + private: + // Fetch a little-endian value from ADDRESS in contents_ whose size + // is BYTES, and store it in *VALUE. Return true on success. + template + bool GetMemoryLittleEndian(u_int64_t address, ValueType *value) const { + if (address < base_address_ || + address - base_address_ + sizeof(ValueType) > contents_.size()) + return false; + ValueType v = 0; + int start = address - base_address_; + // The loop condition is odd, but it's correct for size_t. + for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) + v = (v << 8) | static_cast(contents_[start + i]); + *value = v; + return true; + } + + u_int64_t base_address_; + std::string contents_; +}; + +class MockCodeModule: public google_breakpad::CodeModule { + public: + MockCodeModule(u_int64_t base_address, u_int64_t size, + const std::string &code_file, const std::string &version) + : base_address_(base_address), size_(size), code_file_(code_file) { } + + u_int64_t base_address() const { return base_address_; } + u_int64_t size() const { return size_; } + std::string code_file() const { return code_file_; } + std::string code_identifier() const { return code_file_; } + std::string debug_file() const { return code_file_; } + std::string debug_identifier() const { return code_file_; } + std::string version() const { return version_; } + const google_breakpad::CodeModule *Copy() const { + abort(); // Tests won't use this. + } + + private: + u_int64_t base_address_; + u_int64_t size_; + std::string code_file_; + std::string version_; +}; + +class MockCodeModules: public google_breakpad::CodeModules { + public: + typedef google_breakpad::CodeModule CodeModule; + typedef google_breakpad::CodeModules CodeModules; + + void Add(const MockCodeModule *module) { + modules_.push_back(module); + } + + unsigned int module_count() const { return modules_.size(); } + + const CodeModule *GetModuleForAddress(u_int64_t address) const { + for (ModuleVector::const_iterator i = modules_.begin(); + i != modules_.end(); i++) { + const MockCodeModule *module = *i; + if (module->base_address() <= address && + address - module->base_address() < module->size()) + return module; + } + return NULL; + }; + + const CodeModule *GetMainModule() const { return modules_[0]; } + + const CodeModule *GetModuleAtSequence(unsigned int sequence) const { + return modules_.at(sequence); + } + + const CodeModule *GetModuleAtIndex(unsigned int index) const { + return modules_.at(index); + } + + const CodeModules *Copy() const { abort(); } // Tests won't use this. + + private: + typedef std::vector ModuleVector; + ModuleVector modules_; +}; + +class MockSymbolSupplier: public google_breakpad::SymbolSupplier { + public: + typedef google_breakpad::CodeModule CodeModule; + typedef google_breakpad::SystemInfo SystemInfo; + MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + std::string *symbol_file)); + MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + std::string *symbol_file, + std::string *symbol_data)); + MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + std::string *symbol_file, + char **symbol_data)); + MOCK_METHOD1(FreeSymbolData, void(const CodeModule *module)); +}; + +#endif // PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_x86.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_x86.cc new file mode 100644 index 0000000..3c35b08 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_x86.cc @@ -0,0 +1,588 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_x86.cc: x86-specific stackwalker. +// +// See stackwalker_x86.h for documentation. +// +// Author: Mark Mentovai + + +#include "processor/postfix_evaluator-inl.h" + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_x86.h" +#include "processor/windows_frame_info.h" +#include "processor/cfi_frame_info.h" + +namespace google_breakpad { + + +const StackwalkerX86::CFIWalker::RegisterSet +StackwalkerX86::cfi_register_map_[] = { + // It may seem like $eip and $esp are callee-saves, because (with Unix or + // cdecl calling conventions) the callee is responsible for having them + // restored upon return. But the callee_saves flags here really means + // that the walker should assume they're unchanged if the CFI doesn't + // mention them, which is clearly wrong for $eip and $esp. + { "$eip", ".ra", false, + StackFrameX86::CONTEXT_VALID_EIP, &MDRawContextX86::eip }, + { "$esp", ".cfa", false, + StackFrameX86::CONTEXT_VALID_ESP, &MDRawContextX86::esp }, + { "$ebp", NULL, true, + StackFrameX86::CONTEXT_VALID_EBP, &MDRawContextX86::ebp }, + { "$eax", NULL, false, + StackFrameX86::CONTEXT_VALID_EAX, &MDRawContextX86::eax }, + { "$ebx", NULL, true, + StackFrameX86::CONTEXT_VALID_EBX, &MDRawContextX86::ebx }, + { "$ecx", NULL, false, + StackFrameX86::CONTEXT_VALID_ECX, &MDRawContextX86::ecx }, + { "$edx", NULL, false, + StackFrameX86::CONTEXT_VALID_EDX, &MDRawContextX86::edx }, + { "$esi", NULL, true, + StackFrameX86::CONTEXT_VALID_ESI, &MDRawContextX86::esi }, + { "$edi", NULL, true, + StackFrameX86::CONTEXT_VALID_EDI, &MDRawContextX86::edi }, +}; + +StackwalkerX86::StackwalkerX86(const SystemInfo *system_info, + const MDRawContextX86 *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context), + cfi_walker_(cfi_register_map_, + (sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) { + if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) { + // The x86 is a 32-bit CPU, the limits of the supplied stack are invalid. + // Mark memory_ = NULL, which will cause stackwalking to fail. + BPLOG(ERROR) << "Memory out of range for stackwalking: " << + HexString(memory_->GetBase()) << "+" << + HexString(memory_->GetSize()); + memory_ = NULL; + } +} + +StackFrameX86::~StackFrameX86() { + if (windows_frame_info) + delete windows_frame_info; + windows_frame_info = NULL; + if (cfi_frame_info) + delete cfi_frame_info; + cfi_frame_info = NULL; +} + +StackFrame *StackwalkerX86::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameX86 *frame = new StackFrameX86(); + + // The instruction pointer is stored directly in a register, so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.eip; + + return frame; +} + +StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo( + const vector &frames, + WindowsFrameInfo *last_frame_info) { + StackFrame::FrameTrust trust = StackFrame::FRAME_TRUST_NONE; + + StackFrameX86 *last_frame = static_cast(frames.back()); + + // Save the stack walking info we found, in case we need it later to + // find the callee of the frame we're constructing now. + last_frame->windows_frame_info = last_frame_info; + + // This function only covers the full STACK WIN case. If + // last_frame_info is VALID_PARAMETER_SIZE-only, then we should + // assume the traditional frame format or use some other strategy. + if (last_frame_info->valid != WindowsFrameInfo::VALID_ALL) + return NULL; + + // This stackwalker sets each frame's %esp to its value immediately prior + // to the CALL into the callee. This means that %esp points to the last + // callee argument pushed onto the stack, which may not be where %esp points + // after the callee returns. Specifically, the value is correct for the + // cdecl calling convention, but not other conventions. The cdecl + // convention requires a caller to pop its callee's arguments from the + // stack after the callee returns. This is usually accomplished by adding + // the known size of the arguments to %esp. Other calling conventions, + // including stdcall, thiscall, and fastcall, require the callee to pop any + // parameters stored on the stack before returning. This is usually + // accomplished by using the RET n instruction, which pops n bytes off + // the stack after popping the return address. + // + // Because each frame's %esp will point to a location on the stack after + // callee arguments have been PUSHed, when locating things in a stack frame + // relative to %esp, the size of the arguments to the callee need to be + // taken into account. This seems a little bit unclean, but it's better + // than the alternative, which would need to take these same things into + // account, but only for cdecl functions. With this implementation, we get + // to be agnostic about each function's calling convention. Furthermore, + // this is how Windows debugging tools work, so it means that the %esp + // values produced by this stackwalker directly correspond to the %esp + // values you'll see there. + // + // If the last frame has no callee (because it's the context frame), just + // set the callee parameter size to 0: the stack pointer can't point to + // callee arguments because there's no callee. This is correct as long + // as the context wasn't captured while arguments were being pushed for + // a function call. Note that there may be functions whose parameter sizes + // are unknown, 0 is also used in that case. When that happens, it should + // be possible to walk to the next frame without reference to %esp. + + u_int32_t last_frame_callee_parameter_size = 0; + int frames_already_walked = frames.size(); + if (frames_already_walked >= 2) { + const StackFrameX86 *last_frame_callee + = static_cast(frames[frames_already_walked - 2]); + WindowsFrameInfo *last_frame_callee_info + = last_frame_callee->windows_frame_info; + if (last_frame_callee_info && + (last_frame_callee_info->valid + & WindowsFrameInfo::VALID_PARAMETER_SIZE)) { + last_frame_callee_parameter_size = + last_frame_callee_info->parameter_size; + } + } + + // Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used + // in each program string, and their previous values are known, so set them + // here. + PostfixEvaluator::DictionaryType dictionary; + // Provide the current register values. + dictionary["$ebp"] = last_frame->context.ebp; + dictionary["$esp"] = last_frame->context.esp; + // Provide constants from the debug info for last_frame and its callee. + // .cbCalleeParams is a Breakpad extension that allows us to use the + // PostfixEvaluator engine when certain types of debugging information + // are present without having to write the constants into the program + // string as literals. + dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size; + dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size; + dictionary[".cbLocals"] = last_frame_info->local_size; + + u_int32_t raSearchStart = last_frame->context.esp + + last_frame_callee_parameter_size + + last_frame_info->local_size + + last_frame_info->saved_register_size; + u_int32_t found; // dummy value + // Scan up to three words above the calculated search value, in case + // the stack was aligned to a quadword boundary. + ScanForReturnAddress(raSearchStart, &raSearchStart, &found, 3); + + // The difference between raSearch and raSearchStart is unknown, + // but making them the same seems to work well in practice. + dictionary[".raSearchStart"] = raSearchStart; + dictionary[".raSearch"] = raSearchStart; + + dictionary[".cbParams"] = last_frame_info->parameter_size; + + // Decide what type of program string to use. The program string is in + // postfix notation and will be passed to PostfixEvaluator::Evaluate. + // Given the dictionary and the program string, it is possible to compute + // the return address and the values of other registers in the calling + // function. Because of bugs described below, the stack may need to be + // scanned for these values. The results of program string evaluation + // will be used to determine whether to scan for better values. + string program_string; + bool recover_ebp = true; + + trust = StackFrame::FRAME_TRUST_CFI; + if (!last_frame_info->program_string.empty()) { + // The FPO data has its own program string, which will tell us how to + // get to the caller frame, and may even fill in the values of + // nonvolatile registers and provide pointers to local variables and + // parameters. In some cases, particularly with program strings that use + // .raSearchStart, the stack may need to be scanned afterward. + program_string = last_frame_info->program_string; + } else if (last_frame_info->allocates_base_pointer) { + // The function corresponding to the last frame doesn't use the frame + // pointer for conventional purposes, but it does allocate a new + // frame pointer and use it for its own purposes. Its callee's + // information is still accessed relative to %esp, and the previous + // value of %ebp can be recovered from a location in its stack frame, + // within the saved-register area. + // + // Functions that fall into this category use the %ebp register for + // a purpose other than the frame pointer. They restore the caller's + // %ebp before returning. These functions create their stack frame + // after a CALL by decrementing the stack pointer in an amount + // sufficient to store local variables, and then PUSHing saved + // registers onto the stack. Arguments to a callee function, if any, + // are PUSHed after that. Walking up to the caller, therefore, + // can be done solely with calculations relative to the stack pointer + // (%esp). The return address is recovered from the memory location + // above the known sizes of the callee's parameters, saved registers, + // and locals. The caller's stack pointer (the value of %esp when + // the caller executed CALL) is the location immediately above the + // saved return address. The saved value of %ebp to be restored for + // the caller is at a known location in the saved-register area of + // the stack frame. + // + // For this type of frame, MSVC 14 (from Visual Studio 8/2005) in + // link-time code generation mode (/LTCG and /GL) can generate erroneous + // debugging data. The reported size of saved registers can be 0, + // which is clearly an error because these frames must, at the very + // least, save %ebp. For this reason, in addition to those given above + // about the use of .raSearchStart, the stack may need to be scanned + // for a better return address and a better frame pointer after the + // program string is evaluated. + // + // %eip_new = *(%esp_old + callee_params + saved_regs + locals) + // %ebp_new = *(%esp_old + callee_params + saved_regs - 8) + // %esp_new = %esp_old + callee_params + saved_regs + locals + 4 + program_string = "$eip .raSearchStart ^ = " + "$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = " + "$esp .raSearchStart 4 + ="; + } else { + // The function corresponding to the last frame doesn't use %ebp at + // all. The callee frame is located relative to %esp. + // + // The called procedure's instruction pointer and stack pointer are + // recovered in the same way as the case above, except that no + // frame pointer (%ebp) is used at all, so it is not saved anywhere + // in the callee's stack frame and does not need to be recovered. + // Because %ebp wasn't used in the callee, whatever value it has + // is the value that it had in the caller, so it can be carried + // straight through without bringing its validity into question. + // + // Because of the use of .raSearchStart, the stack will possibly be + // examined to locate a better return address after program string + // evaluation. The stack will not be examined to locate a saved + // %ebp value, because these frames do not save (or use) %ebp. + // + // %eip_new = *(%esp_old + callee_params + saved_regs + locals) + // %esp_new = %esp_old + callee_params + saved_regs + locals + 4 + // %ebp_new = %ebp_old + program_string = "$eip .raSearchStart ^ = " + "$esp .raSearchStart 4 + ="; + recover_ebp = false; + } + + // Now crank it out, making sure that the program string set at least the + // two required variables. + PostfixEvaluator evaluator = + PostfixEvaluator(&dictionary, memory_); + PostfixEvaluator::DictionaryValidityType dictionary_validity; + if (!evaluator.Evaluate(program_string, &dictionary_validity) || + dictionary_validity.find("$eip") == dictionary_validity.end() || + dictionary_validity.find("$esp") == dictionary_validity.end()) { + // Program string evaluation failed. It may be that %eip is not somewhere + // with stack frame info, and %ebp is pointing to non-stack memory, so + // our evaluation couldn't succeed. We'll scan the stack for a return + // address. This can happen if the stack is in a module for which + // we don't have symbols, and that module is compiled without a + // frame pointer. + u_int32_t location_start = last_frame->context.esp; + u_int32_t location, eip; + if (!ScanForReturnAddress(location_start, &location, &eip)) { + // if we can't find an instruction pointer even with stack scanning, + // give up. + return NULL; + } + + // This seems like a reasonable return address. Since program string + // evaluation failed, use it and set %esp to the location above the + // one where the return address was found. + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + trust = StackFrame::FRAME_TRUST_SCAN; + } + + // Since this stack frame did not use %ebp in a traditional way, + // locating the return address isn't entirely deterministic. In that + // case, the stack can be scanned to locate the return address. + // + // However, if program string evaluation resulted in both %eip and + // %ebp values of 0, trust that the end of the stack has been + // reached and don't scan for anything else. + if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) { + int offset = 0; + + // This scan can only be done if a CodeModules object is available, to + // check that candidate return addresses are in fact inside a module. + // + // TODO(mmentovai): This ignores dynamically-generated code. One possible + // solution is to check the minidump's memory map to see if the candidate + // %eip value comes from a mapped executable page, although this would + // require dumps that contain MINIDUMP_MEMORY_INFO, which the Breakpad + // client doesn't currently write (it would need to call MiniDumpWriteDump + // with the MiniDumpWithFullMemoryInfo type bit set). Even given this + // ability, older OSes (pre-XP SP2) and CPUs (pre-P4) don't enforce + // an independent execute privilege on memory pages. + + u_int32_t eip = dictionary["$eip"]; + if (modules_ && !modules_->GetModuleForAddress(eip)) { + // The instruction pointer at .raSearchStart was invalid, so start + // looking one 32-bit word above that location. + u_int32_t location_start = dictionary[".raSearchStart"] + 4; + u_int32_t location; + if (ScanForReturnAddress(location_start, &location, &eip)) { + // This is a better return address that what program string + // evaluation found. Use it, and set %esp to the location above the + // one where the return address was found. + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + offset = location - location_start; + trust = StackFrame::FRAME_TRUST_CFI_SCAN; + } + } + + // When trying to recover the previous value of the frame pointer (%ebp), + // start looking at the lowest possible address in the saved-register + // area, and look at the entire saved register area, increased by the + // size of |offset| to account for additional data that may be on the + // stack. The scan is performed from the highest possible address to + // the lowest, because we expect that the function's prolog would have + // saved %ebp early. + u_int32_t ebp = dictionary["$ebp"]; + u_int32_t value; // throwaway variable to check pointer validity + if (recover_ebp && !memory_->GetMemoryAtAddress(ebp, &value)) { + int fp_search_bytes = last_frame_info->saved_register_size + offset; + u_int32_t location_end = last_frame->context.esp + + last_frame_callee_parameter_size; + + for (u_int32_t location = location_end + fp_search_bytes; + location >= location_end; + location -= 4) { + if (!memory_->GetMemoryAtAddress(location, &ebp)) + break; + + if (memory_->GetMemoryAtAddress(ebp, &value)) { + // The candidate value is a pointer to the same memory region + // (the stack). Prefer it as a recovered %ebp result. + dictionary["$ebp"] = ebp; + break; + } + } + } + } + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameX86 *frame = new StackFrameX86(); + + frame->trust = trust; + frame->context = last_frame->context; + frame->context.eip = dictionary["$eip"]; + frame->context.esp = dictionary["$esp"]; + frame->context.ebp = dictionary["$ebp"]; + frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP | + StackFrameX86::CONTEXT_VALID_ESP | + StackFrameX86::CONTEXT_VALID_EBP; + + // These are nonvolatile (callee-save) registers, and the program string + // may have filled them in. + if (dictionary_validity.find("$ebx") != dictionary_validity.end()) { + frame->context.ebx = dictionary["$ebx"]; + frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX; + } + if (dictionary_validity.find("$esi") != dictionary_validity.end()) { + frame->context.esi = dictionary["$esi"]; + frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI; + } + if (dictionary_validity.find("$edi") != dictionary_validity.end()) { + frame->context.edi = dictionary["$edi"]; + frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI; + } + + return frame; +} + +StackFrameX86 *StackwalkerX86::GetCallerByCFIFrameInfo( + const vector &frames, + CFIFrameInfo *cfi_frame_info) { + StackFrameX86 *last_frame = static_cast(frames.back()); + last_frame->cfi_frame_info = cfi_frame_info; + + scoped_ptr frame(new StackFrameX86()); + if (!cfi_walker_ + .FindCallerRegisters(*memory_, *cfi_frame_info, + last_frame->context, last_frame->context_validity, + &frame->context, &frame->context_validity)) + return NULL; + + // Make sure we recovered all the essentials. + static const int essentials = (StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + frame->trust = StackFrame::FRAME_TRUST_CFI; + + return frame.release(); +} + +StackFrameX86 *StackwalkerX86::GetCallerByEBPAtBase( + const vector &frames) { + StackFrame::FrameTrust trust; + StackFrameX86 *last_frame = static_cast(frames.back()); + u_int32_t last_esp = last_frame->context.esp; + u_int32_t last_ebp = last_frame->context.ebp; + + // Assume that the standard %ebp-using x86 calling convention is in + // use. + // + // The typical x86 calling convention, when frame pointers are present, + // is for the calling procedure to use CALL, which pushes the return + // address onto the stack and sets the instruction pointer (%eip) to + // the entry point of the called routine. The called routine then + // PUSHes the calling routine's frame pointer (%ebp) onto the stack + // before copying the stack pointer (%esp) to the frame pointer (%ebp). + // Therefore, the calling procedure's frame pointer is always available + // by dereferencing the called procedure's frame pointer, and the return + // address is always available at the memory location immediately above + // the address pointed to by the called procedure's frame pointer. The + // calling procedure's stack pointer (%esp) is 8 higher than the value + // of the called procedure's frame pointer at the time the calling + // procedure made the CALL: 4 bytes for the return address pushed by the + // CALL itself, and 4 bytes for the callee's PUSH of the caller's frame + // pointer. + // + // %eip_new = *(%ebp_old + 4) + // %esp_new = %ebp_old + 8 + // %ebp_new = *(%ebp_old) + + u_int32_t caller_eip, caller_esp, caller_ebp; + + if (memory_->GetMemoryAtAddress(last_ebp + 4, &caller_eip) && + memory_->GetMemoryAtAddress(last_ebp, &caller_ebp)) { + caller_esp = last_ebp + 8; + trust = StackFrame::FRAME_TRUST_FP; + } else { + // We couldn't read the memory %ebp refers to. It may be that %ebp + // is pointing to non-stack memory. We'll scan the stack for a + // return address. This can happen if last_frame is executing code + // for a module for which we don't have symbols, and that module + // is compiled without a frame pointer. + if (!ScanForReturnAddress(last_esp, &caller_esp, &caller_eip)) { + // if we can't find an instruction pointer even with stack scanning, + // give up. + return NULL; + } + + // ScanForReturnAddress found a reasonable return address. Advance + // %esp to the location above the one where the return address was + // found. Assume that %ebp is unchanged. + caller_esp += 4; + caller_ebp = last_ebp; + + trust = StackFrame::FRAME_TRUST_SCAN; + } + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameX86 *frame = new StackFrameX86(); + + frame->trust = trust; + frame->context = last_frame->context; + frame->context.eip = caller_eip; + frame->context.esp = caller_esp; + frame->context.ebp = caller_ebp; + frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP | + StackFrameX86::CONTEXT_VALID_ESP | + StackFrameX86::CONTEXT_VALID_EBP; + + return frame; +} + +StackFrame *StackwalkerX86::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + const vector &frames = *stack->frames(); + StackFrameX86 *last_frame = static_cast(frames.back()); + scoped_ptr new_frame; + + // If the resolver has Windows stack walking information, use that. + WindowsFrameInfo *windows_frame_info + = resolver_ ? resolver_->FindWindowsFrameInfo(last_frame) : NULL; + if (windows_frame_info) + new_frame.reset(GetCallerByWindowsFrameInfo(frames, windows_frame_info)); + + // If the resolver has DWARF CFI information, use that. + if (!new_frame.get()) { + CFIFrameInfo *cfi_frame_info = + resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL; + if (cfi_frame_info) + new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info)); + } + + // Otherwise, hope that the program was using a traditional frame structure. + if (!new_frame.get()) + new_frame.reset(GetCallerByEBPAtBase(frames)); + + // If nothing worked, tell the caller. + if (!new_frame.get()) + return NULL; + + // Treat an instruction address of 0 as end-of-stack. + if (new_frame->context.eip == 0) + return NULL; + + // If the new stack pointer is at a lower address than the old, then + // that's clearly incorrect. Treat this as end-of-stack to enforce + // progress and avoid infinite loops. + if (new_frame->context.esp <= last_frame->context.esp) + return NULL; + + // new_frame->context.eip is the return address, which is one instruction + // past the CALL that caused us to arrive at the callee. Set + // new_frame->instruction to one less than that. This won't reference the + // beginning of the CALL instruction, but it's guaranteed to be within + // the CALL, which is sufficient to get the source line information to + // match up with the line that contains a function call. Callers that + // require the exact return address value may access the context.eip + // field of StackFrameX86. + new_frame->instruction = new_frame->context.eip - 1; + + return new_frame.release(); +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_x86.h b/src/lib/crashdump/gbreakpad/processor/stackwalker_x86.h new file mode 100644 index 0000000..9c56ae8 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_x86.h @@ -0,0 +1,114 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// stackwalker_x86.h: x86-specific stackwalker. +// +// Provides stack frames given x86 register context and a memory region +// corresponding to an x86 stack. +// +// Author: Mark Mentovai + + +#ifndef PROCESSOR_STACKWALKER_X86_H__ +#define PROCESSOR_STACKWALKER_X86_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" + +namespace google_breakpad { + +class CodeModules; + + +class StackwalkerX86 : public Stackwalker { + public: + // context is an x86 context object that gives access to x86-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerX86(const SystemInfo *system_info, + const MDRawContextX86 *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + private: + // A STACK CFI-driven frame walker for the X86. + typedef SimpleCFIWalker CFIWalker; + + // Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and + // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or + // alternate conventions as guided by any WindowsFrameInfo available for the + // code in question.). + virtual StackFrame *GetContextFrame(); + virtual StackFrame *GetCallerFrame(const CallStack *stack); + + // Use windows_frame_info (derived from STACK WIN and FUNC records) + // to construct the frame that called frames.back(). The caller + // takes ownership of the returned frame. Return NULL on failure. + StackFrameX86 *GetCallerByWindowsFrameInfo( + const vector &frames, + WindowsFrameInfo *windows_frame_info); + + // Use cfi_frame_info (derived from STACK CFI records) to construct + // the frame that called frames.back(). The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameX86 *GetCallerByCFIFrameInfo(const vector &frames, + CFIFrameInfo *cfi_frame_info); + + // Assuming a traditional frame layout --- where the caller's %ebp + // has been pushed just after the return address and the callee's + // %ebp points to the saved %ebp --- construct the frame that called + // frames.back(). The caller takes ownership of the returned frame. + // Return NULL on failure. + StackFrameX86 *GetCallerByEBPAtBase(const vector &frames); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextX86 *context_; + + // Our register map, for cfi_walker_. + static const CFIWalker::RegisterSet cfi_register_map_[]; + + // Our CFI frame walker. + const CFIWalker cfi_walker_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_X86_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/stackwalker_x86_unittest.cc b/src/lib/crashdump/gbreakpad/processor/stackwalker_x86_unittest.cc new file mode 100644 index 0000000..04d264b --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/stackwalker_x86_unittest.cc @@ -0,0 +1,1034 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// stackwalker_x86_unittest.cc: Unit tests for StackwalkerX86 class. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/test_assembler.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_x86.h" +#include "processor/windows_frame_info.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameX86; +using google_breakpad::StackwalkerX86; +using google_breakpad::SystemInfo; +using google_breakpad::WindowsFrameInfo; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using std::string; +using std::vector; +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerX86Fixture { + public: + StackwalkerX86Fixture() + : stack_section(kLittleEndian), + // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(0x40000000, 0x10000, "module1", "version1"), + module2(0x50000000, 0x10000, "module2", "version2") { + // Identify the system as a Linux system. + system_info.os = "Linux"; + system_info.os_short = "linux"; + system_info.os_version = "Salacious Skink"; + system_info.cpu = "x86"; + system_info.cpu_info = ""; + + // Put distinctive values in the raw CPU context. + BrandContext(&raw_context); + + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + unsigned int buffer_size = info.size() + 1; + char *buffer = reinterpret_cast(operator new(buffer_size)); + strcpy(buffer, info.c_str()); + EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), + Return(MockSymbolSupplier::FOUND))); + } + + // Populate stack_region with the contents of stack_section. Use + // stack_section.start() as the region's starting address. + void RegionFromSection() { + string contents; + ASSERT_TRUE(stack_section.GetContents(&contents)); + stack_region.Init(stack_section.start().Value(), contents); + } + + // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. + void BrandContext(MDRawContextX86 *raw_context) { + u_int8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextX86 raw_context; + Section stack_section; + MockMemoryRegion stack_region; + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + CallStack call_stack; + const vector *frames; +}; + +class SanityCheck: public StackwalkerX86Fixture, public Test { }; + +TEST_F(SanityCheck, NoResolver) { + stack_section.start() = 0x80000000; + stack_section.D32(0).D32(0); // end-of-stack marker + RegionFromSection(); + raw_context.eip = 0x40000200; + raw_context.ebp = 0x80000000; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + NULL, NULL); + // This should succeed, even without a resolver or supplier. + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + StackFrameX86 *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetContextFrame: public StackwalkerX86Fixture, public Test { }; + +TEST_F(GetContextFrame, Simple) { + stack_section.start() = 0x80000000; + stack_section.D32(0).D32(0); // end-of-stack marker + RegionFromSection(); + raw_context.eip = 0x40000200; + raw_context.ebp = 0x80000000; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + StackFrameX86 *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetCallerFrame: public StackwalkerX86Fixture, public Test { }; + +// Walk a traditional frame. A traditional frame saves the caller's +// %ebp just below the return address, and has its own %ebp pointing +// at the saved %ebp. +TEST_F(GetCallerFrame, Traditional) { + stack_section.start() = 0x80000000; + Label frame0_ebp, frame1_ebp; + stack_section + .Append(12, 0) // frame 0: space + .Mark(&frame0_ebp) // frame 0 %ebp points here + .D32(frame1_ebp) // frame 0: saved %ebp + .D32(0x40008679) // frame 0: return address + .Append(8, 0) // frame 1: space + .Mark(&frame1_ebp) // frame 1 %ebp points here + .D32(0) // frame 1: saved %ebp (stack end) + .D32(0); // frame 1: return address (stack end) + RegionFromSection(); + raw_context.eip = 0x4000c7a5; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = frame0_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000c7a5U, frame0->instruction); + EXPECT_EQ(0x4000c7a5U, frame0->context.eip); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x40008679U, frame1->instruction + 1); + EXPECT_EQ(0x40008679U, frame1->context.eip); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Walk a traditional frame, but use a bogus %ebp value, forcing a scan +// of the stack for something that looks like a return address. +TEST_F(GetCallerFrame, TraditionalScan) { + stack_section.start() = 0x80000000; + Label frame1_ebp; + stack_section + // frame 0 + .D32(0xf065dc76) // locals area: + .D32(0x46ee2167) // garbage that doesn't look like + .D32(0xbab023ec) // a return address + .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan) + .D32(0x4000129d) // return address + // frame 1 + .Append(8, 0) // space + .Mark(&frame1_ebp) // %ebp points here + .D32(0) // saved %ebp (stack end) + .D32(0); // return address (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000f49d; + raw_context.esp = stack_section.start().Value(); + // Make the frame pointer bogus, to make the stackwalker scan the stack + // for something that looks like a return address. + raw_context.ebp = 0xd43eed6e; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000f49dU, frame0->instruction); + EXPECT_EQ(0x4000f49dU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000129dU, frame1->instruction + 1); + EXPECT_EQ(0x4000129dU, frame1->context.eip); + EXPECT_EQ(0x80000014U, frame1->context.esp); + EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Force scanning for a return address a long way down the stack +TEST_F(GetCallerFrame, TraditionalScanLongWay) { + stack_section.start() = 0x80000000; + Label frame1_ebp; + stack_section + // frame 0 + .D32(0xf065dc76) // locals area: + .D32(0x46ee2167) // garbage that doesn't look like + .D32(0xbab023ec) // a return address + .Append(20 * 4, 0) // a bunch of space + .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan) + .D32(0x4000129d) // return address + // frame 1 + .Append(8, 0) // space + .Mark(&frame1_ebp) // %ebp points here + .D32(0) // saved %ebp (stack end) + .D32(0); // return address (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000f49d; + raw_context.esp = stack_section.start().Value(); + // Make the frame pointer bogus, to make the stackwalker scan the stack + // for something that looks like a return address. + raw_context.ebp = 0xd43eed6e; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000f49dU, frame0->instruction); + EXPECT_EQ(0x4000f49dU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000129dU, frame1->instruction + 1); + EXPECT_EQ(0x4000129dU, frame1->context.eip); + EXPECT_EQ(0x80000064U, frame1->context.esp); + EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame. +TEST_F(GetCallerFrame, WindowsFrameData) { + SetModuleSymbols(&module1, + "STACK WIN 4 aa85 176 0 0 4 10 4 0 1" + " $T2 $esp .cbSavedRegs + =" + " $T0 .raSearchStart =" + " $eip $T0 ^ =" + " $esp $T0 4 + =" + " $ebx $T2 4 - ^ =" + " $edi $T2 8 - ^ =" + " $esi $T2 12 - ^ =" + " $ebp $T2 16 - ^ =\n"); + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0 + .D32(frame1_ebp) // saved regs: %ebp + .D32(0xa7120d1a) // %esi + .D32(0x630891be) // %edi + .D32(0x9068a878) // %ebx + .D32(0xa08ea45f) // locals: unused + .D32(0x40001350) // return address + // frame 1 + .Mark(&frame1_esp) + .Append(12, 0) // empty space + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000aa85; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = 0xf052c1de; // should not be needed to walk frame + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000aa85U, frame0->instruction); + EXPECT_EQ(0x4000aa85U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xf052c1deU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP + | StackFrameX86::CONTEXT_VALID_EBX + | StackFrameX86::CONTEXT_VALID_ESI + | StackFrameX86::CONTEXT_VALID_EDI), + frame1->context_validity); + EXPECT_EQ(0x40001350U, frame1->instruction + 1); + EXPECT_EQ(0x40001350U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(0x9068a878U, frame1->context.ebx); + EXPECT_EQ(0xa7120d1aU, frame1->context.esi); + EXPECT_EQ(0x630891beU, frame1->context.edi); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame where the stack +// is aligned and we must search +TEST_F(GetCallerFrame, WindowsFrameDataAligned) { + SetModuleSymbols(&module1, + "STACK WIN 4 aa85 176 0 0 4 4 8 0 1" + " $T1 .raSearch =" + " $T0 $T1 4 - 8 @ =" + " $ebp $T1 4 - ^ =" + " $eip $T1 ^ =" + " $esp $T1 4 + ="); + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0 + .D32(0x0ffa0ffa) // unused saved register + .D32(0xdeaddead) // locals + .D32(0xbeefbeef) + .D32(0) // 8-byte alignment + .D32(frame1_ebp) + .D32(0x5000129d) // return address + // frame 1 + .Mark(&frame1_esp) + .D32(0x1) // parameter + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000aa85; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = 0xf052c1de; // should not be needed to walk frame + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000aa85U, frame0->instruction); + EXPECT_EQ(0x4000aa85U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xf052c1deU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000129dU, frame1->instruction + 1); + EXPECT_EQ(0x5000129dU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a frame, and depend on the +// parameter size from the callee as well. +TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) { + SetModuleSymbols(&module1, "FUNC 1000 100 c module1::wheedle\n"); + SetModuleSymbols(&module2, + // Note bogus parameter size in FUNC record; the stack walker + // should prefer the STACK WIN record, and see '4' below. + "FUNC aa85 176 beef module2::whine\n" + "STACK WIN 4 aa85 176 0 0 4 10 4 0 1" + " $T2 $esp .cbLocals + .cbSavedRegs + =" + " $T0 .raSearchStart =" + " $eip $T0 ^ =" + " $esp $T0 4 + =" + " $ebp $T0 20 - ^ =" + " $ebx $T0 8 - ^ =\n"); + Label frame0_esp, frame0_ebp; + Label frame1_esp; + Label frame2_esp, frame2_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0, in module1::wheedle. Traditional frame. + .Mark(&frame0_esp) + .Append(16, 0) // frame space + .Mark(&frame0_ebp) + .D32(0x6fa902e0) // saved %ebp. Not a frame pointer. + .D32(0x5000aa95) // return address, in module2::whine + // frame 1, in module2::whine. FrameData frame. + .Mark(&frame1_esp) + .D32(0xbaa0cb7a) // argument 3 passed to module1::wheedle + .D32(0xbdc92f9f) // argument 2 + .D32(0x0b1d8442) // argument 1 + .D32(frame2_ebp) // saved %ebp + .D32(0xb1b90a15) // unused + .D32(0xf18e072d) // unused + .D32(0x2558c7f3) // saved %ebx + .D32(0x0365e25e) // unused + .D32(0x2a179e38) // return address; $T0 points here + // frame 2, in no module + .Mark(&frame2_esp) + .Append(12, 0) // empty space + .Mark(&frame2_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x40001004; // in module1::wheedle + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = frame0_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40001004U, frame0->instruction); + EXPECT_EQ(0x40001004U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::wheedle", frame0->function_name); + EXPECT_EQ(0x40001000U, frame0->function_base); + // The FUNC record for module1::wheedle should have produced a + // WindowsFrameInfo structure with only the parameter size valid. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, + frame0->windows_frame_info->valid); + EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000aa95U, frame1->instruction + 1); + EXPECT_EQ(0x5000aa95U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(0x6fa902e0U, frame1->context.ebp); + EXPECT_EQ(&module2, frame1->module); + EXPECT_EQ("module2::whine", frame1->function_name); + EXPECT_EQ(0x5000aa85U, frame1->function_base); + ASSERT_TRUE(frame1->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); + // This should not see the 0xbeef parameter size from the FUNC + // record, but should instead see the STACK WIN record. + EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size); + + StackFrameX86 *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP + | StackFrameX86::CONTEXT_VALID_EBX), + frame2->context_validity); + EXPECT_EQ(0x2a179e38U, frame2->instruction + 1); + EXPECT_EQ(0x2a179e38U, frame2->context.eip); + EXPECT_EQ(frame2_esp.Value(), frame2->context.esp); + EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp); + EXPECT_EQ(0x2558c7f3U, frame2->context.ebx); + EXPECT_EQ(NULL, frame2->module); + EXPECT_EQ(NULL, frame2->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame, where the +// expression fails to yield both an $eip and an $ebp value, and the stack +// walker must scan. +TEST_F(GetCallerFrame, WindowsFrameDataScan) { + SetModuleSymbols(&module1, + "STACK WIN 4 c8c 111 0 0 4 10 4 0 1 bad program string\n"); + // Mark frame 1's PC as the end of the stack. + SetModuleSymbols(&module2, + "FUNC 7c38 accf 0 module2::function\n" + "STACK WIN 4 7c38 accf 0 0 4 10 4 0 1 $eip 0 = $ebp 0 =\n"); + Label frame1_esp; + stack_section.start() = 0x80000000; + stack_section + // frame 0 + .Append(16, 0x2a) // unused, garbage + .D32(0x50007ce9) // return address + // frame 1 + .Mark(&frame1_esp) + .Append(8, 0); // empty space + + RegionFromSection(); + raw_context.eip = 0x40000c9c; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = 0x2ae314cd; // should not be needed to walk frame + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40000c9cU, frame0->instruction); + EXPECT_EQ(0x40000c9cU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0x2ae314cdU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker + // does not actually fetch the EBP after a scan (forcing the next frame + // to be scanned as well). But let's grandfather the existing behavior in + // for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x50007ce9U, frame1->instruction + 1); + EXPECT_EQ(0x50007ce9U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_TRUE(frame1->windows_frame_info != NULL); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame, where the +// expression yields an $eip that falls outside of any module, and the +// stack walker must scan. +TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) { + SetModuleSymbols(&module1, + "STACK WIN 4 6e6 e7 0 0 0 8 4 0 1" + // A traditional frame, actually. + " $eip $ebp 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =\n"); + // Mark frame 1's PC as the end of the stack. + SetModuleSymbols(&module2, + "FUNC cfdb 8406 0 module2::function\n" + "STACK WIN 4 cfdb 8406 0 0 0 0 0 0 1 $eip 0 = $ebp 0 =\n"); + stack_section.start() = 0x80000000; + + // In this stack, the context's %ebp is pointing at the wrong place, so + // the stack walker needs to scan to find the return address, and then + // scan again to find the caller's saved %ebp. + Label frame0_ebp, frame1_ebp, frame1_esp; + stack_section + // frame 0 + .Append(8, 0x2a) // garbage + .Mark(&frame0_ebp) // frame 0 %ebp points here, but should point + // at *** below + // The STACK WIN record says that the following two values are + // frame 1's saved %ebp and return address, but the %ebp is wrong; + // they're garbage. The stack walker will scan for the right values. + .D32(0x3d937b2b) // alleged to be frame 1's saved %ebp + .D32(0x17847f5b) // alleged to be frame 1's return address + .D32(frame1_ebp) // frame 1's real saved %ebp; scan will find + .D32(0x2b2b2b2b) // first word of realigned register save area + // *** frame 0 %ebp ought to be pointing here + .D32(0x2c2c2c2c) // realigned locals area + .D32(0x5000d000) // frame 1's real saved %eip; scan will find + // Frame 1, in module2::function. The STACK WIN record describes + // this as the oldest frame, without referring to its contents, so + // we needn't to provide any actual data here. + .Mark(&frame1_esp) + .Mark(&frame1_ebp) // frame 1 %ebp points here + // A dummy value for frame 1's %ebp to point at. The scan recognizes the + // saved %ebp because it points to a valid word in the stack memory region. + .D32(0x2d2d2d2d); + + RegionFromSection(); + raw_context.eip = 0x40000700; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = frame0_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40000700U, frame0->instruction); + EXPECT_EQ(0x40000700U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000d000U, frame1->instruction + 1); + EXPECT_EQ(0x5000d000U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_TRUE(frame1->windows_frame_info != NULL); +} + +// Use Windows FrameTypeFPO data to walk a stack frame for a function that +// does not modify %ebp from the value it had in the caller. +TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) { + SetModuleSymbols(&module1, + // Note bogus parameter size in FUNC record; the walker + // should prefer the STACK WIN record, and see the '8' below. + "FUNC e8a8 100 feeb module1::discombobulated\n" + "STACK WIN 0 e8a8 100 0 0 8 4 10 0 0 0\n"); + Label frame0_esp; + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame. + .Mark(&frame0_esp) + // no outgoing parameters; this is the youngest frame. + .D32(0x7c521352) // four bytes of saved registers + .Append(0x10, 0x42) // local area + .D32(0x40009b5b) // return address, in module1, no function + // frame 1, in module1, no function. + .Mark(&frame1_esp) + .D32(0xf60ea7fc) // junk + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000e8b8; // in module1::whine + raw_context.esp = stack_section.start().Value(); + // Frame pointer unchanged from caller. + raw_context.ebp = frame1_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000e8b8U, frame0->instruction); + EXPECT_EQ(0x4000e8b8U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::discombobulated", frame0->function_name); + EXPECT_EQ(0x4000e8a8U, frame0->function_base); + // The STACK WIN record for module1::discombobulated should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x40009b5bU, frame1->instruction + 1); + EXPECT_EQ(0x40009b5bU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module1, frame1->module); + EXPECT_EQ("", frame1->function_name); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows FrameTypeFPO data to walk a stack frame for a function +// that uses %ebp for its own purposes, saving the value it had in the +// caller in the standard place in the saved register area. +TEST_F(GetCallerFrame, WindowsFPOUsedEBP) { + SetModuleSymbols(&module1, + // Note bogus parameter size in FUNC record; the walker + // should prefer the STACK WIN record, and see the '8' below. + "FUNC 9aa8 e6 abbe module1::RaisedByTheAliens\n" + "STACK WIN 0 9aa8 e6 a 0 10 8 4 0 0 1\n"); + Label frame0_esp; + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame. + .Mark(&frame0_esp) + // no outgoing parameters; this is the youngest frame. + .D32(frame1_ebp) // saved register area: saved %ebp + .D32(0xb68bd5f9) // saved register area: something else + .D32(0xd25d05fc) // local area + .D32(0x4000debe) // return address, in module1, no function + // frame 1, in module1, no function. + .Mark(&frame1_esp) + .D32(0xf0c9a974) // junk + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x40009ab8; // in module1::RaisedByTheAliens + raw_context.esp = stack_section.start().Value(); + // RaisedByTheAliens uses %ebp for its own mysterious purposes. + raw_context.ebp = 0xecbdd1a5; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40009ab8U, frame0->instruction); + EXPECT_EQ(0x40009ab8U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(0xecbdd1a5, frame0->context.ebp); + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name); + EXPECT_EQ(0x40009aa8U, frame0->function_base); + // The STACK WIN record for module1::RaisedByTheAliens should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ("", frame0->windows_frame_info->program_string); + EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000debeU, frame1->instruction + 1); + EXPECT_EQ(0x4000debeU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module1, frame1->module); + EXPECT_EQ("", frame1->function_name); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +struct CFIFixture: public StackwalkerX86Fixture { + CFIFixture() { + // Provide a bunch of STACK CFI records; individual tests walk to the + // caller from every point in this series, expecting to find the same + // set of register values. + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 4000 1000 10 enchiridion\n" + // Initially, just a return address. + "STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n" + // Push %ebx. + "STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n" + // Move %esi into %ebx. Weird, but permitted. + "STACK CFI 4002 $esi: $ebx\n" + // Allocate frame space, and save %edi. + "STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n" + // Put the return address in %edi. + "STACK CFI 4005 .ra: $edi\n" + // Save %ebp, and use it as a frame pointer. + "STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n" + + // The calling function. + "FUNC 5000 1000 10 epictetus\n" + // Mark it as end of stack. + "STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n"); + + // Provide some distinctive values for the caller's registers. + expected.esp = 0x80000000; + expected.eip = 0x40005510; + expected.ebp = 0xc0d4aab9; + expected.ebx = 0x60f20ce6; + expected.esi = 0x53d1379d; + expected.edi = 0xafbae234; + + // By default, registers are unchanged. + raw_context = expected; + } + + // Walk the stack, using stack_section as the contents of the stack + // and raw_context as the current register values. (Set + // raw_context.esp to the stack's starting address.) Expect two + // stack frames; in the older frame, expect the callee-saves + // registers to have values matching those in 'expected'. + void CheckWalk() { + RegionFromSection(); + raw_context.esp = stack_section.start().Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40004000U, frame0->function_base); + ASSERT_TRUE(frame0->windows_frame_info != NULL); + ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, + frame0->windows_frame_info->valid); + ASSERT_TRUE(frame0->cfi_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | + StackFrameX86::CONTEXT_VALID_ESP | + StackFrameX86::CONTEXT_VALID_EBP | + StackFrameX86::CONTEXT_VALID_EBX | + StackFrameX86::CONTEXT_VALID_ESI | + StackFrameX86::CONTEXT_VALID_EDI), + frame1->context_validity); + EXPECT_EQ(expected.eip, frame1->context.eip); + EXPECT_EQ(expected.esp, frame1->context.esp); + EXPECT_EQ(expected.ebp, frame1->context.ebp); + EXPECT_EQ(expected.ebx, frame1->context.ebx); + EXPECT_EQ(expected.esi, frame1->context.esi); + EXPECT_EQ(expected.edi, frame1->context.edi); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values the stack walker should find for the caller's registers. + MDRawContextX86 expected; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, At4000) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004000; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004001; + raw_context.ebx = 0x91aa9a8b; // callee's %ebx value + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004002; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0xa5c790ed; // callee's %esi value + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x56ec3db7) // garbage + .D32(0xafbae234) // saved %edi + .D32(0x53d67131) // garbage + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004003; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0xa97f229d; // callee's %esi + raw_context.edi = 0xb05cc997; // callee's %edi + CheckWalk(); +} + +// The results here should be the same as those at module offset +// 0x4003. +TEST_F(CFI, At4004) { + Label frame1_esp = expected.esp; + stack_section + .D32(0xe29782c2) // garbage + .D32(0xafbae234) // saved %edi + .D32(0x5ba29ce9) // garbage + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004004; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0x0fb7dc4e; // callee's %esi + raw_context.edi = 0x993b4280; // callee's %edi + CheckWalk(); +} + +TEST_F(CFI, At4005) { + Label frame1_esp = expected.esp; + stack_section + .D32(0xe29782c2) // garbage + .D32(0xafbae234) // saved %edi + .D32(0x5ba29ce9) // garbage + .D32(0x60f20ce6) // saved %ebx + .D32(0x8036cc02) // garbage + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004005; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0x0fb7dc4e; // callee's %esi + raw_context.edi = 0x40005510; // return address + CheckWalk(); +} + +TEST_F(CFI, At4006) { + Label frame0_ebp; + Label frame1_esp = expected.esp; + stack_section + .D32(0xdcdd25cd) // garbage + .D32(0xafbae234) // saved %edi + .D32(0xc0d4aab9) // saved %ebp + .Mark(&frame0_ebp) // frame pointer points here + .D32(0x60f20ce6) // saved %ebx + .D32(0x8036cc02) // garbage + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004006; + raw_context.ebp = frame0_ebp.Value(); + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0x743833c9; // callee's %esi + raw_context.edi = 0x40005510; // return address + CheckWalk(); +} + diff --git a/src/lib/crashdump/gbreakpad/processor/static_address_map-inl.h b/src/lib/crashdump/gbreakpad/processor/static_address_map-inl.h new file mode 100644 index 0000000..67e0797 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_address_map-inl.h @@ -0,0 +1,71 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_address_map-inl.h: StaticAddressMap implementation. +// +// See static_address_map.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_ADDRESS_MAP_INL_H__ +#define PROCESSOR_STATIC_ADDRESS_MAP_INL_H__ + +#include "processor/static_address_map.h" + +#include "processor/logging.h" + +namespace google_breakpad { + +template +bool StaticAddressMap::Retrieve( + const AddressType &address, + const EntryType *&entry, AddressType *entry_address) const { + + // upper_bound gives the first element whose key is greater than address, + // but we want the first element whose key is less than or equal to address. + // Decrement the iterator to get there, but not if the upper_bound already + // points to the beginning of the map - in that case, address is lower than + // the lowest stored key, so return false. + + MapConstIterator iterator = map_.upper_bound(address); + if (iterator == map_.begin()) + return false; + --iterator; + + entry = iterator.GetValuePtr(); + // Make sure AddressType is a copyable basic type + if (entry_address) + *entry_address = iterator.GetKey(); + + return true; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_ADDRESS_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_address_map.h b/src/lib/crashdump/gbreakpad/processor/static_address_map.h new file mode 100644 index 0000000..6bafc66 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_address_map.h @@ -0,0 +1,78 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_address_map.h: StaticAddressMap. +// +// StaticAddressMap is a wrapper class of StaticMap, just as AddressMap wraps +// std::map. StaticAddressMap provides read-only Retrieve() operation, similar +// as AddressMap. However, the difference between StaticAddressMap and +// AddressMap is that StaticAddressMap does not support dynamic operation +// Store() due to the static nature of the underlying StaticMap. +// +// See address_map.h for reference. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_ADDRESS_MAP_H__ +#define PROCESSOR_STATIC_ADDRESS_MAP_H__ + +#include "processor/static_map-inl.h" + +namespace google_breakpad { + +// AddressType MUST be a basic type, e.g.: integer types etc +// EntryType could be a complex type, so we retrieve its pointer instead. +template +class StaticAddressMap { + public: + StaticAddressMap(): map_() { } + explicit StaticAddressMap(const char *map_data): map_(map_data) { } + + // Locates the entry stored at the highest address less than or equal to + // the address argument. If there is no such range, returns false. The + // entry is returned in entry, which is a required argument. If + // entry_address is not NULL, it will be set to the address that the entry + // was stored at. + bool Retrieve(const AddressType &address, + const EntryType *&entry, AddressType *entry_address) const; + + private: + friend class ModuleComparer; + // Convenience types. + typedef StaticAddressMap* SelfPtr; + typedef StaticMap AddressToEntryMap; + typedef typename AddressToEntryMap::const_iterator MapConstIterator; + + AddressToEntryMap map_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_ADDRESS_MAP_H__ + diff --git a/src/lib/crashdump/gbreakpad/processor/static_address_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/static_address_map_unittest.cc new file mode 100644 index 0000000..5ef0345 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_address_map_unittest.cc @@ -0,0 +1,235 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_address_map_unittest.cc: Unit tests for StaticAddressMap. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include +#include +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "processor/address_map-inl.h" +#include "processor/static_address_map-inl.h" +#include "processor/simple_serializer-inl.h" +#include "map_serializers-inl.h" + +typedef google_breakpad::StaticAddressMap TestMap; +typedef google_breakpad::AddressMap AddrMap; + +class TestStaticAddressMap : public ::testing::Test { + protected: + void SetUp() { + for (int testcase = 0; testcase < kNumberTestCases; ++testcase) { + testdata[testcase] = new int[testsize[testcase]]; + } + + // Test data set0: NULL (empty map) + + // Test data set1: single element. + testdata[1][0] = 10; + + // Test data set2: six elements. + const int tempdata[] = {5, 10, 14, 15, 16, 20}; + for (int i = 0; i < testsize[2]; ++i) + testdata[2][i] = tempdata[i]; + + // Test data set3: + srand(time(NULL)); + for (int i = 0; i < testsize[3]; ++i) + testdata[3][i] = rand(); + + // Setup maps. + std::stringstream sstream; + for (int testcase = 0; testcase < kNumberTestCases; ++testcase) { + for (int data_item = 0; data_item < testsize[testcase]; ++data_item) { + sstream.clear(); + sstream << "test " << testdata[testcase][data_item]; + addr_map[testcase].Store(testdata[testcase][data_item], sstream.str()); + } + map_data[testcase] = serializer.Serialize(addr_map[testcase], NULL); + test_map[testcase] = TestMap(map_data[testcase]); + } + } + + void TearDown() { + for (int i = 0; i < kNumberTestCases; ++i) { + delete [] map_data[i]; + delete [] testdata[i]; + } + } + + void CompareRetrieveResult(int testcase, int target) { + int address; + int address_test; + std::string entry; + std::string entry_test; + const char *entry_cstring = NULL; + bool found; + bool found_test; + + found = addr_map[testcase].Retrieve(target, &entry, &address); + found_test = + test_map[testcase].Retrieve(target, entry_cstring, &address_test); + + ASSERT_EQ(found, found_test); + + if (found && found_test) { + ASSERT_EQ(address, address_test); + entry_test = entry_cstring; + ASSERT_EQ(entry, entry_test); + } + } + + void RetrieveTester(int testcase) { + int target; + target = INT_MIN; + CompareRetrieveResult(testcase, target); + target = INT_MAX; + CompareRetrieveResult(testcase, target); + + srand(time(0)); + for (int data_item = 0; data_item < testsize[testcase]; ++data_item) { + // Retrive (aka, search) for target address and compare results from + // AddressMap and StaticAddressMap. + + // First, assign the search target to be one of original testdata that is + // known to exist in the map. + target = testdata[testcase][data_item]; + CompareRetrieveResult(testcase, target); + // Then, add +2 / -1 bias to target value, in order to test searching for + // a target address not stored in the map. + target -= 1; + CompareRetrieveResult(testcase, target); + target += 3; + CompareRetrieveResult(testcase, target); + // Repeatedly test searching for random target addresses. + target = rand(); + CompareRetrieveResult(testcase, target); + } + } + + // Test data sets: + static const int kNumberTestCases = 4; + static const int testsize[]; + int *testdata[kNumberTestCases]; + + AddrMap addr_map[kNumberTestCases]; + TestMap test_map[kNumberTestCases]; + char *map_data[kNumberTestCases]; + google_breakpad::AddressMapSerializer serializer; +}; + +const int TestStaticAddressMap::testsize[] = {0, 1, 6, 1000}; + +TEST_F(TestStaticAddressMap, TestEmptyMap) { + int testcase = 0; + int target; + target = INT_MIN; + CompareRetrieveResult(testcase, target); + target = INT_MAX; + CompareRetrieveResult(testcase, target); + for (int data_item = 0; data_item < testsize[testcase]; ++data_item) { + target = testdata[testcase][data_item]; + CompareRetrieveResult(testcase, target); + target -= 1; + CompareRetrieveResult(testcase, target); + target += 3; + CompareRetrieveResult(testcase, target); + target = rand(); + CompareRetrieveResult(testcase, target); + } +} + +TEST_F(TestStaticAddressMap, TestOneElementMap) { + int testcase = 1; + int target; + target = INT_MIN; + CompareRetrieveResult(testcase, target); + target = INT_MAX; + CompareRetrieveResult(testcase, target); + for (int data_item = 0; data_item < testsize[testcase]; ++data_item) { + target = testdata[testcase][data_item]; + CompareRetrieveResult(testcase, target); + target -= 1; + CompareRetrieveResult(testcase, target); + target += 3; + CompareRetrieveResult(testcase, target); + target = rand(); + CompareRetrieveResult(testcase, target); + } +} + +TEST_F(TestStaticAddressMap, TestSixElementsMap) { + int testcase = 2; + int target; + target = INT_MIN; + CompareRetrieveResult(testcase, target); + target = INT_MAX; + CompareRetrieveResult(testcase, target); + for (int data_item = 0; data_item < testsize[testcase]; ++data_item) { + target = testdata[testcase][data_item]; + CompareRetrieveResult(testcase, target); + target -= 1; + CompareRetrieveResult(testcase, target); + target += 3; + CompareRetrieveResult(testcase, target); + target = rand(); + CompareRetrieveResult(testcase, target); + } +} + +TEST_F(TestStaticAddressMap, Test1000RandomElementsMap) { + int testcase = 3; + int target; + target = INT_MIN; + CompareRetrieveResult(testcase, target); + target = INT_MAX; + CompareRetrieveResult(testcase, target); + for (int data_item = 0; data_item < testsize[testcase]; ++data_item) { + target = testdata[testcase][data_item]; + CompareRetrieveResult(testcase, target); + target -= 1; + CompareRetrieveResult(testcase, target); + target += 3; + CompareRetrieveResult(testcase, target); + target = rand(); + CompareRetrieveResult(testcase, target); + } +} + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/static_contained_range_map-inl.h b/src/lib/crashdump/gbreakpad/processor/static_contained_range_map-inl.h new file mode 100644 index 0000000..46f9bbb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_contained_range_map-inl.h @@ -0,0 +1,92 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_contained_range_map-inl.h: Hierarchically-organized range map, +// i.e., StaticContainedRangeMap implementation. +// +// See static_contained_range_map.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__ +#define PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__ + +#include "processor/static_contained_range_map.h" +#include "processor/logging.h" + +namespace google_breakpad { + +template +StaticContainedRangeMap::StaticContainedRangeMap( + const char *base) + : base_(*(reinterpret_cast(base))), + entry_size_(*(reinterpret_cast(base + sizeof(base_)))), + entry_ptr_(reinterpret_cast( + base + sizeof(base_) + sizeof(entry_size_))), + map_(base + sizeof(base_) + sizeof(entry_size_) + entry_size_) { + if (entry_size_ == 0) + entry_ptr_ = NULL; +} + + +template +bool StaticContainedRangeMap::RetrieveRange( + const AddressType &address, const EntryType *&entry) const { + + // Get an iterator to the child range whose high address is equal to or + // greater than the supplied address. If the supplied address is higher + // than all of the high addresses in the range, then this range does not + // contain a child at address, so return false. If the supplied address + // is lower than the base address of the child range, then it is not within + // the child range, so return false. + MapConstIterator iterator = map_.lower_bound(address); + + if (iterator == map_.end()) + return false; + + const char *memory_child = + reinterpret_cast(iterator.GetValuePtr()); + + StaticContainedRangeMap child_map(memory_child); + + if (address < child_map.base_) + return false; + + // The child in iterator->second contains the specified address. Find out + // if it has a more-specific descendant that also contains it. If it does, + // it will set |entry| appropriately. If not, set |entry| to the child. + if (!child_map.RetrieveRange(address, entry)) + entry = child_map.entry_ptr_; + + return true; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_contained_range_map.h b/src/lib/crashdump/gbreakpad/processor/static_contained_range_map.h new file mode 100644 index 0000000..4d26e63 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_contained_range_map.h @@ -0,0 +1,96 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_contained_range_map.h: StaticContainedRangeMap. +// +// StaticContainedRangeMap is similar to ContainedRangeMap. However, +// StaticContainedRangeMap wraps a StaticMap instead of std::map, and does not +// support dynamic operations like StoreRange(...). +// StaticContainedRangeMap provides same RetrieveRange(...) interfaces as +// ContainedRangeMap. +// +// Please see contained_range_map.h for more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__ +#define PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__ + +#include "processor/static_map-inl.h" + +namespace google_breakpad { + +template +class StaticContainedRangeMap { + public: + StaticContainedRangeMap(): base_(), entry_size_(), entry_ptr_(), map_() { } + explicit StaticContainedRangeMap(const char *base); + + // Retrieves the most specific (smallest) descendant range encompassing + // the specified address. This method will only return entries held by + // child ranges, and not the entry contained by |this|. This is necessary + // to support a sparsely-populated root range. If no descendant range + // encompasses the address, returns false. + bool RetrieveRange(const AddressType &address, const EntryType *&entry) const; + + private: + friend class ModuleComparer; + // AddressToRangeMap stores pointers. This makes reparenting simpler in + // StoreRange, because it doesn't need to copy entire objects. + typedef StaticContainedRangeMap* SelfPtr; + typedef + StaticMap AddressToRangeMap; + typedef typename AddressToRangeMap::const_iterator MapConstIterator; + + // The base address of this range. The high address does not need to + // be stored, because it is used as the key to an object in its parent's + // map, and all ContainedRangeMaps except for the root range are contained + // within maps. The root range does not actually contain an entry, so its + // base_ field is meaningless, and the fact that it has no parent and thus + // no key is unimportant. For this reason, the base_ field should only be + // is accessed on child ContainedRangeMap objects, and never on |this|. + AddressType base_; + + // The entry corresponding to this range. The root range does not + // actually contain an entry, so its entry_ field is meaningless. For + // this reason, the entry_ field should only be accessed on child + // ContainedRangeMap objects, and never on |this|. + u_int32_t entry_size_; + const EntryType *entry_ptr_; + + // The map containing child ranges, keyed by each child range's high + // address. This is a pointer to avoid allocating map structures for + // leaf nodes, where they are not needed. + AddressToRangeMap map_; +}; + +} // namespace google_breakpad + + +#endif // PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_contained_range_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/static_contained_range_map_unittest.cc new file mode 100644 index 0000000..4c0c72d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_contained_range_map_unittest.cc @@ -0,0 +1,321 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_contained_range_map_unittest.cc: Unit tests for +// StaticContainedRangeMap. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include "breakpad_googletest_includes.h" +#include "processor/contained_range_map-inl.h" +#include "processor/static_contained_range_map-inl.h" +#include "processor/simple_serializer-inl.h" +#include "processor/map_serializers-inl.h" + +#include "processor/scoped_ptr.h" +#include "processor/logging.h" + +namespace { + +typedef google_breakpad::ContainedRangeMap CRMMap; +typedef google_breakpad::StaticContainedRangeMap TestMap; + +// Each element in test_data contains the expected result when calling +// RetrieveRange on an address. +const int test_data[] = { + 0, // 0 + 0, // 1 + 0, // 2 + 0, // 3 + 0, // 4 + 0, // 5 + 0, // 6 + 0, // 7 + 9, // 8 + 7, // 9 + 1, // 10 + 5, // 11 + 6, // 12 + 6, // 13 + 6, // 14 + 6, // 15 + 6, // 16 + 6, // 17 + 6, // 18 + 5, // 19 + 7, // 20 + 8, // 21 + 0, // 22 + 0, // 23 + 0, // 24 + 0, // 25 + 0, // 26 + 0, // 27 + 0, // 28 + 0, // 29 + 10, // 30 + 10, // 31 + 10, // 32 + 11, // 33 + 11, // 34 + 11, // 35 + 0, // 36 + 0, // 37 + 0, // 38 + 0, // 39 + 14, // 40 + 14, // 41 + 14, // 42 + 14, // 43 + 15, // 44 + 15, // 45 + 15, // 46 + 15, // 47 + 0, // 48 + 0, // 49 + 19, // 50 + 18, // 51 + 18, // 52 + 18, // 53 + 18, // 54 + 18, // 55 + 18, // 56 + 18, // 57 + 18, // 58 + 20, // 59 + 21, // 60 + 25, // 61 + 26, // 62 + 26, // 63 + 26, // 64 + 26, // 65 + 26, // 66 + 26, // 67 + 24, // 68 + 22, // 69 + 30, // 70 + 30, // 71 + 30, // 72 + 30, // 73 + 31, // 74 + 31, // 75 + 30, // 76 + 32, // 77 + 32, // 78 + 30, // 79 + 34, // 80 + 35, // 81 + 36, // 82 + 39, // 83 + 38, // 84 + 37, // 85 + 43, // 86 + 44, // 87 + 41, // 88 + 45, // 89 + 42, // 90 + 0, // 91 + 0, // 92 + 0, // 93 + 0, // 94 + 0, // 95 + 0, // 96 + 0, // 97 + 0, // 98 + 0 // 99 +}; + +} // namespace + +namespace google_breakpad { + +class TestStaticCRMMap : public ::testing::Test { + protected: + void SetUp(); + + // A referrence map for testing StaticCRMMap. + google_breakpad::ContainedRangeMap crm_map_; + + // Static version of crm_map using serialized data of crm_map. + // The goal of testing is to make sure TestMap provides same results for + // lookup operation(s) as CRMMap does. + google_breakpad::StaticContainedRangeMap test_map_; + + google_breakpad::ContainedRangeMapSerializer serializer_; + + scoped_array serialized_data_; +}; + +void TestStaticCRMMap::SetUp() { + // First, do the StoreRange tests. This validates the containment + // rules. + // We confirm the referrence map correctly stores data during setup. + ASSERT_TRUE (crm_map_.StoreRange(10, 10, 1)); + ASSERT_FALSE(crm_map_.StoreRange(10, 10, 2)); // exactly equal to 1 + ASSERT_FALSE(crm_map_.StoreRange(11, 10, 3)); // begins inside 1 and extends up + ASSERT_FALSE(crm_map_.StoreRange( 9, 10, 4)); // begins below 1 and ends inside + ASSERT_TRUE (crm_map_.StoreRange(11, 9, 5)); // contained by existing + ASSERT_TRUE (crm_map_.StoreRange(12, 7, 6)); + ASSERT_TRUE (crm_map_.StoreRange( 9, 12, 7)); // contains existing + ASSERT_TRUE (crm_map_.StoreRange( 9, 13, 8)); + ASSERT_TRUE (crm_map_.StoreRange( 8, 14, 9)); + ASSERT_TRUE (crm_map_.StoreRange(30, 3, 10)); + ASSERT_TRUE (crm_map_.StoreRange(33, 3, 11)); + ASSERT_TRUE (crm_map_.StoreRange(30, 6, 12)); // storable but totally masked + ASSERT_TRUE (crm_map_.StoreRange(40, 8, 13)); // will be totally masked + ASSERT_TRUE (crm_map_.StoreRange(40, 4, 14)); + ASSERT_TRUE (crm_map_.StoreRange(44, 4, 15)); + ASSERT_FALSE(crm_map_.StoreRange(32, 10, 16)); // begins in #10, ends in #14 + ASSERT_FALSE(crm_map_.StoreRange(50, 0, 17)); // zero length + ASSERT_TRUE (crm_map_.StoreRange(50, 10, 18)); + ASSERT_TRUE (crm_map_.StoreRange(50, 1, 19)); + ASSERT_TRUE (crm_map_.StoreRange(59, 1, 20)); + ASSERT_TRUE (crm_map_.StoreRange(60, 1, 21)); + ASSERT_TRUE (crm_map_.StoreRange(69, 1, 22)); + ASSERT_TRUE (crm_map_.StoreRange(60, 10, 23)); + ASSERT_TRUE (crm_map_.StoreRange(68, 1, 24)); + ASSERT_TRUE (crm_map_.StoreRange(61, 1, 25)); + ASSERT_TRUE (crm_map_.StoreRange(61, 8, 26)); + ASSERT_FALSE(crm_map_.StoreRange(59, 9, 27)); + ASSERT_FALSE(crm_map_.StoreRange(59, 10, 28)); + ASSERT_FALSE(crm_map_.StoreRange(59, 11, 29)); + ASSERT_TRUE (crm_map_.StoreRange(70, 10, 30)); + ASSERT_TRUE (crm_map_.StoreRange(74, 2, 31)); + ASSERT_TRUE (crm_map_.StoreRange(77, 2, 32)); + ASSERT_FALSE(crm_map_.StoreRange(72, 6, 33)); + ASSERT_TRUE (crm_map_.StoreRange(80, 3, 34)); + ASSERT_TRUE (crm_map_.StoreRange(81, 1, 35)); + ASSERT_TRUE (crm_map_.StoreRange(82, 1, 36)); + ASSERT_TRUE (crm_map_.StoreRange(83, 3, 37)); + ASSERT_TRUE (crm_map_.StoreRange(84, 1, 38)); + ASSERT_TRUE (crm_map_.StoreRange(83, 1, 39)); + ASSERT_TRUE (crm_map_.StoreRange(86, 5, 40)); + ASSERT_TRUE (crm_map_.StoreRange(88, 1, 41)); + ASSERT_TRUE (crm_map_.StoreRange(90, 1, 42)); + ASSERT_TRUE (crm_map_.StoreRange(86, 1, 43)); + ASSERT_TRUE (crm_map_.StoreRange(87, 1, 44)); + ASSERT_TRUE (crm_map_.StoreRange(89, 1, 45)); + ASSERT_TRUE (crm_map_.StoreRange(87, 4, 46)); + ASSERT_TRUE (crm_map_.StoreRange(87, 3, 47)); + ASSERT_FALSE(crm_map_.StoreRange(86, 2, 48)); + + // Serialize crm_map to generate serialized data. + unsigned int size; + serialized_data_.reset(serializer_.Serialize(&crm_map_, &size)); + BPLOG(INFO) << "Serialized data size: " << size << " Bytes."; + + // Construct test_map_ from serialized data. + test_map_ = TestMap(serialized_data_.get()); +} + +TEST_F(TestStaticCRMMap, TestEmptyMap) { + CRMMap empty_crm_map; + + unsigned int size; + scoped_array serialized_data; + serialized_data.reset(serializer_.Serialize(&empty_crm_map, &size)); + scoped_ptr test_map(new TestMap(serialized_data.get())); + + const unsigned int kCorrectSizeForEmptyMap = 16; + ASSERT_EQ(kCorrectSizeForEmptyMap, size); + + const int *entry_test; + ASSERT_FALSE(test_map->RetrieveRange(-1, entry_test)); + ASSERT_FALSE(test_map->RetrieveRange(0, entry_test)); + ASSERT_FALSE(test_map->RetrieveRange(10, entry_test)); +} + +TEST_F(TestStaticCRMMap, TestSingleElementMap) { + CRMMap crm_map; + // Test on one element: + int entry = 1; + crm_map.StoreRange(10, 10, entry); + + unsigned int size; + scoped_array serialized_data; + serialized_data.reset(serializer_.Serialize(&crm_map, &size)); + scoped_ptr test_map(new TestMap(serialized_data.get())); + + const unsigned int kCorrectSizeForSingleElementMap = 40; + ASSERT_EQ(kCorrectSizeForSingleElementMap, size); + + const int *entry_test; + ASSERT_FALSE(test_map->RetrieveRange(-1, entry_test)); + ASSERT_FALSE(test_map->RetrieveRange(0, entry_test)); + ASSERT_TRUE(test_map->RetrieveRange(10, entry_test)); + ASSERT_EQ(*entry_test, entry); + ASSERT_TRUE(test_map->RetrieveRange(13, entry_test)); + ASSERT_EQ(*entry_test, entry); +} + +TEST_F(TestStaticCRMMap, RunTestData) { + unsigned int test_high = sizeof(test_data) / sizeof(test_data[0]); + + // Now, do the RetrieveRange tests. This further validates that the + // objects were stored properly and that retrieval returns the correct + // object. + // If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a + // new test_data array will be printed. Exercise caution when doing this. + // Be sure to verify the results manually! +#ifdef GENERATE_TEST_DATA + printf(" const int test_data[] = {\n"); +#endif // GENERATE_TEST_DATA + + for (unsigned int address = 0; address < test_high; ++address) { + const int *entryptr; + int value = 0; + if (test_map_.RetrieveRange(address, entryptr)) + value = *entryptr; + +#ifndef GENERATE_TEST_DATA + // Don't use ASSERT inside the loop because it won't show the failed + // |address|, and the line number will always be the same. That makes + // it difficult to figure out which test failed. + EXPECT_EQ(value, test_data[address]) << "FAIL: retrieve address " + << address; +#else // !GENERATE_TEST_DATA + printf(" %d%c%s // %d\n", value, + address == test_high - 1 ? ' ' : ',', + value < 10 ? " " : "", + address); +#endif // !GENERATE_TEST_DATA + } + +#ifdef GENERATE_TEST_DATA + printf(" };\n"); +#endif // GENERATE_TEST_DATA +} + +} // namespace google_breakpad + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/static_map-inl.h b/src/lib/crashdump/gbreakpad/processor/static_map-inl.h new file mode 100644 index 0000000..7727052 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_map-inl.h @@ -0,0 +1,176 @@ +// Copyright 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_map-inl.h: StaticMap implementation. +// +// See static_map.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + + +#ifndef PROCESSOR_STATIC_MAP_INL_H__ +#define PROCESSOR_STATIC_MAP_INL_H__ + +#include "processor/static_map.h" +#include "processor/static_map_iterator-inl.h" +#include "processor/logging.h" + +namespace google_breakpad { + +template +StaticMap::StaticMap(const char* raw_data) + : raw_data_(raw_data), + compare_() { + // First 4 Bytes store the number of nodes. + num_nodes_ = *(reinterpret_cast(raw_data_)); + + offsets_ = reinterpret_cast( + raw_data_ + sizeof(num_nodes_)); + + keys_ = reinterpret_cast( + raw_data_ + (1 + num_nodes_) * sizeof(u_int32_t)); +} + +// find(), lower_bound() and upper_bound() implement binary search algorithm. +template +StaticMapIterator +StaticMap::find(const Key &key) const { + int begin = 0; + int end = num_nodes_; + int middle; + int compare_result; + while (begin < end) { + middle = begin + (end - begin) / 2; + compare_result = compare_(key, GetKeyAtIndex(middle)); + if (compare_result == 0) + return IteratorAtIndex(middle); + if (compare_result < 0) { + end = middle; + } else { + begin = middle + 1; + } + } + return this->end(); +} + +template +StaticMapIterator +StaticMap::lower_bound(const Key &key) const { + int begin = 0; + int end = num_nodes_; + int middle; + int comp_result; + while (begin < end) { + middle = begin + (end - begin) / 2; + comp_result = compare_(key, GetKeyAtIndex(middle)); + if (comp_result == 0) + return IteratorAtIndex(middle); + if (comp_result < 0) { + end = middle; + } else { + begin = middle + 1; + } + } + return IteratorAtIndex(begin); +} + +template +StaticMapIterator +StaticMap::upper_bound(const Key &key) const { + int begin = 0; + int end = num_nodes_; + int middle; + int compare_result; + while (begin < end) { + middle = begin + (end - begin) / 2; + compare_result = compare_(key, GetKeyAtIndex(middle)); + if (compare_result == 0) + return IteratorAtIndex(middle + 1); + if (compare_result < 0) { + end = middle; + } else { + begin = middle + 1; + } + } + return IteratorAtIndex(begin); +} + +template +bool StaticMap::ValidateInMemoryStructure() const { + // check the number of nodes is non-negative: + if (!raw_data_) return false; + int32_t num_nodes = *(reinterpret_cast(raw_data_)); + if (num_nodes < 0) { + BPLOG(INFO) << "StaticMap check failed: negative number of nodes"; + return false; + } + + int node_index = 0; + if (num_nodes_) { + u_int64_t first_offset = sizeof(int32_t) * (num_nodes_ + 1) + + sizeof(Key) * num_nodes_; + // Num_nodes_ is too large. + if (first_offset > 0xffffffffUL) { + BPLOG(INFO) << "StaticMap check failed: size exceeds limit"; + return false; + } + if (offsets_[node_index] != static_cast(first_offset)) { + BPLOG(INFO) << "StaticMap check failed: first node offset is incorrect"; + return false; + } + } + + for (node_index = 1; node_index < num_nodes_; ++node_index) { + // Check offsets[i] is strictly increasing: + if (offsets_[node_index] <= offsets_[node_index - 1]) { + BPLOG(INFO) << "StaticMap check failed: node offsets non-increasing"; + return false; + } + // Check Key[i] is strictly increasing as no duplicate keys are allowed. + if (compare_(GetKeyAtIndex(node_index), + GetKeyAtIndex(node_index - 1)) <= 0) { + BPLOG(INFO) << "StaticMap check failed: node keys non-increasing"; + return false; + } + } + return true; +} + +template +const Key StaticMap::GetKeyAtIndex(int index) const { + if (index < 0 || index >= num_nodes_) { + BPLOG(ERROR) << "Key index out of range error"; + // Key type is required to be primitive type. Return 0 if index is invalid. + return 0; + } + return keys_[index]; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_map.h b/src/lib/crashdump/gbreakpad/processor/static_map.h new file mode 100644 index 0000000..023ab3a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_map.h @@ -0,0 +1,144 @@ +// Copyright 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_map.h: StaticMap. +// +// StaticMap provides lookup interfaces and iterators similar as stl::map's. +// These lookup operations are purely Read-Only, thus memory +// allocation & deallocation is mostly avoided (intentionally). +// +// The chunk of memory should contain data with pre-defined pattern: +// **************** header *************** +// uint32 (4 bytes): number of nodes +// uint32 (4 bytes): address offset of node1's mapped_value +// uint32 (4 bytes): address offset of node2's mapped_value +// ... +// uint32 (4 bytes): address offset of nodeN's mapped_value +// +// ************* Key array ************ +// (X bytes): node1's key +// (X bytes): node2's key +// ... +// (X bytes): nodeN's key +// +// ************* Value array ********** +// (? bytes): node1's mapped_value +// (? bytes): node2's mapped_value +// ... +// (? bytes): nodeN's mapped_value +// +// REQUIREMENT: Key type MUST be primitive type or pointers so that: +// X = sizeof(typename Key); +// +// Note: since address offset is stored as uint32, user should keep in mind that +// StaticMap only supports up to 4GB size of memory data. + +// Author: Siyang Xie (lambxsy@google.com) + + +#ifndef PROCESSOR_STATIC_MAP_H__ +#define PROCESSOR_STATIC_MAP_H__ + +#include "processor/static_map_iterator-inl.h" + +namespace google_breakpad { + +// Default functor to compare keys. +template +class DefaultCompare { + public: + int operator()(const Key &k1, const Key &k2) const { + if (k1 < k2) return -1; + if (k1 == k2) return 0; + return 1; + } +}; + +template > +class StaticMap { + public: + typedef StaticMapIterator iterator; + typedef StaticMapIterator const_iterator; + + StaticMap() : raw_data_(0), + num_nodes_(0), + offsets_(0), + compare_() { } + + explicit StaticMap(const char* raw_data); + + inline bool empty() const { return num_nodes_ == 0; } + inline unsigned int size() const { return num_nodes_; } + + // Return iterators. + inline iterator begin() const { return IteratorAtIndex(0); } + inline iterator last() const { return IteratorAtIndex(num_nodes_ - 1); } + inline iterator end() const { return IteratorAtIndex(num_nodes_); } + inline iterator IteratorAtIndex(int index) const { + return iterator(raw_data_, index); + } + + // Lookup operations. + iterator find(const Key &k) const; + + // lower_bound(k) searches in a sorted range for the first element that has a + // key not less than the argument k. + iterator lower_bound(const Key &k) const; + + // upper_bound(k) searches in a sorted range for the first element that has a + // key greater than the argument k. + iterator upper_bound(const Key &k) const; + + // Checks if the underlying memory data conforms to the predefined pattern: + // first check the number of nodes is non-negative, + // then check both offsets and keys are strictly increasing (sorted). + bool ValidateInMemoryStructure() const; + + private: + const Key GetKeyAtIndex(int i) const; + + // Start address of a raw memory chunk with serialized data. + const char* raw_data_; + + // Number of nodes in the static map. + u_int32_t num_nodes_; + + // Array of offset addresses for stored values. + // For example: + // address_of_i-th_node_value = raw_data_ + offsets_[i] + const u_int32_t* offsets_; + + // keys_[i] = key of i_th node + const Key* keys_; + + Compare compare_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_MAP_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_map_iterator-inl.h b/src/lib/crashdump/gbreakpad/processor/static_map_iterator-inl.h new file mode 100644 index 0000000..325046c --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_map_iterator-inl.h @@ -0,0 +1,147 @@ +// Copyright 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_map_iterator-inl.h: StaticMapIterator implementation. +// +// See static_map_iterator.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_MAP_ITERATOR_INL_H__ +#define PROCESSOR_STATIC_MAP_ITERATOR_INL_H__ + +#include "processor/static_map_iterator.h" + +#include "processor/logging.h" + +namespace google_breakpad { + +template +StaticMapIterator::StaticMapIterator(const char* base, + const int &index): + index_(index), base_(base) { + // See static_map.h for documentation on + // bytes format of serialized StaticMap data. + num_nodes_ = *(reinterpret_cast(base_)); + offsets_ = reinterpret_cast(base_ + sizeof(num_nodes_)); + keys_ = reinterpret_cast( + base_ + (1 + num_nodes_) * sizeof(num_nodes_)); +} + +// Increment & Decrement operators: +template +StaticMapIterator& +StaticMapIterator::operator++() { + if (!IsValid()) { + BPLOG(ERROR) << "operator++ on invalid iterator"; + return *this; + } + if (++index_ > num_nodes_) index_ = num_nodes_; + return *this; +} + +template +StaticMapIterator +StaticMapIterator::operator++(int postfix_operator) { + if (!IsValid()) { + BPLOG(ERROR) << "operator++ on invalid iterator"; + return *this; + } + StaticMapIterator tmp = *this; + if (++index_ > num_nodes_) index_ = num_nodes_; + return tmp; +} + +template +StaticMapIterator& +StaticMapIterator::operator--() { + if (!IsValid()) { + BPLOG(ERROR) << "operator++ on invalid iterator"; + return *this; + } + + if (--index_ < 0) index_ = 0; + return *this; +} + +template +StaticMapIterator +StaticMapIterator::operator--(int postfix_operator) { + if (!IsValid()) { + BPLOG(ERROR) << "operator++ on invalid iterator"; + return *this; + } + StaticMapIterator tmp = *this; + + if (--index_ < 0) index_ = 0; + return tmp; +} + +template +const Key* StaticMapIterator::GetKeyPtr() const { + if (!IsValid()) { + BPLOG(ERROR) << "call GetKeyPtr() on invalid iterator"; + return NULL; + } + return &(keys_[index_]); +} + +template +const char* StaticMapIterator::GetValueRawPtr() const { + if (!IsValid()) { + BPLOG(ERROR) << "call GetValuePtr() on invalid iterator"; + return NULL; + } + return base_ + offsets_[index_]; +} + +template +bool StaticMapIterator::operator==( + const StaticMapIterator& x) const { + return base_ == x.base_ && index_ == x.index_; +} + +template +bool StaticMapIterator::operator!=( + const StaticMapIterator& x) const { + // Only need to compare base_ and index_. + // Other data members are auxiliary. + return base_ != x.base_ || index_ != x.index_; +} + +template +bool StaticMapIterator::IsValid() const { + if (!base_ || index_ < 0 || index_ > num_nodes_) + return false; + + return true; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_MAP_ITERATOR_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_map_iterator.h b/src/lib/crashdump/gbreakpad/processor/static_map_iterator.h new file mode 100644 index 0000000..8127667 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_map_iterator.h @@ -0,0 +1,112 @@ +// Copyright 2010 Google Inc. 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_map_iterator.h: StaticMapIterator template class declaration. +// +// StaticMapIterator provides increment and decrement operators to iterate +// through a StaticMap map. It does not provide *, -> operators, user should +// use GetKeyPtr(), GetKey(), GetValuePtr() interfaces to retrieve data or +// pointer to data. StaticMapIterator is essentially a const_iterator. +// +// Author: Siyang Xie (lambxsy@google.com) + + +#ifndef PROCESSOR_STATIC_MAP_ITERATOR_H__ +#define PROCESSOR_STATIC_MAP_ITERATOR_H__ + +#include + +namespace google_breakpad { + +// Forward declaration. +template class StaticMap; + +// StaticMapIterator does not support operator*() or operator->(), +// User should use GetKey(), GetKeyPtr(), GetValuePtr() instead; +template +class StaticMapIterator { + public: + // Constructors. + StaticMapIterator(): index_(-1), base_(NULL) { } + + // Increment & Decrement operators: + StaticMapIterator& operator++(); + StaticMapIterator operator++(int post_fix_operator); + + StaticMapIterator& operator--(); + StaticMapIterator operator--(int post_fix_operator); + + // Interface for retrieving data / pointer to data. + const Key* GetKeyPtr() const; + + // Run time error will occur if GetKey() is called on an invalid iterator. + inline const Key GetKey() const { return *GetKeyPtr(); } + + // return a raw memory pointer that points to the start address of value. + const char* GetValueRawPtr() const; + + // return a reinterpret-casted pointer to the value. + inline const Value* GetValuePtr() const { + return reinterpret_cast(GetValueRawPtr()); + } + + bool operator==(const StaticMapIterator& x) const; + bool operator!=(const StaticMapIterator& x) const; + + // Check if this iterator is valid. + // If iterator is invalid, user is forbidden to use ++/-- operator + // or interfaces for retrieving data / pointer to data. + bool IsValid() const; + + private: + friend class StaticMap; + + // Only StaticMap can call this constructor. + explicit StaticMapIterator(const char* base, const int32_t &index); + + // Index of node that the iterator is pointing to. + int32_t index_; + + // Beginning address of the serialized map data. + const char* base_; + + // Number of nodes in the map. Use it to identify end() iterator. + int32_t num_nodes_; + + // offsets_ is an array of offset addresses of mapped values. + // For example: + // address_of_i-th_node_value = base_ + offsets_[i] + const u_int32_t* offsets_; + + // keys_[i] = key of i_th node. + const Key* keys_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_MAP_ITERATOR_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/static_map_unittest.cc new file mode 100644 index 0000000..eb1e135 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_map_unittest.cc @@ -0,0 +1,386 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_map_unittest.cc: Unit tests for StaticMap. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "processor/static_map-inl.h" + + +typedef int ValueType; +typedef int KeyType; +typedef google_breakpad::StaticMap< KeyType, ValueType > TestMap; +typedef std::map< KeyType, ValueType > StdMap; + +template +class SimpleMapSerializer { + public: + static char* Serialize(const std::map &stdmap, + unsigned int* size = NULL) { + unsigned int size_per_node = + sizeof(u_int32_t) + sizeof(Key) + sizeof(Value); + unsigned int memsize = sizeof(int32_t) + size_per_node * stdmap.size(); + if (size) *size = memsize; + + // Allocate memory for serialized data: + char* mem = reinterpret_cast(operator new(memsize)); + char* address = mem; + + // Writer the number of nodes: + new (address) u_int32_t(static_cast(stdmap.size())); + address += sizeof(u_int32_t); + + // Nodes' offset: + u_int32_t* offsets = reinterpret_cast(address); + address += sizeof(u_int32_t) * stdmap.size(); + + // Keys: + Key* keys = reinterpret_cast(address); + address += sizeof(Key) * stdmap.size(); + + // Traversing map: + typename std::map::const_iterator iter = stdmap.begin(); + for (int index = 0; iter != stdmap.end(); ++iter, ++index) { + offsets[index] = static_cast(address - mem); + keys[index] = iter->first; + new (address) Value(iter->second); + address += sizeof(Value); + } + return mem; + } +}; + + +class TestInvalidMap : public ::testing::Test { + protected: + void SetUp() { + memset(data, 0, kMemorySize); + } + + // 40 Bytes memory can hold a StaticMap with up to 3 nodes. + static const int kMemorySize = 40; + char data[kMemorySize]; + TestMap test_map; +}; + +TEST_F(TestInvalidMap, TestNegativeNumberNodes) { + memset(data, 0xff, sizeof(u_int32_t)); // Set the number of nodes = -1 + test_map = TestMap(data); + ASSERT_FALSE(test_map.ValidateInMemoryStructure()); +} + +TEST_F(TestInvalidMap, TestWrongOffsets) { + u_int32_t* header = reinterpret_cast(data); + const u_int32_t kNumNodes = 2; + const u_int32_t kHeaderOffset = + sizeof(u_int32_t) + kNumNodes * (sizeof(u_int32_t) + sizeof(KeyType)); + + header[0] = kNumNodes; + header[1] = kHeaderOffset + 3; // Wrong offset for first node + test_map = TestMap(data); + ASSERT_FALSE(test_map.ValidateInMemoryStructure()); + + header[1] = kHeaderOffset; // Correct offset for first node + header[2] = kHeaderOffset - 1; // Wrong offset for second node + test_map = TestMap(data); + ASSERT_FALSE(test_map.ValidateInMemoryStructure()); +} + +TEST_F(TestInvalidMap, TestUnSortedKeys) { + u_int32_t* header = reinterpret_cast(data); + const u_int32_t kNumNodes = 2; + const u_int32_t kHeaderOffset = + sizeof(u_int32_t) + kNumNodes * (sizeof(u_int32_t) + sizeof(KeyType)); + header[0] = kNumNodes; + header[1] = kHeaderOffset; + header[2] = kHeaderOffset + sizeof(ValueType); + + KeyType* keys = reinterpret_cast( + data + (kNumNodes + 1) * sizeof(u_int32_t)); + // Set keys in non-increasing order. + keys[0] = 10; + keys[1] = 7; + test_map = TestMap(data); + ASSERT_FALSE(test_map.ValidateInMemoryStructure()); +} + + +class TestValidMap : public ::testing::Test { + protected: + void SetUp() { + int testcase = 0; + + // Empty map. + map_data[testcase] = + serializer.Serialize(std_map[testcase], &size[testcase]); + test_map[testcase] = TestMap(map_data[testcase]); + ++testcase; + + // Single element. + std_map[testcase].insert(std::make_pair(2, 8)); + map_data[testcase] = + serializer.Serialize(std_map[testcase], &size[testcase]); + test_map[testcase] = TestMap(map_data[testcase]); + ++testcase; + + // 100 elements. + for (int i = 0; i < 100; ++i) + std_map[testcase].insert(std::make_pair(i, 2 * i)); + map_data[testcase] = + serializer.Serialize(std_map[testcase], &size[testcase]); + test_map[testcase] = TestMap(map_data[testcase]); + ++testcase; + + // 1000 random elements. + for (int i = 0; i < 1000; ++i) + std_map[testcase].insert(std::make_pair(rand(), rand())); + map_data[testcase] = + serializer.Serialize(std_map[testcase], &size[testcase]); + test_map[testcase] = TestMap(map_data[testcase]); + + // Set correct size of memory allocation for each test case. + unsigned int size_per_node = + sizeof(u_int32_t) + sizeof(KeyType) + sizeof(ValueType); + for (testcase = 0; testcase < kNumberTestCases; ++testcase) { + correct_size[testcase] = + sizeof(u_int32_t) + std_map[testcase].size() * size_per_node; + } + } + + void TearDown() { + for (int i = 0;i < kNumberTestCases; ++i) + delete map_data[i]; + } + + + void IteratorTester(int test_case) { + // scan through: + iter_test = test_map[test_case].begin(); + iter_std = std_map[test_case].begin(); + + for (; iter_test != test_map[test_case].end() && + iter_std != std_map[test_case].end(); + ++iter_test, ++iter_std) { + ASSERT_EQ(iter_test.GetKey(), iter_std->first); + ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); + } + ASSERT_TRUE(iter_test == test_map[test_case].end() + && iter_std == std_map[test_case].end()); + + // Boundary testcase. + if (!std_map[test_case].empty()) { + // rear boundary case: + iter_test = test_map[test_case].end(); + iter_std = std_map[test_case].end(); + --iter_std; + --iter_test; + ASSERT_EQ(iter_test.GetKey(), iter_std->first); + ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); + + ++iter_test; + ++iter_std; + ASSERT_TRUE(iter_test == test_map[test_case].end()); + + --iter_test; + --iter_std; + ASSERT_TRUE(iter_test != test_map[test_case].end()); + ASSERT_TRUE(iter_test == test_map[test_case].last()); + ASSERT_EQ(iter_test.GetKey(), iter_std->first); + ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); + + // front boundary case: + iter_test = test_map[test_case].begin(); + --iter_test; + ASSERT_TRUE(iter_test == test_map[test_case].begin()); + } + } + + void CompareLookupResult(int test_case) { + bool found1 = (iter_test != test_map[test_case].end()); + bool found2 = (iter_std != std_map[test_case].end()); + ASSERT_EQ(found1, found2); + + if (found1 && found2) { + ASSERT_EQ(iter_test.GetKey(), iter_std->first); + ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second); + } + } + + void FindTester(int test_case, const KeyType &key) { + iter_test = test_map[test_case].find(key); + iter_std = std_map[test_case].find(key); + CompareLookupResult(test_case); + } + + void LowerBoundTester(int test_case, const KeyType &key) { + iter_test = test_map[test_case].lower_bound(key); + iter_std = std_map[test_case].lower_bound(key); + CompareLookupResult(test_case); + } + + void UpperBoundTester(int test_case, const KeyType &key) { + iter_test = test_map[test_case].upper_bound(key); + iter_std = std_map[test_case].upper_bound(key); + CompareLookupResult(test_case); + } + + void LookupTester(int test_case) { + StdMap::const_iterator iter; + // Test find(): + for (iter = std_map[test_case].begin(); + iter != std_map[test_case].end(); + ++iter) { + FindTester(test_case, iter->first); + FindTester(test_case, iter->first + 1); + FindTester(test_case, iter->first - 1); + } + FindTester(test_case, INT_MIN); + FindTester(test_case, INT_MAX); + // random test: + for (int i = 0; i < rand()%5000 + 5000; ++i) + FindTester(test_case, rand()); + + // Test lower_bound(): + for (iter = std_map[test_case].begin(); + iter != std_map[test_case].end(); + ++iter) { + LowerBoundTester(test_case, iter->first); + LowerBoundTester(test_case, iter->first + 1); + LowerBoundTester(test_case, iter->first - 1); + } + LowerBoundTester(test_case, INT_MIN); + LowerBoundTester(test_case, INT_MAX); + // random test: + for (int i = 0; i < rand()%5000 + 5000; ++i) + LowerBoundTester(test_case, rand()); + + // Test upper_bound(): + for (iter = std_map[test_case].begin(); + iter != std_map[test_case].end(); + ++iter) { + UpperBoundTester(test_case, iter->first); + UpperBoundTester(test_case, iter->first + 1); + UpperBoundTester(test_case, iter->first - 1); + } + UpperBoundTester(test_case, INT_MIN); + UpperBoundTester(test_case, INT_MAX); + // random test: + for (int i = 0; i < rand()%5000 + 5000; ++i) + UpperBoundTester(test_case, rand()); + } + + static const int kNumberTestCases = 4; + StdMap std_map[kNumberTestCases]; + TestMap test_map[kNumberTestCases]; + TestMap::const_iterator iter_test; + StdMap::const_iterator iter_std; + char* map_data[kNumberTestCases]; + unsigned int size[kNumberTestCases]; + unsigned int correct_size[kNumberTestCases]; + SimpleMapSerializer serializer; +}; + +TEST_F(TestValidMap, TestEmptyMap) { + int test_case = 0; + // Assert memory size allocated during serialization is correct. + ASSERT_EQ(correct_size[test_case], size[test_case]); + + // Sanity check of serialized data: + ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); + ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); + ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); + + // Test Iterator. + IteratorTester(test_case); + + // Test lookup operations. + LookupTester(test_case); +} + +TEST_F(TestValidMap, TestSingleElement) { + int test_case = 1; + // Assert memory size allocated during serialization is correct. + ASSERT_EQ(correct_size[test_case], size[test_case]); + + // Sanity check of serialized data: + ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); + ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); + ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); + + // Test Iterator. + IteratorTester(test_case); + + // Test lookup operations. + LookupTester(test_case); +} + +TEST_F(TestValidMap, Test100Elements) { + int test_case = 2; + // Assert memory size allocated during serialization is correct. + ASSERT_EQ(correct_size[test_case], size[test_case]); + + // Sanity check of serialized data: + ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); + ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); + ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); + + // Test Iterator. + IteratorTester(test_case); + + // Test lookup operations. + LookupTester(test_case); +} + +TEST_F(TestValidMap, Test1000RandomElements) { + int test_case = 3; + // Assert memory size allocated during serialization is correct. + ASSERT_EQ(correct_size[test_case], size[test_case]); + + // Sanity check of serialized data: + ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure()); + ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty()); + ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size()); + + // Test Iterator. + IteratorTester(test_case); + + // Test lookup operations. + LookupTester(test_case); +} + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/static_range_map-inl.h b/src/lib/crashdump/gbreakpad/processor/static_range_map-inl.h new file mode 100644 index 0000000..f6cef1a --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_range_map-inl.h @@ -0,0 +1,130 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_range_map-inl.h: StaticRangeMap implementation. +// +// See static_range_map.h for documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_RANGE_MAP_INL_H__ +#define PROCESSOR_STATIC_RANGE_MAP_INL_H__ + +#include "processor/static_range_map.h" +#include "processor/logging.h" + +namespace google_breakpad { + +template +bool StaticRangeMap::RetrieveRange( + const AddressType &address, const EntryType *&entry, + AddressType *entry_base, AddressType *entry_size) const { + MapConstIterator iterator = map_.lower_bound(address); + if (iterator == map_.end()) + return false; + + // The map is keyed by the high address of each range, so |address| is + // guaranteed to be lower than the range's high address. If |range| is + // not directly preceded by another range, it's possible for address to + // be below the range's low address, though. When that happens, address + // references something not within any range, so return false. + + const Range *range = iterator.GetValuePtr(); + + // Make sure AddressType and EntryType are copyable basic types + // e.g.: integer types, pointers etc + if (address < range->base()) + return false; + + entry = range->entryptr(); + if (entry_base) + *entry_base = range->base(); + if (entry_size) + *entry_size = iterator.GetKey() - range->base() + 1; + + return true; +} + + +template +bool StaticRangeMap::RetrieveNearestRange( + const AddressType &address, const EntryType *&entry, + AddressType *entry_base, AddressType *entry_size) const { + // If address is within a range, RetrieveRange can handle it. + if (RetrieveRange(address, entry, entry_base, entry_size)) + return true; + + // upper_bound gives the first element whose key is greater than address, + // but we want the first element whose key is less than or equal to address. + // Decrement the iterator to get there, but not if the upper_bound already + // points to the beginning of the map - in that case, address is lower than + // the lowest stored key, so return false. + + MapConstIterator iterator = map_.upper_bound(address); + if (iterator == map_.begin()) + return false; + --iterator; + + const Range *range = iterator.GetValuePtr(); + entry = range->entryptr(); + if (entry_base) + *entry_base = range->base(); + if (entry_size) + *entry_size = iterator.GetKey() - range->base() + 1; + + return true; +} + +template +bool StaticRangeMap::RetrieveRangeAtIndex( + int index, const EntryType *&entry, + AddressType *entry_base, AddressType *entry_size) const { + + if (index >= GetCount()) { + BPLOG(ERROR) << "Index out of range: " << index << "/" << GetCount(); + return false; + } + + MapConstIterator iterator = map_.IteratorAtIndex(index); + + const Range *range = iterator.GetValuePtr(); + + entry = range->entryptr(); + if (entry_base) + *entry_base = range->base(); + if (entry_size) + *entry_size = iterator.GetKey() - range->base() + 1; + + return true; +} + +} // namespace google_breakpad + + +#endif // PROCESSOR_STATIC_RANGE_MAP_INL_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_range_map.h b/src/lib/crashdump/gbreakpad/processor/static_range_map.h new file mode 100644 index 0000000..096bbbb --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_range_map.h @@ -0,0 +1,106 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// static_range_map.h: StaticRangeMap. +// +// StaticRangeMap is similar as RangeMap. However, StaticRangeMap wraps a +// StaticMap instead of std::map, and does not support dynamic operations like +// StoreRange(...). StaticRangeMap provides same Retrieve*() interfaces as +// RangeMap. Please see range_map.h for more documentation. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef PROCESSOR_STATIC_RANGE_MAP_H__ +#define PROCESSOR_STATIC_RANGE_MAP_H__ + + +#include "processor/static_map-inl.h" + +namespace google_breakpad { + +// AddressType is basic type, e.g.: integer types, pointers etc +// EntryType could be a complex type, so we retrieve its pointer instead. +template +class StaticRangeMap { + public: + StaticRangeMap(): map_() { } + explicit StaticRangeMap(const char *memory): map_(memory) { } + + // Locates the range encompassing the supplied address. If there is + // no such range, returns false. entry_base and entry_size, if non-NULL, + // are set to the base and size of the entry's range. + bool RetrieveRange(const AddressType &address, const EntryType *&entry, + AddressType *entry_base, AddressType *entry_size) const; + + // Locates the range encompassing the supplied address, if one exists. + // If no range encompasses the supplied address, locates the nearest range + // to the supplied address that is lower than the address. Returns false + // if no range meets these criteria. entry_base and entry_size, if + // non-NULL, are set to the base and size of the entry's range. + bool RetrieveNearestRange(const AddressType &address, const EntryType *&entry, + AddressType *entry_base, AddressType *entry_size) + const; + + // Treating all ranges as a list ordered by the address spaces that they + // occupy, locates the range at the index specified by index. Returns + // false if index is larger than the number of ranges stored. entry_base + // and entry_size, if non-NULL, are set to the base and size of the entry's + // range. + // + // RetrieveRangeAtIndex is not optimized for speedy operation. + bool RetrieveRangeAtIndex(int index, const EntryType *&entry, + AddressType *entry_base, AddressType *entry_size) + const; + + // Returns the number of ranges stored in the RangeMap. + inline unsigned int GetCount() const { return map_.size(); } + + private: + friend class ModuleComparer; + class Range { + public: + AddressType base() const { + return *(reinterpret_cast(this)); + } + const EntryType* entryptr() const { + return reinterpret_cast(this + sizeof(AddressType)); + } + }; + + // Convenience types. + typedef StaticRangeMap* SelfPtr; + typedef StaticMap AddressToRangeMap; + typedef typename AddressToRangeMap::const_iterator MapConstIterator; + + AddressToRangeMap map_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STATIC_RANGE_MAP_H__ diff --git a/src/lib/crashdump/gbreakpad/processor/static_range_map_unittest.cc b/src/lib/crashdump/gbreakpad/processor/static_range_map_unittest.cc new file mode 100644 index 0000000..82b2623 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/static_range_map_unittest.cc @@ -0,0 +1,421 @@ +// Copyright (c) 2010 Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// static_range_map_unittest.cc: Unit tests for StaticRangeMap. +// +// Author: Siyang Xie (lambxsy@google.com) + +#include "breakpad_googletest_includes.h" +#include "processor/range_map-inl.h" +#include "processor/static_range_map-inl.h" +#include "processor/simple_serializer-inl.h" +#include "processor/map_serializers-inl.h" + +#include "processor/logging.h" +#include "processor/scoped_ptr.h" + +namespace { +// Types used for testing. +typedef int AddressType; +typedef int EntryType; +typedef google_breakpad::StaticRangeMap< AddressType, EntryType > TestMap; +typedef google_breakpad::RangeMap< AddressType, EntryType > RMap; + +// RangeTest contains data to use for store and retrieve tests. See +// RunTests for descriptions of the tests. +struct RangeTest { + // Base address to use for test + AddressType address; + + // Size of range to use for test + AddressType size; + + // Unique ID of range - unstorable ranges must have unique IDs too + EntryType id; + + // Whether this range is expected to be stored successfully or not + bool expect_storable; +}; + +// A RangeTestSet encompasses multiple RangeTests, which are run in +// sequence on the same RangeMap. +struct RangeTestSet { + // An array of RangeTests + const RangeTest* range_tests; + + // The number of tests in the set + unsigned int range_test_count; +}; + +// These tests will be run sequentially. The first set of tests exercises +// most functions of RangeTest, and verifies all of the bounds-checking. +const RangeTest range_tests_0[] = { + { INT_MIN, 16, 1, true }, // lowest possible range + { -2, 5, 2, true }, // a range through zero + { INT_MAX - 9, 11, 3, false }, // tests anti-overflow + { INT_MAX - 9, 10, 4, true }, // highest possible range + { 5, 0, 5, false }, // tests anti-zero-size + { 5, 1, 6, true }, // smallest possible range + { -20, 15, 7, true }, // entirely negative + + { 10, 10, 10, true }, // causes the following tests to fail + { 9, 10, 11, false }, // one-less base, one-less high + { 9, 11, 12, false }, // one-less base, identical high + { 9, 12, 13, false }, // completely contains existing + { 10, 9, 14, false }, // identical base, one-less high + { 10, 10, 15, false }, // exactly identical to existing range + { 10, 11, 16, false }, // identical base, one-greater high + { 11, 8, 17, false }, // contained completely within + { 11, 9, 18, false }, // one-greater base, identical high + { 11, 10, 19, false }, // one-greater base, one-greater high + { 9, 2, 20, false }, // overlaps bottom by one + { 10, 1, 21, false }, // overlaps bottom by one, contained + { 19, 1, 22, false }, // overlaps top by one, contained + { 19, 2, 23, false }, // overlaps top by one + + { 9, 1, 24, true }, // directly below without overlap + { 20, 1, 25, true }, // directly above without overlap + + { 6, 3, 26, true }, // exactly between two ranges, gapless + { 7, 3, 27, false }, // tries to span two ranges + { 7, 5, 28, false }, // tries to span three ranges + { 4, 20, 29, false }, // tries to contain several ranges + + { 30, 50, 30, true }, + { 90, 25, 31, true }, + { 35, 65, 32, false }, // tries to span two noncontiguous + { 120, 10000, 33, true }, // > 8-bit + { 20000, 20000, 34, true }, // > 8-bit + { 0x10001, 0x10001, 35, true }, // > 16-bit + + { 27, -1, 36, false } // tests high < base +}; + +// Attempt to fill the entire space. The entire space must be filled with +// three stores because AddressType is signed for these tests, so RangeMap +// treats the size as signed and rejects sizes that appear to be negative. +// Even if these tests were run as unsigned, two stores would be needed +// to fill the space because the entire size of the space could only be +// described by using one more bit than would be present in AddressType. +const RangeTest range_tests_1[] = { + { INT_MIN, INT_MAX, 50, true }, // From INT_MIN to -2, inclusive + { -1, 2, 51, true }, // From -1 to 0, inclusive + { 1, INT_MAX, 52, true }, // From 1 to INT_MAX, inclusive + { INT_MIN, INT_MAX, 53, false }, // Can't fill the space twice + { -1, 2, 54, false }, + { 1, INT_MAX, 55, false }, + { -3, 6, 56, false }, // -3 to 2, inclusive - spans 3 ranges +}; + +// A light round of testing to verify that RetrieveRange does the right +// the right thing at the extremities of the range when nothing is stored +// there. Checks are forced without storing anything at the extremities +// by setting size = 0. +const RangeTest range_tests_2[] = { + { INT_MIN, 0, 100, false }, // makes RetrieveRange check low end + { -1, 3, 101, true }, + { INT_MAX, 0, 102, false }, // makes RetrieveRange check high end +}; + +// Similar to the previous test set, but with a couple of ranges closer +// to the extremities. +const RangeTest range_tests_3[] = { + { INT_MIN + 1, 1, 110, true }, + { INT_MAX - 1, 1, 111, true }, + { INT_MIN, 0, 112, false }, // makes RetrieveRange check low end + { INT_MAX, 0, 113, false } // makes RetrieveRange check high end +}; + +// The range map is cleared between sets of tests listed here. +const RangeTestSet range_test_sets[] = { + { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) }, + { range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) }, + { range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) }, + { range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) }, + { range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) } // Run again +}; + +} // namespace + +namespace google_breakpad { +class TestStaticRangeMap : public ::testing::Test { + protected: + void SetUp() { + kTestCasesCount_ = sizeof(range_test_sets) / sizeof(RangeTestSet); + } + + // StoreTest uses the data in a RangeTest and calls StoreRange on the + // test RangeMap. It returns true if the expected result occurred, and + // false if something else happened. + void StoreTest(RMap* range_map, const RangeTest* range_test); + + // RetrieveTest uses the data in RangeTest and calls RetrieveRange on the + // test RangeMap. If it retrieves the expected value (which can be no + // map entry at the specified range,) it returns true, otherwise, it returns + // false. RetrieveTest will check the values around the base address and + // the high address of a range to guard against off-by-one errors. + void RetrieveTest(TestMap* range_map, const RangeTest* range_test); + + // Test RetrieveRangeAtIndex, which is supposed to return objects in order + // according to their addresses. This test is performed by looping through + // the map, calling RetrieveRangeAtIndex for all possible indices in sequence, + // and verifying that each call returns a different object than the previous + // call, and that ranges are returned with increasing base addresses. Returns + // false if the test fails. + void RetrieveIndexTest(const TestMap* range_map, int set); + + void RunTestCase(int test_case); + + unsigned int kTestCasesCount_; + RangeMapSerializer serializer_; +}; + +void TestStaticRangeMap::StoreTest(RMap* range_map, + const RangeTest* range_test) { + bool stored = range_map->StoreRange(range_test->address, + range_test->size, + range_test->id); + EXPECT_EQ(stored, range_test->expect_storable) + << "StoreRange id " << range_test->id << "FAILED"; +} + +void TestStaticRangeMap::RetrieveTest(TestMap* range_map, + const RangeTest* range_test) { + for (unsigned int side = 0; side <= 1; ++side) { + // When side == 0, check the low side (base address) of each range. + // When side == 1, check the high side (base + size) of each range. + + // Check one-less and one-greater than the target address in addition + // to the target address itself. + + // If the size of the range is only 1, don't check one greater than + // the base or one less than the high - for a successfully stored + // range, these tests would erroneously fail because the range is too + // small. + AddressType low_offset = -1; + AddressType high_offset = 1; + if (range_test->size == 1) { + if (!side) // When checking the low side, + high_offset = 0; // don't check one over the target. + else // When checking the high side, + low_offset = 0; // don't check one under the target. + } + + for (AddressType offset = low_offset; offset <= high_offset; ++offset) { + AddressType address = + offset + + (!side ? range_test->address : + range_test->address + range_test->size - 1); + + bool expected_result = false; // This is correct for tests not stored. + if (range_test->expect_storable) { + if (offset == 0) // When checking the target address, + expected_result = true; // test should always succeed. + else if (offset == -1) // When checking one below the target, + expected_result = side; // should fail low and succeed high. + else // When checking one above the target, + expected_result = !side; // should succeed low and fail high. + } + + const EntryType* id; + AddressType retrieved_base; + AddressType retrieved_size; + bool retrieved = range_map->RetrieveRange(address, id, + &retrieved_base, + &retrieved_size); + + bool observed_result = retrieved && *id == range_test->id; + EXPECT_EQ(observed_result, expected_result) + << "RetrieveRange id " << range_test->id + << ", side " << side << ", offset " << offset << " FAILED."; + + // If a range was successfully retrieved, check that the returned + // bounds match the range as stored. + if (observed_result == true) { + EXPECT_EQ(retrieved_base, range_test->address) + << "RetrieveRange id " << range_test->id + << ", side " << side << ", offset " << offset << " FAILED."; + EXPECT_EQ(retrieved_size, range_test->size) + << "RetrieveRange id " << range_test->id + << ", side " << side << ", offset " << offset << " FAILED."; + } + + // Now, check RetrieveNearestRange. The nearest range is always + // expected to be different from the test range when checking one + // less than the low side. + bool expected_nearest = range_test->expect_storable; + if (!side && offset < 0) + expected_nearest = false; + + AddressType nearest_base; + AddressType nearest_size; + bool retrieved_nearest = range_map->RetrieveNearestRange(address, + id, + &nearest_base, + &nearest_size); + + // When checking one greater than the high side, RetrieveNearestRange + // should usually return the test range. When a different range begins + // at that address, though, then RetrieveNearestRange should return the + // range at the address instead of the test range. + if (side && offset > 0 && nearest_base == address) { + expected_nearest = false; + } + + bool observed_nearest = retrieved_nearest && + *id == range_test->id; + + EXPECT_EQ(observed_nearest, expected_nearest) + << "RetrieveRange id " << range_test->id + << ", side " << side << ", offset " << offset << " FAILED."; + + // If a range was successfully retrieved, check that the returned + // bounds match the range as stored. + if (expected_nearest ==true) { + EXPECT_EQ(nearest_base, range_test->address) + << "RetrieveRange id " << range_test->id + << ", side " << side << ", offset " << offset << " FAILED."; + EXPECT_EQ(nearest_size, range_test->size) + << "RetrieveRange id " << range_test->id + << ", side " << side << ", offset " << offset << " FAILED."; + } + } + } +} + +void TestStaticRangeMap::RetrieveIndexTest(const TestMap* range_map, int set) { + AddressType last_base = 0; + const EntryType* last_entry = 0; + const EntryType* entry; + int object_count = range_map->GetCount(); + for (int object_index = 0; object_index < object_count; ++object_index) { + AddressType base; + ASSERT_TRUE(range_map->RetrieveRangeAtIndex(object_index, + entry, + &base, + NULL)) + << "FAILED: RetrieveRangeAtIndex set " << set + << " index " << object_index; + + ASSERT_TRUE(entry) << "FAILED: RetrieveRangeAtIndex set " << set + << " index " << object_index; + + // It's impossible to do these comparisons unless there's a previous + // object to compare against. + if (last_entry) { + // The object must be different from the last_entry one. + EXPECT_NE(*entry, *last_entry) << "FAILED: RetrieveRangeAtIndex set " + << set << " index " << object_index; + // Each object must have a base greater than the previous object's base. + EXPECT_GT(base, last_base) << "FAILED: RetrieveRangeAtIndex set " << set + << " index " << object_index; + } + last_entry = entry; + last_base = base; + } + + // Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that + // are too high. + ASSERT_FALSE(range_map->RetrieveRangeAtIndex( + object_count, entry, NULL, NULL)) << "FAILED: RetrieveRangeAtIndex set " + << set << " index " << object_count + << " (too large)"; +} + +// RunTests runs a series of test sets. +void TestStaticRangeMap::RunTestCase(int test_case) { + // Maintain the range map in a pointer so that deletion can be meaningfully + // tested. + scoped_ptr rmap(new RMap()); + + const RangeTest* range_tests = range_test_sets[test_case].range_tests; + unsigned int range_test_count = range_test_sets[test_case].range_test_count; + + // Run the StoreRange test, which validates StoreRange and initializes + // the RangeMap with data for the RetrieveRange test. + int stored_count = 0; // The number of ranges successfully stored + for (unsigned int range_test_index = 0; + range_test_index < range_test_count; + ++range_test_index) { + const RangeTest* range_test = &range_tests[range_test_index]; + StoreTest(rmap.get(), range_test); + + if (range_test->expect_storable) + ++stored_count; + } + + scoped_array memaddr(serializer_.Serialize(*rmap, NULL)); + scoped_ptr static_range_map(new TestMap(memaddr.get())); + + // The RangeMap's own count of objects should also match. + EXPECT_EQ(static_range_map->GetCount(), stored_count); + + // Run the RetrieveRange test + for (unsigned int range_test_index = 0; + range_test_index < range_test_count; + ++range_test_index) { + const RangeTest* range_test = &range_tests[range_test_index]; + RetrieveTest(static_range_map.get(), range_test); + } + + RetrieveIndexTest(static_range_map.get(), test_case); +} + +TEST_F(TestStaticRangeMap, TestCase0) { + int test_case = 0; + RunTestCase(test_case); +} + +TEST_F(TestStaticRangeMap, TestCase1) { + int test_case = 1; + RunTestCase(test_case); +} + +TEST_F(TestStaticRangeMap, TestCase2) { + int test_case = 2; + RunTestCase(test_case); +} + +TEST_F(TestStaticRangeMap, TestCase3) { + int test_case = 3; + RunTestCase(test_case); +} + +TEST_F(TestStaticRangeMap, RunTestCase0Again) { + int test_case = 0; + RunTestCase(test_case); +} + +} // namespace google_breakpad + +int main(int argc, char *argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/src/lib/crashdump/gbreakpad/processor/synth_minidump.cc b/src/lib/crashdump/gbreakpad/processor/synth_minidump.cc new file mode 100644 index 0000000..b207c0d --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/synth_minidump.cc @@ -0,0 +1,349 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// synth_minidump.cc: Implementation of SynthMinidump. See synth_minidump.h + +#include "processor/synth_minidump.h" + +namespace google_breakpad { + +namespace SynthMinidump { + +Section::Section(const Dump &dump) + : test_assembler::Section(dump.endianness()) { } + +void Section::CiteLocationIn(test_assembler::Section *section) const { + if (this) + (*section).D32(size_).D32(file_offset_); + else + (*section).D32(0).D32(0); +} + +void Stream::CiteStreamIn(test_assembler::Section *section) const { + section->D32(type_); + CiteLocationIn(section); +} + +SystemInfo::SystemInfo(const Dump &dump, + const MDRawSystemInfo &system_info, + const String &csd_version) + : Stream(dump, MD_SYSTEM_INFO_STREAM) { + D16(system_info.processor_architecture); + D16(system_info.processor_level); + D16(system_info.processor_revision); + D8(system_info.number_of_processors); + D8(system_info.product_type); + D32(system_info.major_version); + D32(system_info.minor_version); + D32(system_info.build_number); + D32(system_info.platform_id); + csd_version.CiteStringIn(this); + D16(system_info.suite_mask); + D16(system_info.reserved2); // Well, why not? + + // MDCPUInformation cpu; + if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_X86) { + D32(system_info.cpu.x86_cpu_info.vendor_id[0]); + D32(system_info.cpu.x86_cpu_info.vendor_id[1]); + D32(system_info.cpu.x86_cpu_info.vendor_id[2]); + D32(system_info.cpu.x86_cpu_info.version_information); + D32(system_info.cpu.x86_cpu_info.feature_information); + D32(system_info.cpu.x86_cpu_info.amd_extended_cpu_features); + } else { + D64(system_info.cpu.other_cpu_info.processor_features[0]); + D64(system_info.cpu.other_cpu_info.processor_features[1]); + } +} + +const MDRawSystemInfo SystemInfo::windows_x86 = { + MD_CPU_ARCHITECTURE_X86, // processor_architecture + 6, // processor_level + 0xd08, // processor_revision + 1, // number_of_processors + 1, // product_type + 5, // major_version + 1, // minor_version + 2600, // build_number + 2, // platform_id + 0xdeadbeef, // csd_version_rva + 0x100, // suite_mask + 0, // reserved2 + { // cpu + { // x86_cpu_info + { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id + 0x6d8, // version_information + 0xafe9fbff, // feature_information + 0xffffffff // amd_extended_cpu_features + } + } +}; + +const string SystemInfo::windows_x86_csd_version = "Service Pack 2"; + +String::String(const Dump &dump, const string &contents) : Section(dump) { + D32(contents.size() * 2); + for (string::const_iterator i = contents.begin(); i != contents.end(); i++) + D16(*i); +} + +void String::CiteStringIn(test_assembler::Section *section) const { + section->D32(file_offset_); +} + +void Memory::CiteMemoryIn(test_assembler::Section *section) const { + section->D64(address_); + CiteLocationIn(section); +} + +Context::Context(const Dump &dump, const MDRawContextX86 &context) + : Section(dump) { + // The caller should have properly set the CPU type flag. + assert(context.context_flags & MD_CONTEXT_X86); + // It doesn't make sense to store x86 registers in big-endian form. + assert(dump.endianness() == kLittleEndian); + D32(context.context_flags); + D32(context.dr0); + D32(context.dr1); + D32(context.dr2); + D32(context.dr3); + D32(context.dr6); + D32(context.dr7); + D32(context.float_save.control_word); + D32(context.float_save.status_word); + D32(context.float_save.tag_word); + D32(context.float_save.error_offset); + D32(context.float_save.error_selector); + D32(context.float_save.data_offset); + D32(context.float_save.data_selector); + // context.float_save.register_area[] contains 8-bit quantities and + // does not need to be swapped. + Append(context.float_save.register_area, + sizeof(context.float_save.register_area)); + D32(context.float_save.cr0_npx_state); + D32(context.gs); + D32(context.fs); + D32(context.es); + D32(context.ds); + D32(context.edi); + D32(context.esi); + D32(context.ebx); + D32(context.edx); + D32(context.ecx); + D32(context.eax); + D32(context.ebp); + D32(context.eip); + D32(context.cs); + D32(context.eflags); + D32(context.esp); + D32(context.ss); + // context.extended_registers[] contains 8-bit quantities and does + // not need to be swapped. + Append(context.extended_registers, sizeof(context.extended_registers)); + assert(Size() == sizeof(MDRawContextX86)); +} + +Context::Context(const Dump &dump, const MDRawContextARM &context) + : Section(dump) { + // The caller should have properly set the CPU type flag. + assert((context.context_flags & MD_CONTEXT_ARM) || + (context.context_flags & MD_CONTEXT_ARM_OLD)); + // It doesn't make sense to store ARM registers in big-endian form. + assert(dump.endianness() == kLittleEndian); + D32(context.context_flags); + for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) + D32(context.iregs[i]); + D32(context.cpsr); + D64(context.float_save.fpscr); + for (int i = 0; i < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; ++i) + D64(context.float_save.regs[i]); + for (int i = 0; i < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; ++i) + D32(context.float_save.extra[i]); + assert(Size() == sizeof(MDRawContextARM)); +} + +Thread::Thread(const Dump &dump, + u_int32_t thread_id, const Memory &stack, const Context &context, + u_int32_t suspend_count, u_int32_t priority_class, + u_int32_t priority, u_int64_t teb) : Section(dump) { + D32(thread_id); + D32(suspend_count); + D32(priority_class); + D32(priority); + D64(teb); + stack.CiteMemoryIn(this); + context.CiteLocationIn(this); + assert(Size() == sizeof(MDRawThread)); +} + +Module::Module(const Dump &dump, + u_int64_t base_of_image, + u_int32_t size_of_image, + const String &name, + u_int32_t time_date_stamp, + u_int32_t checksum, + const MDVSFixedFileInfo &version_info, + const Section *cv_record, + const Section *misc_record) : Section(dump) { + D64(base_of_image); + D32(size_of_image); + D32(checksum); + D32(time_date_stamp); + name.CiteStringIn(this); + D32(version_info.signature); + D32(version_info.struct_version); + D32(version_info.file_version_hi); + D32(version_info.file_version_lo); + D32(version_info.product_version_hi); + D32(version_info.product_version_lo); + D32(version_info.file_flags_mask); + D32(version_info.file_flags); + D32(version_info.file_os); + D32(version_info.file_type); + D32(version_info.file_subtype); + D32(version_info.file_date_hi); + D32(version_info.file_date_lo); + cv_record->CiteLocationIn(this); + misc_record->CiteLocationIn(this); + D64(0).D64(0); +} + +const MDVSFixedFileInfo Module::stock_version_info = { + MD_VSFIXEDFILEINFO_SIGNATURE, // signature + MD_VSFIXEDFILEINFO_VERSION, // struct_version + 0x11111111, // file_version_hi + 0x22222222, // file_version_lo + 0x33333333, // product_version_hi + 0x44444444, // product_version_lo + MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG, // file_flags_mask + MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG, // file_flags + MD_VSFIXEDFILEINFO_FILE_OS_NT | MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS32, + // file_os + MD_VSFIXEDFILEINFO_FILE_TYPE_APP, // file_type + MD_VSFIXEDFILEINFO_FILE_SUBTYPE_UNKNOWN, // file_subtype + 0, // file_date_hi + 0 // file_date_lo +}; + +Exception::Exception(const Dump &dump, + const Context &context, + u_int32_t thread_id, + u_int32_t exception_code, + u_int32_t exception_flags, + u_int64_t exception_address) + : Stream(dump, MD_EXCEPTION_STREAM) { + D32(thread_id); + D32(0); // __align + D32(exception_code); + D32(exception_flags); + D64(0); // exception_record + D64(exception_address); + D32(0); // number_parameters + D32(0); // __align + for (int i = 0; i < MD_EXCEPTION_MAXIMUM_PARAMETERS; ++i) + D64(0); // exception_information + context.CiteLocationIn(this); + assert(Size() == sizeof(MDRawExceptionStream)); +} + +Dump::Dump(u_int64_t flags, + Endianness endianness, + u_int32_t version, + u_int32_t date_time_stamp) + : test_assembler::Section(endianness), + file_start_(0), + stream_directory_(*this), + stream_count_(0), + thread_list_(*this, MD_THREAD_LIST_STREAM), + module_list_(*this, MD_MODULE_LIST_STREAM), + memory_list_(*this, MD_MEMORY_LIST_STREAM) + { + D32(MD_HEADER_SIGNATURE); + D32(version); + D32(stream_count_label_); + D32(stream_directory_rva_); + D32(0); + D32(date_time_stamp); + D64(flags); + assert(Size() == sizeof(MDRawHeader)); +} + +Dump &Dump::Add(SynthMinidump::Section *section) { + section->Finish(file_start_ + Size()); + Append(*section); + return *this; +} + +Dump &Dump::Add(Stream *stream) { + Add(static_cast(stream)); + stream->CiteStreamIn(&stream_directory_); + stream_count_++; + return *this; +} + +Dump &Dump::Add(Memory *memory) { + // Add the memory contents themselves to the file. + Add(static_cast(memory)); + + // The memory list is a list of MDMemoryDescriptors, not of actual + // memory elements. Produce a descriptor, and add that to the list. + SynthMinidump::Section descriptor(*this); + memory->CiteMemoryIn(&descriptor); + memory_list_.Add(&descriptor); + return *this; +} + +Dump &Dump::Add(Thread *thread) { + thread_list_.Add(thread); + return *this; +} + +Dump &Dump::Add(Module *module) { + module_list_.Add(module); + return *this; +} + +void Dump::Finish() { + if (!thread_list_.Empty()) Add(&thread_list_); + if (!module_list_.Empty()) Add(&module_list_); + if (!memory_list_.Empty()) Add(&memory_list_); + + // Create the stream directory. We don't use + // stream_directory_.Finish here, because the stream directory isn't + // cited using a location descriptor; rather, the Minidump header + // has the stream count and MDRVA. + stream_count_label_ = stream_count_; + stream_directory_rva_ = file_start_ + Size(); + Append(static_cast(stream_directory_)); +} + +} // namespace SynthMinidump + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/synth_minidump.h b/src/lib/crashdump/gbreakpad/processor/synth_minidump.h new file mode 100644 index 0000000..245afe6 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/synth_minidump.h @@ -0,0 +1,369 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// synth_minidump.h: Interface to SynthMinidump: fake minidump generator. +// +// We treat a minidump file as the concatenation of a bunch of +// test_assembler::Sections. The file header, stream directory, +// streams, memory regions, strings, and so on --- each is a Section +// that eventually gets appended to the minidump. Dump, Memory, +// Context, Thread, and so on all inherit from test_assembler::Section. +// For example: +// +// using google_breakpad::test_assembler::kLittleEndian; +// using google_breakpad::SynthMinidump::Context; +// using google_breakpad::SynthMinidump::Dump; +// using google_breakpad::SynthMinidump::Memory; +// using google_breakpad::SynthMinidump::Thread; +// +// Dump minidump(MD_NORMAL, kLittleEndian); +// +// Memory stack1(minidump, 0x569eb0a9); +// ... build contents of stack1 with test_assembler::Section functions ... +// +// MDRawContextX86 x86_context1; +// x86_context1.context_flags = MD_CONTEXT_X86; +// x86_context1.eip = 0x7c90eb94; +// x86_context1.esp = 0x569eb0a9; +// x86_context1.ebp = x86_context1.esp + something appropriate; +// Context context1(minidump, x86_context1); +// +// Thread thread1(minidump, 0xe4a4821d, stack1, context1); +// +// minidump.Add(&stack1); +// minidump.Add(&context1); +// minidump.Add(&thread1); +// minidump.Finish(); +// +// string contents; +// EXPECT_TRUE(minidump.GetContents(&contents)); +// // contents now holds the bytes of a minidump file +// +// Because the test_assembler classes let us write Label references to +// sections before the Labels' values are known, this gives us +// flexibility in how we put the dump together: minidump pieces can +// hold the file offsets of other minidump pieces before the +// referents' positions have been decided. As long as everything has +// been placed by the time we call dump.GetContents to obtain the +// bytes, all the Labels' values will be known, and everything will +// get patched up appropriately. +// +// The dump.Add(thing) functions append THINGS's contents to the +// minidump, but they also do two other things: +// +// - dump.Add(thing) invokes thing->Finish, which tells *thing the +// offset within the file at which it was placed, and allows *thing +// to do any final content generation. +// +// - If THING is something which should receive an entry in some sort +// of list or directory, then dump.Add(THING) automatically creates +// the appropriate directory or list entry. Streams must appear in +// the stream directory; memory ranges should be listed in the +// memory list; threads should be placed in the thread list; and so +// on. +// +// By convention, Section subclass constructors that take references +// to other Sections do not take care of 'Add'ing their arguments to +// the dump. For example, although the Thread constructor takes +// references to a Memory and a Context, it does not add them to the +// dump on the caller's behalf. Rather, the caller is responsible for +// 'Add'ing every section they create. This allows Sections to be +// cited from more than one place; for example, Memory ranges are +// cited both from Thread objects (as their stack contents) and by the +// memory list stream. +// +// If you forget to Add some Section, the Dump::GetContents call will +// fail, as the test_assembler::Labels used to cite the Section's +// contents from elsewhere will still be undefined. +#ifndef PROCESSOR_SYNTH_MINIDUMP_H_ +#define PROCESSOR_SYNTH_MINIDUMP_H_ + +#include + +#include +#include + +#include "common/test_assembler.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +namespace SynthMinidump { + +using std::string; +using test_assembler::Endianness; +using test_assembler::kBigEndian; +using test_assembler::kLittleEndian; +using test_assembler::kUnsetEndian; +using test_assembler::Label; + +class Dump; +class Memory; +class String; + +// A test_assembler::Section which will be appended to a minidump. +class Section: public test_assembler::Section { + public: + explicit Section(const Dump &dump); + + // Append an MDLocationDescriptor referring to this section to SECTION. + // If 'this' is NULL, append a descriptor with a zero length and MDRVA. + // + // (I couldn't find the language in the C++ standard that says that + // invoking member functions of a NULL pointer to a class type is + // bad, if such language exists. Having this function handle NULL + // 'this' is convenient, but if it causes trouble, it's not hard to + // do differently.) + void CiteLocationIn(test_assembler::Section *section) const; + + // Note that this section's contents are complete, and that it has + // been placed in the minidump file at OFFSET. The 'Add' member + // functions call the Finish member function of the object being + // added for you; if you are 'Add'ing this section, you needn't Finish it. + virtual void Finish(const Label &offset) { + file_offset_ = offset; size_ = Size(); + } + + protected: + // This section's size and offset within the minidump file. + Label file_offset_, size_; +}; + +// A stream within a minidump file. 'Add'ing a stream to a minidump +// creates an entry for it in the minidump's stream directory. +class Stream: public Section { + public: + // Create a stream of type TYPE. You can append whatever contents + // you like to this stream using the test_assembler::Section methods. + Stream(const Dump &dump, u_int32_t type) : Section(dump), type_(type) { } + + // Append an MDRawDirectory referring to this stream to SECTION. + void CiteStreamIn(test_assembler::Section *section) const; + + private: + // The type of this stream. + u_int32_t type_; +}; + +class SystemInfo: public Stream { + public: + // Create an MD_SYSTEM_INFO_STREAM stream belonging to DUMP holding + // an MDRawSystem info structure initialized with the values from + // SYSTEM_INFO, except that the csd_version field is replaced with + // the file offset of the string CSD_VERSION, which can be 'Add'ed + // to the dump at the desired location. + // + // Remember that you are still responsible for 'Add'ing CSD_VERSION + // to the dump yourself. + SystemInfo(const Dump &dump, + const MDRawSystemInfo &system_info, + const String &csd_version); + + // Stock MDRawSystemInfo information and associated strings, for + // writing tests. + static const MDRawSystemInfo windows_x86; + static const string windows_x86_csd_version; +}; + +// An MDString: a string preceded by a 32-bit length. +class String: public Section { + public: + String(const Dump &dump, const string &value); + + // Append an MDRVA referring to this string to SECTION. + void CiteStringIn(test_assembler::Section *section) const; +}; + +// A range of memory contents. 'Add'ing a memory range to a minidump +// creates n entry for it in the minidump's memory list. By +// convention, the 'start', 'Here', and 'Mark' member functions refer +// to memory addresses. +class Memory: public Section { + public: + Memory(const Dump &dump, u_int64_t address) + : Section(dump), address_(address) { start() = address; } + + // Append an MDMemoryDescriptor referring to this memory range to SECTION. + void CiteMemoryIn(test_assembler::Section *section) const; + + private: + // The process address from which these memory contents were taken. + // Shouldn't this be a Label? + u_int64_t address_; +}; + +class Context: public Section { + public: + // Create a context belonging to DUMP whose contents are a copy of CONTEXT. + Context(const Dump &dump, const MDRawContextX86 &context); + Context(const Dump &dump, const MDRawContextARM &context); + // Add constructors for other architectures here. Remember to byteswap. +}; + +class Thread: public Section { + public: + // Create a thread belonging to DUMP with the given values, citing + // STACK and CONTEXT (which you must Add to the dump separately). + Thread(const Dump &dump, + u_int32_t thread_id, + const Memory &stack, + const Context &context, + u_int32_t suspend_count = 0, + u_int32_t priority_class = 0, + u_int32_t priority = 0, + u_int64_t teb = 0); +}; + +class Module: public Section { + public: + // Create a module with the given values. Note that CV_RECORD and + // MISC_RECORD can be NULL, in which case the corresponding location + // descriptior in the minidump will have a length of zero. + Module(const Dump &dump, + u_int64_t base_of_image, + u_int32_t size_of_image, + const String &name, + u_int32_t time_date_stamp = 1262805309, + u_int32_t checksum = 0, + const MDVSFixedFileInfo &version_info = Module::stock_version_info, + const Section *cv_record = NULL, + const Section *misc_record = NULL); + + private: + // A standard MDVSFixedFileInfo structure to use as a default for + // minidumps. There's no reason to make users write out all this crap + // over and over. + static const MDVSFixedFileInfo stock_version_info; +}; + +class Exception : public Stream { +public: + Exception(const Dump &dump, + const Context &context, + u_int32_t thread_id = 0, + u_int32_t exception_code = 0, + u_int32_t exception_flags = 0, + u_int64_t exception_address = 0); +}; + +// A list of entries starting with a 32-bit count, like a memory list +// or a thread list. +template +class List: public Stream { + public: + List(const Dump &dump, u_int32_t type) : Stream(dump, type), count_(0) { + D32(count_label_); + } + + // Add ELEMENT to this list. + void Add(Element *element) { + element->Finish(file_offset_ + Size()); + Append(*element); + count_++; + } + + // Return true if this List is empty, false otherwise. + bool Empty() { return count_ == 0; } + + // Finish up the contents of this section, mark it as having been + // placed at OFFSET. + virtual void Finish(const Label &offset) { + Stream::Finish(offset); + count_label_ = count_; + } + + private: + size_t count_; + Label count_label_; +}; + +class Dump: public test_assembler::Section { + public: + + // Create a test_assembler::Section containing a minidump file whose + // header uses the given values. ENDIANNESS determines the + // endianness of the signature; we set this section's default + // endianness by this. + Dump(u_int64_t flags, + Endianness endianness = kLittleEndian, + u_int32_t version = MD_HEADER_VERSION, + u_int32_t date_time_stamp = 1262805309); + + // The following functions call OBJECT->Finish(), and append the + // contents of OBJECT to this minidump. They also record OBJECT in + // whatever directory or list is appropriate for its type. The + // stream directory, memory list, thread list, and module list are + // accumulated this way. + Dump &Add(SynthMinidump::Section *object); // simply append data + Dump &Add(Stream *object); // append, record in stream directory + Dump &Add(Memory *object); // append, record in memory list + Dump &Add(Thread *object); // append, record in thread list + Dump &Add(Module *object); // append, record in module list + + // Complete the construction of the minidump, given the Add calls + // we've seen up to this point. After this call, this Dump's + // contents are complete, all labels should be defined if everything + // Cited has been Added, and you may call GetContents on it. + void Finish(); + + private: + // A label representing the start of the minidump file. + Label file_start_; + + // The stream directory. We construct this incrementally from + // Add(Stream *) calls. + SynthMinidump::Section stream_directory_; // The directory's contents. + size_t stream_count_; // The number of streams so far. + Label stream_count_label_; // Cited in file header. + Label stream_directory_rva_; // The directory's file offset. + + // This minidump's thread list. We construct this incrementally from + // Add(Thread *) calls. + List thread_list_; + + // This minidump's module list. We construct this incrementally from + // Add(Module *) calls. + List module_list_; + + // This minidump's memory list. We construct this incrementally from + // Add(Memory *) calls. This is actually a list of MDMemoryDescriptors, + // not memory ranges --- thus the odd type. + List memory_list_; +}; + +} // namespace SynthMinidump + +} // namespace google_breakpad + +#endif // PROCESSOR_SYNTH_MINIDUMP_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest.cc b/src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest.cc new file mode 100644 index 0000000..1ce34dd --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest.cc @@ -0,0 +1,336 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// Original author: Jim Blandy + +// synth_minidump_unittest.cc: Unit tests for google_breakpad::SynthMinidump +// classes. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "processor/synth_minidump.h" +#include "processor/synth_minidump_unittest_data.h" + +using google_breakpad::SynthMinidump::Context; +using google_breakpad::SynthMinidump::Dump; +using google_breakpad::SynthMinidump::Exception; +using google_breakpad::SynthMinidump::List; +using google_breakpad::SynthMinidump::Memory; +using google_breakpad::SynthMinidump::Module; +using google_breakpad::SynthMinidump::Section; +using google_breakpad::SynthMinidump::Stream; +using google_breakpad::SynthMinidump::String; +using google_breakpad::SynthMinidump::SystemInfo; +using google_breakpad::SynthMinidump::Thread; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; +using google_breakpad::test_assembler::Label; +using std::string; + +TEST(Section, Simple) { + Dump dump(0); + Section section(dump); + section.L32(0x12345678); + section.Finish(0); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x78\x56\x34\x12", 4), contents); +} + +TEST(Section, CiteLocationIn) { + Dump dump(0, kBigEndian); + Section section1(dump), section2(dump); + section1.Append("order"); + section2.Append("mayhem"); + section2.Finish(0x32287ec2); + section2.CiteLocationIn(§ion1); + string contents; + ASSERT_TRUE(section1.GetContents(&contents)); + string expected("order\0\0\0\x06\x32\x28\x7e\xc2", 13); + EXPECT_EQ(expected, contents); +} + +TEST(Stream, CiteStreamIn) { + Dump dump(0, kLittleEndian); + Stream stream(dump, 0x40cae2b3); + Section section(dump); + stream.Append("stream contents"); + section.Append("section contents"); + stream.Finish(0x41424344); + stream.CiteStreamIn(§ion); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + string expected("section contents" + "\xb3\xe2\xca\x40" + "\x0f\0\0\0" + "\x44\x43\x42\x41", + 16 + 4 + 4 + 4); + EXPECT_EQ(expected, contents); +} + +TEST(Memory, CiteMemoryIn) { + Dump dump(0, kBigEndian); + Memory memory(dump, 0x76d010874ab019f9ULL); + Section section(dump); + memory.Append("memory contents"); + section.Append("section contents"); + memory.Finish(0x51525354); + memory.CiteMemoryIn(§ion); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + string expected("section contents" + "\x76\xd0\x10\x87\x4a\xb0\x19\xf9" + "\0\0\0\x0f" + "\x51\x52\x53\x54", + 16 + 8 + 4 + 4); + EXPECT_EQ(contents, expected); +} + +TEST(Memory, Here) { + Dump dump(0, kBigEndian); + Memory memory(dump, 0x89979731eb060ed4ULL); + memory.Append(1729, 42); + Label l = memory.Here(); + ASSERT_EQ(0x89979731eb060ed4ULL + 1729, l.Value()); +} + +TEST(Context, X86) { + Dump dump(0, kLittleEndian); + assert(x86_raw_context.context_flags & MD_CONTEXT_X86); + Context context(dump, x86_raw_context); + string contents; + ASSERT_TRUE(context.GetContents(&contents)); + EXPECT_EQ(sizeof(x86_expected_contents), contents.size()); + EXPECT_TRUE(memcmp(contents.data(), x86_expected_contents, contents.size()) + == 0); +} + +TEST(Context, ARM) { + Dump dump(0, kLittleEndian); + assert(arm_raw_context.context_flags & MD_CONTEXT_ARM); + Context context(dump, arm_raw_context); + string contents; + ASSERT_TRUE(context.GetContents(&contents)); + EXPECT_EQ(sizeof(arm_expected_contents), contents.size()); + EXPECT_TRUE(memcmp(contents.data(), arm_expected_contents, contents.size()) + == 0); +} + +TEST(ContextDeathTest, X86BadFlags) { + Dump dump(0, kLittleEndian); + MDRawContextX86 raw; + raw.context_flags = 0; + ASSERT_DEATH(Context context(dump, raw);, + "context\\.context_flags & (0x[0-9a-f]+|MD_CONTEXT_X86)"); +} + +TEST(ContextDeathTest, X86BadEndianness) { + Dump dump(0, kBigEndian); + MDRawContextX86 raw; + raw.context_flags = MD_CONTEXT_X86; + ASSERT_DEATH(Context context(dump, raw);, + "dump\\.endianness\\(\\) == kLittleEndian"); +} + +TEST(Thread, Simple) { + Dump dump(0, kLittleEndian); + Context context(dump, x86_raw_context); + context.Finish(0x8665da0c); + Memory stack(dump, 0xaad55a93cc3c0efcULL); + stack.Append("stack contents"); + stack.Finish(0xe08cdbd1); + Thread thread(dump, 0x3d7ec360, stack, context, + 0x3593f44d, // suspend count + 0xab352b82, // priority class + 0x2753d838, // priority + 0xeb2de4be3f29e3e9ULL); // thread environment block + string contents; + ASSERT_TRUE(thread.GetContents(&contents)); + static const u_int8_t expected_bytes[] = { + 0x60, 0xc3, 0x7e, 0x3d, // thread id + 0x4d, 0xf4, 0x93, 0x35, // suspend count + 0x82, 0x2b, 0x35, 0xab, // priority class + 0x38, 0xd8, 0x53, 0x27, // priority + 0xe9, 0xe3, 0x29, 0x3f, 0xbe, 0xe4, 0x2d, 0xeb, // thread environment block + 0xfc, 0x0e, 0x3c, 0xcc, 0x93, 0x5a, 0xd5, 0xaa, // stack address + 0x0e, 0x00, 0x00, 0x00, // stack size + 0xd1, 0xdb, 0x8c, 0xe0, // stack MDRVA + 0xcc, 0x02, 0x00, 0x00, // context size + 0x0c, 0xda, 0x65, 0x86 // context MDRVA + }; + EXPECT_EQ(sizeof(expected_bytes), contents.size()); + EXPECT_TRUE(memcmp(contents.data(), expected_bytes, contents.size()) == 0); +} + +TEST(Exception, Simple) { + Dump dump(0, kLittleEndian); + Context context(dump, x86_raw_context); + context.Finish(0x8665da0c); + + Exception exception(dump, context, + 0x1234abcd, // thread id + 0xdcba4321, // exception code + 0xf0e0d0c0, // exception flags + 0x0919a9b9c9d9e9f9ULL); // exception address + string contents; + ASSERT_TRUE(exception.GetContents(&contents)); + static const u_int8_t expected_bytes[] = { + 0xcd, 0xab, 0x34, 0x12, // thread id + 0x00, 0x00, 0x00, 0x00, // __align + 0x21, 0x43, 0xba, 0xdc, // exception code + 0xc0, 0xd0, 0xe0, 0xf0, // exception flags + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception record + 0xf9, 0xe9, 0xd9, 0xc9, 0xb9, 0xa9, 0x19, 0x09, // exception address + 0x00, 0x00, 0x00, 0x00, // number parameters + 0x00, 0x00, 0x00, 0x00, // __align + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exception_information + 0xcc, 0x02, 0x00, 0x00, // context size + 0x0c, 0xda, 0x65, 0x86 // context MDRVA + }; + EXPECT_EQ(sizeof(expected_bytes), contents.size()); + EXPECT_TRUE(memcmp(contents.data(), expected_bytes, contents.size()) == 0); +} + +TEST(String, Simple) { + Dump dump(0, kBigEndian); + String s(dump, "All mimsy were the borogoves"); + string contents; + ASSERT_TRUE(s.GetContents(&contents)); + static const char expected[] = + "\x00\x00\x00\x38\0A\0l\0l\0 \0m\0i\0m\0s\0y\0 \0w\0e\0r\0e" + "\0 \0t\0h\0e\0 \0b\0o\0r\0o\0g\0o\0v\0e\0s"; + string expected_string(expected, sizeof(expected) - 1); + EXPECT_EQ(expected_string, contents); +} + +TEST(String, CiteStringIn) { + Dump dump(0, kLittleEndian); + String s(dump, "and the mome wraths outgrabe"); + Section section(dump); + section.Append("initial"); + s.CiteStringIn(§ion); + s.Finish(0xdc2bb469); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("initial\x69\xb4\x2b\xdc", 7 + 4), contents); +} + +TEST(List, Empty) { + Dump dump(0, kBigEndian); + List
    list(dump, 0x2442779c); + EXPECT_TRUE(list.Empty()); + list.Finish(0x84e09808); + string contents; + ASSERT_TRUE(list.GetContents(&contents)); + EXPECT_EQ(string("\0\0\0\0", 4), contents); +} + +TEST(List, Two) { + Dump dump(0, kBigEndian); + List
    list(dump, 0x26c9f498); + Section section1(dump); + section1.Append("section one contents"); + EXPECT_TRUE(list.Empty()); + list.Add(§ion1); + EXPECT_FALSE(list.Empty()); + Section section2(dump); + section2.Append("section two contents"); + list.Add(§ion2); + list.Finish(0x1e5bb60e); + string contents; + ASSERT_TRUE(list.GetContents(&contents)); + EXPECT_EQ(string("\0\0\0\x02section one contentssection two contents", 44), + contents); +} + +TEST(Dump, Header) { + Dump dump(0x9f738b33685cc84cULL, kLittleEndian, 0xb3817faf, 0x2c741c0a); + dump.Finish(); + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + ASSERT_EQ(string("\x4d\x44\x4d\x50" // signature + "\xaf\x7f\x81\xb3" // version + "\0\0\0\0" // stream count + "\x20\0\0\0" // directory RVA (could be anything) + "\0\0\0\0" // checksum + "\x0a\x1c\x74\x2c" // time_date_stamp + "\x4c\xc8\x5c\x68\x33\x8b\x73\x9f", // flags + 32), + contents); +} + +TEST(Dump, HeaderBigEndian) { + Dump dump(0x206ce3cc6fb8e0f0ULL, kBigEndian, 0x161693e2, 0x35667744); + dump.Finish(); + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + ASSERT_EQ(string("\x50\x4d\x44\x4d" // signature + "\x16\x16\x93\xe2" // version + "\0\0\0\0" // stream count + "\0\0\0\x20" // directory RVA (could be anything) + "\0\0\0\0" // checksum + "\x35\x66\x77\x44" // time_date_stamp + "\x20\x6c\xe3\xcc\x6f\xb8\xe0\xf0", // flags + 32), + contents); +} + +TEST(Dump, OneSection) { + Dump dump(0, kLittleEndian); + Section section(dump); + section.Append("section contents"); + dump.Add(§ion); + dump.Finish(); + string dump_contents; + // Just check for undefined labels; don't worry about the contents. + ASSERT_TRUE(dump.GetContents(&dump_contents)); + + Section referencing_section(dump); + section.CiteLocationIn(&referencing_section); + string contents; + ASSERT_TRUE(referencing_section.GetContents(&contents)); + ASSERT_EQ(string("\x10\0\0\0\x20\0\0\0", 8), contents); +} diff --git a/src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest_data.h b/src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest_data.h new file mode 100644 index 0000000..81995d2 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/synth_minidump_unittest_data.h @@ -0,0 +1,412 @@ +// -*- mode: C++ -*- + +// Not copyrightable: random test data. +// synth_minidump_unittest_data.h: verbose test data for SynthMinidump tests. + +#ifndef PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_ +#define PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_ + +#include "google_breakpad/common/minidump_format.h" + +static const MDRawContextX86 x86_raw_context = { + 0xded5d71b, // context_flags + 0x9fdb432e, // dr0 + 0x26b7a81a, // dr1 + 0xcac7e348, // dr2 + 0xcf99ec09, // dr3 + 0x7dc8c2cd, // dr6 + 0x21deb880, // dr7 + + // float_save + { + 0x8a5d2bb0, // control_word + 0x0286c4c9, // status_word + 0xf1feea21, // tag_word + 0xb2d40576, // error_offset + 0x48146cde, // error_selector + 0x983f9b21, // data_offset + 0x475be12c, // data_selector + + // register_area + { + 0xd9, 0x04, 0x20, 0x6b, 0x88, 0x3a, 0x3f, 0xd5, + 0x59, 0x7a, 0xa9, 0xeb, 0xd0, 0x5c, 0xdf, 0xfe, + 0xad, 0xdd, 0x4a, 0x8b, 0x10, 0xcc, 0x9a, 0x33, + 0xcb, 0xb6, 0xf7, 0x86, 0xcd, 0x69, 0x25, 0xae, + 0x25, 0xe5, 0x7a, 0xa1, 0x8f, 0xb2, 0x84, 0xd9, + 0xf7, 0x2d, 0x8a, 0xa1, 0x80, 0x81, 0x7f, 0x67, + 0x07, 0xa8, 0x23, 0xf1, 0x8c, 0xdc, 0xd8, 0x04, + 0x8b, 0x9d, 0xb1, 0xcd, 0x61, 0x0c, 0x9c, 0x69, + 0xc7, 0x8d, 0x17, 0xb6, 0xe5, 0x0b, 0x94, 0xf7, + 0x78, 0x9b, 0x63, 0x49, 0xba, 0xfc, 0x08, 0x4d + }, + + 0x84c53a90, // cr0_npx_state + }, + + 0x79f71e76, // gs + 0x8107bd25, // fs + 0x452d2921, // es + 0x87ec2875, // ds + 0xf8bb73f5, // edi + 0xa63ebb88, // esi + 0x95d35ebe, // ebx + 0x17aa2456, // edx + 0x135fa208, // ecx + 0x500615e6, // eax + 0x66d14205, // ebp + 0x000719a5, // eip + 0x477b481b, // cs + 0x8684dfba, // eflags + 0xe33ccddf, // esp + 0xc0e65d33, // ss + + // extended_registers + { + 0x68, 0x63, 0xdf, 0x50, 0xf7, 0x3b, 0xe8, 0xe5, + 0xcb, 0xd6, 0x66, 0x60, 0xe5, 0xa3, 0x58, 0xb3, + 0x6f, 0x34, 0xca, 0x02, 0x9b, 0x5f, 0xd0, 0x41, + 0xbd, 0xc5, 0x2d, 0xf8, 0xff, 0x15, 0xa2, 0xd0, + 0xe3, 0x2b, 0x3b, 0x8a, 0x9f, 0xc3, 0x9e, 0x28, + 0x0a, 0xc2, 0xac, 0x3b, 0x67, 0x37, 0x01, 0xfd, + 0xc3, 0xaf, 0x60, 0xf6, 0x2c, 0x4f, 0xa9, 0x52, + 0x92, 0xe5, 0x28, 0xde, 0x34, 0xb6, 0x2e, 0x44, + 0x15, 0xa4, 0xb6, 0xe4, 0xc9, 0x1a, 0x14, 0xb9, + 0x51, 0x33, 0x3c, 0xe0, 0xc7, 0x94, 0xf0, 0xf7, + 0x78, 0xdd, 0xe5, 0xca, 0xb7, 0xa6, 0xe0, 0x14, + 0xa6, 0x03, 0xab, 0x77, 0xad, 0xbd, 0xd2, 0x53, + 0x3d, 0x07, 0xe7, 0xaf, 0x90, 0x44, 0x71, 0xbe, + 0x0c, 0xdf, 0x2b, 0x97, 0x40, 0x48, 0xd5, 0xf9, + 0x62, 0x03, 0x91, 0x84, 0xd6, 0xdd, 0x29, 0x97, + 0x35, 0x02, 0xfb, 0x59, 0x97, 0xb0, 0xec, 0xa9, + 0x39, 0x6f, 0x81, 0x71, 0x2a, 0xf0, 0xe7, 0x2c, + 0x4e, 0x93, 0x90, 0xcb, 0x67, 0x69, 0xde, 0xd7, + 0x68, 0x3b, 0x0f, 0x69, 0xa8, 0xf4, 0xa8, 0x83, + 0x42, 0x80, 0x47, 0x65, 0x7a, 0xc9, 0x19, 0x5d, + 0xcb, 0x43, 0xa5, 0xff, 0xf8, 0x9e, 0x62, 0xf4, + 0xe2, 0x6c, 0xcc, 0x17, 0x55, 0x7c, 0x0d, 0x5c, + 0x8d, 0x16, 0x01, 0xd7, 0x3a, 0x0c, 0xf4, 0x7f, + 0x71, 0xdc, 0x48, 0xe9, 0x4b, 0xfe, 0x1a, 0xd0, + 0x04, 0x15, 0x33, 0xec, 0x78, 0xc6, 0x7e, 0xde, + 0x7c, 0x23, 0x18, 0x8d, 0x8f, 0xc2, 0x74, 0xc1, + 0x48, 0xcd, 0x5d, 0xee, 0xee, 0x81, 0x9e, 0x49, + 0x47, 0x8a, 0xf8, 0x61, 0xa3, 0x9c, 0x81, 0x96, + 0xbe, 0x2b, 0x5e, 0xbc, 0xcd, 0x34, 0x0a, 0x2a, + 0x3b, 0x8b, 0x7d, 0xa1, 0xf2, 0x8d, 0xb4, 0x51, + 0x9e, 0x14, 0x78, 0xa3, 0x58, 0x65, 0x2d, 0xd6, + 0x50, 0x40, 0x36, 0x32, 0x31, 0xd4, 0x3e, 0xc2, + 0xe0, 0x87, 0x1c, 0x05, 0x95, 0x80, 0x84, 0x24, + 0x08, 0x6f, 0x5b, 0xc7, 0xe1, 0x1d, 0xd5, 0xa3, + 0x94, 0x44, 0xa1, 0x7c, 0xd8, 0x4b, 0x86, 0xd2, + 0xc6, 0xa9, 0xf3, 0xe2, 0x4d, 0x6e, 0x1f, 0x0e, + 0xf2, 0xf5, 0x71, 0xf9, 0x71, 0x05, 0x24, 0xc9, + 0xc1, 0xe8, 0x91, 0x42, 0x61, 0x86, 0x57, 0x68, + 0xd9, 0xc9, 0x1d, 0xd5, 0x5a, 0xe9, 0xba, 0xe6, + 0x15, 0x8f, 0x87, 0xbd, 0x62, 0x56, 0xed, 0xda, + 0xc2, 0xa5, 0xd5, 0x39, 0xac, 0x05, 0x10, 0x14, + 0x4a, 0xe7, 0xe7, 0x3c, 0x3f, 0xb7, 0xbb, 0xed, + 0x01, 0x6e, 0xcd, 0xee, 0x81, 0xb4, 0x62, 0xf4, + 0x62, 0x16, 0xff, 0x20, 0xb4, 0xf0, 0xbc, 0xff, + 0x7d, 0xd9, 0xcf, 0x95, 0x30, 0x27, 0xe0, 0x2f, + 0x98, 0x53, 0x80, 0x15, 0x13, 0xef, 0x44, 0x58, + 0x12, 0x16, 0xdb, 0x11, 0xef, 0x73, 0x51, 0xcd, + 0x42, 0x3f, 0x98, 0x6c, 0xc9, 0x68, 0xc3, 0xf4, + 0x5b, 0x0f, 0x5d, 0x77, 0xed, 0xdf, 0x0f, 0xff, + 0xb8, 0x69, 0x98, 0x50, 0x77, 0x7a, 0xe8, 0x90, + 0x27, 0x46, 0x10, 0xd2, 0xb5, 0x00, 0x3b, 0x36, + 0x43, 0x6d, 0x67, 0x41, 0x20, 0x3a, 0x32, 0xe0, + 0x2e, 0x5a, 0xfb, 0x4e, 0x4f, 0xa4, 0xf7, 0xc2, + 0xe6, 0x81, 0x1a, 0x51, 0xa8, 0x7c, 0xd4, 0x60, + 0x7c, 0x45, 0xe2, 0xba, 0x5b, 0x42, 0xf3, 0xbf, + 0x28, 0xaa, 0xf2, 0x90, 0xe4, 0x94, 0xdd, 0xaa, + 0x22, 0xd3, 0x71, 0x33, 0xa1, 0x01, 0x43, 0x0e, + 0xfa, 0x46, 0xd2, 0x6e, 0x55, 0x5e, 0x49, 0xeb, + 0x94, 0xf0, 0xb0, 0xb1, 0x2e, 0xf2, 0x3d, 0x6c, + 0x00, 0x5e, 0x01, 0x56, 0x3b, 0xfd, 0x5b, 0xa1, + 0x2f, 0x63, 0x1d, 0xbf, 0xf9, 0xd8, 0x13, 0xf7, + 0x4d, 0xb7, 0x1e, 0x3d, 0x98, 0xd2, 0xee, 0xb8, + 0x48, 0xc8, 0x5b, 0x91, 0x0f, 0x54, 0x9e, 0x26, + 0xb2, 0xc7, 0x3a, 0x6c, 0x8a, 0x35, 0xe1, 0xba + } +}; + +static const u_int8_t x86_expected_contents[] = { + 0x1b, 0xd7, 0xd5, 0xde, + 0x2e, 0x43, 0xdb, 0x9f, + 0x1a, 0xa8, 0xb7, 0x26, + 0x48, 0xe3, 0xc7, 0xca, + 0x09, 0xec, 0x99, 0xcf, + 0xcd, 0xc2, 0xc8, 0x7d, + 0x80, 0xb8, 0xde, 0x21, + 0xb0, 0x2b, 0x5d, 0x8a, + 0xc9, 0xc4, 0x86, 0x02, + 0x21, 0xea, 0xfe, 0xf1, + 0x76, 0x05, 0xd4, 0xb2, + 0xde, 0x6c, 0x14, 0x48, + 0x21, 0x9b, 0x3f, 0x98, + 0x2c, 0xe1, 0x5b, 0x47, + + // float_save.register_area --- unswapped + 0xd9, 0x04, 0x20, 0x6b, 0x88, 0x3a, 0x3f, 0xd5, + 0x59, 0x7a, 0xa9, 0xeb, 0xd0, 0x5c, 0xdf, 0xfe, + 0xad, 0xdd, 0x4a, 0x8b, 0x10, 0xcc, 0x9a, 0x33, + 0xcb, 0xb6, 0xf7, 0x86, 0xcd, 0x69, 0x25, 0xae, + 0x25, 0xe5, 0x7a, 0xa1, 0x8f, 0xb2, 0x84, 0xd9, + 0xf7, 0x2d, 0x8a, 0xa1, 0x80, 0x81, 0x7f, 0x67, + 0x07, 0xa8, 0x23, 0xf1, 0x8c, 0xdc, 0xd8, 0x04, + 0x8b, 0x9d, 0xb1, 0xcd, 0x61, 0x0c, 0x9c, 0x69, + 0xc7, 0x8d, 0x17, 0xb6, 0xe5, 0x0b, 0x94, 0xf7, + 0x78, 0x9b, 0x63, 0x49, 0xba, 0xfc, 0x08, 0x4d, + + 0x90, 0x3a, 0xc5, 0x84, + 0x76, 0x1e, 0xf7, 0x79, + 0x25, 0xbd, 0x07, 0x81, + 0x21, 0x29, 0x2d, 0x45, + 0x75, 0x28, 0xec, 0x87, + 0xf5, 0x73, 0xbb, 0xf8, + 0x88, 0xbb, 0x3e, 0xa6, + 0xbe, 0x5e, 0xd3, 0x95, + 0x56, 0x24, 0xaa, 0x17, + 0x08, 0xa2, 0x5f, 0x13, + 0xe6, 0x15, 0x06, 0x50, + 0x05, 0x42, 0xd1, 0x66, + 0xa5, 0x19, 0x07, 0x00, + 0x1b, 0x48, 0x7b, 0x47, + 0xba, 0xdf, 0x84, 0x86, + 0xdf, 0xcd, 0x3c, 0xe3, + 0x33, 0x5d, 0xe6, 0xc0, + + // extended_registers --- unswapped + 0x68, 0x63, 0xdf, 0x50, 0xf7, 0x3b, 0xe8, 0xe5, + 0xcb, 0xd6, 0x66, 0x60, 0xe5, 0xa3, 0x58, 0xb3, + 0x6f, 0x34, 0xca, 0x02, 0x9b, 0x5f, 0xd0, 0x41, + 0xbd, 0xc5, 0x2d, 0xf8, 0xff, 0x15, 0xa2, 0xd0, + 0xe3, 0x2b, 0x3b, 0x8a, 0x9f, 0xc3, 0x9e, 0x28, + 0x0a, 0xc2, 0xac, 0x3b, 0x67, 0x37, 0x01, 0xfd, + 0xc3, 0xaf, 0x60, 0xf6, 0x2c, 0x4f, 0xa9, 0x52, + 0x92, 0xe5, 0x28, 0xde, 0x34, 0xb6, 0x2e, 0x44, + 0x15, 0xa4, 0xb6, 0xe4, 0xc9, 0x1a, 0x14, 0xb9, + 0x51, 0x33, 0x3c, 0xe0, 0xc7, 0x94, 0xf0, 0xf7, + 0x78, 0xdd, 0xe5, 0xca, 0xb7, 0xa6, 0xe0, 0x14, + 0xa6, 0x03, 0xab, 0x77, 0xad, 0xbd, 0xd2, 0x53, + 0x3d, 0x07, 0xe7, 0xaf, 0x90, 0x44, 0x71, 0xbe, + 0x0c, 0xdf, 0x2b, 0x97, 0x40, 0x48, 0xd5, 0xf9, + 0x62, 0x03, 0x91, 0x84, 0xd6, 0xdd, 0x29, 0x97, + 0x35, 0x02, 0xfb, 0x59, 0x97, 0xb0, 0xec, 0xa9, + 0x39, 0x6f, 0x81, 0x71, 0x2a, 0xf0, 0xe7, 0x2c, + 0x4e, 0x93, 0x90, 0xcb, 0x67, 0x69, 0xde, 0xd7, + 0x68, 0x3b, 0x0f, 0x69, 0xa8, 0xf4, 0xa8, 0x83, + 0x42, 0x80, 0x47, 0x65, 0x7a, 0xc9, 0x19, 0x5d, + 0xcb, 0x43, 0xa5, 0xff, 0xf8, 0x9e, 0x62, 0xf4, + 0xe2, 0x6c, 0xcc, 0x17, 0x55, 0x7c, 0x0d, 0x5c, + 0x8d, 0x16, 0x01, 0xd7, 0x3a, 0x0c, 0xf4, 0x7f, + 0x71, 0xdc, 0x48, 0xe9, 0x4b, 0xfe, 0x1a, 0xd0, + 0x04, 0x15, 0x33, 0xec, 0x78, 0xc6, 0x7e, 0xde, + 0x7c, 0x23, 0x18, 0x8d, 0x8f, 0xc2, 0x74, 0xc1, + 0x48, 0xcd, 0x5d, 0xee, 0xee, 0x81, 0x9e, 0x49, + 0x47, 0x8a, 0xf8, 0x61, 0xa3, 0x9c, 0x81, 0x96, + 0xbe, 0x2b, 0x5e, 0xbc, 0xcd, 0x34, 0x0a, 0x2a, + 0x3b, 0x8b, 0x7d, 0xa1, 0xf2, 0x8d, 0xb4, 0x51, + 0x9e, 0x14, 0x78, 0xa3, 0x58, 0x65, 0x2d, 0xd6, + 0x50, 0x40, 0x36, 0x32, 0x31, 0xd4, 0x3e, 0xc2, + 0xe0, 0x87, 0x1c, 0x05, 0x95, 0x80, 0x84, 0x24, + 0x08, 0x6f, 0x5b, 0xc7, 0xe1, 0x1d, 0xd5, 0xa3, + 0x94, 0x44, 0xa1, 0x7c, 0xd8, 0x4b, 0x86, 0xd2, + 0xc6, 0xa9, 0xf3, 0xe2, 0x4d, 0x6e, 0x1f, 0x0e, + 0xf2, 0xf5, 0x71, 0xf9, 0x71, 0x05, 0x24, 0xc9, + 0xc1, 0xe8, 0x91, 0x42, 0x61, 0x86, 0x57, 0x68, + 0xd9, 0xc9, 0x1d, 0xd5, 0x5a, 0xe9, 0xba, 0xe6, + 0x15, 0x8f, 0x87, 0xbd, 0x62, 0x56, 0xed, 0xda, + 0xc2, 0xa5, 0xd5, 0x39, 0xac, 0x05, 0x10, 0x14, + 0x4a, 0xe7, 0xe7, 0x3c, 0x3f, 0xb7, 0xbb, 0xed, + 0x01, 0x6e, 0xcd, 0xee, 0x81, 0xb4, 0x62, 0xf4, + 0x62, 0x16, 0xff, 0x20, 0xb4, 0xf0, 0xbc, 0xff, + 0x7d, 0xd9, 0xcf, 0x95, 0x30, 0x27, 0xe0, 0x2f, + 0x98, 0x53, 0x80, 0x15, 0x13, 0xef, 0x44, 0x58, + 0x12, 0x16, 0xdb, 0x11, 0xef, 0x73, 0x51, 0xcd, + 0x42, 0x3f, 0x98, 0x6c, 0xc9, 0x68, 0xc3, 0xf4, + 0x5b, 0x0f, 0x5d, 0x77, 0xed, 0xdf, 0x0f, 0xff, + 0xb8, 0x69, 0x98, 0x50, 0x77, 0x7a, 0xe8, 0x90, + 0x27, 0x46, 0x10, 0xd2, 0xb5, 0x00, 0x3b, 0x36, + 0x43, 0x6d, 0x67, 0x41, 0x20, 0x3a, 0x32, 0xe0, + 0x2e, 0x5a, 0xfb, 0x4e, 0x4f, 0xa4, 0xf7, 0xc2, + 0xe6, 0x81, 0x1a, 0x51, 0xa8, 0x7c, 0xd4, 0x60, + 0x7c, 0x45, 0xe2, 0xba, 0x5b, 0x42, 0xf3, 0xbf, + 0x28, 0xaa, 0xf2, 0x90, 0xe4, 0x94, 0xdd, 0xaa, + 0x22, 0xd3, 0x71, 0x33, 0xa1, 0x01, 0x43, 0x0e, + 0xfa, 0x46, 0xd2, 0x6e, 0x55, 0x5e, 0x49, 0xeb, + 0x94, 0xf0, 0xb0, 0xb1, 0x2e, 0xf2, 0x3d, 0x6c, + 0x00, 0x5e, 0x01, 0x56, 0x3b, 0xfd, 0x5b, 0xa1, + 0x2f, 0x63, 0x1d, 0xbf, 0xf9, 0xd8, 0x13, 0xf7, + 0x4d, 0xb7, 0x1e, 0x3d, 0x98, 0xd2, 0xee, 0xb8, + 0x48, 0xc8, 0x5b, 0x91, 0x0f, 0x54, 0x9e, 0x26, + 0xb2, 0xc7, 0x3a, 0x6c, 0x8a, 0x35, 0xe1, 0xba +}; + +static const MDRawContextARM arm_raw_context = { + // context_flags + 0x591b9e6a, + // iregs + 0xa21594de, + 0x820d8a25, + 0xc4e133b2, + 0x173a1c02, + 0x105fb175, + 0xe871793f, + 0x5def70b3, + 0xcee3a623, + 0x7b3aa9b8, + 0x52518537, + 0x627012c5, + 0x22723dcc, + 0x16fcc971, + 0x20988bcb, + 0xf1ab806b, + 0x99d5fc03, + // cpsr + 0xb70df511, + // float_save + { + // fpscr + 0xa1e1f7ce1077e6b5ULL, + // regs + 0xbcb8d002eed7fbdeULL, + 0x4dd26a43b96ae97fULL, + 0x8eec22db8b31741cULL, + 0xfd634bd7c5ad66a0ULL, + 0x1681da0daeb3debeULL, + 0x474a32bdf72d0b71ULL, + 0xcaf464f8b1044834ULL, + 0xcaa6592ae5c7582aULL, + 0x4ee46889d877c3dbULL, + 0xf8930cf301645cf5ULL, + 0x4da7e9ebba27f7c7ULL, + 0x69a7b02761944da3ULL, + 0x2cda2b2e78195c06ULL, + 0x66b227ab9b460a42ULL, + 0x7e77e49e52ee0849ULL, + 0xd62cd9663e76f255ULL, + 0xe9370f082451514bULL, + 0x50a1c674dd1b6029ULL, + 0x405db4575829eac4ULL, + 0x67b948764649eee7ULL, + 0x93731885419229d4ULL, + 0xdb0338bad72a4ce7ULL, + 0xa0a451f996fca4c8ULL, + 0xb4508ea668400a45ULL, + 0xbff28c5c7a142423ULL, + 0x4f31b42b96f3a431ULL, + 0x2ce6789d4ea1ff37ULL, + 0xfa150b52e4f82a3cULL, + 0xe9ec40449e6ed4f3ULL, + 0x5ceca87836fe2251ULL, + 0x66f50de463ee238cULL, + 0x42823efcd59ab511ULL, + // extra + 0xe9e14cd2, + 0x865bb640, + 0x9f3f0b3e, + 0x94a71c52, + 0x3c012f19, + 0x6436637c, + 0x46ccedcb, + 0x7b341be7 + } +}; + +static const u_int8_t arm_expected_contents[] = { + 0x6a, 0x9e, 0x1b, 0x59, + 0xde, 0x94, 0x15, 0xa2, + 0x25, 0x8a, 0x0d, 0x82, + 0xb2, 0x33, 0xe1, 0xc4, + 0x02, 0x1c, 0x3a, 0x17, + 0x75, 0xb1, 0x5f, 0x10, + 0x3f, 0x79, 0x71, 0xe8, + 0xb3, 0x70, 0xef, 0x5d, + 0x23, 0xa6, 0xe3, 0xce, + 0xb8, 0xa9, 0x3a, 0x7b, + 0x37, 0x85, 0x51, 0x52, + 0xc5, 0x12, 0x70, 0x62, + 0xcc, 0x3d, 0x72, 0x22, + 0x71, 0xc9, 0xfc, 0x16, + 0xcb, 0x8b, 0x98, 0x20, + 0x6b, 0x80, 0xab, 0xf1, + 0x03, 0xfc, 0xd5, 0x99, + 0x11, 0xf5, 0x0d, 0xb7, + 0xb5, 0xe6, 0x77, 0x10, + 0xce, 0xf7, 0xe1, 0xa1, + 0xde, 0xfb, 0xd7, 0xee, + 0x02, 0xd0, 0xb8, 0xbc, + 0x7f, 0xe9, 0x6a, 0xb9, + 0x43, 0x6a, 0xd2, 0x4d, + 0x1c, 0x74, 0x31, 0x8b, + 0xdb, 0x22, 0xec, 0x8e, + 0xa0, 0x66, 0xad, 0xc5, + 0xd7, 0x4b, 0x63, 0xfd, + 0xbe, 0xde, 0xb3, 0xae, + 0x0d, 0xda, 0x81, 0x16, + 0x71, 0x0b, 0x2d, 0xf7, + 0xbd, 0x32, 0x4a, 0x47, + 0x34, 0x48, 0x04, 0xb1, + 0xf8, 0x64, 0xf4, 0xca, + 0x2a, 0x58, 0xc7, 0xe5, + 0x2a, 0x59, 0xa6, 0xca, + 0xdb, 0xc3, 0x77, 0xd8, + 0x89, 0x68, 0xe4, 0x4e, + 0xf5, 0x5c, 0x64, 0x01, + 0xf3, 0x0c, 0x93, 0xf8, + 0xc7, 0xf7, 0x27, 0xba, + 0xeb, 0xe9, 0xa7, 0x4d, + 0xa3, 0x4d, 0x94, 0x61, + 0x27, 0xb0, 0xa7, 0x69, + 0x06, 0x5c, 0x19, 0x78, + 0x2e, 0x2b, 0xda, 0x2c, + 0x42, 0x0a, 0x46, 0x9b, + 0xab, 0x27, 0xb2, 0x66, + 0x49, 0x08, 0xee, 0x52, + 0x9e, 0xe4, 0x77, 0x7e, + 0x55, 0xf2, 0x76, 0x3e, + 0x66, 0xd9, 0x2c, 0xd6, + 0x4b, 0x51, 0x51, 0x24, + 0x08, 0x0f, 0x37, 0xe9, + 0x29, 0x60, 0x1b, 0xdd, + 0x74, 0xc6, 0xa1, 0x50, + 0xc4, 0xea, 0x29, 0x58, + 0x57, 0xb4, 0x5d, 0x40, + 0xe7, 0xee, 0x49, 0x46, + 0x76, 0x48, 0xb9, 0x67, + 0xd4, 0x29, 0x92, 0x41, + 0x85, 0x18, 0x73, 0x93, + 0xe7, 0x4c, 0x2a, 0xd7, + 0xba, 0x38, 0x03, 0xdb, + 0xc8, 0xa4, 0xfc, 0x96, + 0xf9, 0x51, 0xa4, 0xa0, + 0x45, 0x0a, 0x40, 0x68, + 0xa6, 0x8e, 0x50, 0xb4, + 0x23, 0x24, 0x14, 0x7a, + 0x5c, 0x8c, 0xf2, 0xbf, + 0x31, 0xa4, 0xf3, 0x96, + 0x2b, 0xb4, 0x31, 0x4f, + 0x37, 0xff, 0xa1, 0x4e, + 0x9d, 0x78, 0xe6, 0x2c, + 0x3c, 0x2a, 0xf8, 0xe4, + 0x52, 0x0b, 0x15, 0xfa, + 0xf3, 0xd4, 0x6e, 0x9e, + 0x44, 0x40, 0xec, 0xe9, + 0x51, 0x22, 0xfe, 0x36, + 0x78, 0xa8, 0xec, 0x5c, + 0x8c, 0x23, 0xee, 0x63, + 0xe4, 0x0d, 0xf5, 0x66, + 0x11, 0xb5, 0x9a, 0xd5, + 0xfc, 0x3e, 0x82, 0x42, + 0xd2, 0x4c, 0xe1, 0xe9, + 0x40, 0xb6, 0x5b, 0x86, + 0x3e, 0x0b, 0x3f, 0x9f, + 0x52, 0x1c, 0xa7, 0x94, + 0x19, 0x2f, 0x01, 0x3c, + 0x7c, 0x63, 0x36, 0x64, + 0xcb, 0xed, 0xcc, 0x46, + 0xe7, 0x1b, 0x34, 0x7b +}; + +#endif // PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/tokenize.cc b/src/lib/crashdump/gbreakpad/processor/tokenize.cc new file mode 100644 index 0000000..85f7782 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/tokenize.cc @@ -0,0 +1,76 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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 + +namespace google_breakpad { + +using std::string; +using std::vector; + +bool Tokenize(char *line, + const char *separators, + int max_tokens, + vector *tokens) { + tokens->clear(); + tokens->reserve(max_tokens); + + int remaining = max_tokens; + + // Split tokens on the separator character. + // strip them out before exhausting max_tokens. + char *save_ptr; + char *token = strtok_r(line, separators, &save_ptr); + while (token && --remaining > 0) { + tokens->push_back(token); + if (remaining > 1) + token = strtok_r(NULL, separators, &save_ptr); + } + + // If there's anything left, just add it as a single token. + if (!remaining > 0) { + if ((token = strtok_r(NULL, "\r\n", &save_ptr))) { + tokens->push_back(token); + } + } + + return tokens->size() == static_cast(max_tokens); +} + +void StringToVector(const string &str, vector &vec) { + vec.resize(str.length() + 1); + std::copy(str.begin(), str.end(), + vec.begin()); + vec[str.length()] = '\0'; +} + +} // namespace google_breakpad diff --git a/src/lib/crashdump/gbreakpad/processor/tokenize.h b/src/lib/crashdump/gbreakpad/processor/tokenize.h new file mode 100644 index 0000000..1562b82 --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/tokenize.h @@ -0,0 +1,61 @@ +// Copyright (c) 2010, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. +// +// Implements a Tokenize function for splitting up strings. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_TOKENIZE_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_TOKENIZE_H_ + +#include +#include + +namespace google_breakpad { + +// Splits line into at most max_tokens tokens, separated by any of the +// characters in separators and placing them in the tokens vector. +// line is a 0-terminated string that optionally ends with a newline +// character or combination, which will be removed. +// If more tokens than max_tokens are present, the final token is placed +// into the vector without splitting it up at all. This modifies line as +// a side effect. Returns true if exactly max_tokens tokens are returned, +// and false if fewer are returned. This is not considered a failure of +// Tokenize, but may be treated as a failure if the caller expects an +// exact, as opposed to maximum, number of tokens. + +bool Tokenize(char *line, + const char *separators, + int max_tokens, + std::vector *tokens); +// For convenience, since you need a char* to pass to Tokenize. +// You can call StringToVector on a std::string, and use &vec[0]. +void StringToVector(const std::string &str, std::vector &vec); + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_TOKENIZE_H_ diff --git a/src/lib/crashdump/gbreakpad/processor/windows_frame_info.h b/src/lib/crashdump/gbreakpad/processor/windows_frame_info.h new file mode 100644 index 0000000..067f3cf --- /dev/null +++ b/src/lib/crashdump/gbreakpad/processor/windows_frame_info.h @@ -0,0 +1,196 @@ +// Copyright (c) 2006, Google Inc. +// 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER 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. + +// windows_frame_info.h: Holds debugging information about a stack frame. +// +// This structure is specific to Windows debugging information obtained +// from pdb files using the DIA API. +// +// Author: Mark Mentovai + + +#ifndef PROCESSOR_WINDOWS_FRAME_INFO_H__ +#define PROCESSOR_WINDOWS_FRAME_INFO_H__ + +#include +#include + +#include +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "processor/logging.h" +#include "processor/tokenize.h" + +namespace google_breakpad { + +struct WindowsFrameInfo { + public: + enum Validity { + VALID_NONE = 0, + VALID_PARAMETER_SIZE = 1, + VALID_ALL = -1 + }; + + // The types for stack_info_. This is equivalent to MS DIA's + // StackFrameTypeEnum. Each identifies a different type of frame + // information, although all are represented in the symbol file in the + // same format. These are used as indices to the stack_info_ array. + enum StackInfoTypes { + STACK_INFO_FPO = 0, + STACK_INFO_TRAP, // not used here + STACK_INFO_TSS, // not used here + STACK_INFO_STANDARD, + STACK_INFO_FRAME_DATA, + STACK_INFO_LAST, // must be the last sequentially-numbered item + STACK_INFO_UNKNOWN = -1 + }; + + WindowsFrameInfo() : valid(VALID_NONE), + prolog_size(0), + epilog_size(0), + parameter_size(0), + saved_register_size(0), + local_size(0), + max_stack_size(0), + allocates_base_pointer(0), + program_string() {} + + WindowsFrameInfo(u_int32_t set_prolog_size, + u_int32_t set_epilog_size, + u_int32_t set_parameter_size, + u_int32_t set_saved_register_size, + u_int32_t set_local_size, + u_int32_t set_max_stack_size, + int set_allocates_base_pointer, + const std::string set_program_string) + : valid(VALID_ALL), + prolog_size(set_prolog_size), + epilog_size(set_epilog_size), + parameter_size(set_parameter_size), + saved_register_size(set_saved_register_size), + local_size(set_local_size), + max_stack_size(set_max_stack_size), + allocates_base_pointer(set_allocates_base_pointer), + program_string(set_program_string) {} + + // Parse a textual serialization of a WindowsFrameInfo object from + // a string. Returns NULL if parsing fails, or a new object + // otherwise. type, rva and code_size are present in the STACK line, + // but not the StackFrameInfo structure, so return them as outparams. + static WindowsFrameInfo *ParseFromString(const std::string string, + int &type, + u_int64_t &rva, + u_int64_t &code_size) { + // The format of a STACK WIN record is documented at: + // + // http://code.google.com/p/google-breakpad/wiki/SymbolFiles + + std::vector buffer; + StringToVector(string, buffer); + std::vector tokens; + if (!Tokenize(&buffer[0], " \r\n", 11, &tokens)) + return NULL; + + type = strtol(tokens[0], NULL, 16); + if (type < 0 || type > STACK_INFO_LAST - 1) + return NULL; + + rva = strtoull(tokens[1], NULL, 16); + code_size = strtoull(tokens[2], NULL, 16); + u_int32_t prolog_size = strtoul(tokens[3], NULL, 16); + u_int32_t epilog_size = strtoul(tokens[4], NULL, 16); + u_int32_t parameter_size = strtoul(tokens[5], NULL, 16); + u_int32_t saved_register_size = strtoul(tokens[6], NULL, 16); + u_int32_t local_size = strtoul(tokens[7], NULL, 16); + u_int32_t max_stack_size = strtoul(tokens[8], NULL, 16); + int has_program_string = strtoul(tokens[9], NULL, 16); + + const char *program_string = ""; + int allocates_base_pointer = 0; + if (has_program_string) { + program_string = tokens[10]; + } else { + allocates_base_pointer = strtoul(tokens[10], NULL, 16); + } + + return new WindowsFrameInfo(prolog_size, + epilog_size, + parameter_size, + saved_register_size, + local_size, + max_stack_size, + allocates_base_pointer, + program_string); + } + + // CopyFrom makes "this" WindowsFrameInfo object identical to "that". + void CopyFrom(const WindowsFrameInfo &that) { + valid = that.valid; + prolog_size = that.prolog_size; + epilog_size = that.epilog_size; + parameter_size = that.parameter_size; + saved_register_size = that.saved_register_size; + local_size = that.local_size; + max_stack_size = that.max_stack_size; + allocates_base_pointer = that.allocates_base_pointer; + program_string = that.program_string; + } + + // Clears the WindowsFrameInfo object so that users will see it as though + // it contains no information. + void Clear() { + valid = VALID_NONE; + program_string.erase(); + } + + // Identifies which fields in the structure are valid. This is of + // type Validity, but it is defined as an int because it's not + // possible to OR values into an enumerated type. Users must check + // this field before using any other. + int valid; + + // These values come from IDiaFrameData. + u_int32_t prolog_size; + u_int32_t epilog_size; + u_int32_t parameter_size; + u_int32_t saved_register_size; + u_int32_t local_size; + u_int32_t max_stack_size; + + // Only one of allocates_base_pointer or program_string will be valid. + // If program_string is empty, use allocates_base_pointer. + bool allocates_base_pointer; + std::string program_string; +}; + +} // namespace google_breakpad + + +#endif // PROCESSOR_WINDOWS_FRAME_INFO_H__ diff --git a/src/lib/crashdump/gbreakpad/third_party/lss/linux_syscall_support.h b/src/lib/crashdump/gbreakpad/third_party/lss/linux_syscall_support.h new file mode 100644 index 0000000..604649e --- /dev/null +++ b/src/lib/crashdump/gbreakpad/third_party/lss/linux_syscall_support.h @@ -0,0 +1,3500 @@ +/* Copyright (c) 2005-2010, Google Inc. + * 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 Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER 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. + * + * --- + * Author: Markus Gutschke + */ + +/* This file includes Linux-specific support functions common to the + * coredumper and the thread lister; primarily, this is a collection + * of direct system calls, and a couple of symbols missing from + * standard header files. + * There are a few options that the including file can set to control + * the behavior of this file: + * + * SYS_CPLUSPLUS: + * The entire header file will normally be wrapped in 'extern "C" { }", + * making it suitable for compilation as both C and C++ source. If you + * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit + * the wrapping. N.B. doing so will suppress inclusion of all prerequisite + * system header files, too. It is the caller's responsibility to provide + * the necessary definitions. + * + * SYS_ERRNO: + * All system calls will update "errno" unless overriden by setting the + * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be + * an l-value. + * + * SYS_INLINE: + * New symbols will be defined "static inline", unless overridden by + * the SYS_INLINE macro. + * + * SYS_LINUX_SYSCALL_SUPPORT_H + * This macro is used to avoid multiple inclusions of this header file. + * If you need to include this file more than once, make sure to + * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion. + * + * SYS_PREFIX: + * New system calls will have a prefix of "sys_" unless overridden by + * the SYS_PREFIX macro. Valid values for this macro are [0..9] which + * results in prefixes "sys[0..9]_". It is also possible to set this + * macro to -1, which avoids all prefixes. + * + * SYS_SYSCALL_ENTRYPOINT: + * Some applications (such as sandboxes that filter system calls), need + * to be able to run custom-code each time a system call is made. If this + * macro is defined, it expands to the name of a "common" symbol. If + * this symbol is assigned a non-NULL pointer value, it is used as the + * address of the system call entrypoint. + * A pointer to this symbol can be obtained by calling + * get_syscall_entrypoint() + * + * This file defines a few internal symbols that all start with "LSS_". + * Do not access these symbols from outside this file. They are not part + * of the supported API. + */ +#ifndef SYS_LINUX_SYSCALL_SUPPORT_H +#define SYS_LINUX_SYSCALL_SUPPORT_H + +/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux. + * Porting to other related platforms should not be difficult. + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__)) \ + && (defined(__linux) || defined(__ANDROID__)) + +#ifndef SYS_CPLUSPLUS +#ifdef __cplusplus +/* Some system header files in older versions of gcc neglect to properly + * handle being included from C++. As it appears to be harmless to have + * multiple nested 'extern "C"' blocks, just add another one here. + */ +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __mips__ +/* Include definitions of the ABI currently in use. */ +#include +#endif +#endif + +/* As glibc often provides subtly incompatible data structures (and implicit + * wrapper functions that convert them), we provide our own kernel data + * structures for use by the system calls. + * These structures have been developed by using Linux 2.6.23 headers for + * reference. Note though, we do not care about exact API compatibility + * with the kernel, and in fact the kernel often does not have a single + * API that works across architectures. Instead, we try to mimic the glibc + * API where reasonable, and only guarantee ABI compatibility with the + * kernel headers. + * Most notably, here are a few changes that were made to the structures + * defined by kernel headers: + * + * - we only define structures, but not symbolic names for kernel data + * types. For the latter, we directly use the native C datatype + * (i.e. "unsigned" instead of "mode_t"). + * - in a few cases, it is possible to define identical structures for + * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by + * standardizing on the 64bit version of the data types. In particular, + * this means that we use "unsigned" where the 32bit headers say + * "unsigned long". + * - overall, we try to minimize the number of cases where we need to + * conditionally define different structures. + * - the "struct kernel_sigaction" class of structures have been + * modified to more closely mimic glibc's API by introducing an + * anonymous union for the function pointer. + * - a small number of field names had to have an underscore appended to + * them, because glibc defines a global macro by the same name. + */ + +/* include/linux/dirent.h */ +struct kernel_dirent64 { + unsigned long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +/* include/linux/dirent.h */ +struct kernel_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +/* include/linux/uio.h */ +struct kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +/* include/linux/socket.h */ +struct kernel_msghdr { + void *msg_name; + int msg_namelen; + struct kernel_iovec*msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +/* include/asm-generic/poll.h */ +struct kernel_pollfd { + int fd; + short events; + short revents; +}; + +/* include/linux/resource.h */ +struct kernel_rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; + +/* include/linux/time.h */ +struct kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +/* include/linux/time.h */ +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +/* include/linux/resource.h */ +struct kernel_rusage { + struct kernel_timeval ru_utime; + struct kernel_timeval ru_stime; + long ru_maxrss; + long ru_ixrss; + long ru_idrss; + long ru_isrss; + long ru_minflt; + long ru_majflt; + long ru_nswap; + long ru_inblock; + long ru_oublock; + long ru_msgsnd; + long ru_msgrcv; + long ru_nsignals; + long ru_nvcsw; + long ru_nivcsw; +}; + +struct siginfo; +#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \ + || defined(__PPC__) + +/* include/asm-{arm,i386,mips,ppc}/signal.h */ +struct kernel_old_sigaction { + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + unsigned long sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +} __attribute__((packed,aligned(4))); +#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define kernel_old_sigaction kernel_sigaction +#endif + +/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the + * exactly match the size of the signal set, even though the API was + * intended to be extensible. We define our own KERNEL_NSIG to deal with + * this. + * Please note that glibc provides signals [1.._NSIG-1], whereas the + * kernel (and this header) provides the range [1..KERNEL_NSIG]. The + * actual number of signals is obviously the same, but the constants + * differ by one. + */ +#ifdef __mips__ +#define KERNEL_NSIG 128 +#else +#define KERNEL_NSIG 64 +#endif + +/* include/asm-{arm,i386,mips,x86_64}/signal.h */ +struct kernel_sigset_t { + unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/ + (8*sizeof(unsigned long))]; +}; + +/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */ +struct kernel_sigaction { +#ifdef __mips__ + unsigned long sa_flags; + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + struct kernel_sigset_t sa_mask; +#else + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + struct kernel_sigset_t sa_mask; +#endif +}; + +/* include/linux/socket.h */ +struct kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +/* include/asm-{arm,i386,mips,ppc}/stat.h */ +#ifdef __mips__ +#if _MIPS_SIM == _MIPS_SIM_ABI64 +struct kernel_stat { +#else +struct kernel_stat64 { +#endif + unsigned st_dev; + unsigned __pad0[3]; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + unsigned __pad1[3]; + long long st_size; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned st_blksize; + unsigned __pad2; + unsigned long long st_blocks; +}; +#elif defined __PPC__ +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned short int __pad2; + long long st_size; + long st_blksize; + long long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#else +struct kernel_stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + unsigned __st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned char __pad3[4]; + long long st_size; + unsigned st_blksize; + unsigned long long st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned long long st_ino; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */ +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +struct kernel_stat { + /* The kernel headers suggest that st_dev and st_rdev should be 32bit + * quantities encoding 12bit major and 20bit minor numbers in an interleaved + * format. In reality, we do not see useful data in the top bits. So, + * we'll leave the padding in here, until we find a better solution. + */ + unsigned short st_dev; + short pad1; + unsigned st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + short pad2; + unsigned st_size; + unsigned st_blksize; + unsigned st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned __unused4; + unsigned __unused5; +}; +#elif defined(__x86_64__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned st_mode; + unsigned st_uid; + unsigned st_gid; + unsigned __pad0; + unsigned long st_rdev; + long st_size; + long st_blksize; + long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + long __unused[3]; +}; +#elif defined(__PPC__) +struct kernel_stat { + unsigned st_dev; + unsigned long st_ino; // ino_t + unsigned long st_mode; // mode_t + unsigned short st_nlink; // nlink_t + unsigned st_uid; // uid_t + unsigned st_gid; // gid_t + unsigned st_rdev; + long st_size; // off_t + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) +struct kernel_stat { + unsigned st_dev; + int st_pad1[3]; + unsigned st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + int st_pad2[2]; + long st_size; + int st_pad3; + long st_atime_; + long st_atime_nsec_; + long st_mtime_; + long st_mtime_nsec_; + long st_ctime_; + long st_ctime_nsec_; + int st_blksize; + int st_blocks; + int st_pad4[14]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc}/statfs.h */ +#ifdef __mips__ +#if _MIPS_SIM != _MIPS_SIM_ABI64 +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_frsize; + unsigned long __pad; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_files; + unsigned long long f_ffree; + unsigned long long f_bavail; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_spare[6]; +}; +#endif +#elif !defined(__x86_64__) +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc,generic}/statfs.h */ +#ifdef __mips__ +struct kernel_statfs { + long f_type; + long f_bsize; + long f_frsize; + long f_blocks; + long f_bfree; + long f_files; + long f_ffree; + long f_bavail; + struct { int val[2]; } f_fsid; + long f_namelen; + long f_spare[6]; +}; +#else +struct kernel_statfs { + /* x86_64 actually defines all these fields as signed, whereas all other */ + /* platforms define them as unsigned. Leaving them at unsigned should not */ + /* cause any problems. */ + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + + +/* Definitions missing from the standard header files */ +#ifndef O_DIRECTORY +#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +#define O_DIRECTORY 0040000 +#else +#define O_DIRECTORY 0200000 +#endif +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif +#ifndef PTRACE_GETFPXREGS +#define PTRACE_GETFPXREGS ((enum __ptrace_request)18) +#endif +#ifndef PR_GET_DUMPABLE +#define PR_GET_DUMPABLE 3 +#endif +#ifndef PR_SET_DUMPABLE +#define PR_SET_DUMPABLE 4 +#endif +#ifndef PR_GET_SECCOMP +#define PR_GET_SECCOMP 21 +#endif +#ifndef PR_SET_SECCOMP +#define PR_SET_SECCOMP 22 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD (-100) +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_REMOVEDIR +#define AT_REMOVEDIR 0x200 +#endif +#ifndef MREMAP_FIXED +#define MREMAP_FIXED 2 +#endif +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif +#ifndef CPUCLOCK_PROF +#define CPUCLOCK_PROF 0 +#endif +#ifndef CPUCLOCK_VIRT +#define CPUCLOCK_VIRT 1 +#endif +#ifndef CPUCLOCK_SCHED +#define CPUCLOCK_SCHED 2 +#endif +#ifndef CPUCLOCK_PERTHREAD_MASK +#define CPUCLOCK_PERTHREAD_MASK 4 +#endif +#ifndef MAKE_PROCESS_CPUCLOCK +#define MAKE_PROCESS_CPUCLOCK(pid, clock) \ + ((~(int)(pid) << 3) | (int)(clock)) +#endif +#ifndef MAKE_THREAD_CPUCLOCK +#define MAKE_THREAD_CPUCLOCK(tid, clock) \ + ((~(int)(tid) << 3) | (int)((clock) | CPUCLOCK_PERTHREAD_MASK)) +#endif + +#ifndef FUTEX_WAIT +#define FUTEX_WAIT 0 +#endif +#ifndef FUTEX_WAKE +#define FUTEX_WAKE 1 +#endif +#ifndef FUTEX_FD +#define FUTEX_FD 2 +#endif +#ifndef FUTEX_REQUEUE +#define FUTEX_REQUEUE 3 +#endif +#ifndef FUTEX_CMP_REQUEUE +#define FUTEX_CMP_REQUEUE 4 +#endif +#ifndef FUTEX_WAKE_OP +#define FUTEX_WAKE_OP 5 +#endif +#ifndef FUTEX_LOCK_PI +#define FUTEX_LOCK_PI 6 +#endif +#ifndef FUTEX_UNLOCK_PI +#define FUTEX_UNLOCK_PI 7 +#endif +#ifndef FUTEX_TRYLOCK_PI +#define FUTEX_TRYLOCK_PI 8 +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#ifndef FUTEX_CMD_MASK +#define FUTEX_CMD_MASK ~FUTEX_PRIVATE_FLAG +#endif +#ifndef FUTEX_WAIT_PRIVATE +#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_WAKE_PRIVATE +#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_REQUEUE_PRIVATE +#define FUTEX_REQUEUE_PRIVATE (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_CMP_REQUEUE_PRIVATE +#define FUTEX_CMP_REQUEUE_PRIVATE (FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_WAKE_OP_PRIVATE +#define FUTEX_WAKE_OP_PRIVATE (FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_LOCK_PI_PRIVATE +#define FUTEX_LOCK_PI_PRIVATE (FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_UNLOCK_PI_PRIVATE +#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_TRYLOCK_PI_PRIVATE +#define FUTEX_TRYLOCK_PI_PRIVATE (FUTEX_TRYLOCK_PI | FUTEX_PRIVATE_FLAG) +#endif + + +#if defined(__x86_64__) +#ifndef ARCH_SET_GS +#define ARCH_SET_GS 0x1001 +#endif +#ifndef ARCH_GET_GS +#define ARCH_GET_GS 0x1004 +#endif +#endif + +#if defined(__i386__) +#ifndef __NR_quotactl +#define __NR_quotactl 131 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_gettid +#define __NR_gettid 224 +#endif +#ifndef __NR_readahead +#define __NR_readahead 225 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 226 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 227 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 229 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 230 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 232 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 233 +#endif +#ifndef __NR_tkill +#define __NR_tkill 238 +#endif +#ifndef __NR_futex +#define __NR_futex 240 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 258 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 265 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 266 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 268 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 269 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 272 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 289 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 290 +#endif +#ifndef __NR_openat +#define __NR_openat 295 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 300 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 301 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 317 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 318 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 324 +#endif +/* End of i386 definitions */ +#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_SYSCALL_BASE + 164) +#define __NR_getresuid (__NR_SYSCALL_BASE + 165) +#define __NR_setresgid (__NR_SYSCALL_BASE + 170) +#define __NR_getresgid (__NR_SYSCALL_BASE + 171) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn (__NR_SYSCALL_BASE + 173) +#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) +#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) +#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) +#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_SYSCALL_BASE + 180) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_SYSCALL_BASE + 195) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) +#define __NR_getresuid32 (__NR_SYSCALL_BASE + 209) +#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) +#define __NR_getresgid32 (__NR_SYSCALL_BASE + 211) +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) +#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_SYSCALL_BASE + 224) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_SYSCALL_BASE + 225) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_SYSCALL_BASE + 226) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_SYSCALL_BASE + 229) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_SYSCALL_BASE + 232) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_SYSCALL_BASE + 233) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_SYSCALL_BASE + 238) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_SYSCALL_BASE + 240) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) +#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_SYSCALL_BASE + 263) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_SYSCALL_BASE + 264) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_SYSCALL_BASE + 314) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_SYSCALL_BASE + 315) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_SYSCALL_BASE + 344) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_SYSCALL_BASE + 345) +#endif +/* End of ARM 3/EABI definitions */ +#elif defined(__x86_64__) +#ifndef __NR_pread64 +#define __NR_pread64 17 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 18 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 117 +#define __NR_getresuid 118 +#define __NR_setresgid 119 +#define __NR_getresgid 120 +#endif +#ifndef __NR_quotactl +#define __NR_quotactl 179 +#endif +#ifndef __NR_gettid +#define __NR_gettid 186 +#endif +#ifndef __NR_readahead +#define __NR_readahead 187 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 188 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 189 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 191 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 192 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 194 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 195 +#endif +#ifndef __NR_tkill +#define __NR_tkill 200 +#endif +#ifndef __NR_futex +#define __NR_futex 202 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 203 +#define __NR_sched_getaffinity 204 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 217 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 218 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 221 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 228 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 229 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 251 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 252 +#endif +#ifndef __NR_openat +#define __NR_openat 257 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 262 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 263 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 279 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 285 +#endif +/* End of x86-64 definitions */ +#elif defined(__mips__) +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 185) +#define __NR_getresuid (__NR_Linux + 186) +#define __NR_setresgid (__NR_Linux + 190) +#define __NR_getresgid (__NR_Linux + 191) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn (__NR_Linux + 193) +#define __NR_rt_sigaction (__NR_Linux + 194) +#define __NR_rt_sigprocmask (__NR_Linux + 195) +#define __NR_rt_sigpending (__NR_Linux + 196) +#define __NR_rt_sigsuspend (__NR_Linux + 199) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 200) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 201) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_Linux + 213) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_Linux + 215) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_Linux + 219) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 222) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 223) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 224) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 225) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 227) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 228) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 230) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 231) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 236) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 238) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 252) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 255) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 256) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 263) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 264) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 288) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 293) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 294) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 308) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 312) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 314) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 315) +#endif +/* End of MIPS (old 32bit API) definitions */ +#elif _MIPS_SIM == _MIPS_SIM_ABI64 +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 16) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 17) +#endif +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 186) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 187) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 192) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 212) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 222) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 223) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 247) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 252) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 253) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 267) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 271) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 273) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 274) +#endif +/* End of MIPS (64bit API) definitions */ +#else +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 186) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 187) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 192) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 213) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 217) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 218) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 226) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 227) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 251) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 256) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 257) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 271) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 275) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 277) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 278) +#endif +/* End of MIPS (new 32bit API) definitions */ +#endif +/* End of MIPS definitions */ +#elif defined(__PPC__) +#ifndef __NR_setfsuid +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_setresgid 169 +#define __NR_getresgid 170 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn 172 +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigsuspend 178 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 179 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 180 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 190 +#endif +#ifndef __NR_readahead +#define __NR_readahead 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 202 +#endif +#ifndef __NR_gettid +#define __NR_gettid 207 +#endif +#ifndef __NR_tkill +#define __NR_tkill 208 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 209 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 210 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 212 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 213 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 215 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 216 +#endif +#ifndef __NR_futex +#define __NR_futex 221 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 222 +#define __NR_sched_getaffinity 223 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 232 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 246 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 247 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 252 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 253 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 254 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 273 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 274 +#endif +#ifndef __NR_openat +#define __NR_openat 286 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 291 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 292 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 301 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 302 +#endif +/* End of powerpc defininitions */ +#endif + + +/* After forking, we must make sure to only call system calls. */ +#if __BOUNDED_POINTERS__ + #error "Need to port invocations of syscalls for bounded ptrs" +#else + /* The core dumper and the thread lister get executed after threads + * have been suspended. As a consequence, we cannot call any functions + * that acquire locks. Unfortunately, libc wraps most system calls + * (e.g. in order to implement pthread_atfork, and to make calls + * cancellable), which means we cannot call these functions. Instead, + * we have to call syscall() directly. + */ + #undef LSS_ERRNO + #ifdef SYS_ERRNO + /* Allow the including file to override the location of errno. This can + * be useful when using clone() with the CLONE_VM option. + */ + #define LSS_ERRNO SYS_ERRNO + #else + #define LSS_ERRNO errno + #endif + + #undef LSS_INLINE + #ifdef SYS_INLINE + #define LSS_INLINE SYS_INLINE + #else + #define LSS_INLINE static inline + #endif + + /* Allow the including file to override the prefix used for all new + * system calls. By default, it will be set to "sys_". + */ + #undef LSS_NAME + #ifndef SYS_PREFIX + #define LSS_NAME(name) sys_##name + #elif SYS_PREFIX < 0 + #define LSS_NAME(name) name + #elif SYS_PREFIX == 0 + #define LSS_NAME(name) sys0_##name + #elif SYS_PREFIX == 1 + #define LSS_NAME(name) sys1_##name + #elif SYS_PREFIX == 2 + #define LSS_NAME(name) sys2_##name + #elif SYS_PREFIX == 3 + #define LSS_NAME(name) sys3_##name + #elif SYS_PREFIX == 4 + #define LSS_NAME(name) sys4_##name + #elif SYS_PREFIX == 5 + #define LSS_NAME(name) sys5_##name + #elif SYS_PREFIX == 6 + #define LSS_NAME(name) sys6_##name + #elif SYS_PREFIX == 7 + #define LSS_NAME(name) sys7_##name + #elif SYS_PREFIX == 8 + #define LSS_NAME(name) sys8_##name + #elif SYS_PREFIX == 9 + #define LSS_NAME(name) sys9_##name + #endif + + #undef LSS_RETURN + #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \ + || defined(__ARM_EABI__)) + /* Failing system calls return a negative result in the range of + * -1..-4095. These are "errno" values with the sign inverted. + */ + #define LSS_RETURN(type, res) \ + do { \ + if ((unsigned long)(res) >= (unsigned long)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__mips__) + /* On MIPS, failing system calls return -1, and set errno in a + * separate CPU register. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__PPC__) + /* On PPC, failing system calls return -1, and set errno in a + * separate CPU register. See linux/unistd.h. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err & 0x10000000 ) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #endif + #if defined(__i386__) + /* In PIC mode (e.g. when building shared libraries), gcc for i386 + * reserves ebx. Unfortunately, most distribution ship with implementations + * of _syscallX() which clobber ebx. + * Also, most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_ENTRYPOINT + #ifdef SYS_SYSCALL_ENTRYPOINT + static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) { + void (**entrypoint)(void); + asm volatile(".bss\n" + ".align 8\n" + ".globl "SYS_SYSCALL_ENTRYPOINT"\n" + ".common "SYS_SYSCALL_ENTRYPOINT",8,8\n" + ".previous\n" + /* This logically does 'lea "SYS_SYSCALL_ENTRYPOINT", %0' */ + "call 0f\n" + "0:pop %0\n" + "add $_GLOBAL_OFFSET_TABLE_+[.-0b], %0\n" + "mov "SYS_SYSCALL_ENTRYPOINT"@GOT(%0), %0\n" + : "=r"(entrypoint)); + return entrypoint; + } + + #define LSS_ENTRYPOINT ".bss\n" \ + ".align 8\n" \ + ".globl "SYS_SYSCALL_ENTRYPOINT"\n" \ + ".common "SYS_SYSCALL_ENTRYPOINT",8,8\n" \ + ".previous\n" \ + /* Check the SYS_SYSCALL_ENTRYPOINT vector */ \ + "push %%eax\n" \ + "call 10000f\n" \ + "10000:pop %%eax\n" \ + "add $_GLOBAL_OFFSET_TABLE_+[.-10000b], %%eax\n" \ + "mov "SYS_SYSCALL_ENTRYPOINT"@GOT(%%eax), %%eax\n"\ + "mov 0(%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ + "jz 10001f\n" \ + "push %%eax\n" \ + "lea 10002f, %%eax\n" \ + "xchg 4(%%esp), %%eax\n" \ + "ret\n" \ + "10001:pop %%eax\n" \ + "int $0x80\n" \ + "10002:\n" + #else + #define LSS_ENTRYPOINT "int $0x80\n" + #endif + #undef LSS_BODY + #define LSS_BODY(type,args...) \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx" \ + args \ + : "esp", "memory"); \ + LSS_RETURN(type,__res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + long __res; \ + __asm__ volatile(LSS_ENTRYPOINT \ + : "=a" (__res) \ + : "0" (__NR_##name) \ + : "esp", "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1,type2 arg2) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "movl %1,%%eax\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx" \ + : "=a" (__res) \ + : "i" (__NR_##name), "ri" ((long)(arg1)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "esp", "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \ + __asm__ __volatile__("push %%ebp\n" \ + "push %%ebx\n" \ + "movl 4(%2),%%ebp\n" \ + "movl 0(%2), %%ebx\n" \ + "movl %1,%%eax\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx\n" \ + "pop %%ebp" \ + : "=a" (__res) \ + : "i" (__NR_##name), "0" ((long)(&__s)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "esp", "memory"); \ + LSS_RETURN(type,__res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "movl %3,%%ecx\n" + "jecxz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "movl %4,%%ecx\n" + "jecxz 1f\n" + + /* Set up alignment of the child stack: + * child_stack = (child_stack & ~0xF) - 20; + */ + "andl $-16,%%ecx\n" + "subl $20,%%ecx\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movl %6,%%eax\n" + "movl %%eax,4(%%ecx)\n" + "movl %3,%%eax\n" + "movl %%eax,(%%ecx)\n" + + /* %eax = syscall(%eax = __NR_clone, + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = newtls, + * %edi = child_tidptr) + * Also, make sure that %ebx gets preserved as it is + * used in PIC mode. + */ + "movl %8,%%esi\n" + "movl %7,%%edx\n" + "movl %5,%%eax\n" + "movl %9,%%edi\n" + "pushl %%ebx\n" + "movl %%eax,%%ebx\n" + "movl %2,%%eax\n" + LSS_ENTRYPOINT + + /* In the parent: restore %ebx + * In the child: move "fn" into %ebx + */ + "popl %%ebx\n" + + /* if (%eax != 0) + * return %eax; + */ + "test %%eax,%%eax\n" + "jnz 1f\n" + + /* In the child, now. Terminate frame pointer chain. + */ + "movl $0,%%ebp\n" + + /* Call "fn". "arg" is already on the stack. + */ + "call *%%ebx\n" + + /* Call _exit(%ebx). Unfortunately older versions + * of gcc restrict the number of arguments that can + * be passed to asm(). So, we need to hard-code the + * system call number. + */ + "movl %%eax,%%ebx\n" + "movl $1,%%eax\n" + LSS_ENTRYPOINT + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), + "m"(fn), "m"(child_stack), "m"(flags), "m"(arg), + "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr) + : "esp", "memory", "ecx", "edx", "esi", "edi"); + LSS_RETURN(int, __res); + } + + #define __NR__fadvise64_64 __NR_fadvise64_64 + LSS_INLINE _syscall6(int, _fadvise64_64, int, fd, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi, + int, advice) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + return LSS_NAME(_fadvise64_64)(fd, + (unsigned)offset, (unsigned)(offset >>32), + (unsigned)len, (unsigned)(len >> 32), + advice); + } + + #define __NR__fallocate __NR_fallocate + LSS_INLINE _syscall6(int, _fallocate, int, fd, + int, mode, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi) + + LSS_INLINE int LSS_NAME(fallocate)(int fd, int mode, + loff_t offset, loff_t len) { + union { loff_t off; unsigned w[2]; } o = { offset }, l = { len }; + return LSS_NAME(_fallocate)(fd, mode, o.w[0], o.w[1], l.w[0], l.w[1]); + } + + LSS_INLINE _syscall1(int, set_thread_area, void *, u) + LSS_INLINE _syscall1(int, get_thread_area, void *, u) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movl %1,%%eax\n" + LSS_ENTRYPOINT + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + LSS_INLINE void (*LSS_NAME(restore)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:pop %%eax\n" + "movl %1,%%eax\n" + LSS_ENTRYPOINT + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_sigreturn)); + return res; + } + #elif defined(__x86_64__) + /* There are no known problems with any of the _syscallX() macros + * currently shipping for x86_64, but we still need to be able to define + * our own version so that we can override the location of the errno + * location (e.g. when using the clone() system call with the CLONE_VM + * option). + */ + #undef LSS_ENTRYPOINT + #ifdef SYS_SYSCALL_ENTRYPOINT + static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) { + void (**entrypoint)(void); + asm volatile(".bss\n" + ".align 8\n" + ".globl "SYS_SYSCALL_ENTRYPOINT"\n" + ".common "SYS_SYSCALL_ENTRYPOINT",8,8\n" + ".previous\n" + "mov "SYS_SYSCALL_ENTRYPOINT"@GOTPCREL(%%rip), %0\n" + : "=r"(entrypoint)); + return entrypoint; + } + + #define LSS_ENTRYPOINT \ + ".bss\n" \ + ".align 8\n" \ + ".globl "SYS_SYSCALL_ENTRYPOINT"\n" \ + ".common "SYS_SYSCALL_ENTRYPOINT",8,8\n" \ + ".previous\n" \ + "mov "SYS_SYSCALL_ENTRYPOINT"@GOTPCREL(%%rip), %%rcx\n" \ + "mov 0(%%rcx), %%rcx\n" \ + "test %%rcx, %%rcx\n" \ + "jz 10001f\n" \ + "call *%%rcx\n" \ + "jmp 10002f\n" \ + "10001:syscall\n" \ + "10002:\n" + + #else + #define LSS_ENTRYPOINT "syscall\n" + #endif + #undef LSS_BODY + #define LSS_BODY(type,name, ...) \ + long __res; \ + __asm__ __volatile__(LSS_ENTRYPOINT \ + : "=a" (__res) : "0" (__NR_##name), \ + ##__VA_ARGS__ : "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, name, "D" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10;" LSS_ENTRYPOINT : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "r" ((long)(arg4)) : "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8;" LSS_ENTRYPOINT :\ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "r" ((long)(arg4)), "r" ((long)(arg5)) : \ + "r8", "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; movq %7,%%r9;" \ + LSS_ENTRYPOINT : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "r" ((long)(arg4)), "r" ((long)(arg5)), "r" ((long)(arg6)) : \ + "r8", "r9", "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "testq %4,%4\n" + "jz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "testq %5,%5\n" + "jz 1f\n" + + /* childstack -= 2*sizeof(void *); + */ + "subq $16,%5\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movq %7,8(%5)\n" + "movq %4,0(%5)\n" + + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %2,%%rax\n" + "movq %9,%%r8\n" + "movq %10,%%r10\n" + LSS_ENTRYPOINT + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate frame pointer chain. + */ + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". + */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%ebx). + */ + "movq %%rax,%%rdi\n" + "movq %3,%%rax\n" + LSS_ENTRYPOINT + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(fn), "S"(child_stack), "D"(flags), "r"(arg), + "d"(parent_tidptr), "r"(newtls), + "r"(child_tidptr) + : "rsp", "memory", "r8", "r10", "r11", "rcx"); + } + LSS_RETURN(int, __res); + } + LSS_INLINE _syscall2(int, arch_prctl, int, c, void *, a) + LSS_INLINE _syscall4(int, fadvise64, int, fd, loff_t, offset, loff_t, len, + int, advice) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On x86-64, the kernel does not know how to return from + * a signal handler. Instead, it relies on user space to provide a + * restorer function that calls the rt_sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movq %1,%%rax\n" + LSS_ENTRYPOINT + "2:popq %0\n" + "addq $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + #elif defined(__ARM_ARCH_3__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ (__syscall(name) \ + : "=r"(__res_r0) : args : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "cmpne %3,#0\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + __syscall(clone)"\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + "mov lr,pc\n" + "ldr pc,[sp]\n" + + /* Call _exit(%r0). + */ + __syscall(exit)"\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid) + : "cc", "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__ARM_EABI__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all fo the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ ("push {r7}\n" \ + "mov r7, %1\n" \ + "swi 0x0\n" \ + "pop {r7}\n" \ + : "=r"(__res_r0) \ + : "i"(__NR_##name) , ## args \ + : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "it ne\n" + "cmpne %3,#0\n" + "it eq\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + "mov r7, %9\n" + "swi 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + + /* When compiling for Thumb-2 the "MOV LR,PC" here + * won't work because it loads PC+4 into LR, + * whereas the LDR is a 4-byte instruction. + * This results in the child thread always + * crashing with an "Illegal Instruction" when it + * returned into the middle of the LDR instruction + * The instruction sequence used instead was + * recommended by + * "https://wiki.edubuntu.org/ARM/Thumb2PortingHowto#Quick_Reference". + */ + #ifdef __thumb2__ + "ldr r7,[sp]\n" + "blx r7\n" + #else + "mov lr,pc\n" + "ldr pc,[sp]\n" + #endif + + /* Call _exit(%r0). + */ + "mov r7, %10\n" + "swi 0x0\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "cc", "r7", "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__mips__) + #undef LSS_REG + #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \ + (unsigned long)(a) + #undef LSS_BODY + #define LSS_BODY(type,name,r7,...) \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ ("syscall\n" \ + : "=&r"(__v0), r7 (__r7) \ + : "0"(__v0), ##__VA_ARGS__ \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_BODY(type, name, "=r"); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall5 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2"); \ + __asm__ __volatile__ (".set noreorder\n" \ + "lw $2, %6\n" \ + "subu $29, 32\n" \ + "sw $2, 16($29)\n" \ + "li $2, %2\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "=&r"(__v0), "+r" (__r7) \ + : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ + "r"(__r6), "m" ((unsigned long)arg5) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8)); \ + } + #endif + #undef _syscall6 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2"); \ + __asm__ __volatile__ (".set noreorder\n" \ + "lw $2, %6\n" \ + "lw $8, %7\n" \ + "subu $29, 32\n" \ + "sw $2, 16($29)\n" \ + "sw $8, 20($29)\n" \ + "li $2, %2\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "=&r"(__v0), "+r" (__r7) \ + : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5), \ + "r" ((unsigned long)arg6) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5,type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8), "r"(__r9)); \ + } + #endif + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + register unsigned long __v0 __asm__("$2"); + register unsigned long __r7 __asm__("$7") = (unsigned long)newtls; + { + register int __flags __asm__("$4") = flags; + register void *__stack __asm__("$5") = child_stack; + register void *__ptid __asm__("$6") = parent_tidptr; + register int *__ctid __asm__("$8") = child_tidptr; + __asm__ __volatile__( + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu $29,24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub $29,16\n" + #else + "dsubu $29,16\n" + #endif + + /* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "li %0,%2\n" + "beqz %5,1f\n" + "beqz %6,1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu %6,32\n" + "sw %5,0(%6)\n" + "sw %8,4(%6)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub %6,32\n" + "sw %5,0(%6)\n" + "sw %8,8(%6)\n" + #else + "dsubu %6,32\n" + "sd %5,0(%6)\n" + "sd %8,8(%6)\n" + #endif + + /* $7 = syscall($4 = flags, + * $5 = child_stack, + * $6 = parent_tidptr, + * $7 = newtls, + * $8 = child_tidptr) + */ + "li $2,%3\n" + "syscall\n" + + /* if ($7 != 0) + * return $2; + */ + "bnez $7,1f\n" + "bnez $2,1f\n" + + /* In the child, now. Call "fn(arg)". + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "lw $25,0($29)\n" + "lw $4,4($29)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "lw $25,0($29)\n" + "lw $4,8($29)\n" + #else + "ld $25,0($29)\n" + "ld $4,8($29)\n" + #endif + "jalr $25\n" + + /* Call _exit($2) + */ + "move $4,$2\n" + "li $2,%4\n" + "syscall\n" + + "1:\n" + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "addu $29, 24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "add $29, 16\n" + #else + "daddu $29,16\n" + #endif + : "=&r" (__v0), "=r" (__r7) + : "i"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__r7), "r"(__ctid) + : "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$24", "memory"); + } + LSS_RETURN(int, __v0, __r7); + } + #elif defined (__PPC__) + #undef LSS_LOADARGS_0 + #define LSS_LOADARGS_0(name, dummy...) \ + __sc_0 = __NR_##name + #undef LSS_LOADARGS_1 + #define LSS_LOADARGS_1(name, arg1) \ + LSS_LOADARGS_0(name); \ + __sc_3 = (unsigned long) (arg1) + #undef LSS_LOADARGS_2 + #define LSS_LOADARGS_2(name, arg1, arg2) \ + LSS_LOADARGS_1(name, arg1); \ + __sc_4 = (unsigned long) (arg2) + #undef LSS_LOADARGS_3 + #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \ + LSS_LOADARGS_2(name, arg1, arg2); \ + __sc_5 = (unsigned long) (arg3) + #undef LSS_LOADARGS_4 + #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \ + LSS_LOADARGS_3(name, arg1, arg2, arg3); \ + __sc_6 = (unsigned long) (arg4) + #undef LSS_LOADARGS_5 + #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \ + LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \ + __sc_7 = (unsigned long) (arg5) + #undef LSS_LOADARGS_6 + #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \ + __sc_8 = (unsigned long) (arg6) + #undef LSS_ASMINPUT_0 + #define LSS_ASMINPUT_0 "0" (__sc_0) + #undef LSS_ASMINPUT_1 + #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3) + #undef LSS_ASMINPUT_2 + #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4) + #undef LSS_ASMINPUT_3 + #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5) + #undef LSS_ASMINPUT_4 + #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6) + #undef LSS_ASMINPUT_5 + #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7) + #undef LSS_ASMINPUT_6 + #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + \ + LSS_LOADARGS_##nr(name, args); \ + __asm__ __volatile__ \ + ("sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory", \ + "r9", "r10", "r11", "r12"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + /* clone function adapted from glibc 2.3.6 clone.S */ + /* TODO(csilvers): consider wrapping some args up in a struct, like we + * do for i386's _syscall6, so we can compile successfully on gcc 2.95 + */ + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret, __err; + { + register int (*__fn)(void *) __asm__ ("r8") = fn; + register void *__cstack __asm__ ("r4") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void * __arg __asm__ ("r9") = arg; + register int * __ptidptr __asm__ ("r5") = parent_tidptr; + register void * __newtls __asm__ ("r6") = newtls; + register int * __ctidptr __asm__ ("r7") = child_tidptr; + __asm__ __volatile__( + /* check for fn == NULL + * and child_stack == NULL + */ + "cmpwi cr0, %6, 0\n\t" + "cmpwi cr1, %7, 0\n\t" + "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t" + "beq- cr0, 1f\n\t" + + /* set up stack frame for child */ + "clrrwi %7, %7, 4\n\t" + "li 0, 0\n\t" + "stwu 0, -16(%7)\n\t" + + /* fn, arg, child_stack are saved across the syscall: r28-30 */ + "mr 28, %6\n\t" + "mr 29, %7\n\t" + "mr 27, %9\n\t" + + /* syscall */ + "li 0, %4\n\t" + /* flags already in r3 + * child_stack already in r4 + * ptidptr already in r5 + * newtls already in r6 + * ctidptr already in r7 + */ + "sc\n\t" + + /* Test if syscall was successful */ + "cmpwi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Do the function call */ + "mtctr 28\n\t" + "mr 3, 27\n\t" + "bctrl\n\t" + + /* Call _exit(r3) */ + "li 0, %5\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n" + "mfcr %1\n\t" + "mr %0, 3\n\t" + : "=r" (__ret), "=r" (__err) + : "0" (-1), "1" (EINVAL), + "i" (__NR_clone), "i" (__NR_exit), + "r" (__fn), "r" (__cstack), "r" (__flags), + "r" (__arg), "r" (__ptidptr), "r" (__newtls), + "r" (__ctidptr) + : "cr0", "cr1", "memory", "ctr", + "r0", "r29", "r27", "r28"); + } + LSS_RETURN(int, __ret, __err); + } + #endif + #define __NR__exit __NR_exit + #define __NR__gettid __NR_gettid + #define __NR__mremap __NR_mremap + LSS_INLINE _syscall1(int, brk, void *, e) + LSS_INLINE _syscall1(int, chdir, const char *,p) + LSS_INLINE _syscall1(int, close, int, f) + LSS_INLINE _syscall2(int, clock_getres, int, c, + struct kernel_timespec*, t) + LSS_INLINE _syscall2(int, clock_gettime, int, c, + struct kernel_timespec*, t) + LSS_INLINE _syscall1(int, dup, int, f) + LSS_INLINE _syscall2(int, dup2, int, s, + int, d) + LSS_INLINE _syscall3(int, execve, const char*, f, + const char*const*,a,const char*const*, e) + LSS_INLINE _syscall1(int, _exit, int, e) + LSS_INLINE _syscall1(int, exit_group, int, e) + LSS_INLINE _syscall3(int, fcntl, int, f, + int, c, long, a) + LSS_INLINE _syscall0(pid_t, fork) + LSS_INLINE _syscall2(int, fstat, int, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, fstatfs, int, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall2(int, ftruncate, int, f, + off_t, l) + LSS_INLINE _syscall4(int, futex, int*, a, + int, o, int, v, + struct kernel_timespec*, t) + LSS_INLINE _syscall3(int, getdents, int, f, + struct kernel_dirent*, d, int, c) + LSS_INLINE _syscall3(int, getdents64, int, f, + struct kernel_dirent64*, d, int, c) + LSS_INLINE _syscall0(gid_t, getegid) + LSS_INLINE _syscall0(uid_t, geteuid) + LSS_INLINE _syscall0(pid_t, getpgrp) + LSS_INLINE _syscall0(pid_t, getpid) + LSS_INLINE _syscall0(pid_t, getppid) + LSS_INLINE _syscall2(int, getpriority, int, a, + int, b) + LSS_INLINE _syscall3(int, getresgid, gid_t *, r, + gid_t *, e, gid_t *, s) + LSS_INLINE _syscall3(int, getresuid, uid_t *, r, + uid_t *, e, uid_t *, s) +#if !defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, getrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) + LSS_INLINE _syscall0(pid_t, _gettid) + LSS_INLINE _syscall2(pid_t, gettimeofday, struct kernel_timeval*, t, + void*, tz) + LSS_INLINE _syscall5(int, setxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall5(int, lsetxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall3(ssize_t, listxattr, const char *,p, + char *, l, size_t, s) + LSS_INLINE _syscall3(ssize_t, llistxattr, const char *,p, + char *, l, size_t, s) + LSS_INLINE _syscall3(int, ioctl, int, d, + int, r, void *, a) + LSS_INLINE _syscall2(int, ioprio_get, int, which, + int, who) + LSS_INLINE _syscall3(int, ioprio_set, int, which, + int, who, int, ioprio) + LSS_INLINE _syscall2(int, kill, pid_t, p, + int, s) + LSS_INLINE _syscall3(off_t, lseek, int, f, + off_t, o, int, w) + LSS_INLINE _syscall2(int, munmap, void*, s, + size_t, l) + LSS_INLINE _syscall6(long, move_pages, pid_t, p, + unsigned long, n, void **,g, int *, d, + int *, s, int, f) + LSS_INLINE _syscall3(int, mprotect, const void *,a, + size_t, l, int, p) + LSS_INLINE _syscall5(void*, _mremap, void*, o, + size_t, os, size_t, ns, + unsigned long, f, void *, a) + LSS_INLINE _syscall3(int, open, const char*, p, + int, f, int, m) + LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u, + unsigned int, n, int, t) + LSS_INLINE _syscall2(int, prctl, int, o, + long, a) + LSS_INLINE _syscall4(long, ptrace, int, r, + pid_t, p, void *, a, void *, d) + #if defined(__NR_quotactl) + // Defined on x86_64 / i386 only + LSS_INLINE _syscall4(int, quotactl, int, cmd, const char *, special, + int, id, caddr_t, addr) + #endif + LSS_INLINE _syscall3(ssize_t, read, int, f, + void *, b, size_t, c) + LSS_INLINE _syscall3(int, readlink, const char*, p, + char*, b, size_t, s) + LSS_INLINE _syscall4(int, rt_sigaction, int, s, + const struct kernel_sigaction*, a, + struct kernel_sigaction*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, + size_t, c) + LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, + const struct kernel_sigset_t*, s, + struct kernel_sigset_t*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigsuspend, + const struct kernel_sigset_t*, s, size_t, c) + LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall0(int, sched_yield) + LSS_INLINE _syscall1(long, set_tid_address, int *, t) + LSS_INLINE _syscall1(int, setfsgid, gid_t, g) + LSS_INLINE _syscall1(int, setfsuid, uid_t, u) + LSS_INLINE _syscall1(int, setuid, uid_t, u) + LSS_INLINE _syscall1(int, setgid, gid_t, g) + LSS_INLINE _syscall2(int, setpgid, pid_t, p, + pid_t, g) + LSS_INLINE _syscall3(int, setpriority, int, a, + int, b, int, p) + LSS_INLINE _syscall3(int, setresgid, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, setresuid, uid_t, r, + uid_t, e, uid_t, s) + LSS_INLINE _syscall2(int, setrlimit, int, r, + const struct kernel_rlimit*, l) + LSS_INLINE _syscall0(pid_t, setsid) + LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, + const stack_t*, o) + #if defined(__NR_sigreturn) + LSS_INLINE _syscall1(int, sigreturn, unsigned long, u) + #endif + LSS_INLINE _syscall2(int, stat, const char*, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, statfs, const char*, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall3(int, tgkill, pid_t, p, + pid_t, t, int, s) + LSS_INLINE _syscall2(int, tkill, pid_t, p, + int, s) + LSS_INLINE _syscall1(int, unlink, const char*, f) + LSS_INLINE _syscall3(ssize_t, write, int, f, + const void *, b, size_t, c) + LSS_INLINE _syscall3(ssize_t, writev, int, f, + const struct kernel_iovec*, v, size_t, c) + #if defined(__NR_getcpu) + LSS_INLINE _syscall3(long, getcpu, unsigned *, cpu, + unsigned *, node, void *, unused) + #endif + #if defined(__x86_64__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(int, recvmsg, int, s, + struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall3(int, sendmsg, int, s, + const struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall6(int, sendto, int, s, + const void*, m, size_t, l, + int, f, + const struct kernel_sockaddr*, a, int, t) + LSS_INLINE _syscall2(int, shutdown, int, s, + int, h) + LSS_INLINE _syscall3(int, socket, int, d, + int, t, int, p) + LSS_INLINE _syscall4(int, socketpair, int, d, + int, t, int, p, int*, s) + #endif + #if defined(__x86_64__) + LSS_INLINE _syscall4(int, fallocate, int, fd, int, mode, + loff_t, offset, loff_t, len) + + LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid, + gid_t *egid, + gid_t *sgid) { + return LSS_NAME(getresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid, + uid_t *euid, + uid_t *suid) { + return LSS_NAME(getresuid)(ruid, euid, suid); + } + + LSS_INLINE _syscall6(void*, mmap, void*, s, + size_t, l, int, p, + int, f, int, d, + __off64_t, o) + + LSS_INLINE _syscall4(int, newfstatat, int, d, + const char *, p, + struct kernel_stat*, b, int, f) + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + return LSS_NAME(setfsgid)(gid); + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + return LSS_NAME(setfsuid)(uid); + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + return LSS_NAME(setresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + return LSS_NAME(setresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + /* On x86_64, the kernel requires us to always set our own + * SA_RESTORER in order to be able to return from a signal handler. + * This function must have a "magic" signature that the "gdb" + * (and maybe the kernel?) can recognize. + */ + if (act != NULL && !(act->sa_flags & SA_RESTORER)) { + struct kernel_sigaction a = *act; + a.sa_flags |= SA_RESTORER; + a.sa_restorer = LSS_NAME(restore_rt)(); + return LSS_NAME(rt_sigaction)(signum, &a, oldact, + (KERNEL_NSIG+7)/8); + } else { + return LSS_NAME(rt_sigaction)(signum, act, oldact, + (KERNEL_NSIG+7)/8); + } + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, + int*, s, int, o, + struct kernel_rusage*, r) + + LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options){ + return LSS_NAME(wait4)(pid, status, options, 0); + } + #endif + #if defined(__i386__) || defined(__x86_64__) + LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) + LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) + #define __NR__getresgid32 __NR_getresgid32 + #define __NR__getresuid32 __NR_getresuid32 + #define __NR__setfsgid32 __NR_setfsgid32 + #define __NR__setfsuid32 __NR_setfsuid32 + #define __NR__setresgid32 __NR_setresgid32 + #define __NR__setresuid32 __NR_setresuid32 +#if defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, ugetrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall3(int, _getresgid32, gid_t *, r, + gid_t *, e, gid_t *, s) + LSS_INLINE _syscall3(int, _getresuid32, uid_t *, r, + uid_t *, e, uid_t *, s) + LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f) + LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f) + LSS_INLINE _syscall3(int, _setresgid32, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, _setresuid32, uid_t, r, + uid_t, e, uid_t, s) + + LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid, + gid_t *egid, + gid_t *sgid) { + int rc; + if ((rc = LSS_NAME(_getresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((rgid == NULL) || (egid == NULL) || (sgid == NULL)) { + return EFAULT; + } + // Clear the high bits first, since getresgid only sets 16 bits + *rgid = *egid = *sgid = 0; + rc = LSS_NAME(getresgid)(rgid, egid, sgid); + } + return rc; + } + + LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid, + uid_t *euid, + uid_t *suid) { + int rc; + if ((rc = LSS_NAME(_getresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((ruid == NULL) || (euid == NULL) || (suid == NULL)) { + return EFAULT; + } + // Clear the high bits first, since getresuid only sets 16 bits + *ruid = *euid = *suid = 0; + rc = LSS_NAME(getresuid)(ruid, euid, suid); + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + int rc; + if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)gid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsgid)(gid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + int rc; + if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)uid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsuid)(uid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + int rc; + if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)rgid & ~0xFFFFu || + (unsigned int)egid & ~0xFFFFu || + (unsigned int)sgid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresgid)(rgid, egid, sgid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + int rc; + if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)ruid & ~0xFFFFu || + (unsigned int)euid & ~0xFFFFu || + (unsigned int)suid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresuid)(ruid, euid, suid); + } + } + return rc; + } + #endif + LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) { + memset(&set->sig, 0, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) { + memset(&set->sig, -1, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0]))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0])))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] & + (1UL << ((signum - 1) % (8*sizeof(set->sig[0]))))); + } + } + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__) + #define __NR__sigaction __NR_sigaction + #define __NR__sigpending __NR_sigpending + #define __NR__sigprocmask __NR_sigprocmask + #define __NR__sigsuspend __NR_sigsuspend + #define __NR__socketcall __NR_socketcall +#if ! defined(__ANDROID__) + /* The Android NDK #defines stat64 stat, so avoid multiple-definition */ + LSS_INLINE _syscall2(int, fstat64, int, f, + struct kernel_stat64 *, b) +#endif + LSS_INLINE _syscall5(int, _llseek, uint, fd, + unsigned long, hi, unsigned long, lo, + loff_t *, res, uint, wh) +#if !defined(__ARM_EABI__) + LSS_INLINE _syscall1(void*, mmap, void*, a) +#endif + LSS_INLINE _syscall6(void*, mmap2, void*, s, + size_t, l, int, p, + int, f, int, d, + off_t, o) + LSS_INLINE _syscall3(int, _sigaction, int, s, + const struct kernel_old_sigaction*, a, + struct kernel_old_sigaction*, o) + LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s) + LSS_INLINE _syscall3(int, _sigprocmask, int, h, + const unsigned long*, s, + unsigned long*, o) + #ifdef __PPC__ + LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s) + #else + LSS_INLINE _syscall3(int, _sigsuspend, const void*, a, + int, b, + unsigned long, s) + #endif +#if ! defined(__ANDROID__) + /* The Android NDK #defines stat64 stat, so avoid multiple-definition */ + LSS_INLINE _syscall2(int, stat64, const char *, p, + struct kernel_stat64 *, b) +#endif + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + int old_errno = LSS_ERRNO; + int rc; + struct kernel_sigaction a; + if (act != NULL) { + a = *act; + #ifdef __i386__ + /* On i386, the kernel requires us to always set our own + * SA_RESTORER when using realtime signals. Otherwise, it does not + * know how to return from a signal handler. This function must have + * a "magic" signature that the "gdb" (and maybe the kernel?) can + * recognize. + * Apparently, a SA_RESTORER is implicitly set by the kernel, when + * using non-realtime signals. + * + * TODO: Test whether ARM needs a restorer + */ + if (!(a.sa_flags & SA_RESTORER)) { + a.sa_flags |= SA_RESTORER; + a.sa_restorer = (a.sa_flags & SA_SIGINFO) + ? LSS_NAME(restore_rt)() : LSS_NAME(restore)(); + } + #endif + } + rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact, + (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa; + if (!act) { + ptr_a = NULL; + } else { + oa.sa_handler_ = act->sa_handler_; + memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask)); + #ifndef __mips__ + oa.sa_restorer = act->sa_restorer; + #endif + oa.sa_flags = act->sa_flags; + } + if (!oldact) { + ptr_oa = NULL; + } + LSS_ERRNO = old_errno; + rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa); + if (rc == 0 && oldact) { + if (act) { + memcpy(oldact, act, sizeof(*act)); + } else { + memset(oldact, 0, sizeof(*oldact)); + } + oldact->sa_handler_ = ptr_oa->sa_handler_; + oldact->sa_flags = ptr_oa->sa_flags; + memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask)); + #ifndef __mips__ + oldact->sa_restorer = ptr_oa->sa_restorer; + #endif + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + int old_errno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = old_errno; + LSS_NAME(sigemptyset)(set); + rc = LSS_NAME(_sigpending)(&set->sig[0]); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + if (oldset) { + LSS_NAME(sigemptyset)(oldset); + } + rc = LSS_NAME(_sigprocmask)(how, + set ? &set->sig[0] : NULL, + oldset ? &oldset->sig[0] : NULL); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + rc = LSS_NAME(_sigsuspend)( + #ifndef __PPC__ + set, 0, + #endif + set->sig[0]); + } + return rc; + } + #endif + #if defined(__PPC__) + #undef LSS_SC_LOADARGS_0 + #define LSS_SC_LOADARGS_0(dummy...) + #undef LSS_SC_LOADARGS_1 + #define LSS_SC_LOADARGS_1(arg1) \ + __sc_4 = (unsigned long) (arg1) + #undef LSS_SC_LOADARGS_2 + #define LSS_SC_LOADARGS_2(arg1, arg2) \ + LSS_SC_LOADARGS_1(arg1); \ + __sc_5 = (unsigned long) (arg2) + #undef LSS_SC_LOADARGS_3 + #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \ + LSS_SC_LOADARGS_2(arg1, arg2); \ + __sc_6 = (unsigned long) (arg3) + #undef LSS_SC_LOADARGS_4 + #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \ + LSS_SC_LOADARGS_3(arg1, arg2, arg3); \ + __sc_7 = (unsigned long) (arg4) + #undef LSS_SC_LOADARGS_5 + #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \ + LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \ + __sc_8 = (unsigned long) (arg5) + #undef LSS_SC_BODY + #define LSS_SC_BODY(nr, type, opt, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \ + register unsigned long __sc_3 __asm__ ("r3") = opt; \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + LSS_SC_LOADARGS_##nr(args); \ + __asm__ __volatile__ \ + ("stwu 1, -48(1)\n\t" \ + "stw 4, 20(1)\n\t" \ + "stw 5, 24(1)\n\t" \ + "stw 6, 28(1)\n\t" \ + "stw 7, 32(1)\n\t" \ + "stw 8, 36(1)\n\t" \ + "addi 4, 1, 20\n\t" \ + "sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + LSS_SC_BODY(3, ssize_t, 17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + LSS_SC_BODY(3, ssize_t, 16, s, msg, flags); + } + + // TODO(csilvers): why is this ifdef'ed out? +#if 0 + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen); + } +#endif + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + LSS_SC_BODY(2, int, 13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + LSS_SC_BODY(3, int, 1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + LSS_SC_BODY(4, int, 8, d, type, protocol, sv); + } + #endif + #if defined(__ARM_EABI__) + LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg, + int, flags) + LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*, + msg, int, flags) + LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t,len, + int, flags, const struct kernel_sockaddr*, to, + unsigned int, tolen) + LSS_INLINE _syscall2(int, shutdown, int, s, int, how) + LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol) + LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol, + int*, sv) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, _socketcall, int, c, + va_list, a) + LSS_INLINE int LSS_NAME(socketcall)(int op, ...) { + int rc; + va_list ap; + va_start(ap, op); + rc = LSS_NAME(_socketcall)(op, ap); + va_end(ap); + return rc; + } + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen); + } + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + return LSS_NAME(socketcall)(13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + return LSS_NAME(socketcall)(1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + return LSS_NAME(socketcall)(8, d, type, protocol, sv); + } + #endif + #if defined(__i386__) || defined(__PPC__) + LSS_INLINE _syscall4(int, fstatat64, int, d, + const char *, p, + struct kernel_stat64 *, b, int, f) + #endif + #if defined(__i386__) || defined(__PPC__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p, + int*, s, int, o) + #endif + #if defined(__mips__) + /* sys_pipe() on MIPS has non-standard calling conventions, as it returns + * both file handles through CPU registers. + */ + LSS_INLINE int LSS_NAME(pipe)(int *p) { + register unsigned long __v0 __asm__("$2") = __NR_pipe; + register unsigned long __v1 __asm__("$3"); + register unsigned long __r7 __asm__("$7"); + __asm__ __volatile__ ("syscall\n" + : "=&r"(__v0), "=&r"(__v1), "+r" (__r7) + : "0"(__v0) + : "$8", "$9", "$10", "$11", "$12", + "$13", "$14", "$15", "$24", "memory"); + if (__r7) { + LSS_ERRNO = __v0; + return -1; + } else { + p[0] = __v0; + p[1] = __v1; + return 0; + } + } + #else + LSS_INLINE _syscall1(int, pipe, int *, p) + #endif + /* TODO(csilvers): see if ppc can/should support this as well */ + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) + #define __NR__statfs64 __NR_statfs64 + #define __NR__fstatfs64 __NR_fstatfs64 + LSS_INLINE _syscall3(int, _statfs64, const char*, p, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE _syscall3(int, _fstatfs64, int, f, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE int LSS_NAME(statfs64)(const char *p, + struct kernel_statfs64 *b) { + return LSS_NAME(_statfs64)(p, sizeof(*b), b); + } + LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) { + return LSS_NAME(_fstatfs64)(f, sizeof(*b), b); + } + #endif + + LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) { + extern char **environ; + return LSS_NAME(execve)(path, argv, (const char *const *)environ); + } + + LSS_INLINE pid_t LSS_NAME(gettid)() { + pid_t tid = LSS_NAME(_gettid)(); + if (tid != -1) { + return tid; + } + return LSS_NAME(getpid)(); + } + + LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size, + size_t new_size, int flags, ...) { + va_list ap; + void *new_address, *rc; + va_start(ap, flags); + new_address = va_arg(ap, void *); + rc = LSS_NAME(_mremap)(old_address, old_size, new_size, + flags, new_address); + va_end(ap); + return rc; + } + + LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) { + /* PTRACE_DETACH can sometimes forget to wake up the tracee and it + * then sends job control signals to the real parent, rather than to + * the tracer. We reduce the risk of this happening by starting a + * whole new time slice, and then quickly sending a SIGCONT signal + * right after detaching from the tracee. + * + * We use tkill to ensure that we only issue a wakeup for the thread being + * detached. Large multi threaded apps can take a long time in the kernel + * processing SIGCONT. + */ + int rc, err; + LSS_NAME(sched_yield)(); + rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0); + err = LSS_ERRNO; + LSS_NAME(tkill)(pid, SIGCONT); + /* Old systems don't have tkill */ + if (LSS_ERRNO == ENOSYS) + LSS_NAME(kill)(pid, SIGCONT); + LSS_ERRNO = err; + return rc; + } + + LSS_INLINE int LSS_NAME(raise)(int sig) { + return LSS_NAME(kill)(LSS_NAME(getpid)(), sig); + } + + LSS_INLINE int LSS_NAME(setpgrp)() { + return LSS_NAME(setpgid)(0, 0); + } + + LSS_INLINE int LSS_NAME(sysconf)(int name) { + extern int __getpagesize(void); + switch (name) { + case _SC_OPEN_MAX: { + struct kernel_rlimit limit; +#if defined(__ARM_EABI__) + return LSS_NAME(ugetrlimit)(RLIMIT_NOFILE, &limit) < 0 + ? 8192 : limit.rlim_cur; +#else + return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0 + ? 8192 : limit.rlim_cur; +#endif + } + case _SC_PAGESIZE: + return __getpagesize(); + default: + LSS_ERRNO = ENOSYS; + return -1; + } + } + #if defined(__x86_64__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64) + LSS_INLINE _syscall4(ssize_t, pread64, int, f, + void *, b, size_t, c, + loff_t, o) + LSS_INLINE _syscall4(ssize_t, pwrite64, int, f, + const void *, b, size_t, c, + loff_t, o) + LSS_INLINE _syscall3(int, readahead, int, f, + loff_t, o, unsigned, c) + #else + #define __NR__pread64 __NR_pread64 + #define __NR__pwrite64 __NR_pwrite64 + #define __NR__readahead __NR_readahead + LSS_INLINE _syscall5(ssize_t, _pread64, int, f, + void *, b, size_t, c, unsigned, o1, + unsigned, o2) + LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, unsigned, o1, + long, o2) + LSS_INLINE _syscall4(int, _readahead, int, f, + unsigned, o1, unsigned, o2, size_t, c) + /* We force 64bit-wide parameters onto the stack, then access each + * 32-bit component individually. This guarantees that we build the + * correct parameters independent of the native byte-order of the + * underlying architecture. + */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count, + loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pread64)(fd, buf, count, o.arg[0], o.arg[1]); + } + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf, + size_t count, loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pwrite64)(fd, buf, count, o.arg[0], o.arg[1]); + } + LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_readahead)(fd, o.arg[0], o.arg[1], len); + } + #endif +#endif + +#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS) +} +#endif + +#endif +#endif diff --git a/src/lib/dllglobal.h b/src/lib/dllglobal.h new file mode 100644 index 0000000..374c707 --- /dev/null +++ b/src/lib/dllglobal.h @@ -0,0 +1,12 @@ +#ifndef DLLGLOBAL_H +#define DLLGLOBAL_H + +#include + +#ifndef AS_DYNAMIC_LIB +# define AS_DYNAMIC_LIB Q_DECL_EXPORT +#else +# define AS_DYNAMIC_LIB Q_DECL_IMPORT +#endif + +#endif diff --git a/src/lib/hunspell/hunspell.pro b/src/lib/hunspell/hunspell.pro new file mode 100644 index 0000000..c5d1af6 --- /dev/null +++ b/src/lib/hunspell/hunspell.pro @@ -0,0 +1,56 @@ +TEMPLATE = lib + +CONFIG += warn_off plugin +unix:VERSION = 1.3.2 + +win32: { + RC_FILE = $$PWD/res/hunspell.rc +} +win32:DEFINES *= WIN32 +win32:INCLUDEPATH += . +win32:DEPENDPATH += . + +DEFINES -= HUNSPELL_WARNING_ON +DEFINES -= HUNSPELL_EXPERIMENTAL +win32-msvc*:DEFINES += BUILDING_LIBHUNSPELL + +CONFIG(debug, debug|release){ + TARGET = hunspell_d + DESTDIR = ../../debug +} else { + TARGET = hunspell + DESTDIR = ../../release +} + +HEADERS += src/affentry.hxx \ + src/affixmgr.hxx \ + src/atypes.hxx \ + src/baseaffix.hxx \ + src/csutil.hxx \ + src/dictmgr.hxx \ + src/filemgr.hxx \ + src/hashmgr.hxx \ + src/htypes.hxx \ + src/hunspell.hxx \ + src/hunzip.hxx \ + src/langnum.hxx \ + src/phonet.hxx \ + src/replist.hxx \ + src/suggestmgr.hxx \ + src/w_char.hxx \ + src/hunspell.h \ + src/hunvisapi.h \ + src/config.h + +SOURCES += src/affentry.cxx \ + src/affixmgr.cxx \ + src/csutil.cxx \ + src/dictmgr.cxx \ + src/filemgr.cxx \ + src/hashmgr.cxx \ + src/hunspell.cxx \ + src/hunzip.cxx \ + src/phonet.cxx \ + src/replist.cxx \ + src/suggestmgr.cxx \ + src/utf_info.cxx diff --git a/src/lib/hunspell/res/hunspell.rc b/src/lib/hunspell/res/hunspell.rc new file mode 100644 index 0000000..847017a --- /dev/null +++ b/src/lib/hunspell/res/hunspell.rc @@ -0,0 +1,38 @@ +# if defined(UNDER_CE) +# include +# else +# include +# endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,3,2,0 + PRODUCTVERSION 1,3,2,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L + BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "\0" + VALUE "FileVersion", "1.2.3.0\0" + VALUE "LegalCopyright", "\0" + VALUE "OriginalFilename", "hunspell.dll\0" + VALUE "ProductName", "hunspell\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END + END +/* End of Version info */ + diff --git a/src/lib/hunspell/src/README b/src/lib/hunspell/src/README new file mode 100644 index 0000000..ba84f60 --- /dev/null +++ b/src/lib/hunspell/src/README @@ -0,0 +1,23 @@ +Version 1.3.2 +------------- +Hunspell spell checker and morphological analyser library + +Documentation, tests, examples: http://hunspell.sourceforge.net + +Author of Hunspell: +László Németh (nemethl (at) gyorsposta.hu) + +Hunspell based on OpenOffice.org's Myspell. MySpell's author: +Kevin Hendricks (kevin.hendricks (at) sympatico.ca) + +License: GPL 2.0/LGPL 2.1/MPL 1.1 tri-license + +The contents of this library may be used under the terms of +the GNU General Public License Version 2 or later (the "GPL"), or +the GNU Lesser General Public License Version 2.1 or later (the "LGPL", +see http://gnu.org/copyleft/lesser.html) or the Mozilla Public License +Version 1.1 or later (the "MPL", see http://mozilla.org/MPL/MPL-1.1.html). + +Software distributed under these licenses is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the licences +for the specific language governing rights and limitations under the licenses. diff --git a/src/lib/hunspell/src/affentry.cxx b/src/lib/hunspell/src/affentry.cxx new file mode 100644 index 0000000..fef0cca --- /dev/null +++ b/src/lib/hunspell/src/affentry.cxx @@ -0,0 +1,962 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "affentry.hxx" +#include "csutil.hxx" + +PfxEntry::PfxEntry(AffixMgr* pmgr, affentry* dp) +{ + // register affix manager + pmyMgr = pmgr; + + // set up its initial values + + aflag = dp->aflag; // flag + strip = dp->strip; // string to strip + appnd = dp->appnd; // string to append + stripl = dp->stripl; // length of strip string + appndl = dp->appndl; // length of append string + numconds = dp->numconds; // length of the condition + opts = dp->opts; // cross product flag + // then copy over all of the conditions + if (opts & aeLONGCOND) { + memcpy(c.conds, dp->c.l.conds1, MAXCONDLEN_1); + c.l.conds2 = dp->c.l.conds2; + } else memcpy(c.conds, dp->c.conds, MAXCONDLEN); + next = NULL; + nextne = NULL; + nexteq = NULL; + morphcode = dp->morphcode; + contclass = dp->contclass; + contclasslen = dp->contclasslen; +} + + +PfxEntry::~PfxEntry() +{ + aflag = 0; + if (appnd) free(appnd); + if (strip) free(strip); + pmyMgr = NULL; + appnd = NULL; + strip = NULL; + if (opts & aeLONGCOND) free(c.l.conds2); + if (morphcode && !(opts & aeALIASM)) free(morphcode); + if (contclass && !(opts & aeALIASF)) free(contclass); +} + +// add prefix to this word assuming conditions hold +char * PfxEntry::add(const char * word, int len) +{ + char tword[MAXWORDUTF8LEN + 4]; + + if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) && + (len >= numconds) && test_condition(word) && + (!stripl || (strncmp(word, strip, stripl) == 0)) && + ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) { + /* we have a match so add prefix */ + char * pp = tword; + if (appndl) { + strcpy(tword,appnd); + pp += appndl; + } + strcpy(pp, (word + stripl)); + return mystrdup(tword); + } + return NULL; +} + +inline char * PfxEntry::nextchar(char * p) { + if (p) { + p++; + if (opts & aeLONGCOND) { + // jump to the 2nd part of the condition + if (p == c.conds + MAXCONDLEN_1) return c.l.conds2; + // end of the MAXCONDLEN length condition + } else if (p == c.conds + MAXCONDLEN) return NULL; + return *p ? p : NULL; + } + return NULL; +} + +inline int PfxEntry::test_condition(const char * st) +{ + const char * pos = NULL; // group with pos input position + bool neg = false; // complementer + bool ingroup = false; // character in the group + if (numconds == 0) return 1; + char * p = c.conds; + while (1) { + switch (*p) { + case '\0': return 1; + case '[': { + neg = false; + ingroup = false; + p = nextchar(p); + pos = st; break; + } + case '^': { p = nextchar(p); neg = true; break; } + case ']': { + if ((neg && ingroup) || (!neg && !ingroup)) return 0; + pos = NULL; + p = nextchar(p); + // skip the next character + if (!ingroup && *st) for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++); + if (*st == '\0' && p) return 0; // word <= condition + break; + } + case '.': if (!pos) { // dots are not metacharacters in groups: [.] + p = nextchar(p); + // skip the next character + for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++); + if (*st == '\0' && p) return 0; // word <= condition + break; + } + default: { + if (*st == *p) { + st++; + p = nextchar(p); + if ((opts & aeUTF8) && (*(st - 1) & 0x80)) { // multibyte + while (p && (*p & 0xc0) == 0x80) { // character + if (*p != *st) { + if (!pos) return 0; + st = pos; + break; + } + p = nextchar(p); + st++; + } + if (pos && st != pos) { + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); + } + } else if (pos) { + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); + } + } else if (pos) { // group + p = nextchar(p); + } else return 0; + } + } + if (!p) return 1; + } +} + +// check if this prefix entry matches +struct hentry * PfxEntry::checkword(const char * word, int len, char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry of root word or NULL + char tmpword[MAXWORDUTF8LEN + 4]; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if (tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + if ((he = pmyMgr->lookup(tmpword)) != NULL) { + do { + if (TESTAFF(he->astr, aflag, he->alen) && + // forbid single prefixes with needaffix flag + ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) && + // needflag + ((!needflag) || TESTAFF(he->astr, needflag, he->alen) || + (contclass && TESTAFF(contclass, needflag, contclasslen)))) + return he; + he = he->next_homonym; // check homonyms + } while (he); + } + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // ross checked combined with a suffix + + //if ((opts & aeXPRODUCT) && in_compound) { + if ((opts & aeXPRODUCT)) { + he = pmyMgr->suffix_check(tmpword, tmpl, aeXPRODUCT, this, NULL, + 0, NULL, FLAG_NULL, needflag, in_compound); + if (he) return he; + } + } + } + return NULL; +} + +// check if this prefix entry matches +struct hentry * PfxEntry::check_twosfx(const char * word, int len, + char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry of root word or NULL + char tmpword[MAXWORDUTF8LEN + 4]; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // cross checked combined with a suffix + + if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { + he = pmyMgr->suffix_check_twosfx(tmpword, tmpl, aeXPRODUCT, this, needflag); + if (he) return he; + } + } + } + return NULL; +} + +// check if this prefix entry matches +char * PfxEntry::check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + char tmpword[MAXWORDUTF8LEN + 4]; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // ross checked combined with a suffix + + if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { + return pmyMgr->suffix_check_twosfx_morph(tmpword, tmpl, + aeXPRODUCT, this, needflag); + } + } + } + return NULL; +} + +// check if this prefix entry matches +char * PfxEntry::check_morph(const char * word, int len, char in_compound, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry of root word or NULL + char tmpword[MAXWORDUTF8LEN + 4]; + char result[MAXLNLEN]; + char * st; + + *result = '\0'; + + // on entry prefix is 0 length or already matches the beginning of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing prefix and adding + // back any characters that would have been stripped + + if (stripl) strcpy (tmpword, strip); + strcpy ((tmpword + stripl), (word + appndl)); + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition(tmpword)) { + tmpl += stripl; + if ((he = pmyMgr->lookup(tmpword)) != NULL) { + do { + if (TESTAFF(he->astr, aflag, he->alen) && + // forbid single prefixes with needaffix flag + ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) && + // needflag + ((!needflag) || TESTAFF(he->astr, needflag, he->alen) || + (contclass && TESTAFF(contclass, needflag, contclasslen)))) { + if (morphcode) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, morphcode, MAXLNLEN); + } else mystrcat(result,getKey(), MAXLNLEN); + if (!HENTRY_FIND(he, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, HENTRY_WORD(he), MAXLNLEN); + } + // store the pointer of the hash entry + if (HENTRY_DATA(he)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(he), MAXLNLEN); + } else { + // return with debug information + char * flag = pmyMgr->encode_flag(getFlag()); + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_FLAG, MAXLNLEN); + mystrcat(result, flag, MAXLNLEN); + free(flag); + } + mystrcat(result, "\n", MAXLNLEN); + } + he = he->next_homonym; + } while (he); + } + + // prefix matched but no root word was found + // if aeXPRODUCT is allowed, try again but now + // ross checked combined with a suffix + + if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, aeXPRODUCT, this, + FLAG_NULL, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +SfxEntry::SfxEntry(AffixMgr * pmgr, affentry* dp) +{ + // register affix manager + pmyMgr = pmgr; + + // set up its initial values + aflag = dp->aflag; // char flag + strip = dp->strip; // string to strip + appnd = dp->appnd; // string to append + stripl = dp->stripl; // length of strip string + appndl = dp->appndl; // length of append string + numconds = dp->numconds; // length of the condition + opts = dp->opts; // cross product flag + + // then copy over all of the conditions + if (opts & aeLONGCOND) { + memcpy(c.l.conds1, dp->c.l.conds1, MAXCONDLEN_1); + c.l.conds2 = dp->c.l.conds2; + } else memcpy(c.conds, dp->c.conds, MAXCONDLEN); + + rappnd = myrevstrdup(appnd); + morphcode = dp->morphcode; + contclass = dp->contclass; + contclasslen = dp->contclasslen; +} + + +SfxEntry::~SfxEntry() +{ + aflag = 0; + if (appnd) free(appnd); + if (rappnd) free(rappnd); + if (strip) free(strip); + pmyMgr = NULL; + appnd = NULL; + strip = NULL; + if (opts & aeLONGCOND) free(c.l.conds2); + if (morphcode && !(opts & aeALIASM)) free(morphcode); + if (contclass && !(opts & aeALIASF)) free(contclass); +} + +// add suffix to this word assuming conditions hold +char * SfxEntry::add(const char * word, int len) +{ + char tword[MAXWORDUTF8LEN + 4]; + + /* make sure all conditions match */ + if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) && + (len >= numconds) && test_condition(word + len, word) && + (!stripl || (strcmp(word + len - stripl, strip) == 0)) && + ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) { + /* we have a match so add suffix */ + strcpy(tword,word); + if (appndl) { + strcpy(tword + len - stripl, appnd); + } else { + *(tword + len - stripl) = '\0'; + } + return mystrdup(tword); + } + return NULL; +} + +inline char * SfxEntry::nextchar(char * p) { + if (p) { + p++; + if (opts & aeLONGCOND) { + // jump to the 2nd part of the condition + if (p == c.l.conds1 + MAXCONDLEN_1) return c.l.conds2; + // end of the MAXCONDLEN length condition + } else if (p == c.conds + MAXCONDLEN) return NULL; + return *p ? p : NULL; + } + return NULL; +} + +inline int SfxEntry::test_condition(const char * st, const char * beg) +{ + const char * pos = NULL; // group with pos input position + bool neg = false; // complementer + bool ingroup = false; // character in the group + if (numconds == 0) return 1; + char * p = c.conds; + st--; + int i = 1; + while (1) { + switch (*p) { + case '\0': return 1; + case '[': { p = nextchar(p); pos = st; break; } + case '^': { p = nextchar(p); neg = true; break; } + case ']': { if (!neg && !ingroup) return 0; + i++; + // skip the next character + if (!ingroup) { + for (; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--); + st--; + } + pos = NULL; + neg = false; + ingroup = false; + p = nextchar(p); + if (st < beg && p) return 0; // word <= condition + break; + } + case '.': if (!pos) { // dots are not metacharacters in groups: [.] + p = nextchar(p); + // skip the next character + for (st--; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--); + if (st < beg) { // word <= condition + if (p) return 0; else return 1; + } + if ((opts & aeUTF8) && (*st & 0x80)) { // head of the UTF-8 character + st--; + if (st < beg) { // word <= condition + if (p) return 0; else return 1; + } + } + break; + } + default: { + if (*st == *p) { + p = nextchar(p); + if ((opts & aeUTF8) && (*st & 0x80)) { + st--; + while (p && (st >= beg)) { + if (*p != *st) { + if (!pos) return 0; + st = pos; + break; + } + // first byte of the UTF-8 multibyte character + if ((*p & 0xc0) != 0x80) break; + p = nextchar(p); + st--; + } + if (pos && st != pos) { + if (neg) return 0; + else if (i == numconds) return 1; + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); + st--; + } + if (p && *p != ']') p = nextchar(p); + } else if (pos) { + if (neg) return 0; + else if (i == numconds) return 1; + ingroup = true; + while (p && *p != ']' && (p = nextchar(p))); +// if (p && *p != ']') p = nextchar(p); + st--; + } + if (!pos) { + i++; + st--; + } + if (st < beg && p && *p != ']') return 0; // word <= condition + } else if (pos) { // group + p = nextchar(p); + } else return 0; + } + } + if (!p) return 1; + } +} + +// see if this suffix is present in the word +struct hentry * SfxEntry::checkword(const char * word, int len, int optflags, + PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, const FLAG cclass, const FLAG needflag, + const FLAG badflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry pointer + unsigned char * cp; + char tmpword[MAXWORDUTF8LEN + 4]; + PfxEntry* ep = ppfx; + + // if this suffix is being cross checked with a prefix + // but it does not support cross products skip it + + if (((optflags & aeXPRODUCT) != 0) && ((opts & aeXPRODUCT) == 0)) + return NULL; + + // upon entry suffix is 0 length or already matches the end of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + // the second condition is not enough for UTF-8 strings + // it checked in test_condition() + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing suffix and adding + // back any characters that would have been stripped or + // or null terminating the shorter string + + strcpy (tmpword, word); + cp = (unsigned char *)(tmpword + tmpl); + if (stripl) { + strcpy ((char *)cp, strip); + tmpl += stripl; + cp = (unsigned char *)(tmpword + tmpl); + } else *cp = '\0'; + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then check if resulting + // root word in the dictionary + + if (test_condition((char *) cp, (char *) tmpword)) { + +#ifdef SZOSZABLYA_POSSIBLE_ROOTS + fprintf(stdout,"%s %s %c\n", word, tmpword, aflag); +#endif + if ((he = pmyMgr->lookup(tmpword)) != NULL) { + do { + // check conditional suffix (enabled by prefix) + if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && + TESTAFF(ep->getCont(), aflag, ep->getContLen()))) && + (((optflags & aeXPRODUCT) == 0) || + (ep && TESTAFF(he->astr, ep->getFlag(), he->alen)) || + // enabled by prefix + ((contclass) && (ep && TESTAFF(contclass, ep->getFlag(), contclasslen))) + ) && + // handle cont. class + ((!cclass) || + ((contclass) && TESTAFF(contclass, cclass, contclasslen)) + ) && + // check only in compound homonyms (bad flags) + (!badflag || !TESTAFF(he->astr, badflag, he->alen) + ) && + // handle required flag + ((!needflag) || + (TESTAFF(he->astr, needflag, he->alen) || + ((contclass) && TESTAFF(contclass, needflag, contclasslen))) + ) + ) return he; + he = he->next_homonym; // check homonyms + } while (he); + + // obsolote stemming code (used only by the + // experimental SuffixMgr:suggest_pos_stems) + // store resulting root in wlst + } else if (wlst && (*ns < maxSug)) { + int cwrd = 1; + for (int k=0; k < *ns; k++) + if (strcmp(tmpword, wlst[k]) == 0) cwrd = 0; + if (cwrd) { + wlst[*ns] = mystrdup(tmpword); + if (wlst[*ns] == NULL) { + for (int j=0; j<*ns; j++) free(wlst[j]); + *ns = -1; + return NULL; + } + (*ns)++; + } + } + } + } + return NULL; +} + +// see if two-level suffix is present in the word +struct hentry * SfxEntry::check_twosfx(const char * word, int len, int optflags, + PfxEntry* ppfx, const FLAG needflag) +{ + int tmpl; // length of tmpword + struct hentry * he; // hash entry pointer + unsigned char * cp; + char tmpword[MAXWORDUTF8LEN + 4]; + PfxEntry* ep = ppfx; + + + // if this suffix is being cross checked with a prefix + // but it does not support cross products skip it + + if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0) + return NULL; + + // upon entry suffix is 0 length or already matches the end of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing suffix and adding + // back any characters that would have been stripped or + // or null terminating the shorter string + + strcpy (tmpword, word); + cp = (unsigned char *)(tmpword + tmpl); + if (stripl) { + strcpy ((char *)cp, strip); + tmpl += stripl; + cp = (unsigned char *)(tmpword + tmpl); + } else *cp = '\0'; + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then recall suffix_check + + if (test_condition((char *) cp, (char *) tmpword)) { + if (ppfx) { + // handle conditional suffix + if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) + he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag); + else + he = pmyMgr->suffix_check(tmpword, tmpl, optflags, ppfx, NULL, 0, NULL, (FLAG) aflag, needflag); + } else { + he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag); + } + if (he) return he; + } + } + return NULL; +} + +// see if two-level suffix is present in the word +char * SfxEntry::check_twosfx_morph(const char * word, int len, int optflags, + PfxEntry* ppfx, const FLAG needflag) +{ + int tmpl; // length of tmpword + unsigned char * cp; + char tmpword[MAXWORDUTF8LEN + 4]; + PfxEntry* ep = ppfx; + char * st; + + char result[MAXLNLEN]; + + *result = '\0'; + + // if this suffix is being cross checked with a prefix + // but it does not support cross products skip it + + if ((optflags & aeXPRODUCT) != 0 && (opts & aeXPRODUCT) == 0) + return NULL; + + // upon entry suffix is 0 length or already matches the end of the word. + // So if the remaining root word has positive length + // and if there are enough chars in root word and added back strip chars + // to meet the number of characters conditions, then test it + + tmpl = len - appndl; + + if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) && + (tmpl + stripl >= numconds)) { + + // generate new root word by removing suffix and adding + // back any characters that would have been stripped or + // or null terminating the shorter string + + strcpy (tmpword, word); + cp = (unsigned char *)(tmpword + tmpl); + if (stripl) { + strcpy ((char *)cp, strip); + tmpl += stripl; + cp = (unsigned char *)(tmpword + tmpl); + } else *cp = '\0'; + + // now make sure all of the conditions on characters + // are met. Please see the appendix at the end of + // this file for more info on exactly what is being + // tested + + // if all conditions are met then recall suffix_check + + if (test_condition((char *) cp, (char *) tmpword)) { + if (ppfx) { + // handle conditional suffix + if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag); + if (st) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } + mystrcat(result,st, MAXLNLEN); + free(st); + mychomp(result); + } + } else { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, optflags, ppfx, aflag, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + mychomp(result); + } + } + } else { + st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + mychomp(result); + } + } + if (*result) return mystrdup(result); + } + } + return NULL; +} + +// get next homonym with same affix +struct hentry * SfxEntry::get_next_homonym(struct hentry * he, int optflags, PfxEntry* ppfx, + const FLAG cclass, const FLAG needflag) +{ + PfxEntry* ep = ppfx; + FLAG eFlag = ep ? ep->getFlag() : FLAG_NULL; + + while (he->next_homonym) { + he = he->next_homonym; + if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && TESTAFF(ep->getCont(), aflag, ep->getContLen()))) && + ((optflags & aeXPRODUCT) == 0 || + TESTAFF(he->astr, eFlag, he->alen) || + // handle conditional suffix + ((contclass) && TESTAFF(contclass, eFlag, contclasslen)) + ) && + // handle cont. class + ((!cclass) || + ((contclass) && TESTAFF(contclass, cclass, contclasslen)) + ) && + // handle required flag + ((!needflag) || + (TESTAFF(he->astr, needflag, he->alen) || + ((contclass) && TESTAFF(contclass, needflag, contclasslen))) + ) + ) return he; + } + return NULL; +} + + +#if 0 + +Appendix: Understanding Affix Code + + +An affix is either a prefix or a suffix attached to root words to make +other words. + +Basically a Prefix or a Suffix is set of AffEntry objects +which store information about the prefix or suffix along +with supporting routines to check if a word has a particular +prefix or suffix or a combination. + +The structure affentry is defined as follows: + +struct affentry +{ + unsigned short aflag; // ID used to represent the affix + char * strip; // string to strip before adding affix + char * appnd; // the affix string to add + unsigned char stripl; // length of the strip string + unsigned char appndl; // length of the affix string + char numconds; // the number of conditions that must be met + char opts; // flag: aeXPRODUCT- combine both prefix and suffix + char conds[SETSIZE]; // array which encodes the conditions to be met +}; + + +Here is a suffix borrowed from the en_US.aff file. This file +is whitespace delimited. + +SFX D Y 4 +SFX D 0 e d +SFX D y ied [^aeiou]y +SFX D 0 ed [^ey] +SFX D 0 ed [aeiou]y + +This information can be interpreted as follows: + +In the first line has 4 fields + +Field +----- +1 SFX - indicates this is a suffix +2 D - is the name of the character flag which represents this suffix +3 Y - indicates it can be combined with prefixes (cross product) +4 4 - indicates that sequence of 4 affentry structures are needed to + properly store the affix information + +The remaining lines describe the unique information for the 4 SfxEntry +objects that make up this affix. Each line can be interpreted +as follows: (note fields 1 and 2 are as a check against line 1 info) + +Field +----- +1 SFX - indicates this is a suffix +2 D - is the name of the character flag for this affix +3 y - the string of chars to strip off before adding affix + (a 0 here indicates the NULL string) +4 ied - the string of affix characters to add +5 [^aeiou]y - the conditions which must be met before the affix + can be applied + +Field 5 is interesting. Since this is a suffix, field 5 tells us that +there are 2 conditions that must be met. The first condition is that +the next to the last character in the word must *NOT* be any of the +following "a", "e", "i", "o" or "u". The second condition is that +the last character of the word must end in "y". + +So how can we encode this information concisely and be able to +test for both conditions in a fast manner? The answer is found +but studying the wonderful ispell code of Geoff Kuenning, et.al. +(now available under a normal BSD license). + +If we set up a conds array of 256 bytes indexed (0 to 255) and access it +using a character (cast to an unsigned char) of a string, we have 8 bits +of information we can store about that character. Specifically we +could use each bit to say if that character is allowed in any of the +last (or first for prefixes) 8 characters of the word. + +Basically, each character at one end of the word (up to the number +of conditions) is used to index into the conds array and the resulting +value found there says whether the that character is valid for a +specific character position in the word. + +For prefixes, it does this by setting bit 0 if that char is valid +in the first position, bit 1 if valid in the second position, and so on. + +If a bit is not set, then that char is not valid for that postion in the +word. + +If working with suffixes bit 0 is used for the character closest +to the front, bit 1 for the next character towards the end, ..., +with bit numconds-1 representing the last char at the end of the string. + +Note: since entries in the conds[] are 8 bits, only 8 conditions +(read that only 8 character positions) can be examined at one +end of a word (the beginning for prefixes and the end for suffixes. + +So to make this clearer, lets encode the conds array values for the +first two affentries for the suffix D described earlier. + + + For the first affentry: + numconds = 1 (only examine the last character) + + conds['e'] = (1 << 0) (the word must end in an E) + all others are all 0 + + For the second affentry: + numconds = 2 (only examine the last two characters) + + conds[X] = conds[X] | (1 << 0) (aeiou are not allowed) + where X is all characters *but* a, e, i, o, or u + + + conds['y'] = (1 << 1) (the last char must be a y) + all other bits for all other entries in the conds array are zero + + +#endif + diff --git a/src/lib/hunspell/src/affentry.hxx b/src/lib/hunspell/src/affentry.hxx new file mode 100644 index 0000000..eaf361f --- /dev/null +++ b/src/lib/hunspell/src/affentry.hxx @@ -0,0 +1,136 @@ +#ifndef _AFFIX_HXX_ +#define _AFFIX_HXX_ + +#include "hunvisapi.h" + +#include "atypes.hxx" +#include "baseaffix.hxx" +#include "affixmgr.hxx" + +/* A Prefix Entry */ + +class LIBHUNSPELL_DLL_EXPORTED PfxEntry : protected AffEntry +{ + AffixMgr* pmyMgr; + + PfxEntry * next; + PfxEntry * nexteq; + PfxEntry * nextne; + PfxEntry * flgnxt; + +public: + + PfxEntry(AffixMgr* pmgr, affentry* dp ); + ~PfxEntry(); + + inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); } + struct hentry * checkword(const char * word, int len, char in_compound, + const FLAG needflag = FLAG_NULL); + + struct hentry * check_twosfx(const char * word, int len, char in_compound, const FLAG needflag = NULL); + + char * check_morph(const char * word, int len, char in_compound, + const FLAG needflag = FLAG_NULL); + + char * check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + + inline FLAG getFlag() { return aflag; } + inline const char * getKey() { return appnd; } + char * add(const char * word, int len); + + inline short getKeyLen() { return appndl; } + + inline const char * getMorph() { return morphcode; } + + inline const unsigned short * getCont() { return contclass; } + inline short getContLen() { return contclasslen; } + + inline PfxEntry * getNext() { return next; } + inline PfxEntry * getNextNE() { return nextne; } + inline PfxEntry * getNextEQ() { return nexteq; } + inline PfxEntry * getFlgNxt() { return flgnxt; } + + inline void setNext(PfxEntry * ptr) { next = ptr; } + inline void setNextNE(PfxEntry * ptr) { nextne = ptr; } + inline void setNextEQ(PfxEntry * ptr) { nexteq = ptr; } + inline void setFlgNxt(PfxEntry * ptr) { flgnxt = ptr; } + + inline char * nextchar(char * p); + inline int test_condition(const char * st); +}; + + + + +/* A Suffix Entry */ + +class LIBHUNSPELL_DLL_EXPORTED SfxEntry : protected AffEntry +{ + AffixMgr* pmyMgr; + char * rappnd; + + SfxEntry * next; + SfxEntry * nexteq; + SfxEntry * nextne; + SfxEntry * flgnxt; + + SfxEntry * l_morph; + SfxEntry * r_morph; + SfxEntry * eq_morph; + +public: + + SfxEntry(AffixMgr* pmgr, affentry* dp ); + ~SfxEntry(); + + inline bool allowCross() { return ((opts & aeXPRODUCT) != 0); } + struct hentry * checkword(const char * word, int len, int optflags, + PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, +// const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, char in_compound=IN_CPD_NOT); + const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, const FLAG badflag = 0); + + struct hentry * check_twosfx(const char * word, int len, int optflags, PfxEntry* ppfx, const FLAG needflag = NULL); + + char * check_twosfx_morph(const char * word, int len, int optflags, + PfxEntry* ppfx, const FLAG needflag = FLAG_NULL); + struct hentry * get_next_homonym(struct hentry * he); + struct hentry * get_next_homonym(struct hentry * word, int optflags, PfxEntry* ppfx, + const FLAG cclass, const FLAG needflag); + + + inline FLAG getFlag() { return aflag; } + inline const char * getKey() { return rappnd; } + char * add(const char * word, int len); + + + inline const char * getMorph() { return morphcode; } + + inline const unsigned short * getCont() { return contclass; } + inline short getContLen() { return contclasslen; } + inline const char * getAffix() { return appnd; } + + inline short getKeyLen() { return appndl; } + + inline SfxEntry * getNext() { return next; } + inline SfxEntry * getNextNE() { return nextne; } + inline SfxEntry * getNextEQ() { return nexteq; } + + inline SfxEntry * getLM() { return l_morph; } + inline SfxEntry * getRM() { return r_morph; } + inline SfxEntry * getEQM() { return eq_morph; } + inline SfxEntry * getFlgNxt() { return flgnxt; } + + inline void setNext(SfxEntry * ptr) { next = ptr; } + inline void setNextNE(SfxEntry * ptr) { nextne = ptr; } + inline void setNextEQ(SfxEntry * ptr) { nexteq = ptr; } + inline void setFlgNxt(SfxEntry * ptr) { flgnxt = ptr; } + + inline char * nextchar(char * p); + inline int test_condition(const char * st, const char * begin); + +}; + +#endif + + diff --git a/src/lib/hunspell/src/affixmgr.cxx b/src/lib/hunspell/src/affixmgr.cxx new file mode 100644 index 0000000..b9108d4 --- /dev/null +++ b/src/lib/hunspell/src/affixmgr.cxx @@ -0,0 +1,4521 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include + +#include "affixmgr.hxx" +#include "affentry.hxx" +#include "langnum.hxx" + +#include "csutil.hxx" + +AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key) +{ + // register hash manager and load affix data from aff file + pHMgr = ptr[0]; + alldic = ptr; + maxdic = md; + keystring = NULL; + trystring = NULL; + encoding=NULL; + csconv=NULL; + utf8 = 0; + complexprefixes = 0; + maptable = NULL; + nummap = 0; + breaktable = NULL; + numbreak = -1; + reptable = NULL; + numrep = 0; + iconvtable = NULL; + oconvtable = NULL; + checkcpdtable = NULL; + // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN) + simplifiedcpd = 0; + numcheckcpd = 0; + defcpdtable = NULL; + numdefcpd = 0; + phone = NULL; + compoundflag = FLAG_NULL; // permits word in compound forms + compoundbegin = FLAG_NULL; // may be first word in compound forms + compoundmiddle = FLAG_NULL; // may be middle word in compound forms + compoundend = FLAG_NULL; // may be last word in compound forms + compoundroot = FLAG_NULL; // compound word signing flag + compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word + compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word + checkcompounddup = 0; // forbid double words in compounds + checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution) + checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds + checkcompoundtriple = 0; // forbid compounds with triple letters + simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt) + forbiddenword = FORBIDDENWORD; // forbidden word signing flag + nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag + nongramsuggest = FLAG_NULL; + lang = NULL; // language + langnum = 0; // language code (see http://l10n.openoffice.org/languages.html) + needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes + cpdwordmax = -1; // default: unlimited wordcount in compound words + cpdmin = -1; // undefined + cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words + cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX) + cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search) + cpdvowels_utf16_len=0; // vowels + pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG + sfxappnd=NULL; // previous suffix for counting a special syllables BUG + cpdsyllablenum=NULL; // syllable count incrementing flag + checknum=0; // checking numbers, and word with numbers + wordchars=NULL; // letters + spec. word characters + wordchars_utf16=NULL; // letters + spec. word characters + wordchars_utf16_len=0; // letters + spec. word characters + ignorechars=NULL; // letters + spec. word characters + ignorechars_utf16=NULL; // letters + spec. word characters + ignorechars_utf16_len=0; // letters + spec. word characters + version=NULL; // affix and dictionary file version string + havecontclass=0; // flags of possible continuing classes (double affix) + // LEMMA_PRESENT: not put root into the morphological output. Lemma presents + // in morhological description in dictionary file. It's often combined with PSEUDOROOT. + lemma_present = FLAG_NULL; + circumfix = FLAG_NULL; + onlyincompound = FLAG_NULL; + maxngramsugs = -1; // undefined + maxdiff = -1; // undefined + onlymaxdiff = 0; + maxcpdsugs = -1; // undefined + nosplitsugs = 0; + sugswithdots = 0; + keepcase = 0; + forceucase = 0; + warn = 0; + forbidwarn = 0; + checksharps = 0; + substandard = FLAG_NULL; + fullstrip = 0; + + sfx = NULL; + pfx = NULL; + + for (int i=0; i < SETSIZE; i++) { + pStart[i] = NULL; + sStart[i] = NULL; + pFlag[i] = NULL; + sFlag[i] = NULL; + } + + for (int j=0; j < CONTSIZE; j++) { + contclasses[j] = 0; + } + + if (parse_file(affpath, key)) { + HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath); + } + + if (cpdmin == -1) cpdmin = MINCPDLEN; + +} + + +AffixMgr::~AffixMgr() +{ + // pass through linked prefix entries and clean up + for (int i=0; i < SETSIZE ;i++) { + pFlag[i] = NULL; + PfxEntry * ptr = pStart[i]; + PfxEntry * nptr = NULL; + while (ptr) { + nptr = ptr->getNext(); + delete(ptr); + ptr = nptr; + nptr = NULL; + } + } + + // pass through linked suffix entries and clean up + for (int j=0; j < SETSIZE ; j++) { + sFlag[j] = NULL; + SfxEntry * ptr = sStart[j]; + SfxEntry * nptr = NULL; + while (ptr) { + nptr = ptr->getNext(); + delete(ptr); + ptr = nptr; + nptr = NULL; + } + sStart[j] = NULL; + } + + if (keystring) free(keystring); + keystring=NULL; + if (trystring) free(trystring); + trystring=NULL; + if (encoding) free(encoding); + encoding=NULL; + if (maptable) { + for (int j=0; j < nummap; j++) { + for (int k=0; k < maptable[j].len; k++) { + if (maptable[j].set[k]) free(maptable[j].set[k]); + } + free(maptable[j].set); + maptable[j].set = NULL; + maptable[j].len = 0; + } + free(maptable); + maptable = NULL; + } + nummap = 0; + if (breaktable) { + for (int j=0; j < numbreak; j++) { + if (breaktable[j]) free(breaktable[j]); + breaktable[j] = NULL; + } + free(breaktable); + breaktable = NULL; + } + numbreak = 0; + if (reptable) { + for (int j=0; j < numrep; j++) { + free(reptable[j].pattern); + free(reptable[j].pattern2); + } + free(reptable); + reptable = NULL; + } + if (iconvtable) delete iconvtable; + if (oconvtable) delete oconvtable; + if (phone && phone->rules) { + for (int j=0; j < phone->num + 1; j++) { + free(phone->rules[j * 2]); + free(phone->rules[j * 2 + 1]); + } + free(phone->rules); + free(phone); + phone = NULL; + } + + if (defcpdtable) { + for (int j=0; j < numdefcpd; j++) { + free(defcpdtable[j].def); + defcpdtable[j].def = NULL; + } + free(defcpdtable); + defcpdtable = NULL; + } + numrep = 0; + if (checkcpdtable) { + for (int j=0; j < numcheckcpd; j++) { + free(checkcpdtable[j].pattern); + free(checkcpdtable[j].pattern2); + free(checkcpdtable[j].pattern3); + checkcpdtable[j].pattern = NULL; + checkcpdtable[j].pattern2 = NULL; + checkcpdtable[j].pattern3 = NULL; + } + free(checkcpdtable); + checkcpdtable = NULL; + } + numcheckcpd = 0; + FREE_FLAG(compoundflag); + FREE_FLAG(compoundbegin); + FREE_FLAG(compoundmiddle); + FREE_FLAG(compoundend); + FREE_FLAG(compoundpermitflag); + FREE_FLAG(compoundforbidflag); + FREE_FLAG(compoundroot); + FREE_FLAG(forbiddenword); + FREE_FLAG(nosuggest); + FREE_FLAG(nongramsuggest); + FREE_FLAG(needaffix); + FREE_FLAG(lemma_present); + FREE_FLAG(circumfix); + FREE_FLAG(onlyincompound); + + cpdwordmax = 0; + pHMgr = NULL; + cpdmin = 0; + cpdmaxsyllable = 0; + if (cpdvowels) free(cpdvowels); + if (cpdvowels_utf16) free(cpdvowels_utf16); + if (cpdsyllablenum) free(cpdsyllablenum); + free_utf_tbl(); + if (lang) free(lang); + if (wordchars) free(wordchars); + if (wordchars_utf16) free(wordchars_utf16); + if (ignorechars) free(ignorechars); + if (ignorechars_utf16) free(ignorechars_utf16); + if (version) free(version); + checknum=0; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif +} + + +// read in aff file and build up prefix and suffix entry objects +int AffixMgr::parse_file(const char * affpath, const char * key) +{ + char * line; // io buffers + char ft; // affix type + + // checking flag duplication + char dupflags[CONTSIZE]; + char dupflags_ini = 1; + + // first line indicator for removing byte order mark + int firstline = 1; + + // open the affix file + FileMgr * afflst = new FileMgr(affpath, key); + if (!afflst) { + HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath); + return 1; + } + + // step one is to parse the affix file building up the internal + // affix data structures + + // read in each line ignoring any that do not + // start with a known line type indicator + while ((line = afflst->getline())) { + mychomp(line); + + /* remove byte order mark */ + if (firstline) { + firstline = 0; + // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions + if (strncmp(line,"\xEF\xBB\xBF",3) == 0) { + memmove(line, line+3, strlen(line+3)+1); + } + } + + /* parse in the keyboard string */ + if (strncmp(line,"KEY",3) == 0) { + if (parse_string(line, &keystring, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the try string */ + if (strncmp(line,"TRY",3) == 0) { + if (parse_string(line, &trystring, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the name of the character set used by the .dict and .aff */ + if (strncmp(line,"SET",3) == 0) { + if (parse_string(line, &encoding, afflst->getlinenum())) { + delete afflst; + return 1; + } + if (strcmp(encoding, "UTF-8") == 0) { + utf8 = 1; +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT + if (initialize_utf_tbl()) return 1; +#endif +#endif + } + } + + /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */ + if (strncmp(line,"COMPLEXPREFIXES",15) == 0) + complexprefixes = 1; + + /* parse in the flag used by the controlled compound words */ + if (strncmp(line,"COMPOUNDFLAG",12) == 0) { + if (parse_flag(line, &compoundflag, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound words */ + if (strncmp(line,"COMPOUNDBEGIN",13) == 0) { + if (complexprefixes) { + if (parse_flag(line, &compoundend, afflst)) { + delete afflst; + return 1; + } + } else { + if (parse_flag(line, &compoundbegin, afflst)) { + delete afflst; + return 1; + } + } + } + + /* parse in the flag used by compound words */ + if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) { + if (parse_flag(line, &compoundmiddle, afflst)) { + delete afflst; + return 1; + } + } + /* parse in the flag used by compound words */ + if (strncmp(line,"COMPOUNDEND",11) == 0) { + if (complexprefixes) { + if (parse_flag(line, &compoundbegin, afflst)) { + delete afflst; + return 1; + } + } else { + if (parse_flag(line, &compoundend, afflst)) { + delete afflst; + return 1; + } + } + } + + /* parse in the data used by compound_check() method */ + if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) { + if (parse_num(line, &cpdwordmax, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag sign compounds in dictionary */ + if (strncmp(line,"COMPOUNDROOT",12) == 0) { + if (parse_flag(line, &compoundroot, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound_check() method */ + if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) { + if (parse_flag(line, &compoundpermitflag, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound_check() method */ + if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) { + if (parse_flag(line, &compoundforbidflag, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) { + checkcompounddup = 1; + } + + if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) { + checkcompoundrep = 1; + } + + if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) { + checkcompoundtriple = 1; + } + + if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) { + simplifiedtriple = 1; + } + + if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) { + checkcompoundcase = 1; + } + + if (strncmp(line,"NOSUGGEST",9) == 0) { + if (parse_flag(line, &nosuggest, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"NONGRAMSUGGEST",14) == 0) { + if (parse_flag(line, &nongramsuggest, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by forbidden words */ + if (strncmp(line,"FORBIDDENWORD",13) == 0) { + if (parse_flag(line, &forbiddenword, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by forbidden words */ + if (strncmp(line,"LEMMA_PRESENT",13) == 0) { + if (parse_flag(line, &lemma_present, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by circumfixes */ + if (strncmp(line,"CIRCUMFIX",9) == 0) { + if (parse_flag(line, &circumfix, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by fogemorphemes */ + if (strncmp(line,"ONLYINCOMPOUND",14) == 0) { + if (parse_flag(line, &onlyincompound, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `needaffixs' */ + if (strncmp(line,"PSEUDOROOT",10) == 0) { + if (parse_flag(line, &needaffix, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `needaffixs' */ + if (strncmp(line,"NEEDAFFIX",9) == 0) { + if (parse_flag(line, &needaffix, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the minimal length for words in compounds */ + if (strncmp(line,"COMPOUNDMIN",11) == 0) { + if (parse_num(line, &cpdmin, afflst)) { + delete afflst; + return 1; + } + if (cpdmin < 1) cpdmin = 1; + } + + /* parse in the max. words and syllables in compounds */ + if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) { + if (parse_cpdsyllable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by compound_check() method */ + if (strncmp(line,"SYLLABLENUM",11) == 0) { + if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by the controlled compound words */ + if (strncmp(line,"CHECKNUM",8) == 0) { + checknum=1; + } + + /* parse in the extra word characters */ + if (strncmp(line,"WORDCHARS",9) == 0) { + if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the ignored characters (for example, Arabic optional diacretics charachters */ + if (strncmp(line,"IGNORE",6) == 0) { + if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + /* parse in the typical fault correcting table */ + if (strncmp(line,"REP",3) == 0) { + if (parse_reptable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the input conversion table */ + if (strncmp(line,"ICONV",5) == 0) { + if (parse_convtable(line, afflst, &iconvtable, "ICONV")) { + delete afflst; + return 1; + } + } + + /* parse in the input conversion table */ + if (strncmp(line,"OCONV",5) == 0) { + if (parse_convtable(line, afflst, &oconvtable, "OCONV")) { + delete afflst; + return 1; + } + } + + /* parse in the phonetic translation table */ + if (strncmp(line,"PHONE",5) == 0) { + if (parse_phonetable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the checkcompoundpattern table */ + if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) { + if (parse_checkcpdtable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the defcompound table */ + if (strncmp(line,"COMPOUNDRULE",12) == 0) { + if (parse_defcpdtable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the related character map table */ + if (strncmp(line,"MAP",3) == 0) { + if (parse_maptable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the word breakpoints table */ + if (strncmp(line,"BREAK",5) == 0) { + if (parse_breaktable(line, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the language for language specific codes */ + if (strncmp(line,"LANG",4) == 0) { + if (parse_string(line, &lang, afflst->getlinenum())) { + delete afflst; + return 1; + } + langnum = get_lang_num(lang); + } + + if (strncmp(line,"VERSION",7) == 0) { + for(line = line + 7; *line == ' ' || *line == '\t'; line++); + version = mystrdup(line); + } + + if (strncmp(line,"MAXNGRAMSUGS",12) == 0) { + if (parse_num(line, &maxngramsugs, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"ONLYMAXDIFF", 11) == 0) + onlymaxdiff = 1; + + if (strncmp(line,"MAXDIFF",7) == 0) { + if (parse_num(line, &maxdiff, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"MAXCPDSUGS",10) == 0) { + if (parse_num(line, &maxcpdsugs, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"NOSPLITSUGS",11) == 0) { + nosplitsugs=1; + } + + if (strncmp(line,"FULLSTRIP",9) == 0) { + fullstrip=1; + } + + if (strncmp(line,"SUGSWITHDOTS",12) == 0) { + sugswithdots=1; + } + + /* parse in the flag used by forbidden words */ + if (strncmp(line,"KEEPCASE",8) == 0) { + if (parse_flag(line, &keepcase, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `forceucase' */ + if (strncmp(line,"FORCEUCASE",10) == 0) { + if (parse_flag(line, &forceucase, afflst)) { + delete afflst; + return 1; + } + } + + /* parse in the flag used by `warn' */ + if (strncmp(line,"WARN",4) == 0) { + if (parse_flag(line, &warn, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"FORBIDWARN",10) == 0) { + forbidwarn=1; + } + + /* parse in the flag used by the affix generator */ + if (strncmp(line,"SUBSTANDARD",11) == 0) { + if (parse_flag(line, &substandard, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"CHECKSHARPS",11) == 0) { + checksharps=1; + } + + /* parse this affix: P - prefix, S - suffix */ + ft = ' '; + if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P'; + if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S'; + if (ft != ' ') { + if (dupflags_ini) { + memset(dupflags, 0, sizeof(dupflags)); + dupflags_ini = 0; + } + if (parse_affix(line, ft, afflst, dupflags)) { + delete afflst; + process_pfx_tree_to_list(); + process_sfx_tree_to_list(); + return 1; + } + } + + } + delete afflst; + + // convert affix trees to sorted list + process_pfx_tree_to_list(); + process_sfx_tree_to_list(); + + // now we can speed up performance greatly taking advantage of the + // relationship between the affixes and the idea of "subsets". + + // View each prefix as a potential leading subset of another and view + // each suffix (reversed) as a potential trailing subset of another. + + // To illustrate this relationship if we know the prefix "ab" is found in the + // word to examine, only prefixes that "ab" is a leading subset of need be examined. + // Furthermore is "ab" is not present then none of the prefixes that "ab" is + // is a subset need be examined. + // The same argument goes for suffix string that are reversed. + + // Then to top this off why not examine the first char of the word to quickly + // limit the set of prefixes to examine (i.e. the prefixes to examine must + // be leading supersets of the first character of the word (if they exist) + + // To take advantage of this "subset" relationship, we need to add two links + // from entry. One to take next if the current prefix is found (call it nexteq) + // and one to take next if the current prefix is not found (call it nextne). + + // Since we have built ordered lists, all that remains is to properly initialize + // the nextne and nexteq pointers that relate them + + process_pfx_order(); + process_sfx_order(); + + /* get encoding for CHECKCOMPOUNDCASE */ + if (!utf8) { + char * enc = get_encoding(); + csconv = get_current_cs(enc); + free(enc); + enc = NULL; + + char expw[MAXLNLEN]; + if (wordchars) { + strcpy(expw, wordchars); + free(wordchars); + } else *expw = '\0'; + + for (int i = 0; i <= 255; i++) { + if ( (csconv[i].cupper != csconv[i].clower) && + (! strchr(expw, (char) i))) { + *(expw + strlen(expw) + 1) = '\0'; + *(expw + strlen(expw)) = (char) i; + } + } + + wordchars = mystrdup(expw); + } + + // default BREAK definition + if (numbreak == -1) { + breaktable = (char **) malloc(sizeof(char *) * 3); + if (!breaktable) return 1; + breaktable[0] = mystrdup("-"); + breaktable[1] = mystrdup("^-"); + breaktable[2] = mystrdup("-$"); + if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3; + } + return 0; +} + + +// we want to be able to quickly access prefix information +// both by prefix flag, and sorted by prefix string itself +// so we need to set up two indexes + +int AffixMgr::build_pfxtree(PfxEntry* pfxptr) +{ + PfxEntry * ptr; + PfxEntry * pptr; + PfxEntry * ep = pfxptr; + + // get the right starting points + const char * key = ep->getKey(); + const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF); + + // first index by flag which must exist + ptr = pFlag[flg]; + ep->setFlgNxt(ptr); + pFlag[flg] = ep; + + + // handle the special case of null affix string + if (strlen(key) == 0) { + // always inset them at head of list at element 0 + ptr = pStart[0]; + ep->setNext(ptr); + pStart[0] = ep; + return 0; + } + + // now handle the normal case + ep->setNextEQ(NULL); + ep->setNextNE(NULL); + + unsigned char sp = *((const unsigned char *)key); + ptr = pStart[sp]; + + // handle the first insert + if (!ptr) { + pStart[sp] = ep; + return 0; + } + + + // otherwise use binary tree insertion so that a sorted + // list can easily be generated later + pptr = NULL; + for (;;) { + pptr = ptr; + if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) { + ptr = ptr->getNextEQ(); + if (!ptr) { + pptr->setNextEQ(ep); + break; + } + } else { + ptr = ptr->getNextNE(); + if (!ptr) { + pptr->setNextNE(ep); + break; + } + } + } + return 0; +} + +// we want to be able to quickly access suffix information +// both by suffix flag, and sorted by the reverse of the +// suffix string itself; so we need to set up two indexes +int AffixMgr::build_sfxtree(SfxEntry* sfxptr) +{ + SfxEntry * ptr; + SfxEntry * pptr; + SfxEntry * ep = sfxptr; + + /* get the right starting point */ + const char * key = ep->getKey(); + const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF); + + // first index by flag which must exist + ptr = sFlag[flg]; + ep->setFlgNxt(ptr); + sFlag[flg] = ep; + + // next index by affix string + + // handle the special case of null affix string + if (strlen(key) == 0) { + // always inset them at head of list at element 0 + ptr = sStart[0]; + ep->setNext(ptr); + sStart[0] = ep; + return 0; + } + + // now handle the normal case + ep->setNextEQ(NULL); + ep->setNextNE(NULL); + + unsigned char sp = *((const unsigned char *)key); + ptr = sStart[sp]; + + // handle the first insert + if (!ptr) { + sStart[sp] = ep; + return 0; + } + + // otherwise use binary tree insertion so that a sorted + // list can easily be generated later + pptr = NULL; + for (;;) { + pptr = ptr; + if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) { + ptr = ptr->getNextEQ(); + if (!ptr) { + pptr->setNextEQ(ep); + break; + } + } else { + ptr = ptr->getNextNE(); + if (!ptr) { + pptr->setNextNE(ep); + break; + } + } + } + return 0; +} + +// convert from binary tree to sorted list +int AffixMgr::process_pfx_tree_to_list() +{ + for (int i=1; i< SETSIZE; i++) { + pStart[i] = process_pfx_in_order(pStart[i],NULL); + } + return 0; +} + + +PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr) +{ + if (ptr) { + nptr = process_pfx_in_order(ptr->getNextNE(), nptr); + ptr->setNext(nptr); + nptr = process_pfx_in_order(ptr->getNextEQ(), ptr); + } + return nptr; +} + + +// convert from binary tree to sorted list +int AffixMgr:: process_sfx_tree_to_list() +{ + for (int i=1; i< SETSIZE; i++) { + sStart[i] = process_sfx_in_order(sStart[i],NULL); + } + return 0; +} + +SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr) +{ + if (ptr) { + nptr = process_sfx_in_order(ptr->getNextNE(), nptr); + ptr->setNext(nptr); + nptr = process_sfx_in_order(ptr->getNextEQ(), ptr); + } + return nptr; +} + + +// reinitialize the PfxEntry links NextEQ and NextNE to speed searching +// using the idea of leading subsets this time +int AffixMgr::process_pfx_order() +{ + PfxEntry* ptr; + + // loop through each prefix list starting point + for (int i=1; i < SETSIZE; i++) { + + ptr = pStart[i]; + + // look through the remainder of the list + // and find next entry with affix that + // the current one is not a subset of + // mark that as destination for NextNE + // use next in list that you are a subset + // of as NextEQ + + for (; ptr != NULL; ptr = ptr->getNext()) { + + PfxEntry * nptr = ptr->getNext(); + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset( ptr->getKey() , nptr->getKey() )) break; + } + ptr->setNextNE(nptr); + ptr->setNextEQ(NULL); + if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey())) + ptr->setNextEQ(ptr->getNext()); + } + + // now clean up by adding smart search termination strings: + // if you are already a superset of the previous prefix + // but not a subset of the next, search can end here + // so set NextNE properly + + ptr = pStart[i]; + for (; ptr != NULL; ptr = ptr->getNext()) { + PfxEntry * nptr = ptr->getNext(); + PfxEntry * mptr = NULL; + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset(ptr->getKey(),nptr->getKey())) break; + mptr = nptr; + } + if (mptr) mptr->setNextNE(NULL); + } + } + return 0; +} + +// initialize the SfxEntry links NextEQ and NextNE to speed searching +// using the idea of leading subsets this time +int AffixMgr::process_sfx_order() +{ + SfxEntry* ptr; + + // loop through each prefix list starting point + for (int i=1; i < SETSIZE; i++) { + + ptr = sStart[i]; + + // look through the remainder of the list + // and find next entry with affix that + // the current one is not a subset of + // mark that as destination for NextNE + // use next in list that you are a subset + // of as NextEQ + + for (; ptr != NULL; ptr = ptr->getNext()) { + SfxEntry * nptr = ptr->getNext(); + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset(ptr->getKey(),nptr->getKey())) break; + } + ptr->setNextNE(nptr); + ptr->setNextEQ(NULL); + if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey())) + ptr->setNextEQ(ptr->getNext()); + } + + + // now clean up by adding smart search termination strings: + // if you are already a superset of the previous suffix + // but not a subset of the next, search can end here + // so set NextNE properly + + ptr = sStart[i]; + for (; ptr != NULL; ptr = ptr->getNext()) { + SfxEntry * nptr = ptr->getNext(); + SfxEntry * mptr = NULL; + for (; nptr != NULL; nptr = nptr->getNext()) { + if (! isSubset(ptr->getKey(),nptr->getKey())) break; + mptr = nptr; + } + if (mptr) mptr->setNextNE(NULL); + } + } + return 0; +} + +// add flags to the result for dictionary debugging +void AffixMgr::debugflag(char * result, unsigned short flag) { + char * st = encode_flag(flag); + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_FLAG, MAXLNLEN); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } +} + +// calculate the character length of the condition +int AffixMgr::condlen(char * st) +{ + int l = 0; + bool group = false; + for(; *st; st++) { + if (*st == '[') { + group = true; + l++; + } else if (*st == ']') group = false; + else if (!group && (!utf8 || + (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++; + } + return l; +} + +int AffixMgr::encodeit(affentry &entry, char * cs) +{ + if (strcmp(cs,".") != 0) { + entry.numconds = (char) condlen(cs); + strncpy(entry.c.conds, cs, MAXCONDLEN); + // long condition (end of conds padded by strncpy) + if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) { + entry.opts += aeLONGCOND; + entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1); + if (!entry.c.l.conds2) return 1; + } + } else { + entry.numconds = 0; + entry.c.conds[0] = '\0'; + } + return 0; +} + +// return 1 if s1 is a leading subset of s2 (dots are for infixes) +inline int AffixMgr::isSubset(const char * s1, const char * s2) + { + while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) { + s1++; + s2++; + } + return (*s1 == '\0'); + } + + +// check word for prefixes +struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound, + const FLAG needflag) +{ + struct hentry * rv= NULL; + + pfx = NULL; + pfxappnd = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + while (pe) { + if ( + // fogemorpheme + ((in_compound != IN_CPD_NOT) || !(pe->getCont() && + (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) && + // permit prefixes in compounds + ((in_compound != IN_CPD_END) || (pe->getCont() && + (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen())))) + ) { + // check prefix + rv = pe->checkword(word, len, in_compound, needflag); + if (rv) { + pfx=pe; // BUG: pfx not stateless + return rv; + } + } + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + if ( + // fogemorpheme + ((in_compound != IN_CPD_NOT) || !(pptr->getCont() && + (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) && + // permit prefixes in compounds + ((in_compound != IN_CPD_END) || (pptr->getCont() && + (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen())))) + ) { + // check prefix + rv = pptr->checkword(word, len, in_compound, needflag); + if (rv) { + pfx=pptr; // BUG: pfx not stateless + return rv; + } + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + return NULL; +} + +// check word for prefixes +struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len, + char in_compound, const FLAG needflag) +{ + struct hentry * rv= NULL; + + pfx = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + + while (pe) { + rv = pe->check_twosfx(word, len, in_compound, needflag); + if (rv) return rv; + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + rv = pptr->check_twosfx(word, len, in_compound, needflag); + if (rv) { + pfx = pptr; + return rv; + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + return NULL; +} + +// check word for prefixes +char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound, + const FLAG needflag) +{ + char * st; + + char result[MAXLNLEN]; + result[0] = '\0'; + + pfx = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + while (pe) { + st = pe->check_morph(word,len,in_compound, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + // if (rv) return rv; + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + st = pptr->check_morph(word,len,in_compound, needflag); + if (st) { + // fogemorpheme + if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() && + (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) { + mystrcat(result, st, MAXLNLEN); + pfx = pptr; + } + free(st); + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + if (*result) return mystrdup(result); + return NULL; +} + + +// check word for prefixes +char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag) +{ + char * st; + + char result[MAXLNLEN]; + result[0] = '\0'; + + pfx = NULL; + sfxappnd = NULL; + + // first handle the special case of 0 length prefixes + PfxEntry * pe = pStart[0]; + while (pe) { + st = pe->check_twosfx_morph(word,len,in_compound, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + pe = pe->getNext(); + } + + // now handle the general case + unsigned char sp = *((const unsigned char *)word); + PfxEntry * pptr = pStart[sp]; + + while (pptr) { + if (isSubset(pptr->getKey(),word)) { + st = pptr->check_twosfx_morph(word, len, in_compound, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + pfx = pptr; + } + pptr = pptr->getNextEQ(); + } else { + pptr = pptr->getNextNE(); + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +// Is word a non compound with a REP substitution (see checkcompoundrep)? +int AffixMgr::cpdrep_check(const char * word, int wl) +{ + char candidate[MAXLNLEN]; + const char * r; + int lenr, lenp; + + if ((wl < 2) || !numrep) return 0; + + for (int i=0; i < numrep; i++ ) { + r = word; + lenr = strlen(reptable[i].pattern2); + lenp = strlen(reptable[i].pattern); + // search every occurence of the pattern in the word + while ((r=strstr(r, reptable[i].pattern)) != NULL) { + strcpy(candidate, word); + if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break; + strcpy(candidate+(r-word),reptable[i].pattern2); + strcpy(candidate+(r-word)+lenr, r+lenp); + if (candidate_check(candidate,strlen(candidate))) return 1; + r++; // search for the next letter + } + } + return 0; +} + +// forbid compoundings when there are special patterns at word bound +int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed) +{ + int len; + for (int i = 0; i < numcheckcpd; i++) { + if (isSubset(checkcpdtable[i].pattern2, word + pos) && + (!r1 || !checkcpdtable[i].cond || + (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) && + (!r2 || !checkcpdtable[i].cond2 || + (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) && + // zero length pattern => only TESTAFF + // zero pattern (0/flag) => unmodified stem (zero affixes allowed) + (!*(checkcpdtable[i].pattern) || ( + (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) || + (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) && + strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) { + return 1; + } + } + return 0; +} + +// forbid compounding with neighbouring upper and lower case characters at word bounds +int AffixMgr::cpdcase_check(const char * word, int pos) +{ + if (utf8) { + w_char u, w; + const char * p; + u8_u16(&u, 1, word + pos); + for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--); + u8_u16(&w, 1, p); + unsigned short a = (u.h << 8) + u.l; + unsigned short b = (w.h << 8) + w.l; + if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) && + (a != '-') && (b != '-')) return 1; + } else { + unsigned char a = *(word + pos - 1); + unsigned char b = *(word + pos); + if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1; + } + return 0; +} + +// check compound patterns +int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all) +{ + signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking + signed short btwp[MAXWORDLEN]; // word positions for metacharacters + int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions + short bt = 0; + int i, j; + int ok; + int w = 0; + + if (!*words) { + w = 1; + *words = def; + } + + if (!*words) { + return 0; + } + + (*words)[wnum] = rv; + + // has the last word COMPOUNDRULE flag? + if (rv->alen == 0) { + (*words)[wnum] = NULL; + if (w) *words = NULL; + return 0; + } + ok = 0; + for (i = 0; i < numdefcpd; i++) { + for (j = 0; j < defcpdtable[i].len; j++) { + if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' && + TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1; + } + } + if (ok == 0) { + (*words)[wnum] = NULL; + if (w) *words = NULL; + return 0; + } + + for (i = 0; i < numdefcpd; i++) { + signed short pp = 0; // pattern position + signed short wp = 0; // "words" position + int ok2; + ok = 1; + ok2 = 1; + do { + while ((pp < defcpdtable[i].len) && (wp <= wnum)) { + if (((pp+1) < defcpdtable[i].len) && + ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) { + int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum; + ok2 = 1; + pp+=2; + btpp[bt] = pp; + btwp[bt] = wp; + while (wp <= wend) { + if (!(*words)[wp]->alen || + !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) { + ok2 = 0; + break; + } + wp++; + } + if (wp <= wnum) ok2 = 0; + btnum[bt] = wp - btwp[bt]; + if (btnum[bt] > 0) bt++; + if (ok2) break; + } else { + ok2 = 1; + if (!(*words)[wp] || !(*words)[wp]->alen || + !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) { + ok = 0; + break; + } + pp++; + wp++; + if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0; + } + } + if (ok && ok2) { + int r = pp; + while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) && + ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2; + if (defcpdtable[i].len <= r) return 1; + } + // backtrack + if (bt) do { + ok = 1; + btnum[bt - 1]--; + pp = btpp[bt - 1]; + wp = btwp[bt - 1] + (signed short) btnum[bt - 1]; + } while ((btnum[bt - 1] < 0) && --bt); + } while (bt); + + if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1; + + // check zero ending + while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) && + ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2; + if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1; + } + (*words)[wnum] = NULL; + if (w) *words = NULL; + return 0; +} + +inline int AffixMgr::candidate_check(const char * word, int len) +{ + struct hentry * rv=NULL; + + rv = lookup(word); + if (rv) return 1; + +// rv = prefix_check(word,len,1); +// if (rv) return 1; + + rv = affix_check(word,len); + if (rv) return 1; + return 0; +} + +// calculate number of syllable for compound-checking +short AffixMgr::get_syllable(const char * word, int wlen) +{ + if (cpdmaxsyllable==0) return 0; + + short num=0; + + if (!utf8) { + for (int i=0; i 0; i--) { + if (flag_bsearch((unsigned short *) cpdvowels_utf16, + ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++; + } + } + return num; +} + +void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) { + if (utf8) { + int i; + for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) { + for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++); + } + for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) { + for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--); + } + } else { + *cmin = cpdmin; + *cmax = len - cpdmin + 1; + } +} + + +// check if compound word is correctly spelled +// hu_mov_rule = spec. Hungarian rule (XXX) +struct hentry * AffixMgr::compound_check(const char * word, int len, + short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL, + char hu_mov_rule = 0, char is_sug = 0, int * info = NULL) +{ + int i; + short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; + struct hentry * rv = NULL; + struct hentry * rv_first; + struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking + char st [MAXWORDUTF8LEN + 4]; + char ch = '\0'; + int cmin; + int cmax; + int striple = 0; + int scpd = 0; + int soldi = 0; + int oldcmin = 0; + int oldcmax = 0; + int oldlen = 0; + int checkedstriple = 0; + int onlycpdrule; + int affixed = 0; + hentry ** oldwords = words; + + int checked_prefix; + + setcminmax(&cmin, &cmax, word, len); + + strcpy(st, word); + + for (i = cmin; i < cmax; i++) { + // go to end of the UTF-8 character + if (utf8) { + for (; (st[i] & 0xc0) == 0x80; i++); + if (i >= cmax) return NULL; + } + + words = oldwords; + onlycpdrule = (words) ? 1 : 0; + + do { // onlycpdrule loop + + oldnumsyllable = numsyllable; + oldwordnum = wordnum; + checked_prefix = 0; + + + do { // simplified checkcompoundpattern loop + + if (scpd > 0) { + for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 || + strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++); + + if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop + strcpy(st + i, checkcpdtable[scpd-1].pattern); + soldi = i; + i += strlen(checkcpdtable[scpd-1].pattern); + strcpy(st + i, checkcpdtable[scpd-1].pattern2); + strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3)); + + oldlen = len; + len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3); + oldcmin = cmin; + oldcmax = cmax; + setcminmax(&cmin, &cmax, st, len); + + cmax = len - cpdmin + 1; + } + + ch = st[i]; + st[i] = '\0'; + + sfx = NULL; + pfx = NULL; + + // FIRST WORD + + affixed = 1; + rv = lookup(st); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && !hu_mov_rule && + ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundbegin && !wordnum && !onlycpdrule && + TESTAFF(rv->astr, compoundbegin, rv->alen)) || + (compoundmiddle && wordnum && !words && !onlycpdrule && + TESTAFF(rv->astr, compoundmiddle, rv->alen)) || + (numdefcpd && onlycpdrule && + ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) || + (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) || + (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL && + !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen))) + ) { + rv = rv->next_homonym; + } + + if (rv) affixed = 0; + + if (!rv) { + if (onlycpdrule) break; + if (compoundflag && + !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { + if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, + FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule && + sfx->getCont() && + ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())) || (compoundend && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + rv = NULL; + } + } + + if (rv || + (((wordnum == 0) && compoundbegin && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || + ((wordnum > 0) && compoundmiddle && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle))))) + ) checked_prefix = 1; + // else check forbiddenwords and needaffix + } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, needaffix, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)) + )) { + st[i] = ch; + //continue; + break; + } + + // check non_compound flag in suffix and prefix + if ((rv) && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + rv = NULL; + } + + // check compoundend flag in suffix and prefix + if ((rv) && !checked_prefix && compoundend && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundend, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + rv = NULL; + } + + // check compoundmiddle flag in suffix and prefix + if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundmiddle, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundmiddle, + sfx->getContLen())))) { + rv = NULL; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) { + return NULL; + } + + // increment word number, if the second root has a compoundroot flag + if ((rv) && compoundroot && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // first word is acceptable in compound words? + if (((rv) && + ( checked_prefix || (words && words[wnum]) || + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || + ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// || +// (numdefcpd && ) + +// LANG_hu section: spec. Hungarian rule + || ((langnum == LANG_hu) && hu_mov_rule && ( + TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes + TESTAFF(rv->astr, 'G', rv->alen) || + TESTAFF(rv->astr, 'H', rv->alen) + ) + ) +// END of LANG_hu section + ) && + ( + // test CHECKCOMPOUNDPATTERN conditions + scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL || + TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen) + ) + && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters + (word[i-1]==word[i]) && ( + ((i>1) && (word[i-1]==word[i-2])) || + ((word[i-1]==word[i+1])) // may be word[i+1] == '\0' + ) + ) || + ( + checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i) + )) + ) +// LANG_hu section: spec. Hungarian rule + || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) && + (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes + TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) || + TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen()) + ) + ) + ) + ) { // first word is ok condition + +// LANG_hu section: spec. Hungarian rule + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(st, i); + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + } +// END of LANG_hu section + + // NEXT WORD(S) + rv_first = rv; + st[i] = ch; + + do { // striple loop + + // check simplifiedtriple + if (simplifiedtriple) { + if (striple) { + checkedstriple = 1; + i--; // check "fahrt" instead of "ahrt" in "Schiffahrt" + } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1; + } + + rv = lookup((st+i)); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || + (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) || + (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL && + !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen)) + )) { + rv = rv->next_homonym; + } + + // check FORCEUCASE + if (rv && forceucase && (rv) && + (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL; + + if (rv && words && words[wnum + 1]) return rv_first; + + oldnumsyllable2 = numsyllable; + oldwordnum2 = wordnum; + + +// LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code + if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) { + numsyllable--; + } +// END of LANG_hu section + + // increment word number, if the second root has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL; + + // second word is acceptable, as a root? + // hungarian conventions: compounding is acceptable, + // when compound forms consist of 2 words, or if more, + // then the syllable number of root words must be 6, or lesser. + + if ((rv) && ( + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && TESTAFF(rv->astr, compoundend, rv->alen)) + ) + && ( + ((cpdwordmax==-1) || (wordnum+1clen)<=cpdmaxsyllable)) + ) && + ( + // test CHECKCOMPOUNDPATTERN + !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0) + ) && + ( + (!checkcompounddup || (rv != rv_first)) + ) + // test CHECKCOMPOUNDPATTERN conditions + && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || + TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen)) + ) + { + // forbid compound word, if it is a non compound word with typical fault + if (checkcompoundrep && cpdrep_check(word,len)) return NULL; + return rv_first; + } + + numsyllable = oldnumsyllable2; + wordnum = oldwordnum2; + + // perhaps second word has prefix or/and suffix + sfx = NULL; + sfxflag = FLAG_NULL; + rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL; + if (!rv && compoundend && !onlycpdrule) { + sfx = NULL; + pfx = NULL; + rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END); + } + + if (!rv && numdefcpd && words) { + rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END); + if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first; + rv = NULL; + } + + // test CHECKCOMPOUNDPATTERN conditions (allowed forms) + if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || + TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL; + + // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds) + if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL; + + // check non_compound flag in suffix and prefix + if ((rv) && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + rv = NULL; + } + + // check FORCEUCASE + if (rv && forceucase && (rv) && + (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL; + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL; + + // pfxappnd = prefix of word+i, or NULL + // calculate syllable number of prefix. + // hungarian convention: when syllable number of prefix is more, + // than 1, the prefix+word counts as two words. + + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(word + i, strlen(word + i)); + + // - affix syllable num. + // XXX only second suffix (inflections, not derivations) + if (sfxappnd) { + char * tmp = myrevstrdup(sfxappnd); + numsyllable -= get_syllable(tmp, strlen(tmp)); + free(tmp); + } + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + + // increment syllable num, if last word has a SYLLABLENUM flag + // and the suffix is beginning `s' + + if (cpdsyllablenum) { + switch (sfxflag) { + case 'c': { numsyllable+=2; break; } + case 'J': { numsyllable += 1; break; } + case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; } + } + } + } + + // increment word number, if the second word has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // second word is acceptable, as a word with prefix or/and suffix? + // hungarian conventions: compounding is acceptable, + // when compound forms consist 2 word, otherwise + // the syllable number of root words is 6, or lesser. + if ((rv) && + ( + ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) || + ((cpdmaxsyllable != 0) && + (numsyllable <= cpdmaxsyllable)) + ) + && ( + (!checkcompounddup || (rv != rv_first)) + )) { + // forbid compound word, if it is a non compound word with typical fault + if (checkcompoundrep && cpdrep_check(word, len)) return NULL; + return rv_first; + } + + numsyllable = oldnumsyllable2; + wordnum = oldwordnum2; + + // perhaps second word is a compound word (recursive call) + if (wordnum < maxwordnum) { + rv = compound_check((st+i),strlen(st+i), wordnum+1, + numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info); + + if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) || + (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL; + } else { + rv=NULL; + } + if (rv) { + // forbid compound word, if it is a non compound word with typical fault + if (checkcompoundrep || forbiddenword) { + struct hentry * rv2 = NULL; + + if (checkcompoundrep && cpdrep_check(word, len)) return NULL; + + // check first part + if (strncmp(rv->word, word + i, rv->blen) == 0) { + char r = *(st + i + rv->blen); + *(st + i + rv->blen) = '\0'; + + if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) { + *(st + i + rv->blen) = r; + continue; + } + + if (forbiddenword) { + rv2 = lookup(word); + if (!rv2) rv2 = affix_check(word, len); + if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) && + (strncmp(rv2->word, st, i + rv->blen) == 0)) { + return NULL; + } + } + *(st + i + rv->blen) = r; + } + } + return rv_first; + } + } while (striple && !checkedstriple); // end of striple loop + + if (checkedstriple) { + i++; + checkedstriple = 0; + striple = 0; + } + + } // first word is ok condition + + if (soldi != 0) { + i = soldi; + soldi = 0; + len = oldlen; + cmin = oldcmin; + cmax = oldcmax; + } + scpd++; + + + } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop + + scpd = 0; + wordnum = oldwordnum; + numsyllable = oldnumsyllable; + + if (soldi != 0) { + i = soldi; + strcpy(st, word); // XXX add more optim. + soldi = 0; + } else st[i] = ch; + + } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop + + } + + return NULL; +} + +// check if compound word is correctly spelled +// hu_mov_rule = spec. Hungarian rule (XXX) +int AffixMgr::compound_check_morph(const char * word, int len, + short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words, + char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL) +{ + int i; + short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; + int ok = 0; + + struct hentry * rv = NULL; + struct hentry * rv_first; + struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking + char st [MAXWORDUTF8LEN + 4]; + char ch; + + int checked_prefix; + char presult[MAXLNLEN]; + + int cmin; + int cmax; + + int onlycpdrule; + int affixed = 0; + hentry ** oldwords = words; + + setcminmax(&cmin, &cmax, word, len); + + strcpy(st, word); + + for (i = cmin; i < cmax; i++) { + oldnumsyllable = numsyllable; + oldwordnum = wordnum; + checked_prefix = 0; + + // go to end of the UTF-8 character + if (utf8) { + for (; (st[i] & 0xc0) == 0x80; i++); + if (i >= cmax) return 0; + } + + words = oldwords; + onlycpdrule = (words) ? 1 : 0; + + do { // onlycpdrule loop + + oldnumsyllable = numsyllable; + oldwordnum = wordnum; + checked_prefix = 0; + + ch = st[i]; + st[i] = '\0'; + sfx = NULL; + + // FIRST WORD + + affixed = 1; + + *presult = '\0'; + if (partresult) mystrcat(presult, partresult, MAXLNLEN); + + rv = lookup(st); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && !hu_mov_rule && + ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundbegin && !wordnum && !onlycpdrule && + TESTAFF(rv->astr, compoundbegin, rv->alen)) || + (compoundmiddle && wordnum && !words && !onlycpdrule && + TESTAFF(rv->astr, compoundmiddle, rv->alen)) || + (numdefcpd && onlycpdrule && + ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) || + (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)))) + ))) { + rv = rv->next_homonym; + } + + if (rv) affixed = 0; + + if (rv) { + sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st); + if (!HENTRY_FIND(rv, MORPH_STEM)) { + sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st); + } + // store the pointer of the hash entry +// sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv); + if (HENTRY_DATA(rv)) { + sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv)); + } + } + + if (!rv) { + if (onlycpdrule) break; + if (compoundflag && + !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { + if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, + FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule && + sfx->getCont() && + ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())) || (compoundend && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + rv = NULL; + } + } + + if (rv || + (((wordnum == 0) && compoundbegin && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || + ((wordnum > 0) && compoundmiddle && + ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || + (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle))))) + ) { + // char * p = prefix_check_morph(st, i, 0, compound); + char * p = NULL; + if (compoundflag) p = affix_check_morph(st, i, compoundflag); + if (!p || (*p == '\0')) { + if (p) free(p); + p = NULL; + if ((wordnum == 0) && compoundbegin) { + p = affix_check_morph(st, i, compoundbegin); + } else if ((wordnum > 0) && compoundmiddle) { + p = affix_check_morph(st, i, compoundmiddle); + } + } + if (p && (*p != '\0')) { + sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD, + MORPH_PART, st, line_uniq_app(&p, MSEP_REC)); + } + if (p) free(p); + checked_prefix = 1; + } + // else check forbiddenwords + } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + TESTAFF(rv->astr, needaffix, rv->alen))) { + st[i] = ch; + continue; + } + + // check non_compound flag in suffix and prefix + if ((rv) && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + continue; + } + + // check compoundend flag in suffix and prefix + if ((rv) && !checked_prefix && compoundend && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundend, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundend, + sfx->getContLen())))) { + continue; + } + + // check compoundmiddle flag in suffix and prefix + if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundmiddle, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundmiddle, + sfx->getContLen())))) { + rv = NULL; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) + || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue; + + // increment word number, if the second root has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // first word is acceptable in compound words? + if (((rv) && + ( checked_prefix || (words && words[wnum]) || + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || + ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen)) +// LANG_hu section: spec. Hungarian rule + || ((langnum == LANG_hu) && // hu_mov_rule + hu_mov_rule && ( + TESTAFF(rv->astr, 'F', rv->alen) || + TESTAFF(rv->astr, 'G', rv->alen) || + TESTAFF(rv->astr, 'H', rv->alen) + ) + ) +// END of LANG_hu section + ) + && ! (( checkcompoundtriple && !words && // test triple letters + (word[i-1]==word[i]) && ( + ((i>1) && (word[i-1]==word[i-2])) || + ((word[i-1]==word[i+1])) // may be word[i+1] == '\0' + ) + ) || + ( + // test CHECKCOMPOUNDPATTERN + numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, affixed) + ) || + ( + checkcompoundcase && !words && cpdcase_check(word, i) + )) + ) +// LANG_hu section: spec. Hungarian rule + || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) && + (sfx && sfx->getCont() && ( + TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) || + TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen()) + ) + ) + ) +// END of LANG_hu section + ) { + +// LANG_hu section: spec. Hungarian rule + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(st, i); + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + } +// END of LANG_hu section + + // NEXT WORD(S) + rv_first = rv; + rv = lookup((word+i)); // perhaps without prefix + + // search homonym with compound flag + while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || + !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || + (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) { + rv = rv->next_homonym; + } + + if (rv && words && words[wnum + 1]) { + mystrcat(*result, presult, MAXLNLEN); + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_PART, MAXLNLEN); + mystrcat(*result, word+i, MAXLNLEN); + if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + if (!HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_STEM, MAXLNLEN); + mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv); + if (!complexprefixes && HENTRY_DATA(rv)) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + } + mystrcat(*result, "\n", MAXLNLEN); + ok = 1; + return 0; + } + + oldnumsyllable2 = numsyllable; + oldwordnum2 = wordnum; + +// LANG_hu section: spec. Hungarian rule + if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) { + numsyllable--; + } +// END of LANG_hu section + // increment word number, if the second root has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) { + st[i] = ch; + continue; + } + + // second word is acceptable, as a root? + // hungarian conventions: compounding is acceptable, + // when compound forms consist of 2 words, or if more, + // then the syllable number of root words must be 6, or lesser. + if ((rv) && ( + (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || + (compoundend && TESTAFF(rv->astr, compoundend, rv->alen)) + ) + && ( + ((cpdwordmax==-1) || (wordnum+1blen)<=cpdmaxsyllable)) + ) + && ( + (!checkcompounddup || (rv != rv_first)) + ) + ) + { + // bad compound word + mystrcat(*result, presult, MAXLNLEN); + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_PART, MAXLNLEN); + mystrcat(*result, word+i, MAXLNLEN); + + if (HENTRY_DATA(rv)) { + if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + if (! HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, MORPH_STEM, MAXLNLEN); + mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv); + if (!complexprefixes) { + mystrcat(*result, " ", MAXLNLEN); + mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); + } + } + mystrcat(*result, "\n", MAXLNLEN); + ok = 1; + } + + numsyllable = oldnumsyllable2 ; + wordnum = oldwordnum2; + + // perhaps second word has prefix or/and suffix + sfx = NULL; + sfxflag = FLAG_NULL; + + if (compoundflag && !onlycpdrule) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL; + + if (!rv && compoundend && !onlycpdrule) { + sfx = NULL; + pfx = NULL; + rv = affix_check((word+i),strlen(word+i), compoundend); + } + + if (!rv && numdefcpd && words) { + rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END); + if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) { + char * m = NULL; + if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag); + if ((!m || *m == '\0') && compoundend) { + if (m) free(m); + m = affix_check_morph((word+i),strlen(word+i), compoundend); + } + mystrcat(*result, presult, MAXLNLEN); + if (m || (*m != '\0')) { + sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD, + MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC)); + } + if (m) free(m); + mystrcat(*result, "\n", MAXLNLEN); + ok = 1; + } + } + + // check non_compound flag in suffix and prefix + if ((rv) && + ((pfx && pfx->getCont() && + TESTAFF(pfx->getCont(), compoundforbidflag, + pfx->getContLen())) || + (sfx && sfx->getCont() && + TESTAFF(sfx->getCont(), compoundforbidflag, + sfx->getContLen())))) { + rv = NULL; + } + + // check forbiddenwords + if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen)) + && (! TESTAFF(rv->astr, needaffix, rv->alen))) { + st[i] = ch; + continue; + } + + if (langnum == LANG_hu) { + // calculate syllable number of the word + numsyllable += get_syllable(word + i, strlen(word + i)); + + // - affix syllable num. + // XXX only second suffix (inflections, not derivations) + if (sfxappnd) { + char * tmp = myrevstrdup(sfxappnd); + numsyllable -= get_syllable(tmp, strlen(tmp)); + free(tmp); + } + + // + 1 word, if syllable number of the prefix > 1 (hungarian convention) + if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; + + // increment syllable num, if last word has a SYLLABLENUM flag + // and the suffix is beginning `s' + + if (cpdsyllablenum) { + switch (sfxflag) { + case 'c': { numsyllable+=2; break; } + case 'J': { numsyllable += 1; break; } + case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; } + } + } + } + + // increment word number, if the second word has a compoundroot flag + if ((rv) && (compoundroot) && + (TESTAFF(rv->astr, compoundroot, rv->alen))) { + wordnum++; + } + // second word is acceptable, as a word with prefix or/and suffix? + // hungarian conventions: compounding is acceptable, + // when compound forms consist 2 word, otherwise + // the syllable number of root words is 6, or lesser. + if ((rv) && + ( + ((cpdwordmax==-1) || (wordnum+1 0) && *s1 && (*s1 == *end_of_s2)) { + s1++; + end_of_s2--; + len--; + } + return (*s1 == '\0'); + } + */ + +inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len) + { + while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) { + s1++; + end_of_s2--; + len--; + } + return (*s1 == '\0'); + } + +// check word for suffixes + +struct hentry * AffixMgr::suffix_check (const char * word, int len, + int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns, + const FLAG cclass, const FLAG needflag, char in_compound) +{ + struct hentry * rv = NULL; + PfxEntry* ep = ppfx; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + + while (se) { + if (!cclass || se->getCont()) { + // suffixes are not allowed in beginning of compounds + if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (se->getCont() && compoundpermitflag && + TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) && + // fogemorpheme + (in_compound || + !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) && + // needaffix on prefix or first suffix + (cclass || + !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) || + (ppfx && !((ep->getCont()) && + TESTAFF(ep->getCont(), needaffix, + ep->getContLen()))) + )) { + rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass, + needflag, (in_compound ? 0 : onlyincompound)); + if (rv) { + sfx=se; // BUG: sfx not stateless + return rv; + } + } + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp= *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len) + ) { + // suffixes are not allowed in beginning of compounds + if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (sptr->getCont() && compoundpermitflag && + TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) && + // fogemorpheme + (in_compound || + !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) && + // needaffix on prefix or first suffix + (cclass || + !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) || + (ppfx && !((ep->getCont()) && + TESTAFF(ep->getCont(), needaffix, + ep->getContLen()))) + ) + ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) { + rv = sptr->checkword(word,len, sfxopts, ppfx, wlst, + maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound)); + if (rv) { + sfx=sptr; // BUG: sfx not stateless + sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless + if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless + return rv; + } + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + + return NULL; +} + +// check word for two-level suffixes + +struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG needflag) +{ + struct hentry * rv = NULL; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + while (se) { + if (contclasses[se->getFlag()]) + { + rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag); + if (rv) return rv; + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp = *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len)) { + if (contclasses[sptr->getFlag()]) + { + rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag); + if (rv) { + sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless + if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless + return rv; + } + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + + return NULL; +} + +char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG needflag) +{ + char result[MAXLNLEN]; + char result2[MAXLNLEN]; + char result3[MAXLNLEN]; + + char * st; + + result[0] = '\0'; + result2[0] = '\0'; + result3[0] = '\0'; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + while (se) { + if (contclasses[se->getFlag()]) + { + st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag); + if (st) { + if (ppfx) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } else debugflag(result, ppfx->getFlag()); + } + mystrcat(result, st, MAXLNLEN); + free(st); + if (se->getMorph()) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, se->getMorph(), MAXLNLEN); + } else debugflag(result, se->getFlag()); + mystrcat(result, "\n", MAXLNLEN); + } + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp = *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len)) { + if (contclasses[sptr->getFlag()]) + { + st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag); + if (st) { + sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless + if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless + strcpy(result2, st); + free(st); + + result3[0] = '\0'; + + if (sptr->getMorph()) { + mystrcat(result3, " ", MAXLNLEN); + mystrcat(result3, sptr->getMorph(), MAXLNLEN); + } else debugflag(result3, sptr->getFlag()); + strlinecat(result2, result3); + mystrcat(result2, "\n", MAXLNLEN); + mystrcat(result, result2, MAXLNLEN); + } + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + if (*result) return mystrdup(result); + return NULL; +} + +char * AffixMgr::suffix_check_morph(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound) +{ + char result[MAXLNLEN]; + + struct hentry * rv = NULL; + + result[0] = '\0'; + + PfxEntry* ep = ppfx; + + // first handle the special case of 0 length suffixes + SfxEntry * se = sStart[0]; + while (se) { + if (!cclass || se->getCont()) { + // suffixes are not allowed in beginning of compounds + if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (se->getCont() && compoundpermitflag && + TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) && + // fogemorpheme + (in_compound || + !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) && + // needaffix on prefix or first suffix + (cclass || + !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) || + (ppfx && !((ep->getCont()) && + TESTAFF(ep->getCont(), needaffix, + ep->getContLen()))) + ) + )) + rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag); + while (rv) { + if (ppfx) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } else debugflag(result, ppfx->getFlag()); + } + if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + if (! HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv); + + if (!complexprefixes && HENTRY_DATA(rv)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + } + if (se->getMorph()) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, se->getMorph(), MAXLNLEN); + } else debugflag(result, se->getFlag()); + mystrcat(result, "\n", MAXLNLEN); + rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); + } + } + se = se->getNext(); + } + + // now handle the general case + if (len == 0) return NULL; // FULLSTRIP + unsigned char sp = *((const unsigned char *)(word + len - 1)); + SfxEntry * sptr = sStart[sp]; + + while (sptr) { + if (isRevSubset(sptr->getKey(), word + len - 1, len) + ) { + // suffixes are not allowed in beginning of compounds + if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass + // except when signed with compoundpermitflag flag + (sptr->getCont() && compoundpermitflag && + TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix || + // no circumfix flag in prefix and suffix + ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) || + // circumfix flag in prefix AND suffix + ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), + circumfix, ep->getContLen())) && + (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) && + // fogemorpheme + (in_compound || + !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) && + // needaffix on first suffix + (cclass || !(sptr->getCont() && + TESTAFF(sptr->getCont(), needaffix, sptr->getContLen()))) + )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag); + while (rv) { + if (ppfx) { + if (ppfx->getMorph()) { + mystrcat(result, ppfx->getMorph(), MAXLNLEN); + mystrcat(result, " ", MAXLNLEN); + } else debugflag(result, ppfx->getFlag()); + } + if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + if (! HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); + } + // store the pointer of the hash entry +// sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv); + + if (!complexprefixes && HENTRY_DATA(rv)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + } + + if (sptr->getMorph()) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, sptr->getMorph(), MAXLNLEN); + } else debugflag(result, sptr->getFlag()); + mystrcat(result, "\n", MAXLNLEN); + rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); + } + sptr = sptr->getNextEQ(); + } else { + sptr = sptr->getNextNE(); + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +// check if word with affixes is correctly spelled +struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound) +{ + struct hentry * rv= NULL; + + // check all prefixes (also crossed with suffixes if allowed) + rv = prefix_check(word, len, in_compound, needflag); + if (rv) return rv; + + // if still not found check all suffixes + rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound); + + if (havecontclass) { + sfx = NULL; + pfx = NULL; + + if (rv) return rv; + // if still not found check all two-level suffixes + rv = suffix_check_twosfx(word, len, 0, NULL, needflag); + + if (rv) return rv; + // if still not found check all two-level suffixes + rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag); + } + + return rv; +} + +// check if word with affixes is correctly spelled +char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound) +{ + char result[MAXLNLEN]; + char * st = NULL; + + *result = '\0'; + + // check all prefixes (also crossed with suffixes if allowed) + st = prefix_check_morph(word, len, in_compound); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + // if still not found check all suffixes + st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + if (havecontclass) { + sfx = NULL; + pfx = NULL; + // if still not found check all two-level suffixes + st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + // if still not found check all two-level suffixes + st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + + return mystrdup(result); +} + +char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap, + unsigned short al, char * morph, char * targetmorph, int level) +{ + // handle suffixes + char * stemmorph; + char * stemmorphcatpos; + char mymorph[MAXLNLEN]; + + if (!morph) return NULL; + + // check substandard flag + if (TESTAFF(ap, substandard, al)) return NULL; + + if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts); + +// int targetcount = get_sfxcount(targetmorph); + + // use input suffix fields, if exist + if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) { + stemmorph = mymorph; + strcpy(stemmorph, morph); + mystrcat(stemmorph, " ", MAXLNLEN); + stemmorphcatpos = stemmorph + strlen(stemmorph); + } else { + stemmorph = morph; + stemmorphcatpos = NULL; + } + + for (int i = 0; i < al; i++) { + const unsigned char c = (unsigned char) (ap[i] & 0x00FF); + SfxEntry * sptr = sFlag[c]; + while (sptr) { + if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) || + // don't generate forms with substandard affixes + !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) { + + if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph()); + else stemmorph = (char *) sptr->getMorph(); + + int cmp = morphcmp(stemmorph, targetmorph); + + if (cmp == 0) { + char * newword = sptr->add(ts, wl); + if (newword) { + hentry * check = pHMgr->lookup(newword); // XXX extra dic + if (!check || !check->astr || + !(TESTAFF(check->astr, forbiddenword, check->alen) || + TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) { + return newword; + } + free(newword); + } + } + + // recursive call for secondary suffixes + if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) && +// (get_sfxcount(stemmorph) < targetcount) && + !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) { + char * newword = sptr->add(ts, wl); + if (newword) { + char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(), + sptr->getContLen(), stemmorph, targetmorph, 1); + + if (newword2) { + free(newword); + return newword2; + } + free(newword); + newword = NULL; + } + } + } + sptr = sptr->getFlgNxt(); + } + } + return NULL; +} + + +int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts, + int wl, const unsigned short * ap, unsigned short al, char * bad, int badl, + char * phon) +{ + int nh=0; + // first add root word to list + if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) || + (onlyincompound && TESTAFF(ap, onlyincompound, al))))) { + wlst[nh].word = mystrdup(ts); + if (!wlst[nh].word) return 0; + wlst[nh].allow = (1 == 0); + wlst[nh].orig = NULL; + nh++; + // add special phonetic version + if (phon && (nh < maxn)) { + wlst[nh].word = mystrdup(phon); + if (!wlst[nh].word) return nh - 1; + wlst[nh].allow = (1 == 0); + wlst[nh].orig = mystrdup(ts); + if (!wlst[nh].orig) return nh - 1; + nh++; + } + } + + // handle suffixes + for (int i = 0; i < al; i++) { + const unsigned char c = (unsigned char) (ap[i] & 0x00FF); + SfxEntry * sptr = sFlag[c]; + while (sptr) { + if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) && + (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) && + // check needaffix flag + !(sptr->getCont() && ((needaffix && + TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) || + (circumfix && + TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) || + (onlyincompound && + TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen())))) + ) { + char * newword = sptr->add(ts, wl); + if (newword) { + if (nh < maxn) { + wlst[nh].word = newword; + wlst[nh].allow = sptr->allowCross(); + wlst[nh].orig = NULL; + nh++; + // add special phonetic version + if (phon && (nh < maxn)) { + char st[MAXWORDUTF8LEN]; + strcpy(st, phon); + strcat(st, sptr->getKey()); + reverseword(st + strlen(phon)); + wlst[nh].word = mystrdup(st); + if (!wlst[nh].word) return nh - 1; + wlst[nh].allow = (1 == 0); + wlst[nh].orig = mystrdup(newword); + if (!wlst[nh].orig) return nh - 1; + nh++; + } + } else { + free(newword); + } + } + } + sptr = sptr->getFlgNxt(); + } + } + + int n = nh; + + // handle cross products of prefixes and suffixes + for (int j=1;jgetFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) && + (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) { + int l1 = strlen(wlst[j].word); + char * newword = cptr->add(wlst[j].word, l1); + if (newword) { + if (nh < maxn) { + wlst[nh].word = newword; + wlst[nh].allow = cptr->allowCross(); + wlst[nh].orig = NULL; + nh++; + } else { + free(newword); + } + } + } + cptr = cptr->getFlgNxt(); + } + } + } + + + // now handle pure prefixes + for (int m = 0; m < al; m ++) { + const unsigned char c = (unsigned char) (ap[m] & 0x00FF); + PfxEntry * ptr = pFlag[c]; + while (ptr) { + if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) && + (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) && + // check needaffix flag + !(ptr->getCont() && ((needaffix && + TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) || + (circumfix && + TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) || + (onlyincompound && + TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen())))) + ) { + char * newword = ptr->add(ts, wl); + if (newword) { + if (nh < maxn) { + wlst[nh].word = newword; + wlst[nh].allow = ptr->allowCross(); + wlst[nh].orig = NULL; + nh++; + } else { + free(newword); + } + } + } + ptr = ptr->getFlgNxt(); + } + } + + return nh; +} + +// return length of replacing table +int AffixMgr::get_numrep() const +{ + return numrep; +} + +// return replacing table +struct replentry * AffixMgr::get_reptable() const +{ + if (! reptable ) return NULL; + return reptable; +} + +// return iconv table +RepList * AffixMgr::get_iconvtable() const +{ + if (! iconvtable ) return NULL; + return iconvtable; +} + +// return oconv table +RepList * AffixMgr::get_oconvtable() const +{ + if (! oconvtable ) return NULL; + return oconvtable; +} + +// return replacing table +struct phonetable * AffixMgr::get_phonetable() const +{ + if (! phone ) return NULL; + return phone; +} + +// return length of character map table +int AffixMgr::get_nummap() const +{ + return nummap; +} + +// return character map table +struct mapentry * AffixMgr::get_maptable() const +{ + if (! maptable ) return NULL; + return maptable; +} + +// return length of word break table +int AffixMgr::get_numbreak() const +{ + return numbreak; +} + +// return character map table +char ** AffixMgr::get_breaktable() const +{ + if (! breaktable ) return NULL; + return breaktable; +} + +// return text encoding of dictionary +char * AffixMgr::get_encoding() +{ + if (! encoding ) encoding = mystrdup(SPELL_ENCODING); + return mystrdup(encoding); +} + +// return text encoding of dictionary +int AffixMgr::get_langnum() const +{ + return langnum; +} + +// return double prefix option +int AffixMgr::get_complexprefixes() const +{ + return complexprefixes; +} + +// return FULLSTRIP option +int AffixMgr::get_fullstrip() const +{ + return fullstrip; +} + +FLAG AffixMgr::get_keepcase() const +{ + return keepcase; +} + +FLAG AffixMgr::get_forceucase() const +{ + return forceucase; +} + +FLAG AffixMgr::get_warn() const +{ + return warn; +} + +int AffixMgr::get_forbidwarn() const +{ + return forbidwarn; +} + +int AffixMgr::get_checksharps() const +{ + return checksharps; +} + +char * AffixMgr::encode_flag(unsigned short aflag) const +{ + return pHMgr->encode_flag(aflag); +} + + +// return the preferred ignore string for suggestions +char * AffixMgr::get_ignore() const +{ + if (!ignorechars) return NULL; + return ignorechars; +} + +// return the preferred ignore string for suggestions +unsigned short * AffixMgr::get_ignore_utf16(int * len) const +{ + *len = ignorechars_utf16_len; + return ignorechars_utf16; +} + +// return the keyboard string for suggestions +char * AffixMgr::get_key_string() +{ + if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING); + return mystrdup(keystring); +} + +// return the preferred try string for suggestions +char * AffixMgr::get_try_string() const +{ + if (! trystring ) return NULL; + return mystrdup(trystring); +} + +// return the preferred try string for suggestions +const char * AffixMgr::get_wordchars() const +{ + return wordchars; +} + +unsigned short * AffixMgr::get_wordchars_utf16(int * len) const +{ + *len = wordchars_utf16_len; + return wordchars_utf16; +} + +// is there compounding? +int AffixMgr::get_compound() const +{ + return compoundflag || compoundbegin || numdefcpd; +} + +// return the compound words control flag +FLAG AffixMgr::get_compoundflag() const +{ + return compoundflag; +} + +// return the forbidden words control flag +FLAG AffixMgr::get_forbiddenword() const +{ + return forbiddenword; +} + +// return the forbidden words control flag +FLAG AffixMgr::get_nosuggest() const +{ + return nosuggest; +} + +// return the forbidden words control flag +FLAG AffixMgr::get_nongramsuggest() const +{ + return nongramsuggest; +} + +// return the forbidden words flag modify flag +FLAG AffixMgr::get_needaffix() const +{ + return needaffix; +} + +// return the onlyincompound flag +FLAG AffixMgr::get_onlyincompound() const +{ + return onlyincompound; +} + +// return the compound word signal flag +FLAG AffixMgr::get_compoundroot() const +{ + return compoundroot; +} + +// return the compound begin signal flag +FLAG AffixMgr::get_compoundbegin() const +{ + return compoundbegin; +} + +// return the value of checknum +int AffixMgr::get_checknum() const +{ + return checknum; +} + +// return the value of prefix +const char * AffixMgr::get_prefix() const +{ + if (pfx) return pfx->getKey(); + return NULL; +} + +// return the value of suffix +const char * AffixMgr::get_suffix() const +{ + return sfxappnd; +} + +// return the value of suffix +const char * AffixMgr::get_version() const +{ + return version; +} + +// return lemma_present flag +FLAG AffixMgr::get_lemma_present() const +{ + return lemma_present; +} + +// utility method to look up root words in hash table +struct hentry * AffixMgr::lookup(const char * word) +{ + int i; + struct hentry * he = NULL; + for (i = 0; i < *maxdic && !he; i++) { + he = (alldic[i])->lookup(word); + } + return he; +} + +// return the value of suffix +int AffixMgr::have_contclass() const +{ + return havecontclass; +} + +// return utf8 +int AffixMgr::get_utf8() const +{ + return utf8; +} + +int AffixMgr::get_maxngramsugs(void) const +{ + return maxngramsugs; +} + +int AffixMgr::get_maxcpdsugs(void) const +{ + return maxcpdsugs; +} + +int AffixMgr::get_maxdiff(void) const +{ + return maxdiff; +} + +int AffixMgr::get_onlymaxdiff(void) const +{ + return onlymaxdiff; +} + +// return nosplitsugs +int AffixMgr::get_nosplitsugs(void) const +{ + return nosplitsugs; +} + +// return sugswithdots +int AffixMgr::get_sugswithdots(void) const +{ + return sugswithdots; +} + +/* parse flag */ +int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) { + char * s = NULL; + if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); + return 1; + } + if (parse_string(line, &s, af->getlinenum())) return 1; + *out = pHMgr->decode_flag(s); + free(s); + return 0; +} + +/* parse num */ +int AffixMgr::parse_num(char * line, int * out, FileMgr * af) { + char * s = NULL; + if (*out != -1) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); + return 1; + } + if (parse_string(line, &s, af->getlinenum())) return 1; + *out = atoi(s); + free(s); + return 0; +} + +/* parse in the max syllablecount of compound words and */ +int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af) +{ + char * tp = line; + char * piece; + int i = 0; + int np = 0; + w_char w[MAXWORDLEN]; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { cpdmaxsyllable = atoi(piece); np++; break; } + case 2: { + if (!utf8) { + cpdvowels = mystrdup(piece); + } else { + int n = u8_u16(w, MAXWORDLEN, piece); + if (n > 0) { + flag_qsort((unsigned short *) w, 0, n); + cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char)); + if (!cpdvowels_utf16) return 1; + memcpy(cpdvowels_utf16, w, n * sizeof(w_char)); + } + cpdvowels_utf16_len = n; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np < 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum()); + return 1; + } + if (np == 2) cpdvowels = mystrdup("aeiouAEIOU"); + return 0; +} + +/* parse in the typical fault correcting table */ +int AffixMgr::parse_reptable(char * line, FileMgr * af) +{ + if (numrep != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numrep = atoi(piece); + if (numrep < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum()); + return 1; + } + reptable = (replentry *) malloc(numrep * sizeof(struct replentry)); + if (!reptable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numrep lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numrep; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + reptable[j].pattern = NULL; + reptable[j].pattern2 = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"REP",3) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numrep = 0; + return 1; + } + break; + } + case 1: { + if (*piece == '^') reptable[j].start = true; else reptable[j].start = false; + reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," "); + int lr = strlen(reptable[j].pattern) - 1; + if (reptable[j].pattern[lr] == '$') { + reptable[j].end = true; + reptable[j].pattern[lr] = '\0'; + } else reptable[j].end = false; + break; + } + case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numrep = 0; + return 1; + } + } + return 0; +} + +/* parse in the typical fault correcting table */ +int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword) +{ + if (*rl) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + int numrl = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numrl = atoi(piece); + if (numrl < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum()); + return 1; + } + *rl = new RepList(numrl); + if (!*rl) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the num lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numrl; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + char * pattern = NULL; + char * pattern2 = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece, keyword, sizeof(keyword)) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + delete *rl; + *rl = NULL; + return 1; + } + break; + } + case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; } + case 2: { + pattern2 = mystrrep(mystrdup(piece),"_"," "); + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!pattern || !pattern2) { + if (pattern) + free(pattern); + if (pattern2) + free(pattern2); + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + (*rl)->add(pattern, pattern2); + } + return 0; +} + + +/* parse in the typical fault correcting table */ +int AffixMgr::parse_phonetable(char * line, FileMgr * af) +{ + if (phone) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + phone = (phonetable *) malloc(sizeof(struct phonetable)); + if (!phone) return 1; + phone->num = atoi(piece); + phone->rules = NULL; + phone->utf8 = (char) utf8; + if (phone->num < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *)); + if (!phone->rules) { + free(phone); + phone = NULL; + return 1; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the phone->num lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < phone->num; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + phone->rules[j * 2] = NULL; + phone->rules[j * 2 + 1] = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"PHONE",5) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + phone->num = 0; + return 1; + } + break; + } + case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; } + case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + phone->num = 0; + return 1; + } + } + phone->rules[phone->num * 2] = mystrdup(""); + phone->rules[phone->num * 2 + 1] = mystrdup(""); + init_phonet_hash(*phone); + return 0; +} + +/* parse in the checkcompoundpattern table */ +int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af) +{ + if (numcheckcpd != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numcheckcpd = atoi(piece); + if (numcheckcpd < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry)); + if (!checkcpdtable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numcheckcpd lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numcheckcpd; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + checkcpdtable[j].pattern = NULL; + checkcpdtable[j].pattern2 = NULL; + checkcpdtable[j].pattern3 = NULL; + checkcpdtable[j].cond = FLAG_NULL; + checkcpdtable[j].cond2 = FLAG_NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numcheckcpd = 0; + return 1; + } + break; + } + case 1: { + checkcpdtable[j].pattern = mystrdup(piece); + char * p = strchr(checkcpdtable[j].pattern, '/'); + if (p) { + *p = '\0'; + checkcpdtable[j].cond = pHMgr->decode_flag(p + 1); + } + break; } + case 2: { + checkcpdtable[j].pattern2 = mystrdup(piece); + char * p = strchr(checkcpdtable[j].pattern2, '/'); + if (p) { + *p = '\0'; + checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1); + } + break; + } + case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numcheckcpd = 0; + return 1; + } + } + return 0; +} + +/* parse in the compound rule table */ +int AffixMgr::parse_defcpdtable(char * line, FileMgr * af) +{ + if (numdefcpd != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numdefcpd = atoi(piece); + if (numdefcpd < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry)); + if (!defcpdtable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numdefcpd lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numdefcpd; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + defcpdtable[j].def = NULL; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece, "COMPOUNDRULE", 12) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numdefcpd = 0; + return 1; + } + break; + } + case 1: { // handle parenthesized flags + if (strchr(piece, '(')) { + defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG)); + defcpdtable[j].len = 0; + int end = 0; + FLAG * conv; + while (!end) { + char * par = piece + 1; + while (*par != '(' && *par != ')' && *par != '\0') par++; + if (*par == '\0') end = 1; else *par = '\0'; + if (*piece == '(') piece++; + if (*piece == '*' || *piece == '?') { + defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece; + } else if (*piece != '\0') { + int l = pHMgr->decode_flags(&conv, piece, af); + for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k]; + free(conv); + } + piece = par + 1; + } + } else { + defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af); + } + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!defcpdtable[j].len) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numdefcpd = 0; + return 1; + } + } + return 0; +} + + +/* parse in the character map table */ +int AffixMgr::parse_maptable(char * line, FileMgr * af) +{ + if (nummap != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + nummap = atoi(piece); + if (nummap < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry)); + if (!maptable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the nummap lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < nummap; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + maptable[j].set = NULL; + maptable[j].len = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"MAP",3) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + nummap = 0; + return 1; + } + break; + } + case 1: { + int setn = 0; + maptable[j].len = strlen(piece); + maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*)); + if (!maptable[j].set) return 1; + for (int k = 0; k < maptable[j].len; k++) { + int chl = 1; + int chb = k; + if (piece[k] == '(') { + char * parpos = strchr(piece + k, ')'); + if (parpos != NULL) { + chb = k + 1; + chl = (int)(parpos - piece) - k - 1; + k = k + chl + 1; + } + } else { + if (utf8 && (piece[k] & 0xc0) == 0xc0) { + for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++); + chl = k - chb; + k--; + } + } + maptable[j].set[setn] = (char *) malloc(chl + 1); + if (!maptable[j].set[setn]) return 1; + strncpy(maptable[j].set[setn], piece + chb, chl); + maptable[j].set[setn][chl] = '\0'; + setn++; + } + maptable[j].len = setn; + break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!maptable[j].set || !maptable[j].len) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + nummap = 0; + return 1; + } + } + return 0; +} + +/* parse in the word breakpoint table */ +int AffixMgr::parse_breaktable(char * line, FileMgr * af) +{ + if (numbreak > -1) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numbreak = atoi(piece); + if (numbreak < 0) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + if (numbreak == 0) return 0; + breaktable = (char **) malloc(numbreak * sizeof(char *)); + if (!breaktable) return 1; + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numbreak lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numbreak; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"BREAK",5) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numbreak = 0; + return 1; + } + break; + } + case 1: { + breaktable[j] = mystrdup(piece); + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!breaktable) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numbreak = 0; + return 1; + } + } + return 0; +} + +void AffixMgr::reverse_condition(char * piece) { + int neg = 0; + for (char * k = piece + strlen(piece) - 1; k >= piece; k--) { + switch(*k) { + case '[': { + if (neg) *(k+1) = '['; else *k = ']'; + break; + } + case ']': { + *k = '['; + if (neg) *(k+1) = '^'; + neg = 0; + break; + } + case '^': { + if (*(k+1) == ']') neg = 1; else *(k+1) = *k; + break; + } + default: { + if (neg) *(k+1) = *k; + } + } + } +} + +int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags) +{ + int numents = 0; // number of affentry structures to parse + + unsigned short aflag = 0; // affix char identifier + + char ff=0; + std::vector affentries; + + char * tp = line; + char * nl = line; + char * piece; + int i = 0; + + // checking lines with bad syntax +#ifdef DEBUG + int basefieldnum = 0; +#endif + + // split affix header line into pieces + + int np = 0; + + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + // piece 1 - is type of affix + case 0: { np++; break; } + + // piece 2 - is affix char + case 1: { + np++; + aflag = pHMgr->decode_flag(piece); + if (((at == 'S') && (dupflags[aflag] & dupSFX)) || + ((at == 'P') && (dupflags[aflag] & dupPFX))) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n", + af->getlinenum()); + // return 1; XXX permissive mode for bad dictionaries + } + dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX); + break; + } + // piece 3 - is cross product indicator + case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; } + + // piece 4 - is number of affentries + case 3: { + np++; + numents = atoi(piece); + if (numents == 0) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", + af->getlinenum()); + free(err); + } + return 1; + } + affentries.resize(numents); + affentries[0].opts = ff; + if (utf8) affentries[0].opts += aeUTF8; + if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF; + if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM; + affentries[0].aflag = aflag; + } + + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + // check to make sure we parsed enough pieces + if (np != 4) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + free(err); + } + return 1; + } + + // now parse numents affentries for this affix + std::vector::iterator start = affentries.begin(); + std::vector::iterator end = affentries.end(); + for (std::vector::iterator entry = start; entry != end; ++entry) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + np = 0; + + // split line into pieces + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + // piece 1 - is type + case 0: { + np++; + if (entry != start) entry->opts = start->opts & + (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM); + break; + } + + // piece 2 - is affix char + case 1: { + np++; + if (pHMgr->decode_flag(piece) != aflag) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n", + af->getlinenum(), err); + free(err); + } + return 1; + } + + if (entry != start) entry->aflag = start->aflag; + break; + } + + // piece 3 - is string to strip or 0 for null + case 2: { + np++; + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + entry->strip = mystrdup(piece); + entry->stripl = (unsigned char) strlen(entry->strip); + if (strcmp(entry->strip,"0") == 0) { + free(entry->strip); + entry->strip=mystrdup(""); + entry->stripl = 0; + } + break; + } + + // piece 4 - is affix string or 0 for null + case 3: { + char * dash; + entry->morphcode = NULL; + entry->contclass = NULL; + entry->contclasslen = 0; + np++; + dash = strchr(piece, '/'); + if (dash) { + *dash = '\0'; + + if (ignorechars) { + if (utf8) { + remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len); + } else { + remove_ignored_chars(piece,ignorechars); + } + } + + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + entry->appnd = mystrdup(piece); + + if (pHMgr->is_aliasf()) { + int index = atoi(dash + 1); + entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af); + if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1); + } else { + entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af); + flag_qsort(entry->contclass, 0, entry->contclasslen); + } + *dash = '/'; + + havecontclass = 1; + for (unsigned short _i = 0; _i < entry->contclasslen; _i++) { + contclasses[(entry->contclass)[_i]] = 1; + } + } else { + if (ignorechars) { + if (utf8) { + remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len); + } else { + remove_ignored_chars(piece,ignorechars); + } + } + + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + entry->appnd = mystrdup(piece); + } + + entry->appndl = (unsigned char) strlen(entry->appnd); + if (strcmp(entry->appnd,"0") == 0) { + free(entry->appnd); + entry->appnd=mystrdup(""); + entry->appndl = 0; + } + break; + } + + // piece 5 - is the conditions descriptions + case 4: { + np++; + if (complexprefixes) { + if (utf8) reverseword_utf(piece); else reverseword(piece); + reverse_condition(piece); + } + if (entry->stripl && (strcmp(piece, ".") != 0) && + redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum())) + strcpy(piece, "."); + if (at == 'S') { + reverseword(piece); + reverse_condition(piece); + } + if (encodeit(*entry, piece)) return 1; + break; + } + + case 5: { + np++; + if (pHMgr->is_aliasm()) { + int index = atoi(piece); + entry->morphcode = pHMgr->get_aliasm(index); + } else { + if (complexprefixes) { // XXX - fix me for morph. gen. + if (utf8) reverseword_utf(piece); else reverseword(piece); + } + // add the remaining of the line + if (*tp) { + *(tp - 1) = ' '; + tp = tp + strlen(tp); + } + entry->morphcode = mystrdup(piece); + if (!entry->morphcode) return 1; + } + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + // check to make sure we parsed enough pieces + if (np < 4) { + char * err = pHMgr->encode_flag(aflag); + if (err) { + HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n", + af->getlinenum(), err); + free(err); + } + return 1; + } + +#ifdef DEBUG + // detect unnecessary fields, excepting comments + if (basefieldnum) { + int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6); + if (fieldnum != basefieldnum) + HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum()); + } else { + basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6); + } +#endif + } + + // now create SfxEntry or PfxEntry objects and use links to + // build an ordered (sorted by affix string) list + for (std::vector::iterator entry = start; entry != end; ++entry) { + if (at == 'P') { + PfxEntry * pfxptr = new PfxEntry(this,&(*entry)); + build_pfxtree(pfxptr); + } else { + SfxEntry * sfxptr = new SfxEntry(this,&(*entry)); + build_sfxtree(sfxptr); + } + } + return 0; +} + +int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) { + int condl = strlen(cond); + int i; + int j; + int neg; + int in; + if (ft == 'P') { // prefix + if (strncmp(strip, cond, condl) == 0) return 1; + if (utf8) { + } else { + for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) { + if (cond[j] != '[') { + if (cond[j] != strip[i]) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } else { + neg = (cond[j+1] == '^') ? 1 : 0; + in = 0; + do { + j++; + if (strip[i] == cond[j]) in = 1; + } while ((j < (condl - 1)) && (cond[j] != ']')); + if (j == (condl - 1) && (cond[j] != ']')) { + HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond); + return 0; + } + if ((!neg && !in) || (neg && in)) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } + } + if (j >= condl) return 1; + } + } else { // suffix + if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1; + if (utf8) { + } else { + for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) { + if (cond[j] != ']') { + if (cond[j] != strip[i]) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } else { + in = 0; + do { + j--; + if (strip[i] == cond[j]) in = 1; + } while ((j > 0) && (cond[j] != '[')); + if ((j == 0) && (cond[j] != '[')) { + HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond); + return 0; + } + neg = (cond[j+1] == '^') ? 1 : 0; + if ((!neg && !in) || (neg && in)) { + HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); + return 0; + } + } + } + if (j < 0) return 1; + } + } + return 0; +} diff --git a/src/lib/hunspell/src/affixmgr.hxx b/src/lib/hunspell/src/affixmgr.hxx new file mode 100644 index 0000000..d9c625a --- /dev/null +++ b/src/lib/hunspell/src/affixmgr.hxx @@ -0,0 +1,250 @@ +#ifndef _AFFIXMGR_HXX_ +#define _AFFIXMGR_HXX_ + +#include "hunvisapi.h" + +#include + +#include "atypes.hxx" +#include "baseaffix.hxx" +#include "hashmgr.hxx" +#include "phonet.hxx" +#include "replist.hxx" + +// check flag duplication +#define dupSFX (1 << 0) +#define dupPFX (1 << 1) + +class PfxEntry; +class SfxEntry; + +class LIBHUNSPELL_DLL_EXPORTED AffixMgr +{ + + PfxEntry * pStart[SETSIZE]; + SfxEntry * sStart[SETSIZE]; + PfxEntry * pFlag[SETSIZE]; + SfxEntry * sFlag[SETSIZE]; + HashMgr * pHMgr; + HashMgr ** alldic; + int * maxdic; + char * keystring; + char * trystring; + char * encoding; + struct cs_info * csconv; + int utf8; + int complexprefixes; + FLAG compoundflag; + FLAG compoundbegin; + FLAG compoundmiddle; + FLAG compoundend; + FLAG compoundroot; + FLAG compoundforbidflag; + FLAG compoundpermitflag; + int checkcompounddup; + int checkcompoundrep; + int checkcompoundcase; + int checkcompoundtriple; + int simplifiedtriple; + FLAG forbiddenword; + FLAG nosuggest; + FLAG nongramsuggest; + FLAG needaffix; + int cpdmin; + int numrep; + replentry * reptable; + RepList * iconvtable; + RepList * oconvtable; + int nummap; + mapentry * maptable; + int numbreak; + char ** breaktable; + int numcheckcpd; + patentry * checkcpdtable; + int simplifiedcpd; + int numdefcpd; + flagentry * defcpdtable; + phonetable * phone; + int maxngramsugs; + int maxcpdsugs; + int maxdiff; + int onlymaxdiff; + int nosplitsugs; + int sugswithdots; + int cpdwordmax; + int cpdmaxsyllable; + char * cpdvowels; + w_char * cpdvowels_utf16; + int cpdvowels_utf16_len; + char * cpdsyllablenum; + const char * pfxappnd; // BUG: not stateless + const char * sfxappnd; // BUG: not stateless + FLAG sfxflag; // BUG: not stateless + char * derived; // BUG: not stateless + SfxEntry * sfx; // BUG: not stateless + PfxEntry * pfx; // BUG: not stateless + int checknum; + char * wordchars; + unsigned short * wordchars_utf16; + int wordchars_utf16_len; + char * ignorechars; + unsigned short * ignorechars_utf16; + int ignorechars_utf16_len; + char * version; + char * lang; + int langnum; + FLAG lemma_present; + FLAG circumfix; + FLAG onlyincompound; + FLAG keepcase; + FLAG forceucase; + FLAG warn; + int forbidwarn; + FLAG substandard; + int checksharps; + int fullstrip; + + int havecontclass; // boolean variable + char contclasses[CONTSIZE]; // flags of possible continuing classes (twofold affix) + +public: + + AffixMgr(const char * affpath, HashMgr** ptr, int * md, + const char * key = NULL); + ~AffixMgr(); + struct hentry * affix_check(const char * word, int len, + const unsigned short needflag = (unsigned short) 0, + char in_compound = IN_CPD_NOT); + struct hentry * prefix_check(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + inline int isSubset(const char * s1, const char * s2); + struct hentry * prefix_check_twosfx(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + inline int isRevSubset(const char * s1, const char * end_of_s2, int len); + struct hentry * suffix_check(const char * word, int len, int sfxopts, + PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, + const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, + char in_compound = IN_CPD_NOT); + struct hentry * suffix_check_twosfx(const char * word, int len, + int sfxopts, PfxEntry* ppfx, const FLAG needflag = FLAG_NULL); + + char * affix_check_morph(const char * word, int len, + const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT); + char * prefix_check_morph(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + char * suffix_check_morph (const char * word, int len, int sfxopts, + PfxEntry * ppfx, const FLAG cclass = FLAG_NULL, + const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT); + + char * prefix_check_twosfx_morph(const char * word, int len, + char in_compound, const FLAG needflag = FLAG_NULL); + char * suffix_check_twosfx_morph(const char * word, int len, + int sfxopts, PfxEntry * ppfx, const FLAG needflag = FLAG_NULL); + + char * morphgen(char * ts, int wl, const unsigned short * ap, + unsigned short al, char * morph, char * targetmorph, int level); + + int expand_rootword(struct guessword * wlst, int maxn, const char * ts, + int wl, const unsigned short * ap, unsigned short al, char * bad, + int, char *); + + short get_syllable (const char * word, int wlen); + int cpdrep_check(const char * word, int len); + int cpdpat_check(const char * word, int len, hentry * r1, hentry * r2, + const char affixed); + int defcpd_check(hentry *** words, short wnum, hentry * rv, + hentry ** rwords, char all); + int cpdcase_check(const char * word, int len); + inline int candidate_check(const char * word, int len); + void setcminmax(int * cmin, int * cmax, const char * word, int len); + struct hentry * compound_check(const char * word, int len, short wordnum, + short numsyllable, short maxwordnum, short wnum, hentry ** words, + char hu_mov_rule, char is_sug, int * info); + + int compound_check_morph(const char * word, int len, short wordnum, + short numsyllable, short maxwordnum, short wnum, hentry ** words, + char hu_mov_rule, char ** result, char * partresult); + + struct hentry * lookup(const char * word); + int get_numrep() const; + struct replentry * get_reptable() const; + RepList * get_iconvtable() const; + RepList * get_oconvtable() const; + struct phonetable * get_phonetable() const; + int get_nummap() const; + struct mapentry * get_maptable() const; + int get_numbreak() const; + char ** get_breaktable() const; + char * get_encoding(); + int get_langnum() const; + char * get_key_string(); + char * get_try_string() const; + const char * get_wordchars() const; + unsigned short * get_wordchars_utf16(int * len) const; + char * get_ignore() const; + unsigned short * get_ignore_utf16(int * len) const; + int get_compound() const; + FLAG get_compoundflag() const; + FLAG get_compoundbegin() const; + FLAG get_forbiddenword() const; + FLAG get_nosuggest() const; + FLAG get_nongramsuggest() const; + FLAG get_needaffix() const; + FLAG get_onlyincompound() const; + FLAG get_compoundroot() const; + FLAG get_lemma_present() const; + int get_checknum() const; + const char * get_prefix() const; + const char * get_suffix() const; + const char * get_derived() const; + const char * get_version() const; + int have_contclass() const; + int get_utf8() const; + int get_complexprefixes() const; + char * get_suffixed(char ) const; + int get_maxngramsugs() const; + int get_maxcpdsugs() const; + int get_maxdiff() const; + int get_onlymaxdiff() const; + int get_nosplitsugs() const; + int get_sugswithdots(void) const; + FLAG get_keepcase(void) const; + FLAG get_forceucase(void) const; + FLAG get_warn(void) const; + int get_forbidwarn(void) const; + int get_checksharps(void) const; + char * encode_flag(unsigned short aflag) const; + int get_fullstrip() const; + +private: + int parse_file(const char * affpath, const char * key); + int parse_flag(char * line, unsigned short * out, FileMgr * af); + int parse_num(char * line, int * out, FileMgr * af); + int parse_cpdsyllable(char * line, FileMgr * af); + int parse_reptable(char * line, FileMgr * af); + int parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword); + int parse_phonetable(char * line, FileMgr * af); + int parse_maptable(char * line, FileMgr * af); + int parse_breaktable(char * line, FileMgr * af); + int parse_checkcpdtable(char * line, FileMgr * af); + int parse_defcpdtable(char * line, FileMgr * af); + int parse_affix(char * line, const char at, FileMgr * af, char * dupflags); + + void reverse_condition(char *); + void debugflag(char * result, unsigned short flag); + int condlen(char *); + int encodeit(affentry &entry, char * cs); + int build_pfxtree(PfxEntry* pfxptr); + int build_sfxtree(SfxEntry* sfxptr); + int process_pfx_order(); + int process_sfx_order(); + PfxEntry * process_pfx_in_order(PfxEntry * ptr, PfxEntry * nptr); + SfxEntry * process_sfx_in_order(SfxEntry * ptr, SfxEntry * nptr); + int process_pfx_tree_to_list(); + int process_sfx_tree_to_list(); + int redundant_condition(char, char * strip, int stripl, + const char * cond, int); +}; + +#endif + diff --git a/src/lib/hunspell/src/atypes.hxx b/src/lib/hunspell/src/atypes.hxx new file mode 100644 index 0000000..df27c4d --- /dev/null +++ b/src/lib/hunspell/src/atypes.hxx @@ -0,0 +1,107 @@ +#ifndef _ATYPES_HXX_ +#define _ATYPES_HXX_ + +#ifndef HUNSPELL_WARNING +#include +#ifdef HUNSPELL_WARNING_ON +#define HUNSPELL_WARNING fprintf +#else +// empty inline function to switch off warnings (instead of the C99 standard variadic macros) +static inline void HUNSPELL_WARNING(FILE *, const char *, ...) {} +#endif +#endif + +// HUNSTEM def. +#define HUNSTEM + +#include "hashmgr.hxx" +#include "w_char.hxx" + +#define SETSIZE 256 +#define CONTSIZE 65536 +#define MAXWORDLEN 100 +#define MAXWORDUTF8LEN 256 + +// affentry options +#define aeXPRODUCT (1 << 0) +#define aeUTF8 (1 << 1) +#define aeALIASF (1 << 2) +#define aeALIASM (1 << 3) +#define aeLONGCOND (1 << 4) + +// compound options +#define IN_CPD_NOT 0 +#define IN_CPD_BEGIN 1 +#define IN_CPD_END 2 +#define IN_CPD_OTHER 3 + +// info options +#define SPELL_COMPOUND (1 << 0) +#define SPELL_FORBIDDEN (1 << 1) +#define SPELL_ALLCAP (1 << 2) +#define SPELL_NOCAP (1 << 3) +#define SPELL_INITCAP (1 << 4) +#define SPELL_ORIGCAP (1 << 5) +#define SPELL_WARN (1 << 6) + +#define MAXLNLEN 8192 + +#define MINCPDLEN 3 +#define MAXCOMPOUND 10 +#define MAXCONDLEN 20 +#define MAXCONDLEN_1 (MAXCONDLEN - sizeof(char *)) + +#define MAXACC 1000 + +#define FLAG unsigned short +#define FLAG_NULL 0x00 +#define FREE_FLAG(a) a = 0 + +#define TESTAFF( a, b , c ) flag_bsearch((unsigned short *) a, (unsigned short) b, c) + +struct affentry +{ + char * strip; + char * appnd; + unsigned char stripl; + unsigned char appndl; + char numconds; + char opts; + unsigned short aflag; + unsigned short * contclass; + short contclasslen; + union { + char conds[MAXCONDLEN]; + struct { + char conds1[MAXCONDLEN_1]; + char * conds2; + } l; + } c; + char * morphcode; +}; + +struct guessword { + char * word; + bool allow; + char * orig; +}; + +struct mapentry { + char ** set; + int len; +}; + +struct flagentry { + FLAG * def; + int len; +}; + +struct patentry { + char * pattern; + char * pattern2; + char * pattern3; + FLAG cond; + FLAG cond2; +}; + +#endif diff --git a/src/lib/hunspell/src/baseaffix.hxx b/src/lib/hunspell/src/baseaffix.hxx new file mode 100644 index 0000000..ed64f3d --- /dev/null +++ b/src/lib/hunspell/src/baseaffix.hxx @@ -0,0 +1,28 @@ +#ifndef _BASEAFF_HXX_ +#define _BASEAFF_HXX_ + +#include "hunvisapi.h" + +class LIBHUNSPELL_DLL_EXPORTED AffEntry +{ +protected: + char * appnd; + char * strip; + unsigned char appndl; + unsigned char stripl; + char numconds; + char opts; + unsigned short aflag; + union { + char conds[MAXCONDLEN]; + struct { + char conds1[MAXCONDLEN_1]; + char * conds2; + } l; + } c; + char * morphcode; + unsigned short * contclass; + short contclasslen; +}; + +#endif diff --git a/src/lib/hunspell/src/config.h b/src/lib/hunspell/src/config.h new file mode 100644 index 0000000..5596065 --- /dev/null +++ b/src/lib/hunspell/src/config.h @@ -0,0 +1,208 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +#define CRAY_STACKSEG_END 1 + +/* Define to 1 if using `alloca.c'. */ +#define C_ALLOCA 1 + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +#undef ENABLE_NLS + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ARGZ_H 1 + +/* "Define if you have the header" */ +#undef HAVE_CURSES_H + +/* Define if the GNU dcgettext() function is already present or preinstalled. + */ +#define HAVE_DCGETTEXT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ERROR_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `feof_unlocked' function. */ +#define HAVE_FEOF_UNLOCKED 1 + +/* Define to 1 if you have the `fgets_unlocked' function. */ +#define HAVE_FGETS_UNLOCKED 1 + +/* Define to 1 if you have the `getcwd' function. */ +#define HAVE_GETCWD 1 + +/* Define to 1 if you have the `getc_unlocked' function. */ +#define HAVE_GETC_UNLOCKED 1 + +/* Define to 1 if you have the `getegid' function. */ +#define HAVE_GETEGID 1 + +/* Define to 1 if you have the `geteuid' function. */ +#define HAVE_GETEUID 1 + +/* Define to 1 if you have the `getgid' function. */ +#define HAVE_GETGID 1 + +/* Define to 1 if you have the `getpagesize' function. */ +#define HAVE_GETPAGESIZE 1 + +/* Define if the GNU gettext() function is already present or preinstalled. */ +#define HAVE_GETTEXT 1 + +/* Define to 1 if you have the `getuid' function. */ +#define HAVE_GETUID 1 + +/* Define if you have the iconv() function. */ +#undef HAVE_ICONV + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if you have and nl_langinfo(CODESET). */ +#define HAVE_LANGINFO_CODESET 1 + +/* Define if your file defines LC_MESSAGES. */ +#define HAVE_LC_MESSAGES 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIBINTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `memchr' function. */ +#define HAVE_MEMCHR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +#define HAVE_MEMPCPY 1 + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP 1 + +/* Define to 1 if you have the `munmap' function. */ +#define HAVE_MUNMAP 1 + +/* "Define if you have the header" */ +#define HAVE_NCURSESW_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NL_TYPES_H 1 + +/* Define to 1 if you have the `putenv' function. */ +#define HAVE_PUTENV 1 + +/* "Define if you have fancy command input editing with Readline" */ +#undef HAVE_READLINE + +/* Define to 1 if you have the `setenv' function. */ +#define HAVE_SETENV 1 + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#define HAVE_STPCPY 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strstr' function. */ +#define HAVE_STRSTR 1 + +/* Define to 1 if you have the `strtoul' function. */ +#define HAVE_STRTOUL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_PARAM_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the `tsearch' function. */ +#define HAVE_TSEARCH 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `__argz_count' function. */ +#define HAVE___ARGZ_COUNT 1 + +/* Define to 1 if you have the `__argz_next' function. */ +#define HAVE___ARGZ_NEXT 1 + +/* Define to 1 if you have the `__argz_stringify' function. */ +#define HAVE___ARGZ_STRINGIFY 1 + +/* "Define if you use exterimental functions" */ +#undef HUNSPELL_EXPERIMENTAL + +/* "Define if you need warning messages" */ +#define HUNSPELL_WARNING_ON + +/* Define as const if the declaration of iconv() needs const. */ +#define ICONV_CONST 1 + +/* Name of package */ +#define PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#define PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.3.2" +#define VERSION "1.3.2" diff --git a/src/lib/hunspell/src/csutil.cxx b/src/lib/hunspell/src/csutil.cxx new file mode 100644 index 0000000..dd89c19 --- /dev/null +++ b/src/lib/hunspell/src/csutil.cxx @@ -0,0 +1,5834 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "csutil.hxx" +#include "atypes.hxx" +#include "langnum.hxx" + +// Unicode character encoding information +struct unicode_info { + unsigned short c; + unsigned short cupper; + unsigned short clower; +}; + +#ifdef OPENOFFICEORG +# include +#else +# ifndef MOZILLA_CLIENT +# include "utf_info.cxx" +# define UTF_LST_LEN (sizeof(utf_lst) / (sizeof(unicode_info))) +# endif +#endif + +#ifdef MOZILLA_CLIENT +#include "nsCOMPtr.h" +#include "nsServiceManagerUtils.h" +#include "nsIUnicodeEncoder.h" +#include "nsIUnicodeDecoder.h" +#include "nsUnicharUtils.h" +#include "nsICharsetConverterManager.h" + +static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); +#endif + +struct unicode_info2 { + char cletter; + unsigned short cupper; + unsigned short clower; +}; + +static struct unicode_info2 * utf_tbl = NULL; +static int utf_tbl_count = 0; // utf_tbl can be used by multiple Hunspell instances + +/* only UTF-16 (BMP) implementation */ +char * u16_u8(char * dest, int size, const w_char * src, int srclen) { + signed char * u8 = (signed char *)dest; + signed char * u8_max = (signed char *)(u8 + size); + const w_char * u2 = src; + const w_char * u2_max = src + srclen; + while ((u2 < u2_max) && (u8 < u8_max)) { + if (u2->h) { // > 0xFF + // XXX 4-byte haven't implemented yet. + if (u2->h >= 0x08) { // >= 0x800 (3-byte UTF-8 character) + *u8 = 0xe0 + (u2->h >> 4); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + ((u2->h & 0xf) << 2) + (u2->l >> 6); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + (u2->l & 0x3f); + u8++; + } + } + } else { // < 0x800 (2-byte UTF-8 character) + *u8 = 0xc0 + (u2->h << 2) + (u2->l >> 6); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + (u2->l & 0x3f); + u8++; + } + } + } else { // <= 0xFF + if (u2->l & 0x80) { // >0x80 (2-byte UTF-8 character) + *u8 = 0xc0 + (u2->l >> 6); + u8++; + if (u8 < u8_max) { + *u8 = 0x80 + (u2->l & 0x3f); + u8++; + } + } else { // < 0x80 (1-byte UTF-8 character) + *u8 = u2->l; + u8++; + } + } + u2++; + } + *u8 = '\0'; + return dest; +} + + +/* only UTF-16 (BMP) implementation */ +int u8_u16(w_char * dest, int size, const char * src) { + const signed char * u8 = (const signed char *)src; + w_char * u2 = dest; + w_char * u2_max = u2 + size; + + while ((u2 < u2_max) && *u8) { + switch ((*u8) & 0xf0) { + case 0x00: + case 0x10: + case 0x20: + case 0x30: + case 0x40: + case 0x50: + case 0x60: + case 0x70: { + u2->h = 0; + u2->l = *u8; + break; + } + case 0x80: + case 0x90: + case 0xa0: + case 0xb0: { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Unexpected continuation bytes in %ld. character position\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + break; + } + case 0xc0: + case 0xd0: { // 2-byte UTF-8 codes + if ((*(u8+1) & 0xc0) == 0x80) { + u2->h = (*u8 & 0x1f) >> 2; + u2->l = (*u8 << 6) + (*(u8+1) & 0x3f); + u8++; + } else { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + } + break; + } + case 0xe0: { // 3-byte UTF-8 codes + if ((*(u8+1) & 0xc0) == 0x80) { + u2->h = ((*u8 & 0x0f) << 4) + ((*(u8+1) & 0x3f) >> 2); + u8++; + if ((*(u8+1) & 0xc0) == 0x80) { + u2->l = (*u8 << 6) + (*(u8+1) & 0x3f); + u8++; + } else { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + } + } else { + HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast(u8 - (signed char *)src), src); + u2->h = 0xff; + u2->l = 0xfd; + } + break; + } + case 0xf0: { // 4 or more byte UTF-8 codes + HUNSPELL_WARNING(stderr, "This UTF-8 encoding can't convert to UTF-16:\n%s\n", src); + u2->h = 0xff; + u2->l = 0xfd; + return -1; + } + } + u8++; + u2++; + } + return (int)(u2 - dest); +} + +void flag_qsort(unsigned short flags[], int begin, int end) { + unsigned short reg; + if (end > begin) { + unsigned short pivot = flags[begin]; + int l = begin + 1; + int r = end; + while(l < r) { + if (flags[l] <= pivot) { + l++; + } else { + r--; + reg = flags[l]; + flags[l] = flags[r]; + flags[r] = reg; + } + } + l--; + reg = flags[begin]; + flags[begin] = flags[l]; + flags[l] = reg; + + flag_qsort(flags, begin, l); + flag_qsort(flags, r, end); + } + } + +int flag_bsearch(unsigned short flags[], unsigned short flag, int length) { + int mid; + int left = 0; + int right = length - 1; + while (left <= right) { + mid = (left + right) / 2; + if (flags[mid] == flag) return 1; + if (flag < flags[mid]) right = mid - 1; + else left = mid + 1; + } + return 0; +} + + // strip strings into token based on single char delimiter + // acts like strsep() but only uses a delim char and not + // a delim string + // default delimiter: white space characters + + char * mystrsep(char ** stringp, const char delim) + { + char * mp = *stringp; + if (*mp != '\0') { + char * dp; + if (delim) { + dp = strchr(mp, delim); + } else { + // don't use isspace() here, the string can be in some random charset + // that's way different than the locale's + for (dp = mp; (*dp && *dp != ' ' && *dp != '\t'); dp++); + if (!*dp) dp = NULL; + } + if (dp) { + *stringp = dp+1; + *dp = '\0'; + } else { + *stringp = mp + strlen(mp); + } + return mp; + } + return NULL; + } + + // replaces strdup with ansi version + char * mystrdup(const char * s) + { + char * d = NULL; + if (s) { + size_t sl = strlen(s)+1; + d = (char *) malloc(sl); + if (d) { + memcpy(d,s,sl); + } else { + HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); + } + } + return d; + } + + // strcat for limited length destination string + char * mystrcat(char * dest, const char * st, int max) { + int len; + int len2; + if (dest == NULL || st == NULL) return dest; + len = strlen(dest); + len2 = strlen(st); + if (len + len2 + 1 > max) return dest; + strcpy(dest + len, st); + return dest; + } + + // remove cross-platform text line end characters + void mychomp(char * s) + { + size_t k = strlen(s); + if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0'; + if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0'; + } + + + // does an ansi strdup of the reverse of a string + char * myrevstrdup(const char * s) + { + char * d = NULL; + if (s) { + size_t sl = strlen(s); + d = (char *) malloc(sl+1); + if (d) { + const char * p = s + sl - 1; + char * q = d; + while (p >= s) *q++ = *p--; + *q = '\0'; + } else { + HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); + } + } + return d; + } + +// break text to lines +// return number of lines +int line_tok(const char * text, char *** lines, char breakchar) { + int linenum = 0; + if (!text) { + return linenum; + } + char * dup = mystrdup(text); + char * p = strchr(dup, breakchar); + while (p) { + linenum++; + *p = '\0'; + p++; + p = strchr(p, breakchar); + } + linenum++; + *lines = (char **) malloc(linenum * sizeof(char *)); + if (!(*lines)) { + free(dup); + return 0; + } + + p = dup; + int l = 0; + for (int i = 0; i < linenum; i++) { + if (*p != '\0') { + (*lines)[l] = mystrdup(p); + if (!(*lines)[l]) { + for (i = 0; i < l; i++) free((*lines)[i]); + free(dup); + return 0; + } + l++; + } + p += strlen(p) + 1; + } + free(dup); + if (!l) free(*lines); + return l; +} + +// uniq line in place +char * line_uniq(char * text, char breakchar) { + char ** lines; + int linenum = line_tok(text, &lines, breakchar); + int i; + strcpy(text, lines[0]); + for ( i = 1; i < linenum; i++ ) { + int dup = 0; + for (int j = 0; j < i; j++) { + if (strcmp(lines[i], lines[j]) == 0) dup = 1; + } + if (!dup) { + if ((i > 1) || (*(lines[0]) != '\0')) { + sprintf(text + strlen(text), "%c", breakchar); + } + strcat(text, lines[i]); + } + } + for ( i = 0; i < linenum; i++ ) { + if (lines[i]) free(lines[i]); + } + if (lines) free(lines); + return text; +} + +// uniq and boundary for compound analysis: "1\n\2\n\1" -> " ( \1 | \2 ) " +char * line_uniq_app(char ** text, char breakchar) { + if (!strchr(*text, breakchar)) { + return *text; + } + + char ** lines; + int i; + int linenum = line_tok(*text, &lines, breakchar); + int dup = 0; + for (i = 0; i < linenum; i++) { + for (int j = 0; j < (i - 1); j++) { + if (strcmp(lines[i], lines[j]) == 0) { + *(lines[i]) = '\0'; + dup++; + break; + } + } + } + if ((linenum - dup) == 1) { + strcpy(*text, lines[0]); + freelist(&lines, linenum); + return *text; + } + char * newtext = (char *) malloc(strlen(*text) + 2 * linenum + 3 + 1); + if (newtext) { + free(*text); + *text = newtext; + } else { + freelist(&lines, linenum); + return *text; + } + strcpy(*text," ( "); + for (i = 0; i < linenum; i++) if (*(lines[i])) { + sprintf(*text + strlen(*text), "%s%s", lines[i], " | "); + } + (*text)[strlen(*text) - 2] = ')'; // " ) " + freelist(&lines, linenum); + return *text; +} + + // append s to ends of every lines in text + void strlinecat(char * dest, const char * s) + { + char * dup = mystrdup(dest); + char * source = dup; + int len = strlen(s); + if (dup) { + while (*source) { + if (*source == '\n') { + strncpy(dest, s, len); + dest += len; + } + *dest = *source; + source++; dest++; + } + strcpy(dest, s); + free(dup); + } + } + +// change \n to char c +char * tr(char * text, char oldc, char newc) { + char * p; + for (p = text; *p; p++) if (*p == oldc) *p = newc; + return text; +} + +// morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields +// in the first line of the inputs +// return 0, if inputs equal +// return 1, if inputs may equal with a secondary suffix +// otherwise return -1 +int morphcmp(const char * s, const char * t) +{ + int se = 0; + int te = 0; + const char * sl; + const char * tl; + const char * olds; + const char * oldt; + if (!s || !t) return 1; + olds = s; + sl = strchr(s, '\n'); + s = strstr(s, MORPH_DERI_SFX); + if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX); + if (!s || (sl && sl < s)) { + s= strstr(olds, MORPH_TERM_SFX); + olds = NULL; + } + oldt = t; + tl = strchr(t, '\n'); + t = strstr(t, MORPH_DERI_SFX); + if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX); + if (!t || (tl && tl < t)) { + t = strstr(oldt, MORPH_TERM_SFX); + oldt = NULL; + } + while (s && t && (!sl || sl > s) && (!tl || tl > t)) { + s += MORPH_TAG_LEN; + t += MORPH_TAG_LEN; + se = 0; + te = 0; + while ((*s == *t) && !se && !te) { + s++; + t++; + switch(*s) { + case ' ': + case '\n': + case '\t': + case '\0': se = 1; + } + switch(*t) { + case ' ': + case '\n': + case '\t': + case '\0': te = 1; + } + } + if (!se || !te) { + // not terminal suffix difference + if (olds) return -1; + return 1; + } + olds = s; + s = strstr(s, MORPH_DERI_SFX); + if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX); + if (!s || (sl && sl < s)) { + s = strstr(olds, MORPH_TERM_SFX); + olds = NULL; + } + oldt = t; + t = strstr(t, MORPH_DERI_SFX); + if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX); + if (!t || (tl && tl < t)) { + t = strstr(oldt, MORPH_TERM_SFX); + oldt = NULL; + } + } + if (!s && !t && se && te) return 0; + return 1; +} + +int get_sfxcount(const char * morph) +{ + if (!morph || !*morph) return 0; + int n = 0; + const char * old = morph; + morph = strstr(morph, MORPH_DERI_SFX); + if (!morph) morph = strstr(old, MORPH_INFL_SFX); + if (!morph) morph = strstr(old, MORPH_TERM_SFX); + while (morph) { + n++; + old = morph; + morph = strstr(morph + 1, MORPH_DERI_SFX); + if (!morph) morph = strstr(old + 1, MORPH_INFL_SFX); + if (!morph) morph = strstr(old + 1, MORPH_TERM_SFX); + } + return n; +} + + +int fieldlen(const char * r) +{ + int n = 0; + while (r && *r != ' ' && *r != '\t' && *r != '\0' && *r != '\n') { + r++; + n++; + } + return n; +} + +char * copy_field(char * dest, const char * morph, const char * var) +{ + if (!morph) return NULL; + const char * beg = strstr(morph, var); + if (beg) { + char * d = dest; + for (beg += MORPH_TAG_LEN; *beg != ' ' && *beg != '\t' && + *beg != '\n' && *beg != '\0'; d++, beg++) { + *d = *beg; + } + *d = '\0'; + return dest; + } + return NULL; +} + +char * mystrrep(char * word, const char * pat, const char * rep) { + char * pos = strstr(word, pat); + if (pos) { + int replen = strlen(rep); + int patlen = strlen(pat); + while (pos) { + if (replen < patlen) { + char * end = word + strlen(word); + char * next = pos + replen; + char * prev = pos + strlen(pat); + for (; prev < end; *next = *prev, prev++, next++); + *next = '\0'; + } else if (replen > patlen) { + char * end = pos + patlen; + char * next = word + strlen(word) + replen - patlen; + char * prev = next - replen + patlen; + for (; prev >= end; *next = *prev, prev--, next--); + } + strncpy(pos, rep, replen); + pos = strstr(word, pat); + } + } + return word; +} + + // reverse word + int reverseword(char * word) { + char r; + for (char * dest = word + strlen(word) - 1; word < dest; word++, dest--) { + r=*word; + *word = *dest; + *dest = r; + } + return 0; + } + + // reverse word (error: 1) + int reverseword_utf(char * word) { + w_char w[MAXWORDLEN]; + w_char * p; + w_char r; + int l = u8_u16(w, MAXWORDLEN, word); + if (l == -1) return 1; + p = w; + for (w_char * dest = w + l - 1; p < dest; p++, dest--) { + r=*p; + *p = *dest; + *dest = r; + } + u16_u8(word, MAXWORDUTF8LEN, w, l); + return 0; + } + + int uniqlist(char ** list, int n) { + int i; + if (n < 2) return n; + for (i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (list[j] && list[i] && (strcmp(list[j], list[i]) == 0)) { + free(list[i]); + list[i] = NULL; + break; + } + } + } + int m = 1; + for (i = 1; i < n; i++) if (list[i]) { + list[m] = list[i]; + m++; + } + return m; + } + + void freelist(char *** list, int n) { + if (list && *list && n > 0) { + for (int i = 0; i < n; i++) if ((*list)[i]) free((*list)[i]); + free(*list); + *list = NULL; + } + } + + // convert null terminated string to all caps + void mkallcap(char * p, const struct cs_info * csconv) + { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].cupper; + p++; + } + } + + // convert null terminated string to all little + void mkallsmall(char * p, const struct cs_info * csconv) + { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].clower; + p++; + } + } + +void mkallsmall_utf(w_char * u, int nc, int langnum) { + for (int i = 0; i < nc; i++) { + unsigned short idx = (u[i].h << 8) + u[i].l; + if (idx != unicodetolower(idx, langnum)) { + u[i].h = (unsigned char) (unicodetolower(idx, langnum) >> 8); + u[i].l = (unsigned char) (unicodetolower(idx, langnum) & 0x00FF); + } + } +} + +void mkallcap_utf(w_char * u, int nc, int langnum) { + for (int i = 0; i < nc; i++) { + unsigned short idx = (u[i].h << 8) + u[i].l; + if (idx != unicodetoupper(idx, langnum)) { + u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8); + u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF); + } + } +} + + // convert null terminated string to have initial capital + void mkinitcap(char * p, const struct cs_info * csconv) + { + if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper; + } + + // conversion function for protected memory + void store_pointer(char * dest, char * source) + { + memcpy(dest, &source, sizeof(char *)); + } + + // conversion function for protected memory + char * get_stored_pointer(const char * s) + { + char * p; + memcpy(&p, s, sizeof(char *)); + return p; + } + +#ifndef MOZILLA_CLIENT + // convert null terminated string to all caps using encoding + void enmkallcap(char * d, const char * p, const char * encoding) + + { + struct cs_info * csconv = get_current_cs(encoding); + while (*p != '\0') { + *d++ = csconv[((unsigned char) *p)].cupper; + p++; + } + *d = '\0'; + } + + // convert null terminated string to all little using encoding + void enmkallsmall(char * d, const char * p, const char * encoding) + { + struct cs_info * csconv = get_current_cs(encoding); + while (*p != '\0') { + *d++ = csconv[((unsigned char) *p)].clower; + p++; + } + *d = '\0'; + } + + // convert null terminated string to have initial capital using encoding + void enmkinitcap(char * d, const char * p, const char * encoding) + { + struct cs_info * csconv = get_current_cs(encoding); + memcpy(d,p,(strlen(p)+1)); + if (*p != '\0') *d= csconv[((unsigned char)*p)].cupper; + } + +// these are simple character mappings for the +// encodings supported +// supplying isupper, tolower, and toupper + +static struct cs_info iso1_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + + +static struct cs_info iso2_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xb1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x01, 0xb3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x01, 0xb5, 0xa5 }, +{ 0x01, 0xb6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x01, 0xb9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x01, 0xbb, 0xab }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x01, 0xbe, 0xae }, +{ 0x01, 0xbf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xa1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xa3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xa5 }, +{ 0x00, 0xb6, 0xa6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xa9 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xab }, +{ 0x00, 0xbc, 0xac }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xae }, +{ 0x00, 0xbf, 0xaf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + + +static struct cs_info iso3_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xb1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x01, 0xb6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x01, 0x69, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x01, 0xbb, 0xab }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x01, 0xbf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xa1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xa6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0x49 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xab }, +{ 0x00, 0xbc, 0xac }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xaf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso4_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xb1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x01, 0xb3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x01, 0xb5, 0xa5 }, +{ 0x01, 0xb6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x01, 0xb9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x01, 0xbb, 0xab }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x01, 0xbe, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xa1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xa3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xa5 }, +{ 0x00, 0xb6, 0xa6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xa9 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xab }, +{ 0x00, 0xbc, 0xac }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xae }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso5_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xf1, 0xa1 }, +{ 0x01, 0xf2, 0xa2 }, +{ 0x01, 0xf3, 0xa3 }, +{ 0x01, 0xf4, 0xa4 }, +{ 0x01, 0xf5, 0xa5 }, +{ 0x01, 0xf6, 0xa6 }, +{ 0x01, 0xf7, 0xa7 }, +{ 0x01, 0xf8, 0xa8 }, +{ 0x01, 0xf9, 0xa9 }, +{ 0x01, 0xfa, 0xaa }, +{ 0x01, 0xfb, 0xab }, +{ 0x01, 0xfc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x01, 0xfe, 0xae }, +{ 0x01, 0xff, 0xaf }, +{ 0x01, 0xd0, 0xb0 }, +{ 0x01, 0xd1, 0xb1 }, +{ 0x01, 0xd2, 0xb2 }, +{ 0x01, 0xd3, 0xb3 }, +{ 0x01, 0xd4, 0xb4 }, +{ 0x01, 0xd5, 0xb5 }, +{ 0x01, 0xd6, 0xb6 }, +{ 0x01, 0xd7, 0xb7 }, +{ 0x01, 0xd8, 0xb8 }, +{ 0x01, 0xd9, 0xb9 }, +{ 0x01, 0xda, 0xba }, +{ 0x01, 0xdb, 0xbb }, +{ 0x01, 0xdc, 0xbc }, +{ 0x01, 0xdd, 0xbd }, +{ 0x01, 0xde, 0xbe }, +{ 0x01, 0xdf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x00, 0xd0, 0xb0 }, +{ 0x00, 0xd1, 0xb1 }, +{ 0x00, 0xd2, 0xb2 }, +{ 0x00, 0xd3, 0xb3 }, +{ 0x00, 0xd4, 0xb4 }, +{ 0x00, 0xd5, 0xb5 }, +{ 0x00, 0xd6, 0xb6 }, +{ 0x00, 0xd7, 0xb7 }, +{ 0x00, 0xd8, 0xb8 }, +{ 0x00, 0xd9, 0xb9 }, +{ 0x00, 0xda, 0xba }, +{ 0x00, 0xdb, 0xbb }, +{ 0x00, 0xdc, 0xbc }, +{ 0x00, 0xdd, 0xbd }, +{ 0x00, 0xde, 0xbe }, +{ 0x00, 0xdf, 0xbf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xa1 }, +{ 0x00, 0xf2, 0xa2 }, +{ 0x00, 0xf3, 0xa3 }, +{ 0x00, 0xf4, 0xa4 }, +{ 0x00, 0xf5, 0xa5 }, +{ 0x00, 0xf6, 0xa6 }, +{ 0x00, 0xf7, 0xa7 }, +{ 0x00, 0xf8, 0xa8 }, +{ 0x00, 0xf9, 0xa9 }, +{ 0x00, 0xfa, 0xaa }, +{ 0x00, 0xfb, 0xab }, +{ 0x00, 0xfc, 0xac }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xae }, +{ 0x00, 0xff, 0xaf } +}; + +static struct cs_info iso6_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso7_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x01, 0xdc, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x01, 0xdd, 0xb8 }, +{ 0x01, 0xde, 0xb9 }, +{ 0x01, 0xdf, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x01, 0xfc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x01, 0xfd, 0xbe }, +{ 0x01, 0xfe, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x01, 0xf7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x00, 0xdc, 0xb6 }, +{ 0x00, 0xdd, 0xb8 }, +{ 0x00, 0xde, 0xb9 }, +{ 0x00, 0xdf, 0xba }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd3 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xd7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xbc }, +{ 0x00, 0xfd, 0xbe }, +{ 0x00, 0xfe, 0xbf }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso8_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso9_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0xfd, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0xdd }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0x69, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0x49 }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso10_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info koi8r_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xb3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x01, 0xa3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xe0 }, +{ 0x00, 0xc1, 0xe1 }, +{ 0x00, 0xc2, 0xe2 }, +{ 0x00, 0xc3, 0xe3 }, +{ 0x00, 0xc4, 0xe4 }, +{ 0x00, 0xc5, 0xe5 }, +{ 0x00, 0xc6, 0xe6 }, +{ 0x00, 0xc7, 0xe7 }, +{ 0x00, 0xc8, 0xe8 }, +{ 0x00, 0xc9, 0xe9 }, +{ 0x00, 0xca, 0xea }, +{ 0x00, 0xcb, 0xeb }, +{ 0x00, 0xcc, 0xec }, +{ 0x00, 0xcd, 0xed }, +{ 0x00, 0xce, 0xee }, +{ 0x00, 0xcf, 0xef }, +{ 0x00, 0xd0, 0xf0 }, +{ 0x00, 0xd1, 0xf1 }, +{ 0x00, 0xd2, 0xf2 }, +{ 0x00, 0xd3, 0xf3 }, +{ 0x00, 0xd4, 0xf4 }, +{ 0x00, 0xd5, 0xf5 }, +{ 0x00, 0xd6, 0xf6 }, +{ 0x00, 0xd7, 0xf7 }, +{ 0x00, 0xd8, 0xf8 }, +{ 0x00, 0xd9, 0xf9 }, +{ 0x00, 0xda, 0xfa }, +{ 0x00, 0xdb, 0xfb }, +{ 0x00, 0xdc, 0xfc }, +{ 0x00, 0xdd, 0xfd }, +{ 0x00, 0xde, 0xfe }, +{ 0x00, 0xdf, 0xff }, +{ 0x01, 0xc0, 0xe0 }, +{ 0x01, 0xc1, 0xe1 }, +{ 0x01, 0xc2, 0xe2 }, +{ 0x01, 0xc3, 0xe3 }, +{ 0x01, 0xc4, 0xe4 }, +{ 0x01, 0xc5, 0xe5 }, +{ 0x01, 0xc6, 0xe6 }, +{ 0x01, 0xc7, 0xe7 }, +{ 0x01, 0xc8, 0xe8 }, +{ 0x01, 0xc9, 0xe9 }, +{ 0x01, 0xca, 0xea }, +{ 0x01, 0xcb, 0xeb }, +{ 0x01, 0xcc, 0xec }, +{ 0x01, 0xcd, 0xed }, +{ 0x01, 0xce, 0xee }, +{ 0x01, 0xcf, 0xef }, +{ 0x01, 0xd0, 0xf0 }, +{ 0x01, 0xd1, 0xf1 }, +{ 0x01, 0xd2, 0xf2 }, +{ 0x01, 0xd3, 0xf3 }, +{ 0x01, 0xd4, 0xf4 }, +{ 0x01, 0xd5, 0xf5 }, +{ 0x01, 0xd6, 0xf6 }, +{ 0x01, 0xd7, 0xf7 }, +{ 0x01, 0xd8, 0xf8 }, +{ 0x01, 0xd9, 0xf9 }, +{ 0x01, 0xda, 0xfa }, +{ 0x01, 0xdb, 0xfb }, +{ 0x01, 0xdc, 0xfc }, +{ 0x01, 0xdd, 0xfd }, +{ 0x01, 0xde, 0xfe }, +{ 0x01, 0xdf, 0xff } +}; + +static struct cs_info koi8u_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xb3 }, +{ 0x00, 0xa4, 0xb4 }, /* ie */ +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xb6 }, /* i */ +{ 0x00, 0xa7, 0xb7 }, /* ii */ +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xbd }, /* g'' */ +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x01, 0xa3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, /* IE */ +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, /* I */ +{ 0x00, 0xb7, 0xb7 }, /* II */ +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xe0 }, +{ 0x00, 0xc1, 0xe1 }, +{ 0x00, 0xc2, 0xe2 }, +{ 0x00, 0xc3, 0xe3 }, +{ 0x00, 0xc4, 0xe4 }, +{ 0x00, 0xc5, 0xe5 }, +{ 0x00, 0xc6, 0xe6 }, +{ 0x00, 0xc7, 0xe7 }, +{ 0x00, 0xc8, 0xe8 }, +{ 0x00, 0xc9, 0xe9 }, +{ 0x00, 0xca, 0xea }, +{ 0x00, 0xcb, 0xeb }, +{ 0x00, 0xcc, 0xec }, +{ 0x00, 0xcd, 0xed }, +{ 0x00, 0xce, 0xee }, +{ 0x00, 0xcf, 0xef }, +{ 0x00, 0xd0, 0xf0 }, +{ 0x00, 0xd1, 0xf1 }, +{ 0x00, 0xd2, 0xf2 }, +{ 0x00, 0xd3, 0xf3 }, +{ 0x00, 0xd4, 0xf4 }, +{ 0x00, 0xd5, 0xf5 }, +{ 0x00, 0xd6, 0xf6 }, +{ 0x00, 0xd7, 0xf7 }, +{ 0x00, 0xd8, 0xf8 }, +{ 0x00, 0xd9, 0xf9 }, +{ 0x00, 0xda, 0xfa }, +{ 0x00, 0xdb, 0xfb }, +{ 0x00, 0xdc, 0xfc }, +{ 0x00, 0xdd, 0xfd }, +{ 0x00, 0xde, 0xfe }, +{ 0x00, 0xdf, 0xff }, +{ 0x01, 0xc0, 0xe0 }, +{ 0x01, 0xc1, 0xe1 }, +{ 0x01, 0xc2, 0xe2 }, +{ 0x01, 0xc3, 0xe3 }, +{ 0x01, 0xc4, 0xe4 }, +{ 0x01, 0xc5, 0xe5 }, +{ 0x01, 0xc6, 0xe6 }, +{ 0x01, 0xc7, 0xe7 }, +{ 0x01, 0xc8, 0xe8 }, +{ 0x01, 0xc9, 0xe9 }, +{ 0x01, 0xca, 0xea }, +{ 0x01, 0xcb, 0xeb }, +{ 0x01, 0xcc, 0xec }, +{ 0x01, 0xcd, 0xed }, +{ 0x01, 0xce, 0xee }, +{ 0x01, 0xcf, 0xef }, +{ 0x01, 0xd0, 0xf0 }, +{ 0x01, 0xd1, 0xf1 }, +{ 0x01, 0xd2, 0xf2 }, +{ 0x01, 0xd3, 0xf3 }, +{ 0x01, 0xd4, 0xf4 }, +{ 0x01, 0xd5, 0xf5 }, +{ 0x01, 0xd6, 0xf6 }, +{ 0x01, 0xd7, 0xf7 }, +{ 0x01, 0xd8, 0xf8 }, +{ 0x01, 0xd9, 0xf9 }, +{ 0x01, 0xda, 0xfa }, +{ 0x01, 0xdb, 0xfb }, +{ 0x01, 0xdc, 0xfc }, +{ 0x01, 0xdd, 0xfd }, +{ 0x01, 0xde, 0xfe }, +{ 0x01, 0xdf, 0xff } +}; + +static struct cs_info cp1251_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x01, 0x90, 0x80 }, +{ 0x01, 0x83, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x81 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x01, 0x9a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x01, 0x9c, 0x8c }, +{ 0x01, 0x9d, 0x8d }, +{ 0x01, 0x9e, 0x8e }, +{ 0x01, 0x9f, 0x8f }, +{ 0x00, 0x90, 0x80 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x8a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x8c }, +{ 0x00, 0x9d, 0x8d }, +{ 0x00, 0x9e, 0x8e }, +{ 0x00, 0x9f, 0x8f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xa2, 0xa1 }, +{ 0x00, 0xa2, 0xa1 }, +{ 0x01, 0xbc, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x01, 0xb4, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x01, 0xb8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x01, 0xbf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x01, 0xb3, 0xb2 }, +{ 0x00, 0xb3, 0xb2 }, +{ 0x00, 0xb4, 0xa5 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xa8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xaa }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xa3 }, +{ 0x01, 0xbe, 0xbd }, +{ 0x00, 0xbe, 0xbd }, +{ 0x00, 0xbf, 0xaf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x01, 0xf7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x01, 0xff, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xd7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xdf } +}; + +static struct cs_info iso13_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0A, 0x0A }, +{ 0x00, 0x0B, 0x0B }, +{ 0x00, 0x0C, 0x0C }, +{ 0x00, 0x0D, 0x0D }, +{ 0x00, 0x0E, 0x0E }, +{ 0x00, 0x0F, 0x0F }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1A, 0x1A }, +{ 0x00, 0x1B, 0x1B }, +{ 0x00, 0x1C, 0x1C }, +{ 0x00, 0x1D, 0x1D }, +{ 0x00, 0x1E, 0x1E }, +{ 0x00, 0x1F, 0x1F }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2A, 0x2A }, +{ 0x00, 0x2B, 0x2B }, +{ 0x00, 0x2C, 0x2C }, +{ 0x00, 0x2D, 0x2D }, +{ 0x00, 0x2E, 0x2E }, +{ 0x00, 0x2F, 0x2F }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3A, 0x3A }, +{ 0x00, 0x3B, 0x3B }, +{ 0x00, 0x3C, 0x3C }, +{ 0x00, 0x3D, 0x3D }, +{ 0x00, 0x3E, 0x3E }, +{ 0x00, 0x3F, 0x3F }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6A, 0x4A }, +{ 0x01, 0x6B, 0x4B }, +{ 0x01, 0x6C, 0x4C }, +{ 0x01, 0x6D, 0x4D }, +{ 0x01, 0x6E, 0x4E }, +{ 0x01, 0x6F, 0x4F }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7A, 0x5A }, +{ 0x00, 0x5B, 0x5B }, +{ 0x00, 0x5C, 0x5C }, +{ 0x00, 0x5D, 0x5D }, +{ 0x00, 0x5E, 0x5E }, +{ 0x00, 0x5F, 0x5F }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6A, 0x4A }, +{ 0x00, 0x6B, 0x4B }, +{ 0x00, 0x6C, 0x4C }, +{ 0x00, 0x6D, 0x4D }, +{ 0x00, 0x6E, 0x4E }, +{ 0x00, 0x6F, 0x4F }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7A, 0x5A }, +{ 0x00, 0x7B, 0x7B }, +{ 0x00, 0x7C, 0x7C }, +{ 0x00, 0x7D, 0x7D }, +{ 0x00, 0x7E, 0x7E }, +{ 0x00, 0x7F, 0x7F }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8A, 0x8A }, +{ 0x00, 0x8B, 0x8B }, +{ 0x00, 0x8C, 0x8C }, +{ 0x00, 0x8D, 0x8D }, +{ 0x00, 0x8E, 0x8E }, +{ 0x00, 0x8F, 0x8F }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9A, 0x9A }, +{ 0x00, 0x9B, 0x9B }, +{ 0x00, 0x9C, 0x9C }, +{ 0x00, 0x9D, 0x9D }, +{ 0x00, 0x9E, 0x9E }, +{ 0x00, 0x9F, 0x9F }, +{ 0x00, 0xA0, 0xA0 }, +{ 0x00, 0xA1, 0xA1 }, +{ 0x00, 0xA2, 0xA2 }, +{ 0x00, 0xA3, 0xA3 }, +{ 0x00, 0xA4, 0xA4 }, +{ 0x00, 0xA5, 0xA5 }, +{ 0x00, 0xA6, 0xA6 }, +{ 0x00, 0xA7, 0xA7 }, +{ 0x01, 0xB8, 0xA8 }, +{ 0x00, 0xA9, 0xA9 }, +{ 0x01, 0xBA, 0xAA }, +{ 0x00, 0xAB, 0xAB }, +{ 0x00, 0xAC, 0xAC }, +{ 0x00, 0xAD, 0xAD }, +{ 0x00, 0xAE, 0xAE }, +{ 0x01, 0xBF, 0xAF }, +{ 0x00, 0xB0, 0xB0 }, +{ 0x00, 0xB1, 0xB1 }, +{ 0x00, 0xB2, 0xB2 }, +{ 0x00, 0xB3, 0xB3 }, +{ 0x00, 0xB4, 0xB4 }, +{ 0x00, 0xB5, 0xB5 }, +{ 0x00, 0xB6, 0xB6 }, +{ 0x00, 0xB7, 0xB7 }, +{ 0x00, 0xB8, 0xA8 }, +{ 0x00, 0xB9, 0xB9 }, +{ 0x00, 0xBA, 0xAA }, +{ 0x00, 0xBB, 0xBB }, +{ 0x00, 0xBC, 0xBC }, +{ 0x00, 0xBD, 0xBD }, +{ 0x00, 0xBE, 0xBE }, +{ 0x00, 0xBF, 0xAF }, +{ 0x01, 0xE0, 0xC0 }, +{ 0x01, 0xE1, 0xC1 }, +{ 0x01, 0xE2, 0xC2 }, +{ 0x01, 0xE3, 0xC3 }, +{ 0x01, 0xE4, 0xC4 }, +{ 0x01, 0xE5, 0xC5 }, +{ 0x01, 0xE6, 0xC6 }, +{ 0x01, 0xE7, 0xC7 }, +{ 0x01, 0xE8, 0xC8 }, +{ 0x01, 0xE9, 0xC9 }, +{ 0x01, 0xEA, 0xCA }, +{ 0x01, 0xEB, 0xCB }, +{ 0x01, 0xEC, 0xCC }, +{ 0x01, 0xED, 0xCD }, +{ 0x01, 0xEE, 0xCE }, +{ 0x01, 0xEF, 0xCF }, +{ 0x01, 0xF0, 0xD0 }, +{ 0x01, 0xF1, 0xD1 }, +{ 0x01, 0xF2, 0xD2 }, +{ 0x01, 0xF3, 0xD3 }, +{ 0x01, 0xF4, 0xD4 }, +{ 0x01, 0xF5, 0xD5 }, +{ 0x01, 0xF6, 0xD6 }, +{ 0x00, 0xD7, 0xD7 }, +{ 0x01, 0xF8, 0xD8 }, +{ 0x01, 0xF9, 0xD9 }, +{ 0x01, 0xFA, 0xDA }, +{ 0x01, 0xFB, 0xDB }, +{ 0x01, 0xFC, 0xDC }, +{ 0x01, 0xFD, 0xDD }, +{ 0x01, 0xFE, 0xDE }, +{ 0x00, 0xDF, 0xDF }, +{ 0x00, 0xE0, 0xC0 }, +{ 0x00, 0xE1, 0xC1 }, +{ 0x00, 0xE2, 0xC2 }, +{ 0x00, 0xE3, 0xC3 }, +{ 0x00, 0xE4, 0xC4 }, +{ 0x00, 0xE5, 0xC5 }, +{ 0x00, 0xE6, 0xC6 }, +{ 0x00, 0xE7, 0xC7 }, +{ 0x00, 0xE8, 0xC8 }, +{ 0x00, 0xE9, 0xC9 }, +{ 0x00, 0xEA, 0xCA }, +{ 0x00, 0xEB, 0xCB }, +{ 0x00, 0xEC, 0xCC }, +{ 0x00, 0xED, 0xCD }, +{ 0x00, 0xEE, 0xCE }, +{ 0x00, 0xEF, 0xCF }, +{ 0x00, 0xF0, 0xD0 }, +{ 0x00, 0xF1, 0xD1 }, +{ 0x00, 0xF2, 0xD2 }, +{ 0x00, 0xF3, 0xD3 }, +{ 0x00, 0xF4, 0xD4 }, +{ 0x00, 0xF5, 0xD5 }, +{ 0x00, 0xF6, 0xD6 }, +{ 0x00, 0xF7, 0xF7 }, +{ 0x00, 0xF8, 0xD8 }, +{ 0x00, 0xF9, 0xD9 }, +{ 0x00, 0xFA, 0xDA }, +{ 0x00, 0xFB, 0xDB }, +{ 0x00, 0xFC, 0xDC }, +{ 0x00, 0xFD, 0xDD }, +{ 0x00, 0xFE, 0xDE }, +{ 0x00, 0xFF, 0xFF } +}; + + +static struct cs_info iso14_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x01, 0xa2, 0xa1 }, +{ 0x00, 0xa2, 0xa1 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x01, 0xa5, 0xa4 }, +{ 0x00, 0xa5, 0xa4 }, +{ 0x01, 0xa6, 0xab }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x01, 0xb8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x01, 0xba, 0xaa }, +{ 0x00, 0xab, 0xa6 }, +{ 0x01, 0xbc, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x01, 0xff, 0xaf }, +{ 0x01, 0xb1, 0xb0 }, +{ 0x00, 0xb1, 0xb0 }, +{ 0x01, 0xb3, 0xb2 }, +{ 0x00, 0xb3, 0xb2 }, +{ 0x01, 0xb5, 0xb4 }, +{ 0x00, 0xb5, 0xb4 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x01, 0xb9, 0xb7 }, +{ 0x00, 0xb8, 0xa8 }, +{ 0x00, 0xb9, 0xb6 }, +{ 0x00, 0xba, 0xaa }, +{ 0x01, 0xbf, 0xbb }, +{ 0x00, 0xbc, 0xac }, +{ 0x01, 0xbe, 0xbd }, +{ 0x00, 0xbe, 0xbd }, +{ 0x00, 0xbf, 0xbb }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x01, 0xf7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xd7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info iso15_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x01, 0xa8, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa6 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x01, 0xb8, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb4 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x01, 0xbd, 0xbc }, +{ 0x00, 0xbd, 0xbc }, +{ 0x01, 0xff, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x01, 0xe0, 0xc0 }, +{ 0x01, 0xe1, 0xc1 }, +{ 0x01, 0xe2, 0xc2 }, +{ 0x01, 0xe3, 0xc3 }, +{ 0x01, 0xe4, 0xc4 }, +{ 0x01, 0xe5, 0xc5 }, +{ 0x01, 0xe6, 0xc6 }, +{ 0x01, 0xe7, 0xc7 }, +{ 0x01, 0xe8, 0xc8 }, +{ 0x01, 0xe9, 0xc9 }, +{ 0x01, 0xea, 0xca }, +{ 0x01, 0xeb, 0xcb }, +{ 0x01, 0xec, 0xcc }, +{ 0x01, 0xed, 0xcd }, +{ 0x01, 0xee, 0xce }, +{ 0x01, 0xef, 0xcf }, +{ 0x01, 0xf0, 0xd0 }, +{ 0x01, 0xf1, 0xd1 }, +{ 0x01, 0xf2, 0xd2 }, +{ 0x01, 0xf3, 0xd3 }, +{ 0x01, 0xf4, 0xd4 }, +{ 0x01, 0xf5, 0xd5 }, +{ 0x01, 0xf6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x01, 0xf8, 0xd8 }, +{ 0x01, 0xf9, 0xd9 }, +{ 0x01, 0xfa, 0xda }, +{ 0x01, 0xfb, 0xdb }, +{ 0x01, 0xfc, 0xdc }, +{ 0x01, 0xfd, 0xdd }, +{ 0x01, 0xfe, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xc0 }, +{ 0x00, 0xe1, 0xc1 }, +{ 0x00, 0xe2, 0xc2 }, +{ 0x00, 0xe3, 0xc3 }, +{ 0x00, 0xe4, 0xc4 }, +{ 0x00, 0xe5, 0xc5 }, +{ 0x00, 0xe6, 0xc6 }, +{ 0x00, 0xe7, 0xc7 }, +{ 0x00, 0xe8, 0xc8 }, +{ 0x00, 0xe9, 0xc9 }, +{ 0x00, 0xea, 0xca }, +{ 0x00, 0xeb, 0xcb }, +{ 0x00, 0xec, 0xcc }, +{ 0x00, 0xed, 0xcd }, +{ 0x00, 0xee, 0xce }, +{ 0x00, 0xef, 0xcf }, +{ 0x00, 0xf0, 0xd0 }, +{ 0x00, 0xf1, 0xd1 }, +{ 0x00, 0xf2, 0xd2 }, +{ 0x00, 0xf3, 0xd3 }, +{ 0x00, 0xf4, 0xd4 }, +{ 0x00, 0xf5, 0xd5 }, +{ 0x00, 0xf6, 0xd6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xd8 }, +{ 0x00, 0xf9, 0xd9 }, +{ 0x00, 0xfa, 0xda }, +{ 0x00, 0xfb, 0xdb }, +{ 0x00, 0xfc, 0xdc }, +{ 0x00, 0xfd, 0xdd }, +{ 0x00, 0xfe, 0xde }, +{ 0x00, 0xff, 0xbe } +}; + +static struct cs_info iscii_devanagari_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +static struct cs_info tis620_tbl[] = { +{ 0x00, 0x00, 0x00 }, +{ 0x00, 0x01, 0x01 }, +{ 0x00, 0x02, 0x02 }, +{ 0x00, 0x03, 0x03 }, +{ 0x00, 0x04, 0x04 }, +{ 0x00, 0x05, 0x05 }, +{ 0x00, 0x06, 0x06 }, +{ 0x00, 0x07, 0x07 }, +{ 0x00, 0x08, 0x08 }, +{ 0x00, 0x09, 0x09 }, +{ 0x00, 0x0a, 0x0a }, +{ 0x00, 0x0b, 0x0b }, +{ 0x00, 0x0c, 0x0c }, +{ 0x00, 0x0d, 0x0d }, +{ 0x00, 0x0e, 0x0e }, +{ 0x00, 0x0f, 0x0f }, +{ 0x00, 0x10, 0x10 }, +{ 0x00, 0x11, 0x11 }, +{ 0x00, 0x12, 0x12 }, +{ 0x00, 0x13, 0x13 }, +{ 0x00, 0x14, 0x14 }, +{ 0x00, 0x15, 0x15 }, +{ 0x00, 0x16, 0x16 }, +{ 0x00, 0x17, 0x17 }, +{ 0x00, 0x18, 0x18 }, +{ 0x00, 0x19, 0x19 }, +{ 0x00, 0x1a, 0x1a }, +{ 0x00, 0x1b, 0x1b }, +{ 0x00, 0x1c, 0x1c }, +{ 0x00, 0x1d, 0x1d }, +{ 0x00, 0x1e, 0x1e }, +{ 0x00, 0x1f, 0x1f }, +{ 0x00, 0x20, 0x20 }, +{ 0x00, 0x21, 0x21 }, +{ 0x00, 0x22, 0x22 }, +{ 0x00, 0x23, 0x23 }, +{ 0x00, 0x24, 0x24 }, +{ 0x00, 0x25, 0x25 }, +{ 0x00, 0x26, 0x26 }, +{ 0x00, 0x27, 0x27 }, +{ 0x00, 0x28, 0x28 }, +{ 0x00, 0x29, 0x29 }, +{ 0x00, 0x2a, 0x2a }, +{ 0x00, 0x2b, 0x2b }, +{ 0x00, 0x2c, 0x2c }, +{ 0x00, 0x2d, 0x2d }, +{ 0x00, 0x2e, 0x2e }, +{ 0x00, 0x2f, 0x2f }, +{ 0x00, 0x30, 0x30 }, +{ 0x00, 0x31, 0x31 }, +{ 0x00, 0x32, 0x32 }, +{ 0x00, 0x33, 0x33 }, +{ 0x00, 0x34, 0x34 }, +{ 0x00, 0x35, 0x35 }, +{ 0x00, 0x36, 0x36 }, +{ 0x00, 0x37, 0x37 }, +{ 0x00, 0x38, 0x38 }, +{ 0x00, 0x39, 0x39 }, +{ 0x00, 0x3a, 0x3a }, +{ 0x00, 0x3b, 0x3b }, +{ 0x00, 0x3c, 0x3c }, +{ 0x00, 0x3d, 0x3d }, +{ 0x00, 0x3e, 0x3e }, +{ 0x00, 0x3f, 0x3f }, +{ 0x00, 0x40, 0x40 }, +{ 0x01, 0x61, 0x41 }, +{ 0x01, 0x62, 0x42 }, +{ 0x01, 0x63, 0x43 }, +{ 0x01, 0x64, 0x44 }, +{ 0x01, 0x65, 0x45 }, +{ 0x01, 0x66, 0x46 }, +{ 0x01, 0x67, 0x47 }, +{ 0x01, 0x68, 0x48 }, +{ 0x01, 0x69, 0x49 }, +{ 0x01, 0x6a, 0x4a }, +{ 0x01, 0x6b, 0x4b }, +{ 0x01, 0x6c, 0x4c }, +{ 0x01, 0x6d, 0x4d }, +{ 0x01, 0x6e, 0x4e }, +{ 0x01, 0x6f, 0x4f }, +{ 0x01, 0x70, 0x50 }, +{ 0x01, 0x71, 0x51 }, +{ 0x01, 0x72, 0x52 }, +{ 0x01, 0x73, 0x53 }, +{ 0x01, 0x74, 0x54 }, +{ 0x01, 0x75, 0x55 }, +{ 0x01, 0x76, 0x56 }, +{ 0x01, 0x77, 0x57 }, +{ 0x01, 0x78, 0x58 }, +{ 0x01, 0x79, 0x59 }, +{ 0x01, 0x7a, 0x5a }, +{ 0x00, 0x5b, 0x5b }, +{ 0x00, 0x5c, 0x5c }, +{ 0x00, 0x5d, 0x5d }, +{ 0x00, 0x5e, 0x5e }, +{ 0x00, 0x5f, 0x5f }, +{ 0x00, 0x60, 0x60 }, +{ 0x00, 0x61, 0x41 }, +{ 0x00, 0x62, 0x42 }, +{ 0x00, 0x63, 0x43 }, +{ 0x00, 0x64, 0x44 }, +{ 0x00, 0x65, 0x45 }, +{ 0x00, 0x66, 0x46 }, +{ 0x00, 0x67, 0x47 }, +{ 0x00, 0x68, 0x48 }, +{ 0x00, 0x69, 0x49 }, +{ 0x00, 0x6a, 0x4a }, +{ 0x00, 0x6b, 0x4b }, +{ 0x00, 0x6c, 0x4c }, +{ 0x00, 0x6d, 0x4d }, +{ 0x00, 0x6e, 0x4e }, +{ 0x00, 0x6f, 0x4f }, +{ 0x00, 0x70, 0x50 }, +{ 0x00, 0x71, 0x51 }, +{ 0x00, 0x72, 0x52 }, +{ 0x00, 0x73, 0x53 }, +{ 0x00, 0x74, 0x54 }, +{ 0x00, 0x75, 0x55 }, +{ 0x00, 0x76, 0x56 }, +{ 0x00, 0x77, 0x57 }, +{ 0x00, 0x78, 0x58 }, +{ 0x00, 0x79, 0x59 }, +{ 0x00, 0x7a, 0x5a }, +{ 0x00, 0x7b, 0x7b }, +{ 0x00, 0x7c, 0x7c }, +{ 0x00, 0x7d, 0x7d }, +{ 0x00, 0x7e, 0x7e }, +{ 0x00, 0x7f, 0x7f }, +{ 0x00, 0x80, 0x80 }, +{ 0x00, 0x81, 0x81 }, +{ 0x00, 0x82, 0x82 }, +{ 0x00, 0x83, 0x83 }, +{ 0x00, 0x84, 0x84 }, +{ 0x00, 0x85, 0x85 }, +{ 0x00, 0x86, 0x86 }, +{ 0x00, 0x87, 0x87 }, +{ 0x00, 0x88, 0x88 }, +{ 0x00, 0x89, 0x89 }, +{ 0x00, 0x8a, 0x8a }, +{ 0x00, 0x8b, 0x8b }, +{ 0x00, 0x8c, 0x8c }, +{ 0x00, 0x8d, 0x8d }, +{ 0x00, 0x8e, 0x8e }, +{ 0x00, 0x8f, 0x8f }, +{ 0x00, 0x90, 0x90 }, +{ 0x00, 0x91, 0x91 }, +{ 0x00, 0x92, 0x92 }, +{ 0x00, 0x93, 0x93 }, +{ 0x00, 0x94, 0x94 }, +{ 0x00, 0x95, 0x95 }, +{ 0x00, 0x96, 0x96 }, +{ 0x00, 0x97, 0x97 }, +{ 0x00, 0x98, 0x98 }, +{ 0x00, 0x99, 0x99 }, +{ 0x00, 0x9a, 0x9a }, +{ 0x00, 0x9b, 0x9b }, +{ 0x00, 0x9c, 0x9c }, +{ 0x00, 0x9d, 0x9d }, +{ 0x00, 0x9e, 0x9e }, +{ 0x00, 0x9f, 0x9f }, +{ 0x00, 0xa0, 0xa0 }, +{ 0x00, 0xa1, 0xa1 }, +{ 0x00, 0xa2, 0xa2 }, +{ 0x00, 0xa3, 0xa3 }, +{ 0x00, 0xa4, 0xa4 }, +{ 0x00, 0xa5, 0xa5 }, +{ 0x00, 0xa6, 0xa6 }, +{ 0x00, 0xa7, 0xa7 }, +{ 0x00, 0xa8, 0xa8 }, +{ 0x00, 0xa9, 0xa9 }, +{ 0x00, 0xaa, 0xaa }, +{ 0x00, 0xab, 0xab }, +{ 0x00, 0xac, 0xac }, +{ 0x00, 0xad, 0xad }, +{ 0x00, 0xae, 0xae }, +{ 0x00, 0xaf, 0xaf }, +{ 0x00, 0xb0, 0xb0 }, +{ 0x00, 0xb1, 0xb1 }, +{ 0x00, 0xb2, 0xb2 }, +{ 0x00, 0xb3, 0xb3 }, +{ 0x00, 0xb4, 0xb4 }, +{ 0x00, 0xb5, 0xb5 }, +{ 0x00, 0xb6, 0xb6 }, +{ 0x00, 0xb7, 0xb7 }, +{ 0x00, 0xb8, 0xb8 }, +{ 0x00, 0xb9, 0xb9 }, +{ 0x00, 0xba, 0xba }, +{ 0x00, 0xbb, 0xbb }, +{ 0x00, 0xbc, 0xbc }, +{ 0x00, 0xbd, 0xbd }, +{ 0x00, 0xbe, 0xbe }, +{ 0x00, 0xbf, 0xbf }, +{ 0x00, 0xc0, 0xc0 }, +{ 0x00, 0xc1, 0xc1 }, +{ 0x00, 0xc2, 0xc2 }, +{ 0x00, 0xc3, 0xc3 }, +{ 0x00, 0xc4, 0xc4 }, +{ 0x00, 0xc5, 0xc5 }, +{ 0x00, 0xc6, 0xc6 }, +{ 0x00, 0xc7, 0xc7 }, +{ 0x00, 0xc8, 0xc8 }, +{ 0x00, 0xc9, 0xc9 }, +{ 0x00, 0xca, 0xca }, +{ 0x00, 0xcb, 0xcb }, +{ 0x00, 0xcc, 0xcc }, +{ 0x00, 0xcd, 0xcd }, +{ 0x00, 0xce, 0xce }, +{ 0x00, 0xcf, 0xcf }, +{ 0x00, 0xd0, 0xd0 }, +{ 0x00, 0xd1, 0xd1 }, +{ 0x00, 0xd2, 0xd2 }, +{ 0x00, 0xd3, 0xd3 }, +{ 0x00, 0xd4, 0xd4 }, +{ 0x00, 0xd5, 0xd5 }, +{ 0x00, 0xd6, 0xd6 }, +{ 0x00, 0xd7, 0xd7 }, +{ 0x00, 0xd8, 0xd8 }, +{ 0x00, 0xd9, 0xd9 }, +{ 0x00, 0xda, 0xda }, +{ 0x00, 0xdb, 0xdb }, +{ 0x00, 0xdc, 0xdc }, +{ 0x00, 0xdd, 0xdd }, +{ 0x00, 0xde, 0xde }, +{ 0x00, 0xdf, 0xdf }, +{ 0x00, 0xe0, 0xe0 }, +{ 0x00, 0xe1, 0xe1 }, +{ 0x00, 0xe2, 0xe2 }, +{ 0x00, 0xe3, 0xe3 }, +{ 0x00, 0xe4, 0xe4 }, +{ 0x00, 0xe5, 0xe5 }, +{ 0x00, 0xe6, 0xe6 }, +{ 0x00, 0xe7, 0xe7 }, +{ 0x00, 0xe8, 0xe8 }, +{ 0x00, 0xe9, 0xe9 }, +{ 0x00, 0xea, 0xea }, +{ 0x00, 0xeb, 0xeb }, +{ 0x00, 0xec, 0xec }, +{ 0x00, 0xed, 0xed }, +{ 0x00, 0xee, 0xee }, +{ 0x00, 0xef, 0xef }, +{ 0x00, 0xf0, 0xf0 }, +{ 0x00, 0xf1, 0xf1 }, +{ 0x00, 0xf2, 0xf2 }, +{ 0x00, 0xf3, 0xf3 }, +{ 0x00, 0xf4, 0xf4 }, +{ 0x00, 0xf5, 0xf5 }, +{ 0x00, 0xf6, 0xf6 }, +{ 0x00, 0xf7, 0xf7 }, +{ 0x00, 0xf8, 0xf8 }, +{ 0x00, 0xf9, 0xf9 }, +{ 0x00, 0xfa, 0xfa }, +{ 0x00, 0xfb, 0xfb }, +{ 0x00, 0xfc, 0xfc }, +{ 0x00, 0xfd, 0xfd }, +{ 0x00, 0xfe, 0xfe }, +{ 0x00, 0xff, 0xff } +}; + +struct enc_entry { + const char * enc_name; + struct cs_info * cs_table; +}; + +static struct enc_entry encds[] = { + {"iso88591",iso1_tbl}, //ISO-8859-1 + {"iso88592",iso2_tbl}, //ISO-8859-2 + {"iso88593",iso3_tbl}, //ISO-8859-3 + {"iso88594",iso4_tbl}, //ISO-8859-4 + {"iso88595",iso5_tbl}, //ISO-8859-5 + {"iso88596",iso6_tbl}, //ISO-8859-6 + {"iso88597",iso7_tbl}, //ISO-8859-7 + {"iso88598",iso8_tbl}, //ISO-8859-8 + {"iso88599",iso9_tbl}, //ISO-8859-9 + {"iso885910",iso10_tbl}, //ISO-8859-10 + {"tis620",tis620_tbl}, //TIS-620/ISO-8859-11 + {"tis6202533",tis620_tbl}, //TIS-620/ISO-8859-11 + {"iso885911",tis620_tbl}, //TIS-620/ISO-8859-11 + {"iso885913", iso13_tbl}, //ISO-8859-13 + {"iso885914", iso14_tbl}, //ISO-8859-14 + {"iso885915", iso15_tbl}, //ISO-8859-15 + {"koi8r",koi8r_tbl}, //KOI8-R + {"koi8u",koi8u_tbl}, //KOI8-U + {"cp1251",cp1251_tbl}, //CP-1251 + {"microsoftcp1251",cp1251_tbl}, //microsoft-cp1251 + {"xisciias", iscii_devanagari_tbl}, //x-iscii-as + {"isciidevanagari", iscii_devanagari_tbl} //ISCII-DEVANAGARI +}; + +/* map to lower case and remove non alphanumeric chars */ +static void toAsciiLowerAndRemoveNonAlphanumeric( const char* pName, char* pBuf ) +{ + while ( *pName ) + { + /* A-Z */ + if ( (*pName >= 0x41) && (*pName <= 0x5A) ) + { + *pBuf = (*pName)+0x20; /* toAsciiLower */ + pBuf++; + } + /* a-z, 0-9 */ + else if ( ((*pName >= 0x61) && (*pName <= 0x7A)) || + ((*pName >= 0x30) && (*pName <= 0x39)) ) + { + *pBuf = *pName; + pBuf++; + } + + pName++; + } + + *pBuf = '\0'; +} + +struct cs_info * get_current_cs(const char * es) { + char *normalized_encoding = new char[strlen(es)+1]; + toAsciiLowerAndRemoveNonAlphanumeric(es, normalized_encoding); + + struct cs_info * ccs = NULL; + int n = sizeof(encds) / sizeof(encds[0]); + for (int i = 0; i < n; i++) { + if (strcmp(normalized_encoding,encds[i].enc_name) == 0) { + ccs = encds[i].cs_table; + break; + } + } + + delete[] normalized_encoding; + + if (!ccs) { + HUNSPELL_WARNING(stderr, "error: unknown encoding %s: using %s as fallback\n", es, encds[0].enc_name); + ccs = encds[0].cs_table; + } + + return ccs; +} +#else +// XXX This function was rewritten for mozilla. Instead of storing the +// conversion tables static in this file, create them when needed +// with help the mozilla backend. +struct cs_info * get_current_cs(const char * es) { + struct cs_info *ccs; + + nsCOMPtr encoder; + nsCOMPtr decoder; + + nsresult rv; + nsCOMPtr ccm = do_GetService(kCharsetConverterManagerCID, &rv); + if (NS_FAILED(rv)) + return nsnull; + + rv = ccm->GetUnicodeEncoder(es, getter_AddRefs(encoder)); + if (NS_FAILED(rv)) + return nsnull; + encoder->SetOutputErrorBehavior(encoder->kOnError_Signal, nsnull, '?'); + rv = ccm->GetUnicodeDecoder(es, getter_AddRefs(decoder)); + if (NS_FAILED(rv)) + return nsnull; + decoder->SetInputErrorBehavior(decoder->kOnError_Signal); + + if (NS_FAILED(rv)) + return nsnull; + + ccs = new cs_info[256]; + + for (unsigned int i = 0; i <= 0xff; ++i) { + PRBool success = PR_FALSE; + // We want to find the upper/lowercase equivalents of each byte + // in this 1-byte character encoding. Call our encoding/decoding + // APIs separately for each byte since they may reject some of the + // bytes, and we want to handle errors separately for each byte. + char lower, upper; + do { + if (i == 0) + break; + const char source = char(i); + PRUnichar uni, uniCased; + PRInt32 charLength = 1, uniLength = 1; + + rv = decoder->Convert(&source, &charLength, &uni, &uniLength); + // Explicitly check NS_OK because we don't want to allow + // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT. + if (rv != NS_OK || charLength != 1 || uniLength != 1) + break; + uniCased = ToLowerCase(uni); + rv = encoder->Convert(&uniCased, &uniLength, &lower, &charLength); + // Explicitly check NS_OK because we don't want to allow + // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT. + if (rv != NS_OK || charLength != 1 || uniLength != 1) + break; + + uniCased = ToUpperCase(uni); + rv = encoder->Convert(&uniCased, &uniLength, &upper, &charLength); + // Explicitly check NS_OK because we don't want to allow + // NS_OK_UDEC_MOREOUTPUT or NS_OK_UDEC_MOREINPUT. + if (rv != NS_OK || charLength != 1 || uniLength != 1) + break; + + success = PR_TRUE; + } while (0); + + if (success) { + ccs[i].cupper = upper; + ccs[i].clower = lower; + } else { + ccs[i].cupper = i; + ccs[i].clower = i; + } + + if (ccs[i].clower != (unsigned char)i) + ccs[i].ccase = true; + else + ccs[i].ccase = false; + } + + return ccs; +} +#endif + +// primitive isalpha() replacement for tokenization +char * get_casechars(const char * enc) { + struct cs_info * csconv = get_current_cs(enc); + char expw[MAXLNLEN]; + char * p = expw; + for (int i = 0; i <= 255; i++) { + if ((csconv[i].cupper != csconv[i].clower)) { + *p = (char) i; + p++; + } + } + *p = '\0'; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif + return mystrdup(expw); +} + +// language to encoding default map + +struct lang_map { + const char * lang; + int num; +}; + +static struct lang_map lang2enc[] = { +{"ar", LANG_ar}, +{"az", LANG_az}, +{"az_AZ", LANG_az}, // for back-compatibility +{"bg", LANG_bg}, +{"ca", LANG_ca}, +{"cs", LANG_cs}, +{"da", LANG_da}, +{"de", LANG_de}, +{"el", LANG_el}, +{"en", LANG_en}, +{"es", LANG_es}, +{"eu", LANG_eu}, +{"gl", LANG_gl}, +{"fr", LANG_fr}, +{"hr", LANG_hr}, +{"hu", LANG_hu}, +{"hu_HU", LANG_hu}, // for back-compatibility +{"it", LANG_it}, +{"la", LANG_la}, +{"lv", LANG_lv}, +{"nl", LANG_nl}, +{"pl", LANG_pl}, +{"pt", LANG_pt}, +{"sv", LANG_sv}, +{"tr", LANG_tr}, +{"tr_TR", LANG_tr}, // for back-compatibility +{"ru", LANG_ru}, +{"uk", LANG_uk} +}; + + +int get_lang_num(const char * lang) { + int n = sizeof(lang2enc) / sizeof(lang2enc[0]); + for (int i = 0; i < n; i++) { + if (strcmp(lang, lang2enc[i].lang) == 0) { + return lang2enc[i].num; + } + } + return LANG_xx; +} + +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT +int initialize_utf_tbl() { + utf_tbl_count++; + if (utf_tbl) return 0; + utf_tbl = (unicode_info2 *) malloc(CONTSIZE * sizeof(unicode_info2)); + if (utf_tbl) { + size_t j; + for (j = 0; j < CONTSIZE; j++) { + utf_tbl[j].cletter = 0; + utf_tbl[j].clower = (unsigned short) j; + utf_tbl[j].cupper = (unsigned short) j; + } + for (j = 0; j < UTF_LST_LEN; j++) { + utf_tbl[utf_lst[j].c].cletter = 1; + utf_tbl[utf_lst[j].c].clower = utf_lst[j].clower; + utf_tbl[utf_lst[j].c].cupper = utf_lst[j].cupper; + } + } else return 1; + return 0; +} +#endif +#endif + +void free_utf_tbl() { + if (utf_tbl_count > 0) utf_tbl_count--; + if (utf_tbl && (utf_tbl_count == 0)) { + free(utf_tbl); + utf_tbl = NULL; + } +} + +unsigned short unicodetoupper(unsigned short c, int langnum) +{ + // In Azeri and Turkish, I and i dictinct letters: + // There are a dotless lower case i pair of upper `I', + // and an upper I with dot pair of lower `i'. + if (c == 0x0069 && ((langnum == LANG_az) || (langnum == LANG_tr))) + return 0x0130; +#ifdef OPENOFFICEORG + return u_toupper(c); +#else +#ifdef MOZILLA_CLIENT + return ToUpperCase((PRUnichar) c); +#else + return (utf_tbl) ? utf_tbl[c].cupper : c; +#endif +#endif +} + +unsigned short unicodetolower(unsigned short c, int langnum) +{ + // In Azeri and Turkish, I and i dictinct letters: + // There are a dotless lower case i pair of upper `I', + // and an upper I with dot pair of lower `i'. + if (c == 0x0049 && ((langnum == LANG_az) || (langnum == LANG_tr))) + return 0x0131; +#ifdef OPENOFFICEORG + return u_tolower(c); +#else +#ifdef MOZILLA_CLIENT + return ToLowerCase((PRUnichar) c); +#else + return (utf_tbl) ? utf_tbl[c].clower : c; +#endif +#endif +} + +int unicodeisalpha(unsigned short c) +{ +#ifdef OPENOFFICEORG + return u_isalpha(c); +#else + return (utf_tbl) ? utf_tbl[c].cletter : 0; +#endif +} + +/* get type of capitalization */ +int get_captype(char * word, int nl, cs_info * csconv) { + // now determine the capitalization type of the first nl letters + int ncap = 0; + int nneutral = 0; + int firstcap = 0; + if (csconv == NULL) return NOCAP; + for (char * q = word; *q != '\0'; q++) { + if (csconv[*((unsigned char *)q)].ccase) ncap++; + if (csconv[*((unsigned char *)q)].cupper == csconv[*((unsigned char *)q)].clower) nneutral++; + } + if (ncap) { + firstcap = csconv[*((unsigned char *) word)].ccase; + } + + // now finally set the captype + if (ncap == 0) { + return NOCAP; + } else if ((ncap == 1) && firstcap) { + return INITCAP; + } else if ((ncap == nl) || ((ncap + nneutral) == nl)) { + return ALLCAP; + } else if ((ncap > 1) && firstcap) { + return HUHINITCAP; + } + return HUHCAP; +} + +int get_captype_utf8(w_char * word, int nl, int langnum) { + // now determine the capitalization type of the first nl letters + int ncap = 0; + int nneutral = 0; + int firstcap = 0; + unsigned short idx; + // don't check too long words + if (nl >= MAXWORDLEN) return 0; + // big Unicode character (non BMP area) + if (nl == -1) return NOCAP; + for (int i = 0; i < nl; i++) { + idx = (word[i].h << 8) + word[i].l; + if (idx != unicodetolower(idx, langnum)) ncap++; + if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++; + } + if (ncap) { + idx = (word[0].h << 8) + word[0].l; + firstcap = (idx != unicodetolower(idx, langnum)); + } + + // now finally set the captype + if (ncap == 0) { + return NOCAP; + } else if ((ncap == 1) && firstcap) { + return INITCAP; + } else if ((ncap == nl) || ((ncap + nneutral) == nl)) { + return ALLCAP; + } else if ((ncap > 1) && firstcap) { + return HUHINITCAP; + } + return HUHCAP; +} + + +// strip all ignored characters in the string +void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len) +{ + w_char w[MAXWORDLEN]; + w_char w2[MAXWORDLEN]; + int i; + int j; + int len = u8_u16(w, MAXWORDLEN, word); + for (i = 0, j = 0; i < len; i++) { + if (!flag_bsearch(ignored_chars, ((unsigned short *) w)[i], ignored_len)) { + w2[j] = w[i]; + j++; + } + } + if (j < i) u16_u8(word, MAXWORDUTF8LEN, w2, j); +} + +// strip all ignored characters in the string +void remove_ignored_chars(char * word, char * ignored_chars) +{ + for (char * p = word; *p != '\0'; p++) { + if (!strchr(ignored_chars, *p)) { + *word = *p; + word++; + } + } + *word = '\0'; +} + +int parse_string(char * line, char ** out, int ln) +{ + char * tp = line; + char * piece; + int i = 0; + int np = 0; + if (*out) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions\n", ln); + return 1; + } + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + *out = mystrdup(piece); + if (!*out) return 1; + np++; + break; + } + default: break; + } + i++; + } + // free(piece); + piece = mystrsep(&tp, 0); + } + if (np != 2) { + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", ln); + return 1; + } + return 0; +} + +int parse_array(char * line, char ** out, unsigned short ** out_utf16, + int * out_utf16_len, int utf8, int ln) { + if (parse_string(line, out, ln)) return 1; + if (utf8) { + w_char w[MAXWORDLEN]; + int n = u8_u16(w, MAXWORDLEN, *out); + if (n > 0) { + flag_qsort((unsigned short *) w, 0, n); + *out_utf16 = (unsigned short *) malloc(n * sizeof(unsigned short)); + if (!*out_utf16) return 1; + memcpy(*out_utf16, w, n * sizeof(unsigned short)); + } + *out_utf16_len = n; + } + return 0; +} diff --git a/src/lib/hunspell/src/csutil.hxx b/src/lib/hunspell/src/csutil.hxx new file mode 100644 index 0000000..7bd0b91 --- /dev/null +++ b/src/lib/hunspell/src/csutil.hxx @@ -0,0 +1,220 @@ +#ifndef __CSUTILHXX__ +#define __CSUTILHXX__ + +#include "hunvisapi.h" + +// First some base level utility routines + +#include +#include "w_char.hxx" +#include "htypes.hxx" + +#ifdef MOZILLA_CLIENT +#include "nscore.h" // for mozalloc headers +#endif + +// casing +#define NOCAP 0 +#define INITCAP 1 +#define ALLCAP 2 +#define HUHCAP 3 +#define HUHINITCAP 4 + +// default encoding and keystring +#define SPELL_ENCODING "ISO8859-1" +#define SPELL_KEYSTRING "qwertyuiop|asdfghjkl|zxcvbnm" + +// default morphological fields +#define MORPH_STEM "st:" +#define MORPH_ALLOMORPH "al:" +#define MORPH_POS "po:" +#define MORPH_DERI_PFX "dp:" +#define MORPH_INFL_PFX "ip:" +#define MORPH_TERM_PFX "tp:" +#define MORPH_DERI_SFX "ds:" +#define MORPH_INFL_SFX "is:" +#define MORPH_TERM_SFX "ts:" +#define MORPH_SURF_PFX "sp:" +#define MORPH_FREQ "fr:" +#define MORPH_PHON "ph:" +#define MORPH_HYPH "hy:" +#define MORPH_PART "pa:" +#define MORPH_FLAG "fl:" +#define MORPH_HENTRY "_H:" +#define MORPH_TAG_LEN strlen(MORPH_STEM) + +#define MSEP_FLD ' ' +#define MSEP_REC '\n' +#define MSEP_ALT '\v' + +// default flags +#define DEFAULTFLAGS 65510 +#define FORBIDDENWORD 65510 +#define ONLYUPCASEFLAG 65511 + +// convert UTF-16 characters to UTF-8 +LIBHUNSPELL_DLL_EXPORTED char * u16_u8(char * dest, int size, const w_char * src, int srclen); + +// convert UTF-8 characters to UTF-16 +LIBHUNSPELL_DLL_EXPORTED int u8_u16(w_char * dest, int size, const char * src); + +// sort 2-byte vector +LIBHUNSPELL_DLL_EXPORTED void flag_qsort(unsigned short flags[], int begin, int end); + +// binary search in 2-byte vector +LIBHUNSPELL_DLL_EXPORTED int flag_bsearch(unsigned short flags[], unsigned short flag, int right); + +// remove end of line char(s) +LIBHUNSPELL_DLL_EXPORTED void mychomp(char * s); + +// duplicate string +LIBHUNSPELL_DLL_EXPORTED char * mystrdup(const char * s); + +// strcat for limited length destination string +LIBHUNSPELL_DLL_EXPORTED char * mystrcat(char * dest, const char * st, int max); + +// duplicate reverse of string +LIBHUNSPELL_DLL_EXPORTED char * myrevstrdup(const char * s); + +// parse into tokens with char delimiter +LIBHUNSPELL_DLL_EXPORTED char * mystrsep(char ** sptr, const char delim); +// parse into tokens with char delimiter +LIBHUNSPELL_DLL_EXPORTED char * mystrsep2(char ** sptr, const char delim); + +// parse into tokens with char delimiter +LIBHUNSPELL_DLL_EXPORTED char * mystrrep(char *, const char *, const char *); + +// append s to ends of every lines in text +LIBHUNSPELL_DLL_EXPORTED void strlinecat(char * lines, const char * s); + +// tokenize into lines with new line +LIBHUNSPELL_DLL_EXPORTED int line_tok(const char * text, char *** lines, char breakchar); + +// tokenize into lines with new line and uniq in place +LIBHUNSPELL_DLL_EXPORTED char * line_uniq(char * text, char breakchar); +LIBHUNSPELL_DLL_EXPORTED char * line_uniq_app(char ** text, char breakchar); + +// change oldchar to newchar in place +LIBHUNSPELL_DLL_EXPORTED char * tr(char * text, char oldc, char newc); + +// reverse word +LIBHUNSPELL_DLL_EXPORTED int reverseword(char *); + +// reverse word +LIBHUNSPELL_DLL_EXPORTED int reverseword_utf(char *); + +// remove duplicates +LIBHUNSPELL_DLL_EXPORTED int uniqlist(char ** list, int n); + +// free character array list +LIBHUNSPELL_DLL_EXPORTED void freelist(char *** list, int n); + +// character encoding information +struct cs_info { + unsigned char ccase; + unsigned char clower; + unsigned char cupper; +}; + +LIBHUNSPELL_DLL_EXPORTED int initialize_utf_tbl(); +LIBHUNSPELL_DLL_EXPORTED void free_utf_tbl(); +LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetoupper(unsigned short c, int langnum); +LIBHUNSPELL_DLL_EXPORTED unsigned short unicodetolower(unsigned short c, int langnum); +LIBHUNSPELL_DLL_EXPORTED int unicodeisalpha(unsigned short c); + +LIBHUNSPELL_DLL_EXPORTED struct cs_info * get_current_cs(const char * es); + +// get language identifiers of language codes +LIBHUNSPELL_DLL_EXPORTED int get_lang_num(const char * lang); + +// get characters of the given 8bit encoding with lower- and uppercase forms +LIBHUNSPELL_DLL_EXPORTED char * get_casechars(const char * enc); + +// convert null terminated string to all caps using encoding +LIBHUNSPELL_DLL_EXPORTED void enmkallcap(char * d, const char * p, const char * encoding); + +// convert null terminated string to all little using encoding +LIBHUNSPELL_DLL_EXPORTED void enmkallsmall(char * d, const char * p, const char * encoding); + +// convert null terminated string to have initial capital using encoding +LIBHUNSPELL_DLL_EXPORTED void enmkinitcap(char * d, const char * p, const char * encoding); + +// convert null terminated string to all caps +LIBHUNSPELL_DLL_EXPORTED void mkallcap(char * p, const struct cs_info * csconv); + +// convert null terminated string to all little +LIBHUNSPELL_DLL_EXPORTED void mkallsmall(char * p, const struct cs_info * csconv); + +// convert null terminated string to have initial capital +LIBHUNSPELL_DLL_EXPORTED void mkinitcap(char * p, const struct cs_info * csconv); + +// convert first nc characters of UTF-8 string to little +LIBHUNSPELL_DLL_EXPORTED void mkallsmall_utf(w_char * u, int nc, int langnum); + +// convert first nc characters of UTF-8 string to capital +LIBHUNSPELL_DLL_EXPORTED void mkallcap_utf(w_char * u, int nc, int langnum); + +// get type of capitalization +LIBHUNSPELL_DLL_EXPORTED int get_captype(char * q, int nl, cs_info *); + +// get type of capitalization (UTF-8) +LIBHUNSPELL_DLL_EXPORTED int get_captype_utf8(w_char * q, int nl, int langnum); + +// strip all ignored characters in the string +LIBHUNSPELL_DLL_EXPORTED void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len); + +// strip all ignored characters in the string +LIBHUNSPELL_DLL_EXPORTED void remove_ignored_chars(char * word, char * ignored_chars); + +LIBHUNSPELL_DLL_EXPORTED int parse_string(char * line, char ** out, int ln); + +LIBHUNSPELL_DLL_EXPORTED int parse_array(char * line, char ** out, unsigned short ** out_utf16, + int * out_utf16_len, int utf8, int ln); + +LIBHUNSPELL_DLL_EXPORTED int fieldlen(const char * r); +LIBHUNSPELL_DLL_EXPORTED char * copy_field(char * dest, const char * morph, const char * var); + +LIBHUNSPELL_DLL_EXPORTED int morphcmp(const char * s, const char * t); + +LIBHUNSPELL_DLL_EXPORTED int get_sfxcount(const char * morph); + +// conversion function for protected memory +LIBHUNSPELL_DLL_EXPORTED void store_pointer(char * dest, char * source); + +// conversion function for protected memory +LIBHUNSPELL_DLL_EXPORTED char * get_stored_pointer(const char * s); + +// hash entry macros +LIBHUNSPELL_DLL_EXPORTED inline char* HENTRY_DATA(struct hentry *h) +{ + char *ret; + if (!h->var) + ret = NULL; + else if (h->var & H_OPT_ALIASM) + ret = get_stored_pointer(HENTRY_WORD(h) + h->blen + 1); + else + ret = HENTRY_WORD(h) + h->blen + 1; + return ret; +} + +// NULL-free version for warning-free OOo build +LIBHUNSPELL_DLL_EXPORTED inline const char* HENTRY_DATA2(const struct hentry *h) +{ + const char *ret; + if (!h->var) + ret = ""; + else if (h->var & H_OPT_ALIASM) + ret = get_stored_pointer(HENTRY_WORD(h) + h->blen + 1); + else + ret = HENTRY_WORD(h) + h->blen + 1; + return ret; +} + +LIBHUNSPELL_DLL_EXPORTED inline char* HENTRY_FIND(struct hentry *h, const char *p) +{ + return (HENTRY_DATA(h) ? strstr(HENTRY_DATA(h), p) : NULL); +} + +#define w_char_eq(a,b) (((a).l == (b).l) && ((a).h == (b).h)) + +#endif diff --git a/src/lib/hunspell/src/dictmgr.cxx b/src/lib/hunspell/src/dictmgr.cxx new file mode 100644 index 0000000..b4a15b1 --- /dev/null +++ b/src/lib/hunspell/src/dictmgr.cxx @@ -0,0 +1,180 @@ + +#include +#include +#include +#include + +#include "dictmgr.hxx" + +DictMgr::DictMgr(const char * dictpath, const char * etype) : numdict(0) +{ + // load list of etype entries + pdentry = (dictentry *)malloc(MAXDICTIONARIES*sizeof(struct dictentry)); + if (pdentry) { + if (parse_file(dictpath, etype)) { + numdict = 0; + // no dictionary.lst found is okay + } + } +} + + +DictMgr::~DictMgr() +{ + dictentry * pdict = NULL; + if (pdentry) { + pdict = pdentry; + for (int i=0;ilang) { + free(pdict->lang); + pdict->lang = NULL; + } + if (pdict->region) { + free(pdict->region); + pdict->region=NULL; + } + if (pdict->filename) { + free(pdict->filename); + pdict->filename = NULL; + } + pdict++; + } + free(pdentry); + pdentry = NULL; + pdict = NULL; + } + numdict = 0; +} + + +// read in list of etype entries and build up structure to describe them +int DictMgr::parse_file(const char * dictpath, const char * etype) +{ + + int i; + char line[MAXDICTENTRYLEN+1]; + dictentry * pdict = pdentry; + + // open the dictionary list file + FILE * dictlst; + dictlst = fopen(dictpath,"r"); + if (!dictlst) { + return 1; + } + + // step one is to parse the dictionary list building up the + // descriptive structures + + // read in each line ignoring any that dont start with etype + while (fgets(line,MAXDICTENTRYLEN,dictlst)) { + mychomp(line); + + /* parse in a dictionary entry */ + if (strncmp(line,etype,4) == 0) { + if (numdict < MAXDICTIONARIES) { + char * tp = line; + char * piece; + i = 0; + while ((piece=mystrsep(&tp,' '))) { + if (*piece != '\0') { + switch(i) { + case 0: break; + case 1: pdict->lang = mystrdup(piece); break; + case 2: if (strcmp (piece, "ANY") == 0) + pdict->region = mystrdup(""); + else + pdict->region = mystrdup(piece); + break; + case 3: pdict->filename = mystrdup(piece); break; + default: break; + } + i++; + } + free(piece); + } + if (i == 4) { + numdict++; + pdict++; + } else { + switch (i) { + case 3: + free(pdict->region); + pdict->region=NULL; + case 2: //deliberate fallthrough + free(pdict->lang); + pdict->lang=NULL; + default: + break; + } + fprintf(stderr,"dictionary list corruption in line \"%s\"\n",line); + fflush(stderr); + } + } + } + } + fclose(dictlst); + return 0; +} + +// return text encoding of dictionary +int DictMgr::get_list(dictentry ** ppentry) +{ + *ppentry = pdentry; + return numdict; +} + + + +// strip strings into token based on single char delimiter +// acts like strsep() but only uses a delim char and not +// a delim string + +char * DictMgr::mystrsep(char ** stringp, const char delim) +{ + char * rv = NULL; + char * mp = *stringp; + size_t n = strlen(mp); + if (n > 0) { + char * dp = (char *)memchr(mp,(int)((unsigned char)delim),n); + if (dp) { + *stringp = dp+1; + size_t nc = dp - mp; + rv = (char *) malloc(nc+1); + if (rv) { + memcpy(rv,mp,nc); + *(rv+nc) = '\0'; + } + } else { + rv = (char *) malloc(n+1); + if (rv) { + memcpy(rv, mp, n); + *(rv+n) = '\0'; + *stringp = mp + n; + } + } + } + return rv; +} + + +// replaces strdup with ansi version +char * DictMgr::mystrdup(const char * s) +{ + char * d = NULL; + if (s) { + int sl = strlen(s)+1; + d = (char *) malloc(sl); + if (d) memcpy(d,s,sl); + } + return d; +} + + +// remove cross-platform text line end characters +void DictMgr:: mychomp(char * s) +{ + int k = strlen(s); + if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0'; + if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0'; +} + diff --git a/src/lib/hunspell/src/dictmgr.hxx b/src/lib/hunspell/src/dictmgr.hxx new file mode 100644 index 0000000..bb197f8 --- /dev/null +++ b/src/lib/hunspell/src/dictmgr.hxx @@ -0,0 +1,36 @@ +#ifndef _DICTMGR_HXX_ +#define _DICTMGR_HXX_ + +#include "hunvisapi.h" + +#define MAXDICTIONARIES 100 +#define MAXDICTENTRYLEN 1024 + +struct dictentry { + char * filename; + char * lang; + char * region; +}; + + +class LIBHUNSPELL_DLL_EXPORTED DictMgr +{ + + int numdict; + dictentry * pdentry; + +public: + + DictMgr(const char * dictpath, const char * etype); + ~DictMgr(); + int get_list(dictentry** ppentry); + +private: + int parse_file(const char * dictpath, const char * etype); + char * mystrsep(char ** stringp, const char delim); + char * mystrdup(const char * s); + void mychomp(char * s); + +}; + +#endif diff --git a/src/lib/hunspell/src/filemgr.cxx b/src/lib/hunspell/src/filemgr.cxx new file mode 100644 index 0000000..5fb82bc --- /dev/null +++ b/src/lib/hunspell/src/filemgr.cxx @@ -0,0 +1,49 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include + +#include "filemgr.hxx" + +int FileMgr::fail(const char * err, const char * par) { + fprintf(stderr, err, par); + return -1; +} + +FileMgr::FileMgr(const char * file, const char * key) { + linenum = 0; + hin = NULL; + fin = fopen(file, "r"); + if (!fin) { + // check hzipped file + char * st = (char *) malloc(strlen(file) + strlen(HZIP_EXTENSION) + 1); + if (st) { + strcpy(st, file); + strcat(st, HZIP_EXTENSION); + hin = new Hunzip(st, key); + free(st); + } + } + if (!fin && !hin) fail(MSG_OPEN, file); +} + +FileMgr::~FileMgr() +{ + if (fin) fclose(fin); + if (hin) delete hin; +} + +char * FileMgr::getline() { + const char * l; + linenum++; + if (fin) return fgets(in, BUFSIZE - 1, fin); + if (hin && (l = hin->getline())) return strcpy(in, l); + linenum--; + return NULL; +} + +int FileMgr::getlinenum() { + return linenum; +} diff --git a/src/lib/hunspell/src/filemgr.hxx b/src/lib/hunspell/src/filemgr.hxx new file mode 100644 index 0000000..94cb723 --- /dev/null +++ b/src/lib/hunspell/src/filemgr.hxx @@ -0,0 +1,25 @@ +/* file manager class - read lines of files [filename] OR [filename.hz] */ +#ifndef _FILEMGR_HXX_ +#define _FILEMGR_HXX_ + +#include "hunvisapi.h" + +#include "hunzip.hxx" +#include + +class LIBHUNSPELL_DLL_EXPORTED FileMgr +{ +protected: + FILE * fin; + Hunzip * hin; + char in[BUFSIZE + 50]; // input buffer + int fail(const char * err, const char * par); + int linenum; + +public: + FileMgr(const char * filename, const char * key = NULL); + ~FileMgr(); + char * getline(); + int getlinenum(); +}; +#endif diff --git a/src/lib/hunspell/src/hashmgr.cxx b/src/lib/hunspell/src/hashmgr.cxx new file mode 100644 index 0000000..ea93b87 --- /dev/null +++ b/src/lib/hunspell/src/hashmgr.cxx @@ -0,0 +1,928 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "hashmgr.hxx" +#include "csutil.hxx" +#include "atypes.hxx" + +// build a hash table from a munched word list + +HashMgr::HashMgr(const char * tpath, const char * apath, const char * key) +{ + tablesize = 0; + tableptr = NULL; + flag_mode = FLAG_CHAR; + complexprefixes = 0; + utf8 = 0; + langnum = 0; + lang = NULL; + enc = NULL; + csconv = 0; + ignorechars = NULL; + ignorechars_utf16 = NULL; + ignorechars_utf16_len = 0; + numaliasf = 0; + aliasf = NULL; + numaliasm = 0; + aliasm = NULL; + forbiddenword = FORBIDDENWORD; // forbidden word signing flag + load_config(apath, key); + int ec = load_tables(tpath, key); + if (ec) { + /* error condition - what should we do here */ + HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec); + if (tableptr) { + free(tableptr); + tableptr = NULL; + } + tablesize = 0; + } +} + + +HashMgr::~HashMgr() +{ + if (tableptr) { + // now pass through hash table freeing up everything + // go through column by column of the table + for (int i=0; i < tablesize; i++) { + struct hentry * pt = tableptr[i]; + struct hentry * nt = NULL; + while(pt) { + nt = pt->next; + if (pt->astr && (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen))) free(pt->astr); + free(pt); + pt = nt; + } + } + free(tableptr); + } + tablesize = 0; + + if (aliasf) { + for (int j = 0; j < (numaliasf); j++) free(aliasf[j]); + free(aliasf); + aliasf = NULL; + if (aliasflen) { + free(aliasflen); + aliasflen = NULL; + } + } + if (aliasm) { + for (int j = 0; j < (numaliasm); j++) free(aliasm[j]); + free(aliasm); + aliasm = NULL; + } + +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT + if (utf8) free_utf_tbl(); +#endif +#endif + + if (enc) free(enc); + if (lang) free(lang); + + if (ignorechars) free(ignorechars); + if (ignorechars_utf16) free(ignorechars_utf16); + +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif +} + +// lookup a root word in the hashtable + +struct hentry * HashMgr::lookup(const char *word) const +{ + struct hentry * dp; + if (tableptr) { + dp = tableptr[hash(word)]; + if (!dp) return NULL; + for ( ; dp != NULL; dp = dp->next) { + if (strcmp(word, dp->word) == 0) return dp; + } + } + return NULL; +} + +// add a word to the hash table (private) +int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff, + int al, const char * desc, bool onlyupcase) +{ + bool upcasehomonym = false; + int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0; + // variable-length hash record with word and optional fields + struct hentry* hp = + (struct hentry *) malloc (sizeof(struct hentry) + wbl + descl); + if (!hp) return 1; + char * hpw = hp->word; + strcpy(hpw, word); + if (ignorechars != NULL) { + if (utf8) { + remove_ignored_chars_utf(hpw, ignorechars_utf16, ignorechars_utf16_len); + } else { + remove_ignored_chars(hpw, ignorechars); + } + } + if (complexprefixes) { + if (utf8) reverseword_utf(hpw); else reverseword(hpw); + } + + int i = hash(hpw); + + hp->blen = (unsigned char) wbl; + hp->clen = (unsigned char) wcl; + hp->alen = (short) al; + hp->astr = aff; + hp->next = NULL; + hp->next_homonym = NULL; + + // store the description string or its pointer + if (desc) { + hp->var = H_OPT; + if (aliasm) { + hp->var += H_OPT_ALIASM; + store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc))); + } else { + strcpy(hpw + wbl + 1, desc); + if (complexprefixes) { + if (utf8) reverseword_utf(HENTRY_DATA(hp)); + else reverseword(HENTRY_DATA(hp)); + } + } + if (strstr(HENTRY_DATA(hp), MORPH_PHON)) hp->var += H_OPT_PHON; + } else hp->var = 0; + + struct hentry * dp = tableptr[i]; + if (!dp) { + tableptr[i] = hp; + return 0; + } + while (dp->next != NULL) { + if ((!dp->next_homonym) && (strcmp(hp->word, dp->word) == 0)) { + // remove hidden onlyupcase homonym + if (!onlyupcase) { + if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) { + free(dp->astr); + dp->astr = hp->astr; + dp->alen = hp->alen; + free(hp); + return 0; + } else { + dp->next_homonym = hp; + } + } else { + upcasehomonym = true; + } + } + dp=dp->next; + } + if (strcmp(hp->word, dp->word) == 0) { + // remove hidden onlyupcase homonym + if (!onlyupcase) { + if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) { + free(dp->astr); + dp->astr = hp->astr; + dp->alen = hp->alen; + free(hp); + return 0; + } else { + dp->next_homonym = hp; + } + } else { + upcasehomonym = true; + } + } + if (!upcasehomonym) { + dp->next = hp; + } else { + // remove hidden onlyupcase homonym + if (hp->astr) free(hp->astr); + free(hp); + } + return 0; +} + +int HashMgr::add_hidden_capitalized_word(char * word, int wbl, int wcl, + unsigned short * flags, int al, char * dp, int captype) +{ + // add inner capitalized forms to handle the following allcap forms: + // Mixed caps: OpenOffice.org -> OPENOFFICE.ORG + // Allcaps with suffixes: CIA's -> CIA'S + if (((captype == HUHCAP) || (captype == HUHINITCAP) || + ((captype == ALLCAP) && (flags != NULL))) && + !((flags != NULL) && TESTAFF(flags, forbiddenword, al))) { + unsigned short * flags2 = (unsigned short *) malloc (sizeof(unsigned short) * (al+1)); + if (!flags2) return 1; + if (al) memcpy(flags2, flags, al * sizeof(unsigned short)); + flags2[al] = ONLYUPCASEFLAG; + if (utf8) { + char st[BUFSIZE]; + w_char w[BUFSIZE]; + int wlen = u8_u16(w, BUFSIZE, word); + mkallsmall_utf(w, wlen, langnum); + mkallcap_utf(w, 1, langnum); + u16_u8(st, BUFSIZE, w, wlen); + return add_word(st,wbl,wcl,flags2,al+1,dp, true); + } else { + mkallsmall(word, csconv); + mkinitcap(word, csconv); + return add_word(word,wbl,wcl,flags2,al+1,dp, true); + } + } + return 0; +} + +// detect captype and modify word length for UTF-8 encoding +int HashMgr::get_clen_and_captype(const char * word, int wbl, int * captype) { + int len; + if (utf8) { + w_char dest_utf[BUFSIZE]; + len = u8_u16(dest_utf, BUFSIZE, word); + *captype = get_captype_utf8(dest_utf, len, langnum); + } else { + len = wbl; + *captype = get_captype((char *) word, len, csconv); + } + return len; +} + +// remove word (personal dictionary function for standalone applications) +int HashMgr::remove(const char * word) +{ + struct hentry * dp = lookup(word); + while (dp) { + if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) { + unsigned short * flags = + (unsigned short *) malloc(sizeof(short) * (dp->alen + 1)); + if (!flags) return 1; + for (int i = 0; i < dp->alen; i++) flags[i] = dp->astr[i]; + flags[dp->alen] = forbiddenword; + dp->astr = flags; + dp->alen++; + flag_qsort(flags, 0, dp->alen); + } + dp = dp->next_homonym; + } + return 0; +} + +/* remove forbidden flag to add a personal word to the hash */ +int HashMgr::remove_forbidden_flag(const char * word) { + struct hentry * dp = lookup(word); + if (!dp) return 1; + while (dp) { + if (dp->astr && TESTAFF(dp->astr, forbiddenword, dp->alen)) { + if (dp->alen == 1) dp->alen = 0; // XXX forbidden words of personal dic. + else { + unsigned short * flags2 = + (unsigned short *) malloc(sizeof(short) * (dp->alen - 1)); + if (!flags2) return 1; + int i, j = 0; + for (i = 0; i < dp->alen; i++) { + if (dp->astr[i] != forbiddenword) flags2[j++] = dp->astr[i]; + } + dp->alen--; + dp->astr = flags2; // XXX allowed forbidden words + } + } + dp = dp->next_homonym; + } + return 0; +} + +// add a custom dic. word to the hash table (public) +int HashMgr::add(const char * word) +{ + unsigned short * flags = NULL; + int al = 0; + if (remove_forbidden_flag(word)) { + int captype; + int wbl = strlen(word); + int wcl = get_clen_and_captype(word, wbl, &captype); + add_word(word, wbl, wcl, flags, al, NULL, false); + return add_hidden_capitalized_word((char *) word, wbl, wcl, flags, al, NULL, captype); + } + return 0; +} + +int HashMgr::add_with_affix(const char * word, const char * example) +{ + // detect captype and modify word length for UTF-8 encoding + struct hentry * dp = lookup(example); + remove_forbidden_flag(word); + if (dp && dp->astr) { + int captype; + int wbl = strlen(word); + int wcl = get_clen_and_captype(word, wbl, &captype); + if (aliasf) { + add_word(word, wbl, wcl, dp->astr, dp->alen, NULL, false); + } else { + unsigned short * flags = (unsigned short *) malloc (dp->alen * sizeof(short)); + if (flags) { + memcpy((void *) flags, (void *) dp->astr, dp->alen * sizeof(short)); + add_word(word, wbl, wcl, flags, dp->alen, NULL, false); + } else return 1; + } + return add_hidden_capitalized_word((char *) word, wbl, wcl, dp->astr, dp->alen, NULL, captype); + } + return 1; +} + +// walk the hash table entry by entry - null at end +// initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp); +struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const +{ + if (hp && hp->next != NULL) return hp->next; + for (col++; col < tablesize; col++) { + if (tableptr[col]) return tableptr[col]; + } + // null at end and reset to start + col = -1; + return NULL; +} + +// load a munched word list and build a hash table on the fly +int HashMgr::load_tables(const char * tpath, const char * key) +{ + int al; + char * ap; + char * dp; + char * dp2; + unsigned short * flags; + char * ts; + + // open dictionary file + FileMgr * dict = new FileMgr(tpath, key); + if (dict == NULL) return 1; + + // first read the first line of file to get hash table size */ + if (!(ts = dict->getline())) { + HUNSPELL_WARNING(stderr, "error: empty dic file\n"); + delete dict; + return 2; + } + mychomp(ts); + + /* remove byte order mark */ + if (strncmp(ts,"\xEF\xBB\xBF",3) == 0) { + memmove(ts, ts+3, strlen(ts+3)+1); + // warning: dic file begins with byte order mark: possible incompatibility with old Hunspell versions + } + + tablesize = atoi(ts); + if (tablesize == 0) { + HUNSPELL_WARNING(stderr, "error: line 1: missing or bad word count in the dic file\n"); + delete dict; + return 4; + } + tablesize = tablesize + 5 + USERWORD; + if ((tablesize %2) == 0) tablesize++; + + // allocate the hash table + tableptr = (struct hentry **) malloc(tablesize * sizeof(struct hentry *)); + if (! tableptr) { + delete dict; + return 3; + } + for (int i=0; igetline())) { + mychomp(ts); + // split each line into word and morphological description + dp = ts; + while ((dp = strchr(dp, ':'))) { + if ((dp > ts + 3) && (*(dp - 3) == ' ' || *(dp - 3) == '\t')) { + for (dp -= 4; dp >= ts && (*dp == ' ' || *dp == '\t'); dp--); + if (dp < ts) { // missing word + dp = NULL; + } else { + *(dp + 1) = '\0'; + dp = dp + 2; + } + break; + } + dp++; + } + + // tabulator is the old morphological field separator + dp2 = strchr(ts, '\t'); + if (dp2 && (!dp || dp2 < dp)) { + *dp2 = '\0'; + dp = dp2 + 1; + } + + // split each line into word and affix char strings + // "\/" signs slash in words (not affix separator) + // "/" at beginning of the line is word character (not affix separator) + ap = strchr(ts,'/'); + while (ap) { + if (ap == ts) { + ap++; + continue; + } else if (*(ap - 1) != '\\') break; + // replace "\/" with "/" + for (char * sp = ap - 1; *sp; *sp = *(sp + 1), sp++); + ap = strchr(ap,'/'); + } + + if (ap) { + *ap = '\0'; + if (aliasf) { + int index = atoi(ap + 1); + al = get_aliasf(index, &flags, dict); + if (!al) { + HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n", dict->getlinenum()); + *ap = '\0'; + } + } else { + al = decode_flags(&flags, ap + 1, dict); + if (al == -1) { + HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); + delete dict; + return 6; + } + flag_qsort(flags, 0, al); + } + } else { + al = 0; + ap = NULL; + flags = NULL; + } + + int captype; + int wbl = strlen(ts); + int wcl = get_clen_and_captype(ts, wbl, &captype); + // add the word and its index plus its capitalized form optionally + if (add_word(ts,wbl,wcl,flags,al,dp, false) || + add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) { + delete dict; + return 5; + } + } + + delete dict; + return 0; +} + +// the hash function is a simple load and rotate +// algorithm borrowed + +int HashMgr::hash(const char * word) const +{ + long hv = 0; + for (int i=0; i < 4 && *word != 0; i++) + hv = (hv << 8) | (*word++); + while (*word != 0) { + ROTATE(hv,ROTATE_LEN); + hv ^= (*word++); + } + return (unsigned long) hv % tablesize; +} + +int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) { + int len; + if (*flags == '\0') { + *result = NULL; + return 0; + } + switch (flag_mode) { + case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz) + len = strlen(flags); + if (len%2 == 1) HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getlinenum()); + len /= 2; + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + for (int i = 0; i < len; i++) { + (*result)[i] = (((unsigned short) flags[i * 2]) << 8) + (unsigned short) flags[i * 2 + 1]; + } + break; + } + case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 23 233) + int i; + len = 1; + char * src = flags; + unsigned short * dest; + char * p; + for (p = flags; *p; p++) { + if (*p == ',') len++; + } + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + dest = *result; + for (p = flags; *p; p++) { + if (*p == ',') { + i = atoi(src); + if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n", + af->getlinenum(), i, DEFAULTFLAGS - 1); + *dest = (unsigned short) i; + if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum()); + src = p + 1; + dest++; + } + } + i = atoi(src); + if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n", + af->getlinenum(), i, DEFAULTFLAGS - 1); + *dest = (unsigned short) i; + if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum()); + break; + } + case FLAG_UNI: { // UTF-8 characters + w_char w[BUFSIZE/2]; + len = u8_u16(w, BUFSIZE/2, flags); + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + memcpy(*result, w, len * sizeof(short)); + break; + } + default: { // Ispell's one-character flags (erfg -> e r f g) + unsigned short * dest; + len = strlen(flags); + *result = (unsigned short *) malloc(len * sizeof(short)); + if (!*result) return -1; + dest = *result; + for (unsigned char * p = (unsigned char *) flags; *p; p++) { + *dest = (unsigned short) *p; + dest++; + } + } + } + return len; +} + +unsigned short HashMgr::decode_flag(const char * f) { + unsigned short s = 0; + int i; + switch (flag_mode) { + case FLAG_LONG: + s = ((unsigned short) f[0] << 8) + (unsigned short) f[1]; + break; + case FLAG_NUM: + i = atoi(f); + if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: flag id %d is too large (max: %d)\n", i, DEFAULTFLAGS - 1); + s = (unsigned short) i; + break; + case FLAG_UNI: + u8_u16((w_char *) &s, 1, f); + break; + default: + s = (unsigned short) *((unsigned char *)f); + } + if (s == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n"); + return s; +} + +char * HashMgr::encode_flag(unsigned short f) { + unsigned char ch[10]; + if (f==0) return mystrdup("(NULL)"); + if (flag_mode == FLAG_LONG) { + ch[0] = (unsigned char) (f >> 8); + ch[1] = (unsigned char) (f - ((f >> 8) << 8)); + ch[2] = '\0'; + } else if (flag_mode == FLAG_NUM) { + sprintf((char *) ch, "%d", f); + } else if (flag_mode == FLAG_UNI) { + u16_u8((char *) &ch, 10, (w_char *) &f, 1); + } else { + ch[0] = (unsigned char) (f); + ch[1] = '\0'; + } + return mystrdup((char *) ch); +} + +// read in aff file and set flag mode +int HashMgr::load_config(const char * affpath, const char * key) +{ + char * line; // io buffers + int firstline = 1; + + // open the affix file + FileMgr * afflst = new FileMgr(affpath, key); + if (!afflst) { + HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n",affpath); + return 1; + } + + // read in each line ignoring any that do not + // start with a known line type indicator + + while ((line = afflst->getline())) { + mychomp(line); + + /* remove byte order mark */ + if (firstline) { + firstline = 0; + if (strncmp(line,"\xEF\xBB\xBF",3) == 0) memmove(line, line+3, strlen(line+3)+1); + } + + /* parse in the try string */ + if ((strncmp(line,"FLAG",4) == 0) && isspace(line[4])) { + if (flag_mode != FLAG_CHAR) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of the FLAG affix file parameter\n", afflst->getlinenum()); + } + if (strstr(line, "long")) flag_mode = FLAG_LONG; + if (strstr(line, "num")) flag_mode = FLAG_NUM; + if (strstr(line, "UTF-8")) flag_mode = FLAG_UNI; + if (flag_mode == FLAG_CHAR) { + HUNSPELL_WARNING(stderr, "error: line %d: FLAG needs `num', `long' or `UTF-8' parameter\n", afflst->getlinenum()); + } + } + if (strncmp(line,"FORBIDDENWORD",13) == 0) { + char * st = NULL; + if (parse_string(line, &st, afflst->getlinenum())) { + delete afflst; + return 1; + } + forbiddenword = decode_flag(st); + free(st); + } + if (strncmp(line, "SET", 3) == 0) { + if (parse_string(line, &enc, afflst->getlinenum())) { + delete afflst; + return 1; + } + if (strcmp(enc, "UTF-8") == 0) { + utf8 = 1; +#ifndef OPENOFFICEORG +#ifndef MOZILLA_CLIENT + initialize_utf_tbl(); +#endif +#endif + } else csconv = get_current_cs(enc); + } + if (strncmp(line, "LANG", 4) == 0) { + if (parse_string(line, &lang, afflst->getlinenum())) { + delete afflst; + return 1; + } + langnum = get_lang_num(lang); + } + + /* parse in the ignored characters (for example, Arabic optional diacritics characters */ + if (strncmp(line,"IGNORE",6) == 0) { + if (parse_array(line, &ignorechars, &ignorechars_utf16, + &ignorechars_utf16_len, utf8, afflst->getlinenum())) { + delete afflst; + return 1; + } + } + + if ((strncmp(line,"AF",2) == 0) && isspace(line[2])) { + if (parse_aliasf(line, afflst)) { + delete afflst; + return 1; + } + } + + if ((strncmp(line,"AM",2) == 0) && isspace(line[2])) { + if (parse_aliasm(line, afflst)) { + delete afflst; + return 1; + } + } + + if (strncmp(line,"COMPLEXPREFIXES",15) == 0) complexprefixes = 1; + if (((strncmp(line,"SFX",3) == 0) || (strncmp(line,"PFX",3) == 0)) && isspace(line[3])) break; + } + if (csconv == NULL) csconv = get_current_cs(SPELL_ENCODING); + delete afflst; + return 0; +} + +/* parse in the ALIAS table */ +int HashMgr::parse_aliasf(char * line, FileMgr * af) +{ + if (numaliasf != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numaliasf = atoi(piece); + if (numaliasf < 1) { + numaliasf = 0; + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + aliasf = (unsigned short **) malloc(numaliasf * sizeof(unsigned short *)); + aliasflen = (unsigned short *) malloc(numaliasf * sizeof(short)); + if (!aliasf || !aliasflen) { + numaliasf = 0; + if (aliasf) free(aliasf); + if (aliasflen) free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + return 1; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + numaliasf = 0; + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numaliasf lines to read in the remainder of the table */ + char * nl; + for (int j=0; j < numaliasf; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + aliasf[j] = NULL; + aliasflen[j] = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"AF",2) != 0) { + numaliasf = 0; + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + break; + } + case 1: { + aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece, af); + flag_qsort(aliasf[j], 0, aliasflen[j]); + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (!aliasf[j]) { + free(aliasf); + free(aliasflen); + aliasf = NULL; + aliasflen = NULL; + numaliasf = 0; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + } + return 0; +} + +int HashMgr::is_aliasf() { + return (aliasf != NULL); +} + +int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) { + if ((index > 0) && (index <= numaliasf)) { + *fvec = aliasf[index - 1]; + return aliasflen[index - 1]; + } + HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->getlinenum(), index); + *fvec = NULL; + return 0; +} + +/* parse morph alias definitions */ +int HashMgr::parse_aliasm(char * line, FileMgr * af) +{ + if (numaliasm != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); + return 1; + } + char * tp = line; + char * piece; + int i = 0; + int np = 0; + piece = mystrsep(&tp, 0); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { np++; break; } + case 1: { + numaliasm = atoi(piece); + if (numaliasm < 1) { + HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); + return 1; + } + aliasm = (char **) malloc(numaliasm * sizeof(char *)); + if (!aliasm) { + numaliasm = 0; + return 1; + } + np++; + break; + } + default: break; + } + i++; + } + piece = mystrsep(&tp, 0); + } + if (np != 2) { + numaliasm = 0; + free(aliasm); + aliasm = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); + return 1; + } + + /* now parse the numaliasm lines to read in the remainder of the table */ + char * nl = line; + for (int j=0; j < numaliasm; j++) { + if (!(nl = af->getline())) return 1; + mychomp(nl); + tp = nl; + i = 0; + aliasm[j] = NULL; + piece = mystrsep(&tp, ' '); + while (piece) { + if (*piece != '\0') { + switch(i) { + case 0: { + if (strncmp(piece,"AM",2) != 0) { + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + numaliasm = 0; + free(aliasm); + aliasm = NULL; + return 1; + } + break; + } + case 1: { + // add the remaining of the line + if (*tp) { + *(tp - 1) = ' '; + tp = tp + strlen(tp); + } + if (complexprefixes) { + if (utf8) reverseword_utf(piece); + else reverseword(piece); + } + aliasm[j] = mystrdup(piece); + if (!aliasm[j]) { + numaliasm = 0; + free(aliasm); + aliasm = NULL; + return 1; + } + break; } + default: break; + } + i++; + } + piece = mystrsep(&tp, ' '); + } + if (!aliasm[j]) { + numaliasm = 0; + free(aliasm); + aliasm = NULL; + HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); + return 1; + } + } + return 0; +} + +int HashMgr::is_aliasm() { + return (aliasm != NULL); +} + +char * HashMgr::get_aliasm(int index) { + if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1]; + HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index); + return NULL; +} diff --git a/src/lib/hunspell/src/hashmgr.hxx b/src/lib/hunspell/src/hashmgr.hxx new file mode 100644 index 0000000..341b081 --- /dev/null +++ b/src/lib/hunspell/src/hashmgr.hxx @@ -0,0 +1,69 @@ +#ifndef _HASHMGR_HXX_ +#define _HASHMGR_HXX_ + +#include "hunvisapi.h" + +#include + +#include "htypes.hxx" +#include "filemgr.hxx" + +enum flag { FLAG_CHAR, FLAG_LONG, FLAG_NUM, FLAG_UNI }; + +class LIBHUNSPELL_DLL_EXPORTED HashMgr +{ + int tablesize; + struct hentry ** tableptr; + int userword; + flag flag_mode; + int complexprefixes; + int utf8; + unsigned short forbiddenword; + int langnum; + char * enc; + char * lang; + struct cs_info * csconv; + char * ignorechars; + unsigned short * ignorechars_utf16; + int ignorechars_utf16_len; + int numaliasf; // flag vector `compression' with aliases + unsigned short ** aliasf; + unsigned short * aliasflen; + int numaliasm; // morphological desciption `compression' with aliases + char ** aliasm; + + +public: + HashMgr(const char * tpath, const char * apath, const char * key = NULL); + ~HashMgr(); + + struct hentry * lookup(const char *) const; + int hash(const char *) const; + struct hentry * walk_hashtable(int & col, struct hentry * hp) const; + + int add(const char * word); + int add_with_affix(const char * word, const char * pattern); + int remove(const char * word); + int decode_flags(unsigned short ** result, char * flags, FileMgr * af); + unsigned short decode_flag(const char * flag); + char * encode_flag(unsigned short flag); + int is_aliasf(); + int get_aliasf(int index, unsigned short ** fvec, FileMgr * af); + int is_aliasm(); + char * get_aliasm(int index); + +private: + int get_clen_and_captype(const char * word, int wbl, int * captype); + int load_tables(const char * tpath, const char * key); + int add_word(const char * word, int wbl, int wcl, unsigned short * ap, + int al, const char * desc, bool onlyupcase); + int load_config(const char * affpath, const char * key); + int parse_aliasf(char * line, FileMgr * af); + int add_hidden_capitalized_word(char * word, int wbl, int wcl, + unsigned short * flags, int al, char * dp, int captype); + int parse_aliasm(char * line, FileMgr * af); + int remove_forbidden_flag(const char * word); + +}; + +#endif diff --git a/src/lib/hunspell/src/htypes.hxx b/src/lib/hunspell/src/htypes.hxx new file mode 100644 index 0000000..5b6c909 --- /dev/null +++ b/src/lib/hunspell/src/htypes.hxx @@ -0,0 +1,32 @@ +#ifndef _HTYPES_HXX_ +#define _HTYPES_HXX_ + +#define ROTATE_LEN 5 + +#define ROTATE(v,q) \ + (v) = ((v) << (q)) | (((v) >> (32 - q)) & ((1 << (q))-1)); + +// hentry options +#define H_OPT (1 << 0) +#define H_OPT_ALIASM (1 << 1) +#define H_OPT_PHON (1 << 2) + +// see also csutil.hxx +#define HENTRY_WORD(h) &(h->word[0]) + +// approx. number of user defined words +#define USERWORD 1000 + +struct hentry +{ + unsigned char blen; // word length in bytes + unsigned char clen; // word length in characters (different for UTF-8 enc.) + short alen; // length of affix flag vector + unsigned short * astr; // affix flag vector + struct hentry * next; // next word with same hash code + struct hentry * next_homonym; // next homonym word (with same hash code) + char var; // variable fields (only for special pronounciation yet) + char word[1]; // variable-length word (8-bit or UTF-8 encoding) +}; + +#endif diff --git a/src/lib/hunspell/src/hunspell.cxx b/src/lib/hunspell/src/hunspell.cxx new file mode 100644 index 0000000..a9b261a --- /dev/null +++ b/src/lib/hunspell/src/hunspell.cxx @@ -0,0 +1,2006 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include + +#include "hunspell.hxx" +#include "hunspell.h" +#ifndef MOZILLA_CLIENT +# include "config.h" +#endif +#include "csutil.hxx" + +Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key) +{ + encoding = NULL; + csconv = NULL; + utf8 = 0; + complexprefixes = 0; + affixpath = mystrdup(affpath); + maxdic = 0; + + /* first set up the hash manager */ + pHMgr[0] = new HashMgr(dpath, affpath, key); + if (pHMgr[0]) maxdic = 1; + + /* next set up the affix manager */ + /* it needs access to the hash manager lookup methods */ + pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key); + + /* get the preferred try string and the dictionary */ + /* encoding from the Affix Manager for that dictionary */ + char * try_string = pAMgr->get_try_string(); + encoding = pAMgr->get_encoding(); + langnum = pAMgr->get_langnum(); + utf8 = pAMgr->get_utf8(); + if (!utf8) + csconv = get_current_cs(encoding); + complexprefixes = pAMgr->get_complexprefixes(); + wordbreak = pAMgr->get_breaktable(); + + /* and finally set up the suggestion manager */ + pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr); + if (try_string) free(try_string); +} + +Hunspell::~Hunspell() +{ + if (pSMgr) delete pSMgr; + if (pAMgr) delete pAMgr; + for (int i = 0; i < maxdic; i++) delete pHMgr[i]; + maxdic = 0; + pSMgr = NULL; + pAMgr = NULL; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif + csconv= NULL; + if (encoding) free(encoding); + encoding = NULL; + if (affixpath) free(affixpath); + affixpath = NULL; +} + +// load extra dictionaries +int Hunspell::add_dic(const char * dpath, const char * key) { + if (maxdic == MAXDIC || !affixpath) return 1; + pHMgr[maxdic] = new HashMgr(dpath, affixpath, key); + if (pHMgr[maxdic]) maxdic++; else return 1; + return 0; +} + +// make a copy of src at destination while removing all leading +// blanks and removing any trailing periods after recording +// their presence with the abbreviation flag +// also since already going through character by character, +// set the capitalization type +// return the length of the "cleaned" (and UTF-8 encoded) word + +int Hunspell::cleanword2(char * dest, const char * src, + w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev) +{ + unsigned char * p = (unsigned char *) dest; + const unsigned char * q = (const unsigned char * ) src; + + // first skip over any leading blanks + while ((*q != '\0') && (*q == ' ')) q++; + + // now strip off any trailing periods (recording their presence) + *pabbrev = 0; + int nl = strlen((const char *)q); + while ((nl > 0) && (*(q+nl-1)=='.')) { + nl--; + (*pabbrev)++; + } + + // if no characters are left it can't be capitalized + if (nl <= 0) { + *pcaptype = NOCAP; + *p = '\0'; + return 0; + } + + strncpy(dest, (char *) q, nl); + *(dest + nl) = '\0'; + nl = strlen(dest); + if (utf8) { + *nc = u8_u16(dest_utf, MAXWORDLEN, dest); + // don't check too long words + if (*nc >= MAXWORDLEN) return 0; + if (*nc == -1) { // big Unicode character (non BMP area) + *pcaptype = NOCAP; + return nl; + } + *pcaptype = get_captype_utf8(dest_utf, *nc, langnum); + } else { + *pcaptype = get_captype(dest, nl, csconv); + *nc = nl; + } + return nl; +} + +int Hunspell::cleanword(char * dest, const char * src, + int * pcaptype, int * pabbrev) +{ + unsigned char * p = (unsigned char *) dest; + const unsigned char * q = (const unsigned char * ) src; + int firstcap = 0; + + // first skip over any leading blanks + while ((*q != '\0') && (*q == ' ')) q++; + + // now strip off any trailing periods (recording their presence) + *pabbrev = 0; + int nl = strlen((const char *)q); + while ((nl > 0) && (*(q+nl-1)=='.')) { + nl--; + (*pabbrev)++; + } + + // if no characters are left it can't be capitalized + if (nl <= 0) { + *pcaptype = NOCAP; + *p = '\0'; + return 0; + } + + // now determine the capitalization type of the first nl letters + int ncap = 0; + int nneutral = 0; + int nc = 0; + + if (!utf8) { + while (nl > 0) { + nc++; + if (csconv[(*q)].ccase) ncap++; + if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++; + *p++ = *q++; + nl--; + } + // remember to terminate the destination string + *p = '\0'; + firstcap = csconv[(unsigned char)(*dest)].ccase; + } else { + unsigned short idx; + w_char t[MAXWORDLEN]; + nc = u8_u16(t, MAXWORDLEN, src); + for (int i = 0; i < nc; i++) { + idx = (t[i].h << 8) + t[i].l; + unsigned short low = unicodetolower(idx, langnum); + if (idx != low) ncap++; + if (unicodetoupper(idx, langnum) == low) nneutral++; + } + u16_u8(dest, MAXWORDUTF8LEN, t, nc); + if (ncap) { + idx = (t[0].h << 8) + t[0].l; + firstcap = (idx != unicodetolower(idx, langnum)); + } + } + + // now finally set the captype + if (ncap == 0) { + *pcaptype = NOCAP; + } else if ((ncap == 1) && firstcap) { + *pcaptype = INITCAP; + } else if ((ncap == nc) || ((ncap + nneutral) == nc)){ + *pcaptype = ALLCAP; + } else if ((ncap > 1) && firstcap) { + *pcaptype = HUHINITCAP; + } else { + *pcaptype = HUHCAP; + } + return strlen(dest); +} + +void Hunspell::mkallcap(char * p) +{ + if (utf8) { + w_char u[MAXWORDLEN]; + int nc = u8_u16(u, MAXWORDLEN, p); + unsigned short idx; + for (int i = 0; i < nc; i++) { + idx = (u[i].h << 8) + u[i].l; + if (idx != unicodetoupper(idx, langnum)) { + u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8); + u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF); + } + } + u16_u8(p, MAXWORDUTF8LEN, u, nc); + } else { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].cupper; + p++; + } + } +} + +int Hunspell::mkallcap2(char * p, w_char * u, int nc) +{ + if (utf8) { + unsigned short idx; + for (int i = 0; i < nc; i++) { + idx = (u[i].h << 8) + u[i].l; + unsigned short up = unicodetoupper(idx, langnum); + if (idx != up) { + u[i].h = (unsigned char) (up >> 8); + u[i].l = (unsigned char) (up & 0x00FF); + } + } + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } else { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].cupper; + p++; + } + } + return nc; +} + + +void Hunspell::mkallsmall(char * p) +{ + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].clower; + p++; + } +} + +int Hunspell::mkallsmall2(char * p, w_char * u, int nc) +{ + if (utf8) { + unsigned short idx; + for (int i = 0; i < nc; i++) { + idx = (u[i].h << 8) + u[i].l; + unsigned short low = unicodetolower(idx, langnum); + if (idx != low) { + u[i].h = (unsigned char) (low >> 8); + u[i].l = (unsigned char) (low & 0x00FF); + } + } + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } else { + while (*p != '\0') { + *p = csconv[((unsigned char) *p)].clower; + p++; + } + } + return nc; +} + +// convert UTF-8 sharp S codes to latin 1 +char * Hunspell::sharps_u8_l1(char * dest, char * source) { + char * p = dest; + *p = *source; + for (p++, source++; *(source - 1); p++, source++) { + *p = *source; + if (*source == '\x9F') *--p = '\xDF'; + } + return dest; +} + +// recursive search for right ss - sharp s permutations +hentry * Hunspell::spellsharps(char * base, char * pos, int n, + int repnum, char * tmp, int * info, char **root) { + pos = strstr(pos, "ss"); + if (pos && (n < MAXSHARPS)) { + *pos = '\xC3'; + *(pos + 1) = '\x9F'; + hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root); + if (h) return h; + *pos = 's'; + *(pos + 1) = 's'; + h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root); + if (h) return h; + } else if (repnum > 0) { + if (utf8) return checkword(base, info, root); + return checkword(sharps_u8_l1(tmp, base), info, root); + } + return NULL; +} + +int Hunspell::is_keepcase(const hentry * rv) { + return pAMgr && rv->astr && pAMgr->get_keepcase() && + TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen); +} + +/* insert a word to the beginning of the suggestion array and return ns */ +int Hunspell::insert_sug(char ***slst, char * word, int ns) { + char * dup = mystrdup(word); + if (!dup) return ns; + if (ns == MAXSUGGESTION) { + ns--; + free((*slst)[ns]); + } + for (int k = ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1]; + (*slst)[0] = dup; + return ns + 1; +} + +int Hunspell::spell(const char * word, int * info, char ** root) +{ + struct hentry * rv=NULL; + // need larger vector. For example, Turkish capital letter I converted a + // 2-byte UTF-8 character (dotless i) by mkallsmall. + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + w_char unicw[MAXWORDLEN]; + // Hunspell supports XML input of the simplified API (see manual) + if (strcmp(word, SPELL_XML) == 0) return 1; + int nc = strlen(word); + int wl2 = 0; + if (utf8) { + if (nc >= MAXWORDUTF8LEN) return 0; + } else { + if (nc >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + int wl = 0; + + // input conversion + RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv); + else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv); + + int info2 = 0; + if (wl == 0 || maxdic == 0) return 1; + if (root) *root = NULL; + + // allow numbers with dots, dashes and commas (but forbid double separators: "..", "--" etc.) + enum { NBEGIN, NNUM, NSEP }; + int nstate = NBEGIN; + int i; + + for (i = 0; (i < wl); i++) { + if ((cw[i] <= '9') && (cw[i] >= '0')) { + nstate = NNUM; + } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) { + if ((nstate == NSEP) || (i == 0)) break; + nstate = NSEP; + } else break; + } + if ((i == wl) && (nstate == NNUM)) return 1; + if (!info) info = &info2; else *info = 0; + + switch(captype) { + case HUHCAP: + case HUHINITCAP: + *info += SPELL_ORIGCAP; + case NOCAP: { + rv = checkword(cw, info, root); + if ((abbv) && !(rv)) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = checkword(wspace, info, root); + } + break; + } + case ALLCAP: { + *info += SPELL_ORIGCAP; + rv = checkword(cw, info, root); + if (rv) break; + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = checkword(wspace, info, root); + if (rv) break; + } + // Spec. prefix handling for Catalan, French, Italian: + // prefixes separated by apostrophe (SANT'ELIA -> Sant'+Elia). + if (pAMgr && strchr(cw, '\'')) { + wl = mkallsmall2(cw, unicw, nc); + //There are no really sane circumstances where this could fail, + //but anyway... + if (char * apostrophe = strchr(cw, '\'')) { + if (utf8) { + w_char tmpword[MAXWORDLEN]; + *apostrophe = '\0'; + wl2 = u8_u16(tmpword, MAXWORDLEN, cw); + *apostrophe = '\''; + if (wl2 < nc) { + mkinitcap2(apostrophe + 1, unicw + wl2 + 1, nc - wl2 - 1); + rv = checkword(cw, info, root); + if (rv) break; + } + } else { + mkinitcap2(apostrophe + 1, unicw, nc); + rv = checkword(cw, info, root); + if (rv) break; + } + } + mkinitcap2(cw, unicw, nc); + rv = checkword(cw, info, root); + if (rv) break; + } + if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) { + char tmpword[MAXWORDUTF8LEN]; + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root); + if (!rv) { + wl2 = mkinitcap2(cw, unicw, nc); + rv = spellsharps(cw, cw, 0, 0, tmpword, info, root); + } + if ((abbv) && !(rv)) { + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root); + if (!rv) { + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root); + } + } + if (rv) break; + } + } + case INITCAP: { + *info += SPELL_ORIGCAP; + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + wl2 = mkinitcap2(cw, unicw, nc); + if (captype == INITCAP) *info += SPELL_INITCAP; + rv = checkword(cw, info, root); + if (captype == INITCAP) *info -= SPELL_INITCAP; + // forbid bad capitalization + // (for example, ijs -> Ijs instead of IJs in Dutch) + // use explicit forms in dic: Ijs/F (F = FORBIDDENWORD flag) + if (*info & SPELL_FORBIDDEN) { + rv = NULL; + break; + } + if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL; + if (rv) break; + + rv = checkword(wspace, info, root); + if (abbv && !rv) { + + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + rv = checkword(wspace, info, root); + if (!rv) { + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + if (captype == INITCAP) *info += SPELL_INITCAP; + rv = checkword(wspace, info, root); + if (captype == INITCAP) *info -= SPELL_INITCAP; + if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL; + break; + } + } + if (rv && is_keepcase(rv) && + ((captype == ALLCAP) || + // if CHECKSHARPS: KEEPCASE words with \xDF are allowed + // in INITCAP form, too. + !(pAMgr->get_checksharps() && + ((utf8 && strstr(wspace, "\xC3\x9F")) || + (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL; + break; + } + } + + if (rv) { + if (pAMgr && pAMgr->get_warn() && rv->astr && + TESTAFF(rv->astr, pAMgr->get_warn(), rv->alen)) { + *info += SPELL_WARN; + if (pAMgr->get_forbidwarn()) return 0; + return HUNSPELL_OK_WARN; + } + return HUNSPELL_OK; + } + + // recursive breaking at break points + if (wordbreak) { + char * s; + char r; + int nbr = 0; + wl = strlen(cw); + int numbreak = pAMgr ? pAMgr->get_numbreak() : 0; + + // calculate break points for recursion limit + for (int j = 0; j < numbreak; j++) { + s = cw; + do { + s = (char *) strstr(s, wordbreak[j]); + if (s) { + nbr++; + s++; + } + } while (s); + } + if (nbr >= 10) return 0; + + // check boundary patterns (^begin and end$) + for (int j = 0; j < numbreak; j++) { + int plen = strlen(wordbreak[j]); + if (plen == 1 || plen > wl) continue; + if (wordbreak[j][0] == '^' && strncmp(cw, wordbreak[j] + 1, plen - 1) == 0 + && spell(cw + plen - 1)) return 1; + if (wordbreak[j][plen - 1] == '$' && + strncmp(cw + wl - plen + 1, wordbreak[j], plen - 1) == 0) { + r = cw[wl - plen + 1]; + cw[wl - plen + 1] = '\0'; + if (spell(cw)) return 1; + cw[wl - plen + 1] = r; + } + } + + // other patterns + for (int j = 0; j < numbreak; j++) { + int plen = strlen(wordbreak[j]); + s=(char *) strstr(cw, wordbreak[j]); + if (s && (s > cw) && (s < cw + wl - plen)) { + if (!spell(s + plen)) continue; + r = *s; + *s = '\0'; + // examine 2 sides of the break point + if (spell(cw)) return 1; + *s = r; + + // LANG_hu: spec. dash rule + if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) { + r = s[1]; + s[1] = '\0'; + if (spell(cw)) return 1; // check the first part with dash + s[1] = r; + } + // end of LANG speficic region + + } + } + } + + return 0; +} + +struct hentry * Hunspell::checkword(const char * w, int * info, char ** root) +{ + struct hentry * he = NULL; + int len, i; + char w2[MAXWORDUTF8LEN]; + const char * word; + + char * ignoredchars = pAMgr->get_ignore(); + if (ignoredchars != NULL) { + strcpy(w2, w); + if (utf8) { + int ignoredchars_utf16_len; + unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len); + remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len); + } else { + remove_ignored_chars(w2,ignoredchars); + } + word = w2; + } else word = w; + + len = strlen(word); + + if (!len) + return NULL; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + if (word != w2) { + strcpy(w2, word); + word = w2; + } + if (utf8) reverseword_utf(w2); else reverseword(w2); + } + + // look word in hash table + for (i = 0; (i < maxdic) && !he; i ++) { + he = (pHMgr[i])->lookup(word); + + // check forbidden and onlyincompound words + if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) { + if (info) *info += SPELL_FORBIDDEN; + // LANG_hu section: set dash information for suggestions + if (langnum == LANG_hu) { + if (pAMgr->get_compoundflag() && + TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) { + if (info) *info += SPELL_COMPOUND; + } + } + return NULL; + } + + // he = next not needaffix, onlyincompound homonym or onlyupcase word + while (he && (he->astr) && + ((pAMgr->get_needaffix() && TESTAFF(he->astr, pAMgr->get_needaffix(), he->alen)) || + (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) || + (info && (*info & SPELL_INITCAP) && TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)) + )) he = he->next_homonym; + } + + // check with affixes + if (!he && pAMgr) { + // try stripping off affixes */ + he = pAMgr->affix_check(word, len, 0); + + // check compound restriction and onlyupcase + if (he && he->astr && ( + (pAMgr->get_onlyincompound() && + TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) || + (info && (*info & SPELL_INITCAP) && + TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)))) { + he = NULL; + } + + if (he) { + if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) { + if (info) *info += SPELL_FORBIDDEN; + return NULL; + } + if (root) { + *root = mystrdup(he->word); + if (*root && complexprefixes) { + if (utf8) reverseword_utf(*root); else reverseword(*root); + } + } + // try check compound word + } else if (pAMgr->get_compound()) { + he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 0, info); + // LANG_hu section: `moving rule' with last dash + if ((!he) && (langnum == LANG_hu) && (word[len-1] == '-')) { + char * dup = mystrdup(word); + if (!dup) return NULL; + dup[len-1] = '\0'; + he = pAMgr->compound_check(dup, len-1, -5, 0, 100, 0, NULL, 1, 0, info); + free(dup); + } + // end of LANG speficic region + if (he) { + if (root) { + *root = mystrdup(he->word); + if (*root && complexprefixes) { + if (utf8) reverseword_utf(*root); else reverseword(*root); + } + } + if (info) *info += SPELL_COMPOUND; + } + } + + } + + return he; +} + +int Hunspell::suggest(char*** slst, const char * word) +{ + int onlycmpdsug = 0; + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (!pSMgr || maxdic == 0) return 0; + w_char unicw[MAXWORDLEN]; + *slst = NULL; + // process XML input of the simplified API (see manual) + if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) { + return spellml(slst, word); + } + int nc = strlen(word); + if (utf8) { + if (nc >= MAXWORDUTF8LEN) return 0; + } else { + if (nc >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + int wl = 0; + + // input conversion + RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv); + else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv); + + if (wl == 0) return 0; + int ns = 0; + int capwords = 0; + + // check capitalized form for FORCEUCASE + if (pAMgr && captype == NOCAP && pAMgr->get_forceucase()) { + int info = SPELL_ORIGCAP; + char ** wlst; + if (checkword(cw, &info, NULL)) { + if (*slst) { + wlst = *slst; + } else { + wlst = (char **) malloc(MAXSUGGESTION * sizeof(char *)); + if (wlst == NULL) return -1; + *slst = wlst; + for (int i = 0; i < MAXSUGGESTION; i++) { + wlst[i] = NULL; + } + } + wlst[0] = mystrdup(cw); + mkinitcap(wlst[0]); + return 1; + } + } + + switch(captype) { + case NOCAP: { + ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug); + break; + } + + case INITCAP: { + capwords = 1; + ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug); + if (ns == -1) break; + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + break; + } + case HUHINITCAP: + capwords = 1; + case HUHCAP: { + ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug); + if (ns != -1) { + int prevns; + // something.The -> something. The + char * dot = strchr(cw, '.'); + if (dot && (dot > cw)) { + int captype_; + if (utf8) { + w_char w_[MAXWORDLEN]; + int wl_ = u8_u16(w_, MAXWORDLEN, dot + 1); + captype_ = get_captype_utf8(w_, wl_, langnum); + } else captype_ = get_captype(dot+1, strlen(dot+1), csconv); + if (captype_ == INITCAP) { + char * st = mystrdup(cw); + if (st) st = (char *) realloc(st, wl + 2); + if (st) { + st[(dot - cw) + 1] = ' '; + strcpy(st + (dot - cw) + 2, dot + 1); + ns = insert_sug(slst, st, ns); + free(st); + } + } + } + if (captype == HUHINITCAP) { + // TheOpenOffice.org -> The OpenOffice.org + memcpy(wspace,cw,(wl+1)); + mkinitsmall2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + } + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + if (spell(wspace)) ns = insert_sug(slst, wspace, ns); + prevns = ns; + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + if (captype == HUHINITCAP) { + mkinitcap2(wspace, unicw, nc); + if (spell(wspace)) ns = insert_sug(slst, wspace, ns); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + } + // aNew -> "a New" (instead of "a new") + for (int j = prevns; j < ns; j++) { + char * space = strchr((*slst)[j],' '); + if (space) { + int slen = strlen(space + 1); + // different case after space (need capitalisation) + if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) { + w_char w[MAXWORDLEN]; + int wc = 0; + char * r = (*slst)[j]; + if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1); + mkinitcap2(space + 1, w, wc); + // set as first suggestion + for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1]; + (*slst)[0] = r; + } + } + } + } + break; + } + + case ALLCAP: { + memcpy(wspace, cw, (wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + if (ns == -1) break; + if (pAMgr && pAMgr->get_keepcase() && spell(wspace)) + ns = insert_sug(slst, wspace, ns); + mkinitcap2(wspace, unicw, nc); + ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug); + for (int j=0; j < ns; j++) { + mkallcap((*slst)[j]); + if (pAMgr && pAMgr->get_checksharps()) { + char * pos; + if (utf8) { + pos = strstr((*slst)[j], "\xC3\x9F"); + while (pos) { + *pos = 'S'; + *(pos+1) = 'S'; + pos = strstr(pos+2, "\xC3\x9F"); + } + } else { + pos = strchr((*slst)[j], '\xDF'); + while (pos) { + (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2); + mystrrep((*slst)[j], "\xDF", "SS"); + pos = strchr((*slst)[j], '\xDF'); + } + } + } + } + break; + } + } + + // LANG_hu section: replace '-' with ' ' in Hungarian + if (langnum == LANG_hu) { + for (int j=0; j < ns; j++) { + char * pos = strchr((*slst)[j],'-'); + if (pos) { + int info; + char w[MAXWORDUTF8LEN]; + *pos = '\0'; + strcpy(w, (*slst)[j]); + strcat(w, pos + 1); + spell(w, &info, NULL); + if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) { + *pos = ' '; + } else *pos = '-'; + } + } + } + // END OF LANG_hu section + + // try ngram approach since found nothing or only compound words + if (pAMgr && (ns == 0 || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0) && (*slst)) { + switch(captype) { + case NOCAP: { + ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic); + break; + } + case HUHINITCAP: + capwords = 1; + case HUHCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic); + break; + } + case INITCAP: { + capwords = 1; + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic); + break; + } + case ALLCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall2(wspace, unicw, nc); + int oldns = ns; + ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic); + for (int j = oldns; j < ns; j++) + mkallcap((*slst)[j]); + break; + } + } + } + + // try dash suggestion (Afo-American -> Afro-American) + if (char * pos = strchr(cw, '-')) { + char * ppos = cw; + int nodashsug = 1; + char ** nlst = NULL; + int nn = 0; + int last = 0; + if (*slst) { + for (int j = 0; j < ns && nodashsug == 1; j++) { + if (strchr((*slst)[j], '-')) nodashsug = 0; + } + } + while (nodashsug && !last) { + if (*pos == '\0') last = 1; else *pos = '\0'; + if (!spell(ppos)) { + nn = suggest(&nlst, ppos); + for (int j = nn - 1; j >= 0; j--) { + strncpy(wspace, cw, ppos - cw); + strcpy(wspace + (ppos - cw), nlst[j]); + if (!last) { + strcat(wspace, "-"); + strcat(wspace, pos + 1); + } + ns = insert_sug(slst, wspace, ns); + free(nlst[j]); + } + if (nlst != NULL) free(nlst); + nodashsug = 0; + } + if (!last) { + *pos = '-'; + ppos = pos + 1; + pos = strchr(ppos, '-'); + } + if (!pos) pos = cw + strlen(cw); + } + } + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + for (int j = 0; j < ns; j++) { + if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]); + } + } + + // capitalize + if (capwords) for (int j=0; j < ns; j++) { + mkinitcap((*slst)[j]); + } + + // expand suggestions with dot(s) + if (abbv && pAMgr && pAMgr->get_sugswithdots()) { + for (int j = 0; j < ns; j++) { + (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv); + strcat((*slst)[j], word + strlen(word) - abbv); + } + } + + // remove bad capitalized and forbidden forms + if (pAMgr && (pAMgr->get_keepcase() || pAMgr->get_forbiddenword())) { + switch (captype) { + case INITCAP: + case ALLCAP: { + int l = 0; + for (int j=0; j < ns; j++) { + if (!strchr((*slst)[j],' ') && !spell((*slst)[j])) { + char s[MAXSWUTF8L]; + w_char w[MAXSWL]; + int len; + if (utf8) { + len = u8_u16(w, MAXSWL, (*slst)[j]); + } else { + strcpy(s, (*slst)[j]); + len = strlen(s); + } + mkallsmall2(s, w, len); + free((*slst)[j]); + if (spell(s)) { + (*slst)[l] = mystrdup(s); + if ((*slst)[l]) l++; + } else { + mkinitcap2(s, w, len); + if (spell(s)) { + (*slst)[l] = mystrdup(s); + if ((*slst)[l]) l++; + } + } + } else { + (*slst)[l] = (*slst)[j]; + l++; + } + } + ns = l; + } + } + } + + // remove duplications + int l = 0; + for (int j = 0; j < ns; j++) { + (*slst)[l] = (*slst)[j]; + for (int k = 0; k < l; k++) { + if (strcmp((*slst)[k], (*slst)[j]) == 0) { + free((*slst)[j]); + l--; + break; + } + } + l++; + } + ns = l; + + // output conversion + rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL; + for (int j = 0; rl && j < ns; j++) { + if (rl->conv((*slst)[j], wspace)) { + free((*slst)[j]); + (*slst)[j] = mystrdup(wspace); + } + } + + // if suggestions removed by nosuggest, onlyincompound parameters + if (l == 0 && *slst) { + free(*slst); + *slst = NULL; + } + return l; +} + +void Hunspell::free_list(char *** slst, int n) { + freelist(slst, n); +} + +char * Hunspell::get_dic_encoding() +{ + return encoding; +} + +#ifdef HUNSPELL_EXPERIMENTAL +// XXX need UTF-8 support +int Hunspell::suggest_auto(char*** slst, const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (!pSMgr || maxdic == 0) return 0; + int wl = strlen(word); + if (utf8) { + if (wl >= MAXWORDUTF8LEN) return 0; + } else { + if (wl >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + wl = cleanword(cw, word, &captype, &abbv); + if (wl == 0) return 0; + int ns = 0; + *slst = NULL; // HU, nsug in pSMgr->suggest + + switch(captype) { + case NOCAP: { + ns = pSMgr->suggest_auto(slst, cw, ns); + if (ns>0) break; + break; + } + + case INITCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + for (int j=0; j < ns; j++) + mkinitcap((*slst)[j]); + ns = pSMgr->suggest_auto(slst, cw, ns); + break; + + } + + case HUHINITCAP: + case HUHCAP: { + ns = pSMgr->suggest_auto(slst, cw, ns); + if (ns == 0) { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + } + break; + } + + case ALLCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + + mkinitcap(wspace); + ns = pSMgr->suggest_auto(slst, wspace, ns); + + for (int j=0; j < ns; j++) + mkallcap((*slst)[j]); + break; + } + } + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + for (int j = 0; j < ns; j++) { + if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]); + } + } + + // expand suggestions with dot(s) + if (abbv && pAMgr && pAMgr->get_sugswithdots()) { + for (int j = 0; j < ns; j++) { + (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv); + strcat((*slst)[j], word + strlen(word) - abbv); + } + } + + // LANG_hu section: replace '-' with ' ' in Hungarian + if (langnum == LANG_hu) { + for (int j=0; j < ns; j++) { + char * pos = strchr((*slst)[j],'-'); + if (pos) { + int info; + char w[MAXWORDUTF8LEN]; + *pos = '\0'; + strcpy(w, (*slst)[j]); + strcat(w, pos + 1); + spell(w, &info, NULL); + if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) { + *pos = ' '; + } else *pos = '-'; + } + } + } + // END OF LANG_hu section + return ns; +} +#endif + +int Hunspell::stem(char*** slst, char ** desc, int n) +{ + char result[MAXLNLEN]; + char result2[MAXLNLEN]; + *slst = NULL; + if (n == 0) return 0; + *result2 = '\0'; + for (int i = 0; i < n; i++) { + *result = '\0'; + // add compound word parts (except the last one) + char * s = (char *) desc[i]; + char * part = strstr(s, MORPH_PART); + if (part) { + char * nextpart = strstr(part + 1, MORPH_PART); + while (nextpart) { + copy_field(result + strlen(result), part, MORPH_PART); + part = nextpart; + nextpart = strstr(part + 1, MORPH_PART); + } + s = part; + } + + char **pl; + char tok[MAXLNLEN]; + strcpy(tok, s); + char * alt = strstr(tok, " | "); + while (alt) { + alt[1] = MSEP_ALT; + alt = strstr(alt, " | "); + } + int pln = line_tok(tok, &pl, MSEP_ALT); + for (int k = 0; k < pln; k++) { + // add derivational suffixes + if (strstr(pl[k], MORPH_DERI_SFX)) { + // remove inflectional suffixes + char * is = strstr(pl[k], MORPH_INFL_SFX); + if (is) *is = '\0'; + char * sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]); + if (sg) { + char ** gen; + int genl = line_tok(sg, &gen, MSEP_REC); + free(sg); + for (int j = 0; j < genl; j++) { + sprintf(result2 + strlen(result2), "%c%s%s", + MSEP_REC, result, gen[j]); + } + freelist(&gen, genl); + } + } else { + sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result); + if (strstr(pl[k], MORPH_SURF_PFX)) { + copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX); + } + copy_field(result2 + strlen(result2), pl[k], MORPH_STEM); + } + } + freelist(&pl, pln); + } + int sln = line_tok(result2, slst, MSEP_REC); + return uniqlist(*slst, sln); + +} + +int Hunspell::stem(char*** slst, const char * word) +{ + char ** pl; + int pln = analyze(&pl, word); + int pln2 = stem(slst, pl, pln); + freelist(&pl, pln); + return pln2; +} + +#ifdef HUNSPELL_EXPERIMENTAL +int Hunspell::suggest_pos_stems(char*** slst, const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (! pSMgr || maxdic == 0) return 0; + int wl = strlen(word); + if (utf8) { + if (wl >= MAXWORDUTF8LEN) return 0; + } else { + if (wl >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + wl = cleanword(cw, word, &captype, &abbv); + if (wl == 0) return 0; + + int ns = 0; // ns=0 = normalized input + + *slst = NULL; // HU, nsug in pSMgr->suggest + + switch(captype) { + case HUHCAP: + case NOCAP: { + ns = pSMgr->suggest_pos_stems(slst, cw, ns); + + if ((abbv) && (ns == 0)) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + } + + break; + } + + case INITCAP: { + + ns = pSMgr->suggest_pos_stems(slst, cw, ns); + + if (ns == 0 || ((*slst)[0][0] == '#')) { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + } + + break; + + } + + case ALLCAP: { + ns = pSMgr->suggest_pos_stems(slst, cw, ns); + if (ns != 0) break; + + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + + if (ns == 0) { + mkinitcap(wspace); + ns = pSMgr->suggest_pos_stems(slst, wspace, ns); + } + break; + } + } + + return ns; +} +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + +const char * Hunspell::get_wordchars() +{ + return pAMgr->get_wordchars(); +} + +unsigned short * Hunspell::get_wordchars_utf16(int * len) +{ + return pAMgr->get_wordchars_utf16(len); +} + +void Hunspell::mkinitcap(char * p) +{ + if (!utf8) { + if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper; + } else { + int len; + w_char u[MAXWORDLEN]; + len = u8_u16(u, MAXWORDLEN, p); + unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum); + u[0].h = (unsigned char) (i >> 8); + u[0].l = (unsigned char) (i & 0x00FF); + u16_u8(p, MAXWORDUTF8LEN, u, len); + } +} + +int Hunspell::mkinitcap2(char * p, w_char * u, int nc) +{ + if (!utf8) { + if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper; + } else if (nc > 0) { + unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum); + u[0].h = (unsigned char) (i >> 8); + u[0].l = (unsigned char) (i & 0x00FF); + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } + return nc; +} + +int Hunspell::mkinitsmall2(char * p, w_char * u, int nc) +{ + if (!utf8) { + if (*p != '\0') *p = csconv[((unsigned char)*p)].clower; + } else if (nc > 0) { + unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum); + u[0].h = (unsigned char) (i >> 8); + u[0].l = (unsigned char) (i & 0x00FF); + u16_u8(p, MAXWORDUTF8LEN, u, nc); + return strlen(p); + } + return nc; +} + +int Hunspell::add(const char * word) +{ + if (pHMgr[0]) return (pHMgr[0])->add(word); + return 0; +} + +int Hunspell::add_with_affix(const char * word, const char * example) +{ + if (pHMgr[0]) return (pHMgr[0])->add_with_affix(word, example); + return 0; +} + +int Hunspell::remove(const char * word) +{ + if (pHMgr[0]) return (pHMgr[0])->remove(word); + return 0; +} + +const char * Hunspell::get_version() +{ + return pAMgr->get_version(); +} + +struct cs_info * Hunspell::get_csconv() +{ + return csconv; +} + +void Hunspell::cat_result(char * result, char * st) +{ + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } +} + +int Hunspell::analyze(char*** slst, const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + w_char unicw[MAXWORDLEN]; + int wl2 = 0; + *slst = NULL; + if (! pSMgr || maxdic == 0) return 0; + int nc = strlen(word); + if (utf8) { + if (nc >= MAXWORDUTF8LEN) return 0; + } else { + if (nc >= MAXWORDLEN) return 0; + } + int captype = 0; + int abbv = 0; + int wl = 0; + + // input conversion + RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL; + if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv); + else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv); + + if (wl == 0) { + if (abbv) { + for (wl = 0; wl < abbv; wl++) cw[wl] = '.'; + cw[wl] = '\0'; + abbv = 0; + } else return 0; + } + + char result[MAXLNLEN]; + char * st = NULL; + + *result = '\0'; + + int n = 0; + int n2 = 0; + int n3 = 0; + + // test numbers + // LANG_hu section: set dash information for suggestions + if (langnum == LANG_hu) { + while ((n < wl) && + (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) { + n++; + if ((cw[n] == '.') || (cw[n] == ',')) { + if (((n2 == 0) && (n > 3)) || + ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break; + n2++; + n3 = n; + } + } + + if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return 0; + if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='\xB0')) && checkword(cw+n, NULL, NULL))) { + mystrcat(result, cw, MAXLNLEN); + result[n - 1] = '\0'; + if (n == wl) cat_result(result, pSMgr->suggest_morph(cw + n - 1)); + else { + char sign = cw[n]; + cw[n] = '\0'; + cat_result(result, pSMgr->suggest_morph(cw + n - 1)); + mystrcat(result, "+", MAXLNLEN); // XXX SPEC. MORPHCODE + cw[n] = sign; + cat_result(result, pSMgr->suggest_morph(cw + n)); + } + return line_tok(result, slst, MSEP_REC); + } + } + // END OF LANG_hu section + + switch(captype) { + case HUHCAP: + case HUHINITCAP: + case NOCAP: { + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(wspace)); + } + break; + } + case INITCAP: { + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + wl2 = mkinitcap2(cw, unicw, nc); + cat_result(result, pSMgr->suggest_morph(wspace)); + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(wspace)); + + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + + cat_result(result, pSMgr->suggest_morph(wspace)); + } + break; + } + case ALLCAP: { + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(cw)); + } + wl = mkallsmall2(cw, unicw, nc); + memcpy(wspace,cw,(wl+1)); + wl2 = mkinitcap2(cw, unicw, nc); + + cat_result(result, pSMgr->suggest_morph(wspace)); + cat_result(result, pSMgr->suggest_morph(cw)); + if (abbv) { + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + cat_result(result, pSMgr->suggest_morph(wspace)); + + memcpy(wspace, cw, wl2); + *(wspace+wl2) = '.'; + *(wspace+wl2+1) = '\0'; + + cat_result(result, pSMgr->suggest_morph(wspace)); + } + break; + } + } + + if (*result) { + // word reversing wrapper for complex prefixes + if (complexprefixes) { + if (utf8) reverseword_utf(result); else reverseword(result); + } + return line_tok(result, slst, MSEP_REC); + } + + // compound word with dash (HU) I18n + char * dash = NULL; + int nresult = 0; + // LANG_hu section: set dash information for suggestions + if (langnum == LANG_hu) dash = (char *) strchr(cw,'-'); + if ((langnum == LANG_hu) && dash) { + *dash='\0'; + // examine 2 sides of the dash + if (dash[1] == '\0') { // base word ending with dash + if (spell(cw)) { + char * p = pSMgr->suggest_morph(cw); + if (p) { + int ret = line_tok(p, slst, MSEP_REC); + free(p); + return ret; + } + + } + } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat. + if (spell(cw) && (spell("-e"))) { + st = pSMgr->suggest_morph(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE + st = pSMgr->suggest_morph("-e"); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + return line_tok(result, slst, MSEP_REC); + } + } else { + // first word ending with dash: word- XXX ??? + char r2 = *(dash + 1); + dash[0]='-'; + dash[1]='\0'; + nresult = spell(cw); + dash[1] = r2; + dash[0]='\0'; + if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) || + ((dash[1] > '0') && (dash[1] < '9')))) { + st = pSMgr->suggest_morph(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE + } + st = pSMgr->suggest_morph(dash+1); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + return line_tok(result, slst, MSEP_REC); + } + } + // affixed number in correct word + if (nresult && (dash > cw) && (((*(dash-1)<='9') && + (*(dash-1)>='0')) || (*(dash-1)=='.'))) { + *dash='-'; + n = 1; + if (*(dash - n) == '.') n++; + // search first not a number character to left from dash + while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) { + n++; + } + if ((dash - n) < cw) n--; + // numbers: valami1000000-hoz + // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz, + // 56-hoz, 6-hoz + for(; n >= 1; n--) { + if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) { + mystrcat(result, cw, MAXLNLEN); + result[dash - cw - n] = '\0'; + st = pSMgr->suggest_morph(dash - n); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + return line_tok(result, slst, MSEP_REC); + } + } + } + } + return 0; +} + +int Hunspell::generate(char*** slst, const char * word, char ** pl, int pln) +{ + *slst = NULL; + if (!pSMgr || !pln) return 0; + char **pl2; + int pl2n = analyze(&pl2, word); + int captype = 0; + int abbv = 0; + char cw[MAXWORDUTF8LEN]; + cleanword(cw, word, &captype, &abbv); + char result[MAXLNLEN]; + *result = '\0'; + + for (int i = 0; i < pln; i++) { + cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i])); + } + freelist(&pl2, pl2n); + + if (*result) { + // allcap + if (captype == ALLCAP) mkallcap(result); + + // line split + int linenum = line_tok(result, slst, MSEP_REC); + + // capitalize + if (captype == INITCAP || captype == HUHINITCAP) { + for (int j=0; j < linenum; j++) mkinitcap((*slst)[j]); + } + + // temporary filtering of prefix related errors (eg. + // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks") + + int r = 0; + for (int j=0; j < linenum; j++) { + if (!spell((*slst)[j])) { + free((*slst)[j]); + (*slst)[j] = NULL; + } else { + if (r < j) (*slst)[r] = (*slst)[j]; + r++; + } + } + if (r > 0) return r; + free(*slst); + *slst = NULL; + } + return 0; +} + +int Hunspell::generate(char*** slst, const char * word, const char * pattern) +{ + char **pl; + int pln = analyze(&pl, pattern); + int n = generate(slst, word, pl, pln); + freelist(&pl, pln); + return uniqlist(*slst, n); +} + +// minimal XML parser functions +int Hunspell::get_xml_par(char * dest, const char * par, int max) +{ + char * d = dest; + if (!par) return 0; + char end = *par; + char * dmax = dest + max; + if (end == '>') end = '<'; + else if (end != '\'' && end != '"') return 0; // bad XML + for (par++; d < dmax && *par != '\0' && *par != end; par++, d++) *d = *par; + *d = '\0'; + mystrrep(dest, "<", "<"); + mystrrep(dest, "&", "&"); + return (int)(d - dest); +} + +int Hunspell::get_langnum() const +{ + return langnum; +} + +// return the beginning of the element (attr == NULL) or the attribute +const char * Hunspell::get_xml_pos(const char * s, const char * attr) +{ + const char * end = strchr(s, '>'); + const char * p = s; + if (attr == NULL) return end; + do { + p = strstr(p, attr); + if (!p || p >= end) return 0; + } while (*(p-1) != ' ' && *(p-1) != '\n'); + return p + strlen(attr); +} + +int Hunspell::check_xml_par(const char * q, const char * attr, const char * value) { + char cw[MAXWORDUTF8LEN]; + if (get_xml_par(cw, get_xml_pos(q, attr), MAXWORDUTF8LEN - 1) && + strcmp(cw, value) == 0) return 1; + return 0; +} + +int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) { + int n = 0; + char * p; + if (!list) return 0; + for (p = list; (p = strstr(p, tag)); p++) n++; + if (n == 0) return 0; + *slst = (char **) malloc(sizeof(char *) * n); + if (!*slst) return 0; + for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) { + int l = strlen(p); + (*slst)[n] = (char *) malloc(l + 1); + if (!(*slst)[n]) return n; + if (!get_xml_par((*slst)[n], p + strlen(tag) - 1, l)) { + free((*slst)[n]); + break; + } + } + return n; +} + +int Hunspell::spellml(char*** slst, const char * word) +{ + char *q, *q2; + char cw[MAXWORDUTF8LEN], cw2[MAXWORDUTF8LEN]; + q = (char *) strstr(word, "'); + if (!q2) return 0; // bad XML input + q2 = strstr(q2, "'), MAXWORDUTF8LEN - 10)) n = analyze(slst, cw); + if (n == 0) return 0; + // convert the result to ana1ana2 format + for (int i = 0; i < n; i++) s+= strlen((*slst)[i]); + char * r = (char *) malloc(6 + 5 * s + 7 * n + 7 + 1); // XXX 5*s->&->& + if (!r) return 0; + strcpy(r, ""); + for (int i = 0; i < n; i++) { + int l = strlen(r); + strcpy(r + l, ""); + strcpy(r + l + 3, (*slst)[i]); + mystrrep(r + l + 3, "\t", " "); + mystrrep(r + l + 3, "<", "<"); + mystrrep(r + l + 3, "&", "&"); + strcat(r, ""); + free((*slst)[i]); + } + strcat(r, ""); + (*slst)[0] = r; + return 1; + } else if (check_xml_par(q, "type=", "stem")) { + if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1)) return stem(slst, cw); + } else if (check_xml_par(q, "type=", "generate")) { + int n = get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1); + if (n == 0) return 0; + char * q3 = strstr(q2 + 1, "'), MAXWORDUTF8LEN - 1)) { + return generate(slst, cw, cw2); + } + } else { + if ((q2 = strstr(q2 + 1, "'), ""))) { + int n2 = generate(slst, cw, slst2, n); + freelist(&slst2, n); + return uniqlist(*slst, n2); + } + freelist(&slst2, n); + } + } + } + return 0; +} + + +#ifdef HUNSPELL_EXPERIMENTAL +// XXX need UTF-8 support +char * Hunspell::morph_with_correction(const char * word) +{ + char cw[MAXWORDUTF8LEN]; + char wspace[MAXWORDUTF8LEN]; + if (! pSMgr || maxdic == 0) return NULL; + int wl = strlen(word); + if (utf8) { + if (wl >= MAXWORDUTF8LEN) return NULL; + } else { + if (wl >= MAXWORDLEN) return NULL; + } + int captype = 0; + int abbv = 0; + wl = cleanword(cw, word, &captype, &abbv); + if (wl == 0) return NULL; + + char result[MAXLNLEN]; + char * st = NULL; + + *result = '\0'; + + + switch(captype) { + case NOCAP: { + st = pSMgr->suggest_morph_for_spelling_error(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + break; + } + case INITCAP: { + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + st = pSMgr->suggest_morph_for_spelling_error(cw); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + if (abbv) { + memcpy(wspace,cw,wl); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkinitcap(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + break; + } + case HUHCAP: { + st = pSMgr->suggest_morph_for_spelling_error(cw); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + memcpy(wspace,cw,(wl+1)); + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + break; + } + case ALLCAP: { + memcpy(wspace,cw,(wl+1)); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkinitcap(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + if (abbv) { + memcpy(wspace,cw,(wl+1)); + *(wspace+wl) = '.'; + *(wspace+wl+1) = '\0'; + if (*result) mystrcat(result, "\n", MAXLNLEN); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkallsmall(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + mkinitcap(wspace); + st = pSMgr->suggest_morph_for_spelling_error(wspace); + if (st) { + if (*result) mystrcat(result, "\n", MAXLNLEN); + mystrcat(result, st, MAXLNLEN); + free(st); + } + } + break; + } + } + + if (*result) return mystrdup(result); + return NULL; +} + +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + +Hunhandle *Hunspell_create(const char * affpath, const char * dpath) +{ + return (Hunhandle*)(new Hunspell(affpath, dpath)); +} + +Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath, + const char * key) +{ + return (Hunhandle*)(new Hunspell(affpath, dpath, key)); +} + +void Hunspell_destroy(Hunhandle *pHunspell) +{ + delete (Hunspell*)(pHunspell); +} + +int Hunspell_spell(Hunhandle *pHunspell, const char *word) +{ + return ((Hunspell*)pHunspell)->spell(word); +} + +char *Hunspell_get_dic_encoding(Hunhandle *pHunspell) +{ + return ((Hunspell*)pHunspell)->get_dic_encoding(); +} + +int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word) +{ + return ((Hunspell*)pHunspell)->suggest(slst, word); +} + +int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word) +{ + return ((Hunspell*)pHunspell)->analyze(slst, word); +} + +int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word) +{ + return ((Hunspell*)pHunspell)->stem(slst, word); +} + +int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n) +{ + return ((Hunspell*)pHunspell)->stem(slst, desc, n); +} + +int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word, + const char * word2) +{ + return ((Hunspell*)pHunspell)->generate(slst, word, word2); +} + +int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word, + char** desc, int n) +{ + return ((Hunspell*)pHunspell)->generate(slst, word, desc, n); +} + + /* functions for run-time modification of the dictionary */ + + /* add word to the run-time dictionary */ + +int Hunspell_add(Hunhandle *pHunspell, const char * word) { + return ((Hunspell*)pHunspell)->add(word); +} + + /* add word to the run-time dictionary with affix flags of + * the example (a dictionary word): Hunspell will recognize + * affixed forms of the new word, too. + */ + +int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word, + const char * example) { + return ((Hunspell*)pHunspell)->add_with_affix(word, example); +} + + /* remove word from the run-time dictionary */ + +int Hunspell_remove(Hunhandle *pHunspell, const char * word) { + return ((Hunspell*)pHunspell)->remove(word); +} + +void Hunspell_free_list(Hunhandle *, char *** slst, int n) { + freelist(slst, n); +} diff --git a/src/lib/hunspell/src/hunspell.dsp b/src/lib/hunspell/src/hunspell.dsp new file mode 100644 index 0000000..05e072f --- /dev/null +++ b/src/lib/hunspell/src/hunspell.dsp @@ -0,0 +1,164 @@ +# Microsoft Developer Studio Project File - Name="hunspell" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=hunspell - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "hunspell.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "hunspell.mak" CFG="hunspell - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "hunspell - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "hunspell - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "hunspell - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "W32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "W32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD BASE RSC /l 0x40e /d "NDEBUG" +# ADD RSC /l 0x40e /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "hunspell - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "W32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "W32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD BASE RSC /l 0x40e /d "_DEBUG" +# ADD RSC /l 0x40e /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "hunspell - Win32 Release" +# Name "hunspell - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\affentry.cxx +# End Source File +# Begin Source File + +SOURCE=.\affixmgr.cxx +# End Source File +# Begin Source File + +SOURCE=.\csutil.cxx +# End Source File +# Begin Source File + +SOURCE=.\dictmgr.cxx +# End Source File +# Begin Source File + +SOURCE=.\hashmgr.cxx +# End Source File +# Begin Source File + +SOURCE=.\hunspell.cxx +# End Source File +# Begin Source File + +SOURCE=.\suggestmgr.cxx +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\affentry.hxx +# End Source File +# Begin Source File + +SOURCE=.\affixmgr.hxx +# End Source File +# Begin Source File + +SOURCE=.\atypes.hxx +# End Source File +# Begin Source File + +SOURCE=.\baseaffix.hxx +# End Source File +# Begin Source File + +SOURCE=.\csutil.hxx +# End Source File +# Begin Source File + +SOURCE=.\dictmgr.hxx +# End Source File +# Begin Source File + +SOURCE=.\hashmgr.hxx +# End Source File +# Begin Source File + +SOURCE=.\htypes.hxx +# End Source File +# Begin Source File + +SOURCE=.\langnum.hxx +# End Source File +# Begin Source File + +SOURCE=.\hunspell.hxx +# End Source File +# Begin Source File + +SOURCE=.\suggestmgr.hxx +# End Source File +# End Group +# End Target +# End Project diff --git a/src/lib/hunspell/src/hunspell.h b/src/lib/hunspell/src/hunspell.h new file mode 100644 index 0000000..627968a --- /dev/null +++ b/src/lib/hunspell/src/hunspell.h @@ -0,0 +1,95 @@ +#ifndef _MYSPELLMGR_H_ +#define _MYSPELLMGR_H_ + +#include "hunvisapi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Hunhandle Hunhandle; + +LIBHUNSPELL_DLL_EXPORTED Hunhandle *Hunspell_create(const char * affpath, const char * dpath); + +LIBHUNSPELL_DLL_EXPORTED Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath, + const char * key); + +LIBHUNSPELL_DLL_EXPORTED void Hunspell_destroy(Hunhandle *pHunspell); + +/* spell(word) - spellcheck word + * output: 0 = bad word, not 0 = good word + */ +LIBHUNSPELL_DLL_EXPORTED int Hunspell_spell(Hunhandle *pHunspell, const char *); + +LIBHUNSPELL_DLL_EXPORTED char *Hunspell_get_dic_encoding(Hunhandle *pHunspell); + +/* suggest(suggestions, word) - search suggestions + * input: pointer to an array of strings pointer and the (bad) word + * array of strings pointer (here *slst) may not be initialized + * output: number of suggestions in string array, and suggestions in + * a newly allocated array of strings (*slts will be NULL when number + * of suggestion equals 0.) + */ +LIBHUNSPELL_DLL_EXPORTED int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word); + + /* morphological functions */ + + /* analyze(result, word) - morphological analysis of the word */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word); + + /* stem(result, word) - stemmer function */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word); + + /* stem(result, analysis, n) - get stems from a morph. analysis + * example: + * char ** result, result2; + * int n1 = Hunspell_analyze(result, "words"); + * int n2 = Hunspell_stem2(result2, result, n1); + */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n); + + /* generate(result, word, word2) - morphological generation by example(s) */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word, + const char * word2); + + /* generate(result, word, desc, n) - generation by morph. description(s) + * example: + * char ** result; + * char * affix = "is:plural"; // description depends from dictionaries, too + * int n = Hunspell_generate2(result, "word", &affix, 1); + * for (int i = 0; i < n; i++) printf("%s\n", result[i]); + */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word, + char** desc, int n); + + /* functions for run-time modification of the dictionary */ + + /* add word to the run-time dictionary */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_add(Hunhandle *pHunspell, const char * word); + + /* add word to the run-time dictionary with affix flags of + * the example (a dictionary word): Hunspell will recognize + * affixed forms of the new word, too. + */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word, const char * example); + + /* remove word from the run-time dictionary */ + +LIBHUNSPELL_DLL_EXPORTED int Hunspell_remove(Hunhandle *pHunspell, const char * word); + + /* free suggestion lists */ + +LIBHUNSPELL_DLL_EXPORTED void Hunspell_free_list(Hunhandle *pHunspell, char *** slst, int n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/hunspell/src/hunspell.hxx b/src/lib/hunspell/src/hunspell.hxx new file mode 100644 index 0000000..9b6c388 --- /dev/null +++ b/src/lib/hunspell/src/hunspell.hxx @@ -0,0 +1,172 @@ +#include "hunvisapi.h" + +#include "hashmgr.hxx" +#include "affixmgr.hxx" +#include "suggestmgr.hxx" +#include "langnum.hxx" + +#define SPELL_XML "" + +#define MAXDIC 20 +#define MAXSUGGESTION 15 +#define MAXSHARPS 5 + +#define HUNSPELL_OK (1 << 0) +#define HUNSPELL_OK_WARN (1 << 1) + +#ifndef _MYSPELLMGR_HXX_ +#define _MYSPELLMGR_HXX_ + +class LIBHUNSPELL_DLL_EXPORTED Hunspell +{ + AffixMgr* pAMgr; + HashMgr* pHMgr[MAXDIC]; + int maxdic; + SuggestMgr* pSMgr; + char * affixpath; + char * encoding; + struct cs_info * csconv; + int langnum; + int utf8; + int complexprefixes; + char** wordbreak; + +public: + + /* Hunspell(aff, dic) - constructor of Hunspell class + * input: path of affix file and dictionary file + */ + + Hunspell(const char * affpath, const char * dpath, const char * key = NULL); + ~Hunspell(); + + /* load extra dictionaries (only dic files) */ + int add_dic(const char * dpath, const char * key = NULL); + + /* spell(word) - spellcheck word + * output: 0 = bad word, not 0 = good word + * + * plus output: + * info: information bit array, fields: + * SPELL_COMPOUND = a compound word + * SPELL_FORBIDDEN = an explicit forbidden word + * root: root (stem), when input is a word with affix(es) + */ + + int spell(const char * word, int * info = NULL, char ** root = NULL); + + /* suggest(suggestions, word) - search suggestions + * input: pointer to an array of strings pointer and the (bad) word + * array of strings pointer (here *slst) may not be initialized + * output: number of suggestions in string array, and suggestions in + * a newly allocated array of strings (*slts will be NULL when number + * of suggestion equals 0.) + */ + + int suggest(char*** slst, const char * word); + + /* deallocate suggestion lists */ + + void free_list(char *** slst, int n); + + char * get_dic_encoding(); + + /* morphological functions */ + + /* analyze(result, word) - morphological analysis of the word */ + + int analyze(char*** slst, const char * word); + + /* stem(result, word) - stemmer function */ + + int stem(char*** slst, const char * word); + + /* stem(result, analysis, n) - get stems from a morph. analysis + * example: + * char ** result, result2; + * int n1 = analyze(&result, "words"); + * int n2 = stem(&result2, result, n1); + */ + + int stem(char*** slst, char ** morph, int n); + + /* generate(result, word, word2) - morphological generation by example(s) */ + + int generate(char*** slst, const char * word, const char * word2); + + /* generate(result, word, desc, n) - generation by morph. description(s) + * example: + * char ** result; + * char * affix = "is:plural"; // description depends from dictionaries, too + * int n = generate(&result, "word", &affix, 1); + * for (int i = 0; i < n; i++) printf("%s\n", result[i]); + */ + + int generate(char*** slst, const char * word, char ** desc, int n); + + /* functions for run-time modification of the dictionary */ + + /* add word to the run-time dictionary */ + + int add(const char * word); + + /* add word to the run-time dictionary with affix flags of + * the example (a dictionary word): Hunspell will recognize + * affixed forms of the new word, too. + */ + + int add_with_affix(const char * word, const char * example); + + /* remove word from the run-time dictionary */ + + int remove(const char * word); + + /* other */ + + /* get extra word characters definied in affix file for tokenization */ + const char * get_wordchars(); + unsigned short * get_wordchars_utf16(int * len); + + struct cs_info * get_csconv(); + const char * get_version(); + + int get_langnum() const; + + /* experimental and deprecated functions */ + +#ifdef HUNSPELL_EXPERIMENTAL + /* suffix is an affix flag string, similarly in dictionary files */ + int put_word_suffix(const char * word, const char * suffix); + char * morph_with_correction(const char * word); + + /* spec. suggestions */ + int suggest_auto(char*** slst, const char * word); + int suggest_pos_stems(char*** slst, const char * word); +#endif + +private: + int cleanword(char *, const char *, int * pcaptype, int * pabbrev); + int cleanword2(char *, const char *, w_char *, int * w_len, int * pcaptype, int * pabbrev); + void mkinitcap(char *); + int mkinitcap2(char * p, w_char * u, int nc); + int mkinitsmall2(char * p, w_char * u, int nc); + void mkallcap(char *); + int mkallcap2(char * p, w_char * u, int nc); + void mkallsmall(char *); + int mkallsmall2(char * p, w_char * u, int nc); + struct hentry * checkword(const char *, int * info, char **root); + char * sharps_u8_l1(char * dest, char * source); + hentry * spellsharps(char * base, char *, int, int, char * tmp, int * info, char **root); + int is_keepcase(const hentry * rv); + int insert_sug(char ***slst, char * word, int ns); + void cat_result(char * result, char * st); + char * stem_description(const char * desc); + int spellml(char*** slst, const char * word); + int get_xml_par(char * dest, const char * par, int maxl); + const char * get_xml_pos(const char * s, const char * attr); + int get_xml_list(char ***slst, char * list, const char * tag); + int check_xml_par(const char * q, const char * attr, const char * value); + +}; + +#endif diff --git a/src/lib/hunspell/src/hunvisapi.h b/src/lib/hunspell/src/hunvisapi.h new file mode 100644 index 0000000..4712280 --- /dev/null +++ b/src/lib/hunspell/src/hunvisapi.h @@ -0,0 +1,18 @@ +#ifndef _HUNSPELL_VISIBILITY_H_ +#define _HUNSPELL_VISIBILITY_H_ + +#if defined(HUNSPELL_STATIC) +# define LIBHUNSPELL_DLL_EXPORTED +#elif defined(_MSC_VER) +# if defined(BUILDING_LIBHUNSPELL) +# define LIBHUNSPELL_DLL_EXPORTED __declspec(dllexport) +# else +# define LIBHUNSPELL_DLL_EXPORTED __declspec(dllimport) +# endif +#elif BUILDING_LIBHUNSPELL && 1 +# define LIBHUNSPELL_DLL_EXPORTED __attribute__((__visibility__("default"))) +#else +# define LIBHUNSPELL_DLL_EXPORTED +#endif + +#endif diff --git a/src/lib/hunspell/src/hunzip.cxx b/src/lib/hunspell/src/hunzip.cxx new file mode 100644 index 0000000..b50599f --- /dev/null +++ b/src/lib/hunspell/src/hunzip.cxx @@ -0,0 +1,193 @@ +#include +#include +#include + +#include "hunzip.hxx" + +#define CODELEN 65536 +#define BASEBITREC 5000 + +#define UNCOMPRESSED '\002' +#define MAGIC "hz0" +#define MAGIC_ENCRYPT "hz1" +#define MAGICLEN (sizeof(MAGIC) - 1) + +int Hunzip::fail(const char * err, const char * par) { + fprintf(stderr, err, par); + return -1; +} + +Hunzip::Hunzip(const char * file, const char * key) { + bufsiz = 0; + lastbit = 0; + inc = 0; + outc = 0; + dec = NULL; + fin = NULL; + filename = (char *) malloc(strlen(file) + 1); + if (filename) strcpy(filename, file); + if (getcode(key) == -1) bufsiz = -1; + else bufsiz = getbuf(); +} + +int Hunzip::getcode(const char * key) { + unsigned char c[2]; + int i, j, n, p; + int allocatedbit = BASEBITREC; + const char * enc = key; + + if (!filename) return -1; + + fin = fopen(filename, "rb"); + if (!fin) return -1; + + // read magic number + if ((fread(in, 1, 3, fin) < MAGICLEN) + || !(strncmp(MAGIC, in, MAGICLEN) == 0 || + strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0)) { + return fail(MSG_FORMAT, filename); + } + + // check encryption + if (strncmp(MAGIC_ENCRYPT, in, MAGICLEN) == 0) { + unsigned char cs; + if (!key) return fail(MSG_KEY, filename); + if (fread(&c, 1, 1, fin) < 1) return fail(MSG_FORMAT, filename); + for (cs = 0; *enc; enc++) cs ^= *enc; + if (cs != c[0]) return fail(MSG_KEY, filename); + enc = key; + } else key = NULL; + + // read record count + if (fread(&c, 1, 2, fin) < 2) return fail(MSG_FORMAT, filename); + + if (key) { + c[0] ^= *enc; + if (*(++enc) == '\0') enc = key; + c[1] ^= *enc; + } + + n = ((int) c[0] << 8) + c[1]; + dec = (struct bit *) malloc(BASEBITREC * sizeof(struct bit)); + if (!dec) return fail(MSG_MEMORY, filename); + dec[0].v[0] = 0; + dec[0].v[1] = 0; + + // read codes + for (i = 0; i < n; i++) { + unsigned char l; + if (fread(c, 1, 2, fin) < 2) return fail(MSG_FORMAT, filename); + if (key) { + if (*(++enc) == '\0') enc = key; + c[0] ^= *enc; + if (*(++enc) == '\0') enc = key; + c[1] ^= *enc; + } + if (fread(&l, 1, 1, fin) < 1) return fail(MSG_FORMAT, filename); + if (key) { + if (*(++enc) == '\0') enc = key; + l ^= *enc; + } + if (fread(in, 1, l/8+1, fin) < (size_t) l/8+1) return fail(MSG_FORMAT, filename); + if (key) for (j = 0; j <= l/8; j++) { + if (*(++enc) == '\0') enc = key; + in[j] ^= *enc; + } + p = 0; + for (j = 0; j < l; j++) { + int b = (in[j/8] & (1 << (7 - (j % 8)))) ? 1 : 0; + int oldp = p; + p = dec[p].v[b]; + if (p == 0) { + lastbit++; + if (lastbit == allocatedbit) { + allocatedbit += BASEBITREC; + dec = (struct bit *) realloc(dec, allocatedbit * sizeof(struct bit)); + } + dec[lastbit].v[0] = 0; + dec[lastbit].v[1] = 0; + dec[oldp].v[b] = lastbit; + p = lastbit; + } + } + dec[p].c[0] = c[0]; + dec[p].c[1] = c[1]; + } + return 0; +} + +Hunzip::~Hunzip() +{ + if (dec) free(dec); + if (fin) fclose(fin); + if (filename) free(filename); +} + +int Hunzip::getbuf() { + int p = 0; + int o = 0; + do { + if (inc == 0) inbits = fread(in, 1, BUFSIZE, fin) * 8; + for (; inc < inbits; inc++) { + int b = (in[inc / 8] & (1 << (7 - (inc % 8)))) ? 1 : 0; + int oldp = p; + p = dec[p].v[b]; + if (p == 0) { + if (oldp == lastbit) { + fclose(fin); + fin = NULL; + // add last odd byte + if (dec[lastbit].c[0]) out[o++] = dec[lastbit].c[1]; + return o; + } + out[o++] = dec[oldp].c[0]; + out[o++] = dec[oldp].c[1]; + if (o == BUFSIZE) return o; + p = dec[p].v[b]; + } + } + inc = 0; + } while (inbits == BUFSIZE * 8); + return fail(MSG_FORMAT, filename); +} + +const char * Hunzip::getline() { + char linebuf[BUFSIZE]; + int l = 0, eol = 0, left = 0, right = 0; + if (bufsiz == -1) return NULL; + while (l < bufsiz && !eol) { + linebuf[l++] = out[outc]; + switch (out[outc]) { + case '\t': break; + case 31: { // escape + if (++outc == bufsiz) { + bufsiz = getbuf(); + outc = 0; + } + linebuf[l - 1] = out[outc]; + break; + } + case ' ': break; + default: if (((unsigned char) out[outc]) < 47) { + if (out[outc] > 32) { + right = out[outc] - 31; + if (++outc == bufsiz) { + bufsiz = getbuf(); + outc = 0; + } + } + if (out[outc] == 30) left = 9; else left = out[outc]; + linebuf[l-1] = '\n'; + eol = 1; + } + } + if (++outc == bufsiz) { + outc = 0; + bufsiz = fin ? getbuf(): -1; + } + } + if (right) strcpy(linebuf + l - 1, line + strlen(line) - right - 1); + else linebuf[l] = '\0'; + strcpy(line + left, linebuf); + return line; +} diff --git a/src/lib/hunspell/src/hunzip.hxx b/src/lib/hunspell/src/hunzip.hxx new file mode 100644 index 0000000..b58e3ab --- /dev/null +++ b/src/lib/hunspell/src/hunzip.hxx @@ -0,0 +1,45 @@ +/* hunzip: file decompression for sorted dictionaries with optional encryption, + * algorithm: prefix-suffix encoding and 16-bit Huffman encoding */ + +#ifndef _HUNZIP_HXX_ +#define _HUNZIP_HXX_ + +#include "hunvisapi.h" + +#include + +#define BUFSIZE 65536 +#define HZIP_EXTENSION ".hz" + +#define MSG_OPEN "error: %s: cannot open\n" +#define MSG_FORMAT "error: %s: not in hzip format\n" +#define MSG_MEMORY "error: %s: missing memory\n" +#define MSG_KEY "error: %s: missing or bad password\n" + +struct bit { + unsigned char c[2]; + int v[2]; +}; + +class LIBHUNSPELL_DLL_EXPORTED Hunzip +{ + +protected: + char * filename; + FILE * fin; + int bufsiz, lastbit, inc, inbits, outc; + struct bit * dec; // code table + char in[BUFSIZE]; // input buffer + char out[BUFSIZE + 1]; // Huffman-decoded buffer + char line[BUFSIZE + 50]; // decoded line + int getcode(const char * key); + int getbuf(); + int fail(const char * err, const char * par); + +public: + Hunzip(const char * filename, const char * key = NULL); + ~Hunzip(); + const char * getline(); +}; + +#endif diff --git a/src/lib/hunspell/src/langnum.hxx b/src/lib/hunspell/src/langnum.hxx new file mode 100644 index 0000000..1d140a7 --- /dev/null +++ b/src/lib/hunspell/src/langnum.hxx @@ -0,0 +1,38 @@ +#ifndef _LANGNUM_HXX_ +#define _LANGNUM_HXX_ + +/* + language numbers for language specific codes + see http://l10n.openoffice.org/languages.html +*/ + +enum { +LANG_ar=96, +LANG_az=100, // custom number +LANG_bg=41, +LANG_ca=37, +LANG_cs=42, +LANG_da=45, +LANG_de=49, +LANG_el=30, +LANG_en=01, +LANG_es=34, +LANG_eu=10, +LANG_fr=02, +LANG_gl=38, +LANG_hr=78, +LANG_hu=36, +LANG_it=39, +LANG_la=99, // custom number +LANG_lv=101, // custom number +LANG_nl=31, +LANG_pl=48, +LANG_pt=03, +LANG_ru=07, +LANG_sv=50, +LANG_tr=90, +LANG_uk=80, +LANG_xx=999 +}; + +#endif diff --git a/src/lib/hunspell/src/license.hunspell b/src/lib/hunspell/src/license.hunspell new file mode 100644 index 0000000..490e440 --- /dev/null +++ b/src/lib/hunspell/src/license.hunspell @@ -0,0 +1,59 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Hunspell, based on MySpell. + * + * The Initial Developers of the Original Code are + * Kevin Hendricks (MySpell) and Laszlo Nemeth (Hunspell). + * Portions created by the Initial Developers are Copyright (C) 2002-2005 + * the Initial Developers. All Rights Reserved. + * + * Contributor(s): + * David Einstein + * Davide Prina + * Giuseppe Modugno + * Gianluca Turconi + * Simon Brouwer + * Noll Janos + * Biro Arpad + * Goldman Eleonora + * Sarlos Tamas + * Bencsath Boldizsar + * Halacsy Peter + * Dvornik Laszlo + * Gefferth Andras + * Nagy Viktor + * Varga Daniel + * Chris Halls + * Rene Engelhard + * Bram Moolenaar + * Dafydd Jones + * Harri Pitkanen + * Andras Timar + * Tor Lillqvist + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "config.h" diff --git a/src/lib/hunspell/src/license.myspell b/src/lib/hunspell/src/license.myspell new file mode 100644 index 0000000..2da5330 --- /dev/null +++ b/src/lib/hunspell/src/license.myspell @@ -0,0 +1,61 @@ +/* + * Copyright 2002 Kevin B. Hendricks, Stratford, Ontario, Canada + * And Contributors. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. All modifications to the source code must be clearly marked as + * such. Binary redistributions based on modified source code + * must be clearly marked as modified versions in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY KEVIN B. HENDRICKS 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 + * KEVIN B. HENDRICKS 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. + * + * + * NOTE: A special thanks and credit goes to Geoff Kuenning + * the creator of ispell. MySpell's affix algorithms were + * based on those of ispell which should be noted is + * copyright Geoff Kuenning et.al. and now available + * under a BSD style license. For more information on ispell + * and affix compression in general, please see: + * http://www.cs.ucla.edu/ficus-members/geoff/ispell.html + * (the home page for ispell) + * + * An almost complete rewrite of MySpell for use by + * the Mozilla project has been developed by David Einstein + * (Deinst@world.std.com). David and I are now + * working on parallel development tracks to help + * our respective projects (Mozilla and OpenOffice.org + * and we will maintain full affix file and dictionary + * file compatibility and work on merging our versions + * of MySpell back into a single tree. David has been + * a significant help in improving MySpell. + * + * Special thanks also go to La'szlo' Ne'meth + * who is the author of the + * Hungarian dictionary and who developed and contributed + * the code to support compound words in MySpell + * and fixed numerous problems with the encoding + * case conversion tables. + * + */ diff --git a/src/lib/hunspell/src/phonet.cxx b/src/lib/hunspell/src/phonet.cxx new file mode 100644 index 0000000..144bd40 --- /dev/null +++ b/src/lib/hunspell/src/phonet.cxx @@ -0,0 +1,292 @@ +/* phonetic.c - generic replacement aglogithms for phonetic transformation + Copyright (C) 2000 Bjoern Jacke + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation; + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; If not, see + . + + Changelog: + + 2000-01-05 Bjoern Jacke + Initial Release insprired by the article about phonetic + transformations out of c't 25/1999 + + 2007-07-26 Bjoern Jacke + Released under MPL/GPL/LGPL tri-license for Hunspell + + 2007-08-23 Laszlo Nemeth + Porting from Aspell to Hunspell using C-like structs +*/ + +#include +#include +#include +#include + +#include "csutil.hxx" +#include "phonet.hxx" + +void init_phonet_hash(phonetable & parms) + { + int i, k; + + for (i = 0; i < HASHSIZE; i++) { + parms.hash[i] = -1; + } + + for (i = 0; parms.rules[i][0] != '\0'; i += 2) { + /** set hash value **/ + k = (unsigned char) parms.rules[i][0]; + + if (parms.hash[k] < 0) { + parms.hash[k] = i; + } + } + } + +// like strcpy but safe if the strings overlap +// but only if dest < src +static inline void strmove(char * dest, char * src) { + while (*src) + *dest++ = *src++; + *dest = '\0'; +} + +static int myisalpha(char ch) { + if ((unsigned char) ch < 128) return isalpha(ch); + return 1; +} + +/* phonetic transcription algorithm */ +/* see: http://aspell.net/man-html/Phonetic-Code.html */ +/* convert string to uppercase before this call */ +int phonet (const char * inword, char * target, + int len, + phonetable & parms) + { + /** Do phonetic transformation. **/ + /** "len" = length of "inword" incl. '\0'. **/ + + /** result: >= 0: length of "target" **/ + /** otherwise: error **/ + + int i,j,k=0,n,p,z; + int k0,n0,p0=-333,z0; + char c, c0; + const char * s; + typedef unsigned char uchar; + char word[MAXPHONETUTF8LEN + 1]; + if (len == -1) len = strlen(inword); + if (len > MAXPHONETUTF8LEN) return 0; + strcpy(word, inword); + + /** check word **/ + i = j = z = 0; + while ((c = word[i]) != '\0') { + n = parms.hash[(uchar) c]; + z0 = 0; + + if (n >= 0) { + /** check all rules for the same letter **/ + while (parms.rules[n][0] == c) { + + /** check whole string **/ + k = 1; /** number of found letters **/ + p = 5; /** default priority **/ + s = parms.rules[n]; + s++; /** important for (see below) "*(s-1)" **/ + + while (*s != '\0' && word[i+k] == *s + && !isdigit ((unsigned char) *s) && strchr ("(-<^$", *s) == NULL) { + k++; + s++; + } + if (*s == '(') { + /** check letters in "(..)" **/ + if (myisalpha(word[i+k]) // ...could be implied? + && strchr(s+1, word[i+k]) != NULL) { + k++; + while (*s != ')') + s++; + s++; + } + } + p0 = (int) *s; + k0 = k; + while (*s == '-' && k > 1) { + k--; + s++; + } + if (*s == '<') + s++; + if (isdigit ((unsigned char) *s)) { + /** determine priority **/ + p = *s - '0'; + s++; + } + if (*s == '^' && *(s+1) == '^') + s++; + + if (*s == '\0' + || (*s == '^' + && (i == 0 || ! myisalpha(word[i-1])) + && (*(s+1) != '$' + || (! myisalpha(word[i+k0]) ))) + || (*s == '$' && i > 0 + && myisalpha(word[i-1]) + && (! myisalpha(word[i+k0]) ))) + { + /** search for followup rules, if: **/ + /** parms.followup and k > 1 and NO '-' in searchstring **/ + c0 = word[i+k-1]; + n0 = parms.hash[(uchar) c0]; + +// if (parms.followup && k > 1 && n0 >= 0 + if (k > 1 && n0 >= 0 + && p0 != (int) '-' && word[i+k] != '\0') { + /** test follow-up rule for "word[i+k]" **/ + while (parms.rules[n0][0] == c0) { + + /** check whole string **/ + k0 = k; + p0 = 5; + s = parms.rules[n0]; + s++; + while (*s != '\0' && word[i+k0] == *s + && ! isdigit((unsigned char) *s) && strchr("(-<^$",*s) == NULL) { + k0++; + s++; + } + if (*s == '(') { + /** check letters **/ + if (myisalpha(word[i+k0]) + && strchr (s+1, word[i+k0]) != NULL) { + k0++; + while (*s != ')' && *s != '\0') + s++; + if (*s == ')') + s++; + } + } + while (*s == '-') { + /** "k0" gets NOT reduced **/ + /** because "if (k0 == k)" **/ + s++; + } + if (*s == '<') + s++; + if (isdigit ((unsigned char) *s)) { + p0 = *s - '0'; + s++; + } + + if (*s == '\0' + /** *s == '^' cuts **/ + || (*s == '$' && ! myisalpha(word[i+k0]))) + { + if (k0 == k) { + /** this is just a piece of the string **/ + n0 += 2; + continue; + } + + if (p0 < p) { + /** priority too low **/ + n0 += 2; + continue; + } + /** rule fits; stop search **/ + break; + } + n0 += 2; + } /** End of "while (parms.rules[n0][0] == c0)" **/ + + if (p0 >= p && parms.rules[n0][0] == c0) { + n += 2; + continue; + } + } /** end of follow-up stuff **/ + + /** replace string **/ + s = parms.rules[n+1]; + p0 = (parms.rules[n][0] != '\0' + && strchr (parms.rules[n]+1,'<') != NULL) ? 1:0; + if (p0 == 1 && z == 0) { + /** rule with '<' is used **/ + if (j > 0 && *s != '\0' + && (target[j-1] == c || target[j-1] == *s)) { + j--; + } + z0 = 1; + z = 1; + k0 = 0; + while (*s != '\0' && word[i+k0] != '\0') { + word[i+k0] = *s; + k0++; + s++; + } + if (k > k0) + strmove (&word[0]+i+k0, &word[0]+i+k); + + /** new "actual letter" **/ + c = word[i]; + } + else { /** no '<' rule used **/ + i += k - 1; + z = 0; + while (*s != '\0' + && *(s+1) != '\0' && j < len) { + if (j == 0 || target[j-1] != *s) { + target[j] = *s; + j++; + } + s++; + } + /** new "actual letter" **/ + c = *s; + if (parms.rules[n][0] != '\0' + && strstr (parms.rules[n]+1, "^^") != NULL) { + if (c != '\0') { + target[j] = c; + j++; + } + strmove (&word[0], &word[0]+i+1); + i = 0; + z0 = 1; + } + } + break; + } /** end of follow-up stuff **/ + n += 2; + } /** end of while (parms.rules[n][0] == c) **/ + } /** end of if (n >= 0) **/ + if (z0 == 0) { +// if (k && (assert(p0!=-333),!p0) && j < len && c != '\0' +// && (!parms.collapse_result || j == 0 || target[j-1] != c)){ + if (k && !p0 && j < len && c != '\0' + && (1 || j == 0 || target[j-1] != c)){ + /** condense only double letters **/ + target[j] = c; + ///printf("\n setting \n"); + j++; + } + + i++; + z = 0; + k=0; + } + } /** end of while ((c = word[i]) != '\0') **/ + + target[j] = '\0'; + return (j); + + } /** end of function "phonet" **/ diff --git a/src/lib/hunspell/src/phonet.hxx b/src/lib/hunspell/src/phonet.hxx new file mode 100644 index 0000000..f91d3b0 --- /dev/null +++ b/src/lib/hunspell/src/phonet.hxx @@ -0,0 +1,52 @@ +/* phonetic.c - generic replacement aglogithms for phonetic transformation + Copyright (C) 2000 Bjoern Jacke + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License version 2.1 as published by the Free Software Foundation; + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; If not, see + . + + Changelog: + + 2000-01-05 Bjoern Jacke + Initial Release insprired by the article about phonetic + transformations out of c't 25/1999 + + 2007-07-26 Bjoern Jacke + Released under MPL/GPL/LGPL tri-license for Hunspell + + 2007-08-23 Laszlo Nemeth + Porting from Aspell to Hunspell using C-like structs +*/ + +#ifndef __PHONETHXX__ +#define __PHONETHXX__ + +#define HASHSIZE 256 +#define MAXPHONETLEN 256 +#define MAXPHONETUTF8LEN (MAXPHONETLEN * 4) + +#include "hunvisapi.h" + +struct phonetable { + char utf8; + cs_info * lang; + int num; + char * * rules; + int hash[HASHSIZE]; +}; + +LIBHUNSPELL_DLL_EXPORTED void init_phonet_hash(phonetable & parms); + +LIBHUNSPELL_DLL_EXPORTED int phonet (const char * inword, char * target, + int len, phonetable & phone); + +#endif diff --git a/src/lib/hunspell/src/replist.cxx b/src/lib/hunspell/src/replist.cxx new file mode 100644 index 0000000..080cd68 --- /dev/null +++ b/src/lib/hunspell/src/replist.cxx @@ -0,0 +1,87 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include + +#include "replist.hxx" +#include "csutil.hxx" + +RepList::RepList(int n) { + dat = (replentry **) malloc(sizeof(replentry *) * n); + if (dat == 0) size = 0; else size = n; + pos = 0; +} + +RepList::~RepList() +{ + for (int i = 0; i < pos; i++) { + free(dat[i]->pattern); + free(dat[i]->pattern2); + free(dat[i]); + } + free(dat); +} + +int RepList::get_pos() { + return pos; +} + +replentry * RepList::item(int n) { + return dat[n]; +} + +int RepList::near(const char * word) { + int p1 = 0; + int p2 = pos; + while ((p2 - p1) > 1) { + int m = (p1 + p2) / 2; + int c = strcmp(word, dat[m]->pattern); + if (c <= 0) { + if (c < 0) p2 = m; else p1 = p2 = m; + } else p1 = m; + } + return p1; +} + +int RepList::match(const char * word, int n) { + if (strncmp(word, dat[n]->pattern, strlen(dat[n]->pattern)) == 0) return strlen(dat[n]->pattern); + return 0; +} + +int RepList::add(char * pat1, char * pat2) { + if (pos >= size || pat1 == NULL || pat2 == NULL) return 1; + replentry * r = (replentry *) malloc(sizeof(replentry)); + if (r == NULL) return 1; + r->pattern = mystrrep(pat1, "_", " "); + r->pattern2 = mystrrep(pat2, "_", " "); + r->start = false; + r->end = false; + dat[pos++] = r; + for (int i = pos - 1; i > 0; i--) { + r = dat[i]; + if (strcmp(r->pattern, dat[i - 1]->pattern) < 0) { + dat[i] = dat[i - 1]; + dat[i - 1] = r; + } else break; + } + return 0; +} + +int RepList::conv(const char * word, char * dest) { + int stl = 0; + int change = 0; + for (size_t i = 0; i < strlen(word); i++) { + int n = near(word + i); + int l = match(word + i, n); + if (l) { + strcpy(dest + stl, dat[n]->pattern2); + stl += strlen(dat[n]->pattern2); + i += l - 1; + change = 1; + } else dest[stl++] = word[i]; + } + dest[stl] = '\0'; + return change; +} diff --git a/src/lib/hunspell/src/replist.hxx b/src/lib/hunspell/src/replist.hxx new file mode 100644 index 0000000..9c37e29 --- /dev/null +++ b/src/lib/hunspell/src/replist.hxx @@ -0,0 +1,27 @@ +/* string replacement list class */ +#ifndef _REPLIST_HXX_ +#define _REPLIST_HXX_ + +#include "hunvisapi.h" + +#include "w_char.hxx" + +class LIBHUNSPELL_DLL_EXPORTED RepList +{ +protected: + replentry ** dat; + int size; + int pos; + +public: + RepList(int n); + ~RepList(); + + int get_pos(); + int add(char * pat1, char * pat2); + replentry * item(int n); + int near(const char * word); + int match(const char * word, int n); + int conv(const char * word, char * dest); +}; +#endif diff --git a/src/lib/hunspell/src/suggestmgr.cxx b/src/lib/hunspell/src/suggestmgr.cxx new file mode 100644 index 0000000..ebf9bc0 --- /dev/null +++ b/src/lib/hunspell/src/suggestmgr.cxx @@ -0,0 +1,2004 @@ +#include "license.hunspell" +#include "license.myspell" + +#include +#include +#include +#include + +#include "suggestmgr.hxx" +#include "htypes.hxx" +#include "csutil.hxx" + +const w_char W_VLINE = { '\0', '|' }; + +SuggestMgr::SuggestMgr(const char * tryme, int maxn, + AffixMgr * aptr) +{ + + // register affix manager and check in string of chars to + // try when building candidate suggestions + pAMgr = aptr; + + csconv = NULL; + + ckeyl = 0; + ckey = NULL; + ckey_utf = NULL; + + ctryl = 0; + ctry = NULL; + ctry_utf = NULL; + + utf8 = 0; + langnum = 0; + complexprefixes = 0; + + maxSug = maxn; + nosplitsugs = 0; + maxngramsugs = MAXNGRAMSUGS; + maxcpdsugs = MAXCOMPOUNDSUGS; + + if (pAMgr) { + langnum = pAMgr->get_langnum(); + ckey = pAMgr->get_key_string(); + nosplitsugs = pAMgr->get_nosplitsugs(); + if (pAMgr->get_maxngramsugs() >= 0) + maxngramsugs = pAMgr->get_maxngramsugs(); + utf8 = pAMgr->get_utf8(); + if (pAMgr->get_maxcpdsugs() >= 0) + maxcpdsugs = pAMgr->get_maxcpdsugs(); + if (!utf8) + { + char * enc = pAMgr->get_encoding(); + csconv = get_current_cs(enc); + free(enc); + } + complexprefixes = pAMgr->get_complexprefixes(); + } + + if (ckey) { + if (utf8) { + w_char t[MAXSWL]; + ckeyl = u8_u16(t, MAXSWL, ckey); + ckey_utf = (w_char *) malloc(ckeyl * sizeof(w_char)); + if (ckey_utf) memcpy(ckey_utf, t, ckeyl * sizeof(w_char)); + else ckeyl = 0; + } else { + ckeyl = strlen(ckey); + } + } + + if (tryme) { + ctry = mystrdup(tryme); + if (ctry) ctryl = strlen(ctry); + if (ctry && utf8) { + w_char t[MAXSWL]; + ctryl = u8_u16(t, MAXSWL, tryme); + ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char)); + if (ctry_utf) memcpy(ctry_utf, t, ctryl * sizeof(w_char)); + else ctryl = 0; + } + } +} + + +SuggestMgr::~SuggestMgr() +{ + pAMgr = NULL; + if (ckey) free(ckey); + ckey = NULL; + if (ckey_utf) free(ckey_utf); + ckey_utf = NULL; + ckeyl = 0; + if (ctry) free(ctry); + ctry = NULL; + if (ctry_utf) free(ctry_utf); + ctry_utf = NULL; + ctryl = 0; + maxSug = 0; +#ifdef MOZILLA_CLIENT + delete [] csconv; +#endif +} + +int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest, + int * timer, clock_t * timelimit) { + int cwrd = 1; + if (ns == maxSug) return maxSug; + for (int k=0; k < ns; k++) { + if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; + } + if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) { + for (int j=0; j 0) oldSug = nsug; + + // suggestions for an uppercase word (html -> HTML) + if ((nsug < maxSug) && (nsug > -1)) { + nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + capchars(wlst, word, nsug, cpdsuggest); + } + + // perhaps we made a typical fault of spelling + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = replchars(wlst, word, nsug, cpdsuggest); + } + + // perhaps we made chose the wrong char from a related set + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = mapchars(wlst, word, nsug, cpdsuggest); + } + + // only suggest compound words when no other suggestion + if ((cpdsuggest == 0) && (nsug > nsugorig)) nocompoundtwowords=1; + + // did we swap the order of chars by mistake + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + swapchar(wlst, word, nsug, cpdsuggest); + } + + // did we swap the order of non adjacent chars by mistake + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + longswapchar(wlst, word, nsug, cpdsuggest); + } + + // did we just hit the wrong key in place of a good char (case and keyboard) + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? badcharkey_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + badcharkey(wlst, word, nsug, cpdsuggest); + } + + // did we add a char that should not be there + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + extrachar(wlst, word, nsug, cpdsuggest); + } + + + // did we forgot a char + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + forgotchar(wlst, word, nsug, cpdsuggest); + } + + // did we move a char + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + movechar(wlst, word, nsug, cpdsuggest); + } + + // did we just hit the wrong key in place of a good char + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + badchar(wlst, word, nsug, cpdsuggest); + } + + // did we double two characters + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) : + doubletwochars(wlst, word, nsug, cpdsuggest); + } + + // perhaps we forgot to hit space and two words ran together + if (!nosplitsugs && (nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) { + nsug = twowords(wlst, word, nsug, cpdsuggest); + } + + } // repeating ``for'' statement compounding support + + if (nsug < 0) { + // we ran out of memory - we should free up as much as possible + for (int i = 0; i < maxSug; i++) + if (wlst[i] != NULL) free(wlst[i]); + free(wlst); + wlst = NULL; + } + + if (!nocompoundtwowords && (nsug > 0) && onlycompoundsug) *onlycompoundsug = 1; + + *slst = wlst; + return nsug; +} + +// generate suggestions for a word with typical mistake +// pass in address of array of char * pointers +#ifdef HUNSPELL_EXPERIMENTAL +int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug) +{ + int nocompoundtwowords = 0; + char ** wlst; + int oldSug; + + char w2[MAXWORDUTF8LEN]; + const char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + if (*slst) { + wlst = *slst; + } else { + wlst = (char **) malloc(maxSug * sizeof(char *)); + if (wlst == NULL) return -1; + } + + for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) { + + // limit compound suggestion + if (cpdsuggest > 0) oldSug = nsug; + + // perhaps we made a typical fault of spelling + if ((nsug < maxSug) && (nsug > -1)) + nsug = replchars(wlst, word, nsug, cpdsuggest); + + // perhaps we made chose the wrong char from a related set + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs))) + nsug = mapchars(wlst, word, nsug, cpdsuggest); + + if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1; + + // perhaps we forgot to hit space and two words ran together + + if ((nsug < maxSug) && (nsug > -1) && (!cpdsuggest || (nsug < oldSug + maxcpdsugs)) && check_forbidden(word, strlen(word))) { + nsug = twowords(wlst, word, nsug, cpdsuggest); + } + + } // repeating ``for'' statement compounding support + + if (nsug < 0) { + for (int i=0;i HTML) +int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + w_char candidate_utf[MAXSWL]; + memcpy(candidate_utf, word, wl * sizeof(w_char)); + mkallcap_utf(candidate_utf, wl, langnum); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); +} + +// suggestions for an uppercase word (html -> HTML) +int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + strcpy(candidate, word); + mkallcap(candidate, csconv); + return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); +} + +// suggestions for when chose the wrong char out of a related set +int SuggestMgr::mapchars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + clock_t timelimit; + int timer; + candidate[0] = '\0'; + + int wl = strlen(word); + if (wl < 2 || ! pAMgr) return ns; + + int nummap = pAMgr->get_nummap(); + struct mapentry* maptable = pAMgr->get_maptable(); + if (maptable==NULL) return ns; + + timelimit = clock(); + timer = MINTIMER; + return map_related(word, (char *) &candidate, 0, 0, wlst, cpdsuggest, ns, maptable, nummap, &timer, &timelimit); +} + +int SuggestMgr::map_related(const char * word, char * candidate, int wn, int cn, + char** wlst, int cpdsuggest, int ns, + const mapentry* maptable, int nummap, int * timer, clock_t * timelimit) +{ + if (*(word + wn) == '\0') { + int cwrd = 1; + *(candidate + cn) = '\0'; + int wl = strlen(candidate); + for (int m=0; m < ns; m++) + if (strcmp(candidate, wlst[m]) == 0) cwrd = 0; + if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) { + if (ns < maxSug) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) return -1; + ns++; + } + } + return ns; + } + int in_map = 0; + for (int j = 0; j < nummap; j++) { + for (int k = 0; k < maptable[j].len; k++) { + int len = strlen(maptable[j].set[k]); + if (strncmp(maptable[j].set[k], word + wn, len) == 0) { + in_map = 1; + for (int l = 0; l < maptable[j].len; l++) { + strcpy(candidate + cn, maptable[j].set[l]); + ns = map_related(word, candidate, wn + len, strlen(candidate), wlst, + cpdsuggest, ns, maptable, nummap, timer, timelimit); + if (!(*timer)) return ns; + } + } + } + } + if (!in_map) { + *(candidate + cn) = *(word + wn); + ns = map_related(word, candidate, wn + 1, cn + 1, wlst, cpdsuggest, + ns, maptable, nummap, timer, timelimit); + } + return ns; +} + +// suggestions for a typical fault of spelling, that +// differs with more, than 1 letter from the right form. +int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + const char * r; + int lenr, lenp; + int wl = strlen(word); + if (wl < 2 || ! pAMgr) return ns; + int numrep = pAMgr->get_numrep(); + struct replentry* reptable = pAMgr->get_reptable(); + if (reptable==NULL) return ns; + for (int i=0; i < numrep; i++ ) { + r = word; + lenr = strlen(reptable[i].pattern2); + lenp = strlen(reptable[i].pattern); + // search every occurence of the pattern in the word + while ((r=strstr(r, reptable[i].pattern)) != NULL && (!reptable[i].end || strlen(r) == strlen(reptable[i].pattern)) && + (!reptable[i].start || r == word)) { + strcpy(candidate, word); + if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break; + strcpy(candidate+(r-word),reptable[i].pattern2); + strcpy(candidate+(r-word)+lenr, r+lenp); + ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + // check REP suggestions with space + char * sp = strchr(candidate, ' '); + if (sp) { + char * prev = candidate; + while (sp) { + *sp = '\0'; + if (checkword(prev, strlen(prev), 0, NULL, NULL)) { + int oldns = ns; + *sp = ' '; + ns = testsug(wlst, sp + 1, strlen(sp + 1), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + if (oldns < ns) { + free(wlst[ns - 1]); + wlst[ns - 1] = mystrdup(candidate); + if (!wlst[ns - 1]) return -1; + } + } + *sp = ' '; + prev = sp + 1; + sp = strchr(prev, ' '); + } + } + r++; // search for the next letter + } + } + return ns; +} + +// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation) +int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + int state=0; + int wl = strlen(word); + if (wl < 5 || ! pAMgr) return ns; + for (int i=2; i < wl; i++ ) { + if (word[i]==word[i-2]) { + state++; + if (state==3) { + strcpy(candidate,word); + strcpy(candidate+i-1,word+i+1); + ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + state=0; + } + } else { + state=0; + } + } + return ns; +} + +// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation) +int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + int state=0; + if (wl < 5 || ! pAMgr) return ns; + for (int i=2; i < wl; i++) { + if (w_char_eq(word[i], word[i-2])) { + state++; + if (state==3) { + memcpy(candidate_utf, word, (i - 1) * sizeof(w_char)); + memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char)); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + state=0; + } + } else { + state=0; + } + } + return ns; +} + +// error is wrong char in place of correct one (case and keyboard related version) +int SuggestMgr::badcharkey(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char tmpc; + char candidate[MAXSWUTF8L]; + int wl = strlen(word); + strcpy(candidate, word); + // swap out each char one by one and try uppercase and neighbor + // keyboard chars in its place to see if that makes a good word + + for (int i=0; i < wl; i++) { + tmpc = candidate[i]; + // check with uppercase letters + candidate[i] = csconv[((unsigned char)tmpc)].cupper; + if (tmpc != candidate[i]) { + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + candidate[i] = tmpc; + } + // check neighbor characters in keyboard string + if (!ckey) continue; + char * loc = strchr(ckey, tmpc); + while (loc) { + if ((loc > ckey) && (*(loc - 1) != '|')) { + candidate[i] = *(loc - 1); + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + if ((*(loc + 1) != '|') && (*(loc + 1) != '\0')) { + candidate[i] = *(loc + 1); + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + loc = strchr(loc + 1, tmpc); + } + candidate[i] = tmpc; + } + return ns; +} + +// error is wrong char in place of correct one (case and keyboard related version) +int SuggestMgr::badcharkey_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char tmpc; + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + memcpy(candidate_utf, word, wl * sizeof(w_char)); + // swap out each char one by one and try all the tryme + // chars in its place to see if that makes a good word + for (int i=0; i < wl; i++) { + tmpc = candidate_utf[i]; + // check with uppercase letters + mkallcap_utf(candidate_utf + i, 1, langnum); + if (!w_char_eq(tmpc, candidate_utf[i])) { + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + candidate_utf[i] = tmpc; + } + // check neighbor characters in keyboard string + if (!ckey) continue; + w_char * loc = ckey_utf; + while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc)) loc++; + while (loc < (ckey_utf + ckeyl)) { + if ((loc > ckey_utf) && !w_char_eq(*(loc - 1), W_VLINE)) { + candidate_utf[i] = *(loc - 1); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + if (((loc + 1) < (ckey_utf + ckeyl)) && !w_char_eq(*(loc + 1), W_VLINE)) { + candidate_utf[i] = *(loc + 1); + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + do { loc++; } while ((loc < (ckey_utf + ckeyl)) && !w_char_eq(*loc, tmpc)); + } + candidate_utf[i] = tmpc; + } + return ns; +} + +// error is wrong char in place of correct one +int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char tmpc; + char candidate[MAXSWUTF8L]; + clock_t timelimit = clock(); + int timer = MINTIMER; + int wl = strlen(word); + strcpy(candidate, word); + // swap out each char one by one and try all the tryme + // chars in its place to see if that makes a good word + for (int j=0; j < ctryl; j++) { + for (int i=wl-1; i >= 0; i--) { + tmpc = candidate[i]; + if (ctry[j] == tmpc) continue; + candidate[i] = ctry[j]; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + candidate[i] = tmpc; + } + } + return ns; +} + +// error is wrong char in place of correct one +int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char tmpc; + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + clock_t timelimit = clock(); + int timer = MINTIMER; + memcpy(candidate_utf, word, wl * sizeof(w_char)); + // swap out each char one by one and try all the tryme + // chars in its place to see if that makes a good word + for (int j=0; j < ctryl; j++) { + for (int i=wl-1; i >= 0; i--) { + tmpc = candidate_utf[i]; + if (w_char_eq(tmpc, ctry_utf[j])) continue; + candidate_utf[i] = ctry_utf[j]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + candidate_utf[i] = tmpc; + } + } + return ns; +} + +// error is word has an extra letter it does not need +int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + w_char candidate_utf[MAXSWL]; + w_char * p; + w_char tmpc = W_VLINE; // not used value, only for VCC warning message + if (wl < 2) return ns; + // try omitting one char of word at a time + memcpy(candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf + wl - 1; p >= candidate_utf; p--) { + w_char tmpc2 = *p; + if (p < candidate_utf + wl - 1) *p = tmpc; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + tmpc = tmpc2; + } + return ns; +} + +// error is word has an extra letter it does not need +int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest) +{ + char tmpc = '\0'; + char candidate[MAXSWUTF8L]; + char * p; + int wl = strlen(word); + if (wl < 2) return ns; + // try omitting one char of word at a time + strcpy (candidate, word); + for (p = candidate + wl - 1; p >=candidate; p--) { + char tmpc2 = *p; + *p = tmpc; + ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + tmpc = tmpc2; + } + return ns; +} + +// error is missing a letter it needs +int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + clock_t timelimit = clock(); + int timer = MINTIMER; + int wl = strlen(word); + // try inserting a tryme character before every letter (and the null terminator) + for (int i = 0; i < ctryl; i++) { + strcpy(candidate, word); + for (p = candidate + wl; p >= candidate; p--) { + *(p+1) = *p; + *p = ctry[i]; + ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + } + } + return ns; +} + +// error is missing a letter it needs +int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + clock_t timelimit = clock(); + int timer = MINTIMER; + // try inserting a tryme character at the end of the word and before every letter + for (int i = 0; i < ctryl; i++) { + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf + wl; p >= candidate_utf; p--) { + *(p + 1) = *p; + *p = ctry_utf[i]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit); + if (ns == -1) return -1; + if (!timer) return ns; + } + } + return ns; +} + + +/* error is should have been two words */ +int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + int c1, c2; + int forbidden = 0; + int cwrd; + + int wl=strlen(word); + if (wl < 3) return ns; + + if (langnum == LANG_hu) forbidden = check_forbidden(word, wl); + + strcpy(candidate + 1, word); + // split the string into two pieces after every char + // if both pieces are good words make them a suggestion + for (p = candidate + 1; p[1] != '\0'; p++) { + p[-1] = *p; + // go to end of the UTF-8 character + while (utf8 && ((p[1] & 0xc0) == 0x80)) { + *p = p[1]; + p++; + } + if (utf8 && p[1] == '\0') break; // last UTF-8 character + *p = '\0'; + c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL); + if (c1) { + c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL); + if (c2) { + *p = ' '; + + // spec. Hungarian code (need a better compound word support) + if ((langnum == LANG_hu) && !forbidden && + // if 3 repeating letter, use - instead of space + (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) || + // or multiple compounding, with more, than 6 syllables + ((c1 == 3) && (c2 >= 2)))) *p = '-'; + + cwrd = 1; + for (int k=0; k < ns; k++) + if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; + if (ns < maxSug) { + if (cwrd) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) return -1; + ns++; + } + } else return ns; + // add two word suggestion with dash, if TRY string contains + // "a" or "-" + // NOTE: cwrd doesn't modified for REP twoword sugg. + if (ctry && (strchr(ctry, 'a') || strchr(ctry, '-')) && + mystrlen(p + 1) > 1 && + mystrlen(candidate) - mystrlen(p) > 1) { + *p = '-'; + for (int k=0; k < ns; k++) + if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; + if (ns < maxSug) { + if (cwrd) { + wlst[ns] = mystrdup(candidate); + if (wlst[ns] == NULL) return -1; + ns++; + } + } else return ns; + } + } + } + } + return ns; +} + + +// error is adjacent letter were swapped +int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + char tmpc; + int wl=strlen(word); + // try swapping adjacent chars one by one + strcpy(candidate, word); + for (p = candidate; p[1] != 0; p++) { + tmpc = *p; + *p = p[1]; + p[1] = tmpc; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + p[1] = *p; + *p = tmpc; + } + // try double swaps for short words + // ahev -> have, owudl -> would + if (wl == 4 || wl == 5) { + candidate[0] = word[1]; + candidate[1] = word[0]; + candidate[2] = word[2]; + candidate[wl - 2] = word[wl - 1]; + candidate[wl - 1] = word[wl - 2]; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + if (wl == 5) { + candidate[0] = word[0]; + candidate[1] = word[2]; + candidate[2] = word[1]; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + } + return ns; +} + +// error is adjacent letter were swapped +int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + w_char tmpc; + int len = 0; + // try swapping adjacent chars one by one + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf; p < (candidate_utf + wl - 1); p++) { + tmpc = *p; + *p = p[1]; + p[1] = tmpc; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + if (len == 0) len = strlen(candidate); + ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + p[1] = *p; + *p = tmpc; + } + // try double swaps for short words + // ahev -> have, owudl -> would, suodn -> sound + if (wl == 4 || wl == 5) { + candidate_utf[0] = word[1]; + candidate_utf[1] = word[0]; + candidate_utf[2] = word[2]; + candidate_utf[wl - 2] = word[wl - 1]; + candidate_utf[wl - 1] = word[wl - 2]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + if (wl == 5) { + candidate_utf[0] = word[0]; + candidate_utf[1] = word[2]; + candidate_utf[2] = word[1]; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, len, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + } + return ns; +} + +// error is not adjacent letter were swapped +int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + char * q; + char tmpc; + int wl=strlen(word); + // try swapping not adjacent chars one by one + strcpy(candidate, word); + for (p = candidate; *p != 0; p++) { + for (q = candidate; *q != 0; q++) { + if (abs((int)(p-q)) > 1) { + tmpc = *p; + *p = *q; + *q = tmpc; + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + *q = *p; + *p = tmpc; + } + } + } + return ns; +} + + +// error is adjacent letter were swapped +int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + w_char * q; + w_char tmpc; + // try swapping not adjacent chars + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf; p < (candidate_utf + wl); p++) { + for (q = candidate_utf; q < (candidate_utf + wl); q++) { + if (abs((int)(p-q)) > 1) { + tmpc = *p; + *p = *q; + *q = tmpc; + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + *q = *p; + *p = tmpc; + } + } + } + return ns; +} + +// error is a letter was moved +int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest) +{ + char candidate[MAXSWUTF8L]; + char * p; + char * q; + char tmpc; + + int wl=strlen(word); + // try moving a char + strcpy(candidate, word); + for (p = candidate; *p != 0; p++) { + for (q = p + 1; (*q != 0) && ((q - p) < 10); q++) { + tmpc = *(q-1); + *(q-1) = *q; + *q = tmpc; + if ((q-p) < 2) continue; // omit swap char + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + strcpy(candidate, word); + } + for (p = candidate + wl - 1; p > candidate; p--) { + for (q = p - 1; (q >= candidate) && ((p - q) < 10); q--) { + tmpc = *(q+1); + *(q+1) = *q; + *q = tmpc; + if ((p-q) < 2) continue; // omit swap char + ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + strcpy(candidate, word); + } + return ns; +} + +// error is a letter was moved +int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) +{ + w_char candidate_utf[MAXSWL]; + char candidate[MAXSWUTF8L]; + w_char * p; + w_char * q; + w_char tmpc; + // try moving a char + memcpy (candidate_utf, word, wl * sizeof(w_char)); + for (p = candidate_utf; p < (candidate_utf + wl); p++) { + for (q = p + 1; (q < (candidate_utf + wl)) && ((q - p) < 10); q++) { + tmpc = *(q-1); + *(q-1) = *q; + *q = tmpc; + if ((q-p) < 2) continue; // omit swap char + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + memcpy (candidate_utf, word, wl * sizeof(w_char)); + } + for (p = candidate_utf + wl - 1; p > candidate_utf; p--) { + for (q = p - 1; (q >= candidate_utf) && ((p - q) < 10); q--) { + tmpc = *(q+1); + *(q+1) = *q; + *q = tmpc; + if ((p-q) < 2) continue; // omit swap char + u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); + ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); + if (ns == -1) return -1; + } + memcpy (candidate_utf, word, wl * sizeof(w_char)); + } + return ns; +} + +// generate a set of suggestions for very poorly spelled words +int SuggestMgr::ngsuggest(char** wlst, char * w, int ns, HashMgr** pHMgr, int md) +{ + + int i, j; + int lval; + int sc, scphon; + int lp, lpphon; + int nonbmp = 0; + + // exhaustively search through all root words + // keeping track of the MAX_ROOTS most similar root words + struct hentry * roots[MAX_ROOTS]; + char * rootsphon[MAX_ROOTS]; + int scores[MAX_ROOTS]; + int scoresphon[MAX_ROOTS]; + for (i = 0; i < MAX_ROOTS; i++) { + roots[i] = NULL; + scores[i] = -100 * i; + rootsphon[i] = NULL; + scoresphon[i] = -100 * i; + } + lp = MAX_ROOTS - 1; + lpphon = MAX_ROOTS - 1; + scphon = -20000; + int low = NGRAM_LOWERING; + + char w2[MAXWORDUTF8LEN]; + char f[MAXSWUTF8L]; + char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + char mw[MAXSWUTF8L]; + w_char u8[MAXSWL]; + int nc = strlen(word); + int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc; + + // set character based ngram suggestion for words with non-BMP Unicode characters + if (n == -1) { + utf8 = 0; // XXX not state-free + n = nc; + nonbmp = 1; + low = 0; + } + + struct hentry* hp = NULL; + int col = -1; + phonetable * ph = (pAMgr) ? pAMgr->get_phonetable() : NULL; + char target[MAXSWUTF8L]; + char candidate[MAXSWUTF8L]; + if (ph) { + if (utf8) { + w_char _w[MAXSWL]; + int _wl = u8_u16(_w, MAXSWL, word); + mkallcap_utf(_w, _wl, langnum); + u16_u8(candidate, MAXSWUTF8L, _w, _wl); + } else { + strcpy(candidate, word); + if (!nonbmp) mkallcap(candidate, csconv); + } + phonet(candidate, target, nc, *ph); // XXX phonet() is 8-bit (nc, not n) + } + + FLAG forbiddenword = pAMgr ? pAMgr->get_forbiddenword() : FLAG_NULL; + FLAG nosuggest = pAMgr ? pAMgr->get_nosuggest() : FLAG_NULL; + FLAG nongramsuggest = pAMgr ? pAMgr->get_nongramsuggest() : FLAG_NULL; + FLAG onlyincompound = pAMgr ? pAMgr->get_onlyincompound() : FLAG_NULL; + + for (i = 0; i < md; i++) { + while (0 != (hp = (pHMgr[i])->walk_hashtable(col, hp))) { + if ((hp->astr) && (pAMgr) && + (TESTAFF(hp->astr, forbiddenword, hp->alen) || + TESTAFF(hp->astr, ONLYUPCASEFLAG, hp->alen) || + TESTAFF(hp->astr, nosuggest, hp->alen) || + TESTAFF(hp->astr, nongramsuggest, hp->alen) || + TESTAFF(hp->astr, onlyincompound, hp->alen))) continue; + + sc = ngram(3, word, HENTRY_WORD(hp), NGRAM_LONGER_WORSE + low) + + leftcommonsubstring(word, HENTRY_WORD(hp)); + + // check special pronounciation + if ((hp->var & H_OPT_PHON) && copy_field(f, HENTRY_DATA(hp), MORPH_PHON)) { + int sc2 = ngram(3, word, f, NGRAM_LONGER_WORSE + low) + + + leftcommonsubstring(word, f); + if (sc2 > sc) sc = sc2; + } + + scphon = -20000; + if (ph && (sc > 2) && (abs(n - (int) hp->clen) <= 3)) { + char target2[MAXSWUTF8L]; + if (utf8) { + w_char _w[MAXSWL]; + int _wl = u8_u16(_w, MAXSWL, HENTRY_WORD(hp)); + mkallcap_utf(_w, _wl, langnum); + u16_u8(candidate, MAXSWUTF8L, _w, _wl); + } else { + strcpy(candidate, HENTRY_WORD(hp)); + mkallcap(candidate, csconv); + } + phonet(candidate, target2, -1, *ph); + scphon = 2 * ngram(3, target, target2, NGRAM_LONGER_WORSE); + } + + if (sc > scores[lp]) { + scores[lp] = sc; + roots[lp] = hp; + lval = sc; + for (j=0; j < MAX_ROOTS; j++) + if (scores[j] < lval) { + lp = j; + lval = scores[j]; + } + } + + + if (scphon > scoresphon[lpphon]) { + scoresphon[lpphon] = scphon; + rootsphon[lpphon] = HENTRY_WORD(hp); + lval = scphon; + for (j=0; j < MAX_ROOTS; j++) + if (scoresphon[j] < lval) { + lpphon = j; + lval = scoresphon[j]; + } + } + }} + + // find minimum threshold for a passable suggestion + // mangle original word three differnt ways + // and score them to generate a minimum acceptable score + int thresh = 0; + for (int sp = 1; sp < 4; sp++) { + if (utf8) { + for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*'; + u16_u8(mw, MAXSWUTF8L, u8, n); + thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + low); + } else { + strcpy(mw, word); + for (int k=sp; k < n; k+=4) *(mw + k) = '*'; + thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH + low); + } + } + thresh = thresh / 3; + thresh--; + + // now expand affixes on each of these root words and + // and use length adjusted ngram scores to select + // possible suggestions + char * guess[MAX_GUESS]; + char * guessorig[MAX_GUESS]; + int gscore[MAX_GUESS]; + for(i=0;iexpand_rootword(glst, MAX_WORDS, HENTRY_WORD(rp), rp->blen, + rp->astr, rp->alen, word, nc, + ((rp->var & H_OPT_PHON) ? copy_field(f, HENTRY_DATA(rp), MORPH_PHON) : NULL)); + + for (int k = 0; k < nw ; k++) { + sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH + low) + + leftcommonsubstring(word, glst[k].word); + + if (sc > thresh) { + if (sc > gscore[lp]) { + if (guess[lp]) { + free (guess[lp]); + if (guessorig[lp]) { + free(guessorig[lp]); + guessorig[lp] = NULL; + } + } + gscore[lp] = sc; + guess[lp] = glst[k].word; + guessorig[lp] = glst[k].orig; + lval = sc; + for (j=0; j < MAX_GUESS; j++) + if (gscore[j] < lval) { + lp = j; + lval = gscore[j]; + } + } else { + free(glst[k].word); + if (glst[k].orig) free(glst[k].orig); + } + } else { + free(glst[k].word); + if (glst[k].orig) free(glst[k].orig); + } + } + } + } + free(glst); + + // now we are done generating guesses + // sort in order of decreasing score + + + bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS); + if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS); + + // weight suggestions with a similarity index, based on + // the longest common subsequent algorithm and resort + + int is_swap = 0; + int re = 0; + double fact = 1.0; + if (pAMgr) { + int maxd = pAMgr->get_maxdiff(); + if (maxd >= 0) fact = (10.0 - maxd)/5.0; + } + + for (i=0; i < MAX_GUESS; i++) { + if (guess[i]) { + // lowering guess[i] + char gl[MAXSWUTF8L]; + int len; + if (utf8) { + w_char _w[MAXSWL]; + len = u8_u16(_w, MAXSWL, guess[i]); + mkallsmall_utf(_w, len, langnum); + u16_u8(gl, MAXSWUTF8L, _w, len); + } else { + strcpy(gl, guess[i]); + if (!nonbmp) mkallsmall(gl, csconv); + len = strlen(guess[i]); + } + + int _lcs = lcslen(word, gl); + + // same characters with different casing + if ((n == len) && (n == _lcs)) { + gscore[i] += 2000; + break; + } + // using 2-gram instead of 3, and other weightening + + re = ngram(2, word, gl, NGRAM_ANY_MISMATCH + low + NGRAM_WEIGHTED) + + ngram(2, gl, word, NGRAM_ANY_MISMATCH + low + NGRAM_WEIGHTED); + + gscore[i] = + // length of longest common subsequent minus length difference + 2 * _lcs - abs((int) (n - len)) + + // weight length of the left common substring + leftcommonsubstring(word, gl) + + // weight equal character positions + (!nonbmp && commoncharacterpositions(word, gl, &is_swap) ? 1: 0) + + // swap character (not neighboring) + ((is_swap) ? 10 : 0) + + // ngram + ngram(4, word, gl, NGRAM_ANY_MISMATCH + low) + + // weighted ngrams + re + + // different limit for dictionaries with PHONE rules + (ph ? (re < len * fact ? -1000 : 0) : (re < (n + len)*fact? -1000 : 0)); + } + } + + bubblesort(&guess[0], &guessorig[0], &gscore[0], MAX_GUESS); + +// phonetic version + if (ph) for (i=0; i < MAX_ROOTS; i++) { + if (rootsphon[i]) { + // lowering rootphon[i] + char gl[MAXSWUTF8L]; + int len; + if (utf8) { + w_char _w[MAXSWL]; + len = u8_u16(_w, MAXSWL, rootsphon[i]); + mkallsmall_utf(_w, len, langnum); + u16_u8(gl, MAXSWUTF8L, _w, len); + } else { + strcpy(gl, rootsphon[i]); + if (!nonbmp) mkallsmall(gl, csconv); + len = strlen(rootsphon[i]); + } + + // heuristic weigthing of ngram scores + scoresphon[i] += 2 * lcslen(word, gl) - abs((int) (n - len)) + + // weight length of the left common substring + leftcommonsubstring(word, gl); + } + } + + if (ph) bubblesort(&rootsphon[0], NULL, &scoresphon[0], MAX_ROOTS); + + // copy over + int oldns = ns; + + int same = 0; + for (i=0; i < MAX_GUESS; i++) { + if (guess[i]) { + if ((ns < oldns + maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) { + int unique = 1; + // leave only excellent suggestions, if exists + if (gscore[i] > 1000) same = 1; else if (gscore[i] < -100) { + same = 1; + // keep the best ngram suggestions, unless in ONLYMAXDIFF mode + if (ns > oldns || (pAMgr && pAMgr->get_onlymaxdiff())) { + free(guess[i]); + if (guessorig[i]) free(guessorig[i]); + continue; + } + } + for (j = 0; j < ns; j++) { + // don't suggest previous suggestions or a previous suggestion with prefixes or affixes + if ((!guessorig[i] && strstr(guess[i], wlst[j])) || + (guessorig[i] && strstr(guessorig[i], wlst[j])) || + // check forbidden words + !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0; + } + if (unique) { + wlst[ns++] = guess[i]; + if (guessorig[i]) { + free(guess[i]); + wlst[ns-1] = guessorig[i]; + } + } else { + free(guess[i]); + if (guessorig[i]) free(guessorig[i]); + } + } else { + free(guess[i]); + if (guessorig[i]) free(guessorig[i]); + } + } + } + + oldns = ns; + if (ph) for (i=0; i < MAX_ROOTS; i++) { + if (rootsphon[i]) { + if ((ns < oldns + MAXPHONSUGS) && (ns < maxSug)) { + int unique = 1; + for (j = 0; j < ns; j++) { + // don't suggest previous suggestions or a previous suggestion with prefixes or affixes + if (strstr(rootsphon[i], wlst[j]) || + // check forbidden words + !checkword(rootsphon[i], strlen(rootsphon[i]), 0, NULL, NULL)) unique = 0; + } + if (unique) { + wlst[ns++] = mystrdup(rootsphon[i]); + if (!wlst[ns - 1]) return ns - 1; + } + } + } + } + + if (nonbmp) utf8 = 1; + return ns; +} + + +// see if a candidate suggestion is spelled correctly +// needs to check both root words and words with affixes + +// obsolote MySpell-HU modifications: +// return value 2 and 3 marks compounding with hyphen (-) +// `3' marks roots without suffix +int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, clock_t * timelimit) +{ + struct hentry * rv=NULL; + struct hentry * rv2=NULL; + int nosuffix = 0; + + // check time limit + if (timer) { + (*timer)--; + if (!(*timer) && timelimit) { + if ((clock() - *timelimit) > TIMELIMIT) return 0; + *timer = MAXPLUSTIMER; + } + } + + if (pAMgr) { + if (cpdsuggest==1) { + if (pAMgr->get_compound()) { + rv = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 1, 0); //EXT + if (rv && (!(rv2 = pAMgr->lookup(word)) || !rv2->astr || + !(TESTAFF(rv2->astr,pAMgr->get_forbiddenword(),rv2->alen) || + TESTAFF(rv2->astr,pAMgr->get_nosuggest(),rv2->alen)))) return 3; // XXX obsolote categorisation + only ICONV needs affix flag check? + } + return 0; + } + + rv = pAMgr->lookup(word); + + if (rv) { + if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) + || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0; + while (rv) { + if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) { + rv = rv->next_homonym; + } else break; + } + } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX + + if (rv) { + nosuffix=1; + } else { + rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix + } + + if (!rv && pAMgr->have_contclass()) { + rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL); + if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL); + } + + // check forbidden words + if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) || + TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || + TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0; + + if (rv) { // XXX obsolote + if ((pAMgr->get_compoundflag()) && + TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix; + return 1; + } + } + return 0; +} + +int SuggestMgr::check_forbidden(const char * word, int len) +{ + struct hentry * rv = NULL; + + if (pAMgr) { + rv = pAMgr->lookup(word); + if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_needaffix(),rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL; + if (!(pAMgr->prefix_check(word,len,1))) + rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix + // check forbidden words + if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1; + } + return 0; +} + +#ifdef HUNSPELL_EXPERIMENTAL +// suggest possible stems +int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug) +{ + char ** wlst; + + struct hentry * rv = NULL; + + char w2[MAXSWUTF8L]; + const char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + int wl = strlen(word); + + + if (*slst) { + wlst = *slst; + } else { + wlst = (char **) calloc(maxSug, sizeof(char *)); + if (wlst == NULL) return -1; + } + + rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug); + + // delete dash from end of word + if (nsug > 0) { + for (int j=0; j < nsug; j++) { + if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0'; + } + } + + *slst = wlst; + return nsug; +} +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + + +char * SuggestMgr::suggest_morph(const char * w) +{ + char result[MAXLNLEN]; + char * r = (char *) result; + char * st; + + struct hentry * rv = NULL; + + *result = '\0'; + + if (! pAMgr) return NULL; + + char w2[MAXSWUTF8L]; + const char * word = w; + + // word reversing wrapper for complex prefixes + if (complexprefixes) { + strcpy(w2, w); + if (utf8) reverseword_utf(w2); else reverseword(w2); + word = w2; + } + + rv = pAMgr->lookup(word); + + while (rv) { + if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) || + TESTAFF(rv->astr, pAMgr->get_needaffix(), rv->alen) || + TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) { + if (!HENTRY_FIND(rv, MORPH_STEM)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, MORPH_STEM, MAXLNLEN); + mystrcat(result, word, MAXLNLEN); + } + if (HENTRY_DATA(rv)) { + mystrcat(result, " ", MAXLNLEN); + mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); + } + mystrcat(result, "\n", MAXLNLEN); + } + rv = rv->next_homonym; + } + + st = pAMgr->affix_check_morph(word,strlen(word)); + if (st) { + mystrcat(result, st, MAXLNLEN); + free(st); + } + + if (pAMgr->get_compound() && (*result == '\0')) + pAMgr->compound_check_morph(word, strlen(word), + 0, 0, 100, 0,NULL, 0, &r, NULL); + + return (*result) ? mystrdup(line_uniq(result, MSEP_REC)) : NULL; +} + +#ifdef HUNSPELL_EXPERIMENTAL +char * SuggestMgr::suggest_morph_for_spelling_error(const char * word) +{ + char * p = NULL; + char ** wlst = (char **) calloc(maxSug, sizeof(char *)); + if (!**wlst) return NULL; + // we will use only the first suggestion + for (int i = 0; i < maxSug - 1; i++) wlst[i] = ""; + int ns = suggest(&wlst, word, maxSug - 1, NULL); + if (ns == maxSug) { + p = suggest_morph(wlst[maxSug - 1]); + free(wlst[maxSug - 1]); + } + if (wlst) free(wlst); + return p; +} +#endif // END OF HUNSPELL_EXPERIMENTAL CODE + +/* affixation */ +char * SuggestMgr::suggest_hentry_gen(hentry * rv, char * pattern) +{ + char result[MAXLNLEN]; + *result = '\0'; + int sfxcount = get_sfxcount(pattern); + + if (get_sfxcount(HENTRY_DATA(rv)) > sfxcount) return NULL; + + if (HENTRY_DATA(rv)) { + char * aff = pAMgr->morphgen(HENTRY_WORD(rv), rv->blen, rv->astr, rv->alen, + HENTRY_DATA(rv), pattern, 0); + if (aff) { + mystrcat(result, aff, MAXLNLEN); + mystrcat(result, "\n", MAXLNLEN); + free(aff); + } + } + + // check all allomorphs + char allomorph[MAXLNLEN]; + char * p = NULL; + if (HENTRY_DATA(rv)) p = (char *) strstr(HENTRY_DATA2(rv), MORPH_ALLOMORPH); + while (p) { + struct hentry * rv2 = NULL; + p += MORPH_TAG_LEN; + int plen = fieldlen(p); + strncpy(allomorph, p, plen); + allomorph[plen] = '\0'; + rv2 = pAMgr->lookup(allomorph); + while (rv2) { +// if (HENTRY_DATA(rv2) && get_sfxcount(HENTRY_DATA(rv2)) <= sfxcount) { + if (HENTRY_DATA(rv2)) { + char * st = (char *) strstr(HENTRY_DATA2(rv2), MORPH_STEM); + if (st && (strncmp(st + MORPH_TAG_LEN, + HENTRY_WORD(rv), fieldlen(st + MORPH_TAG_LEN)) == 0)) { + char * aff = pAMgr->morphgen(HENTRY_WORD(rv2), rv2->blen, rv2->astr, rv2->alen, + HENTRY_DATA(rv2), pattern, 0); + if (aff) { + mystrcat(result, aff, MAXLNLEN); + mystrcat(result, "\n", MAXLNLEN); + free(aff); + } + } + } + rv2 = rv2->next_homonym; + } + p = strstr(p + plen, MORPH_ALLOMORPH); + } + + return (*result) ? mystrdup(result) : NULL; +} + +char * SuggestMgr::suggest_gen(char ** desc, int n, char * pattern) { + char result[MAXLNLEN]; + char result2[MAXLNLEN]; + char newpattern[MAXLNLEN]; + *newpattern = '\0'; + if (n == 0) return 0; + *result2 = '\0'; + struct hentry * rv = NULL; + if (!pAMgr) return NULL; + +// search affixed forms with and without derivational suffixes + while(1) { + + for (int k = 0; k < n; k++) { + *result = '\0'; + // add compound word parts (except the last one) + char * s = (char *) desc[k]; + char * part = strstr(s, MORPH_PART); + if (part) { + char * nextpart = strstr(part + 1, MORPH_PART); + while (nextpart) { + copy_field(result + strlen(result), part, MORPH_PART); + part = nextpart; + nextpart = strstr(part + 1, MORPH_PART); + } + s = part; + } + + char **pl; + char tok[MAXLNLEN]; + strcpy(tok, s); + char * alt = strstr(tok, " | "); + while (alt) { + alt[1] = MSEP_ALT; + alt = strstr(alt, " | "); + } + int pln = line_tok(tok, &pl, MSEP_ALT); + for (int i = 0; i < pln; i++) { + // remove inflectional and terminal suffixes + char * is = strstr(pl[i], MORPH_INFL_SFX); + if (is) *is = '\0'; + char * ts = strstr(pl[i], MORPH_TERM_SFX); + while (ts) { + *ts = '_'; + ts = strstr(pl[i], MORPH_TERM_SFX); + } + char * st = strstr(s, MORPH_STEM); + if (st) { + copy_field(tok, st, MORPH_STEM); + rv = pAMgr->lookup(tok); + while (rv) { + char newpat[MAXLNLEN]; + strcpy(newpat, pl[i]); + strcat(newpat, pattern); + char * sg = suggest_hentry_gen(rv, newpat); + if (!sg) sg = suggest_hentry_gen(rv, pattern); + if (sg) { + char ** gen; + int genl = line_tok(sg, &gen, MSEP_REC); + free(sg); + sg = NULL; + for (int j = 0; j < genl; j++) { + if (strstr(pl[i], MORPH_SURF_PFX)) { + int r2l = strlen(result2); + result2[r2l] = MSEP_REC; + strcpy(result2 + r2l + 1, result); + copy_field(result2 + strlen(result2), pl[i], MORPH_SURF_PFX); + mystrcat(result2, gen[j], MAXLNLEN); + } else { + sprintf(result2 + strlen(result2), "%c%s%s", + MSEP_REC, result, gen[j]); + } + } + freelist(&gen, genl); + } + rv = rv->next_homonym; + } + } + } + freelist(&pl, pln); + } + + if (*result2 || !strstr(pattern, MORPH_DERI_SFX)) break; + strcpy(newpattern, pattern); + pattern = newpattern; + char * ds = strstr(pattern, MORPH_DERI_SFX); + while (ds) { + strncpy(ds, MORPH_TERM_SFX, MORPH_TAG_LEN); + ds = strstr(pattern, MORPH_DERI_SFX); + } + } + return (*result2 ? mystrdup(result2) : NULL); +} + + +// generate an n-gram score comparing s1 and s2 +int SuggestMgr::ngram(int n, char * s1, const char * s2, int opt) +{ + int nscore = 0; + int ns; + int l1; + int l2; + int test = 0; + + if (utf8) { + w_char su1[MAXSWL]; + w_char su2[MAXSWL]; + l1 = u8_u16(su1, MAXSWL, s1); + l2 = u8_u16(su2, MAXSWL, s2); + if ((l2 <= 0) || (l1 == -1)) return 0; + // lowering dictionary word + if (opt & NGRAM_LOWERING) mkallsmall_utf(su2, l2, langnum); + for (int j = 1; j <= n; j++) { + ns = 0; + for (int i = 0; i <= (l1-j); i++) { + int k = 0; + for (int l = 0; l <= (l2-j); l++) { + for (k = 0; k < j; k++) { + w_char * c1 = su1 + i + k; + w_char * c2 = su2 + l + k; + if ((c1->l != c2->l) || (c1->h != c2->h)) break; + } + if (k == j) { + ns++; + break; + } + } + if (k != j && opt & NGRAM_WEIGHTED) { + ns--; + test++; + if (i == 0 || i == l1-j) ns--; // side weight + } + } + nscore = nscore + ns; + if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break; + } + } else { + l2 = strlen(s2); + if (l2 == 0) return 0; + l1 = strlen(s1); + char *t = mystrdup(s2); + if (opt & NGRAM_LOWERING) mkallsmall(t, csconv); + for (int j = 1; j <= n; j++) { + ns = 0; + for (int i = 0; i <= (l1-j); i++) { + char c = *(s1 + i + j); + *(s1 + i + j) = '\0'; + if (strstr(t,(s1+i))) { + ns++; + } else if (opt & NGRAM_WEIGHTED) { + ns--; +test++; + if (i == 0 || i == l1-j) ns--; // side weight + } + *(s1 + i + j ) = c; + } + nscore = nscore + ns; + if (ns < 2 && !(opt & NGRAM_WEIGHTED)) break; + } + free(t); + } + + ns = 0; + if (opt & NGRAM_LONGER_WORSE) ns = (l2-l1)-2; + if (opt & NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2; + ns = (nscore - ((ns > 0) ? ns : 0)); + return ns; +} + +// length of the left common substring of s1 and (decapitalised) s2 +int SuggestMgr::leftcommonsubstring(char * s1, const char * s2) { + if (utf8) { + w_char su1[MAXSWL]; + w_char su2[MAXSWL]; + su1[0].l = su2[0].l = su1[0].h = su2[0].h = 0; + // decapitalize dictionary word + if (complexprefixes) { + int l1 = u8_u16(su1, MAXSWL, s1); + int l2 = u8_u16(su2, MAXSWL, s2); + if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1; + } else { + int i; + u8_u16(su1, 1, s1); + u8_u16(su2, 1, s2); + unsigned short idx = (su2->h << 8) + su2->l; + unsigned short otheridx = (su1->h << 8) + su1->l; + if (otheridx != idx && + (otheridx != unicodetolower(idx, langnum))) return 0; + int l1 = u8_u16(su1, MAXSWL, s1); + int l2 = u8_u16(su2, MAXSWL, s2); + for(i = 1; (i < l1) && (i < l2) && + (su1[i].l == su2[i].l) && (su1[i].h == su2[i].h); i++); + return i; + } + } else { + if (complexprefixes) { + int l1 = strlen(s1); + int l2 = strlen(s2); + if (*(s2+l1-1) == *(s2+l2-1)) return 1; + } else { + char * olds = s1; + // decapitalise dictionary word + if ((*s1 != *s2) && (*s1 != csconv[((unsigned char)*s2)].clower)) return 0; + do { + s1++; s2++; + } while ((*s1 == *s2) && (*s1 != '\0')); + return (int)(s1 - olds); + } + } + return 0; +} + +int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) { + int num = 0; + int diff = 0; + int diffpos[2]; + *is_swap = 0; + if (utf8) { + w_char su1[MAXSWL]; + w_char su2[MAXSWL]; + int l1 = u8_u16(su1, MAXSWL, s1); + int l2 = u8_u16(su2, MAXSWL, s2); + // decapitalize dictionary word + if (complexprefixes) { + mkallsmall_utf(su2+l2-1, 1, langnum); + } else { + mkallsmall_utf(su2, 1, langnum); + } + for (int i = 0; (i < l1) && (i < l2); i++) { + if (((short *) su1)[i] == ((short *) su2)[i]) { + num++; + } else { + if (diff < 2) diffpos[diff] = i; + diff++; + } + } + if ((diff == 2) && (l1 == l2) && + (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) && + (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1; + } else { + int i; + char t[MAXSWUTF8L]; + strcpy(t, s2); + // decapitalize dictionary word + if (complexprefixes) { + int l2 = strlen(t); + *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower; + } else { + mkallsmall(t, csconv); + } + for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) { + if (*(s1+i) == *(t+i)) { + num++; + } else { + if (diff < 2) diffpos[diff] = i; + diff++; + } + } + if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) && + (*(s1+diffpos[0]) == *(t+diffpos[1])) && + (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1; + } + return num; +} + +int SuggestMgr::mystrlen(const char * word) { + if (utf8) { + w_char w[MAXSWL]; + return u8_u16(w, MAXSWL, word); + } else return strlen(word); +} + +// sort in decreasing order of score +void SuggestMgr::bubblesort(char** rword, char** rword2, int* rsc, int n ) +{ + int m = 1; + while (m < n) { + int j = m; + while (j > 0) { + if (rsc[j-1] < rsc[j]) { + int sctmp = rsc[j-1]; + char * wdtmp = rword[j-1]; + rsc[j-1] = rsc[j]; + rword[j-1] = rword[j]; + rsc[j] = sctmp; + rword[j] = wdtmp; + if (rword2) { + wdtmp = rword2[j-1]; + rword2[j-1] = rword2[j]; + rword2[j] = wdtmp; + } + j--; + } else break; + } + m++; + } + return; +} + +// longest common subsequence +void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) { + int n, m; + w_char su[MAXSWL]; + w_char su2[MAXSWL]; + char * b; + char * c; + int i; + int j; + if (utf8) { + m = u8_u16(su, MAXSWL, s); + n = u8_u16(su2, MAXSWL, s2); + } else { + m = strlen(s); + n = strlen(s2); + } + c = (char *) malloc((m + 1) * (n + 1)); + b = (char *) malloc((m + 1) * (n + 1)); + if (!c || !b) { + if (c) free(c); + if (b) free(b); + *result = NULL; + return; + } + for (i = 1; i <= m; i++) c[i*(n+1)] = 0; + for (j = 0; j <= n; j++) c[j] = 0; + for (i = 1; i <= m; i++) { + for (j = 1; j <= n; j++) { + if ( ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1))) + || ((!utf8) && ((*(s+i-1)) == (*(s2+j-1))))) { + c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1; + b[i*(n+1) + j] = LCS_UPLEFT; + } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) { + c[i*(n+1) + j] = c[(i-1)*(n+1) + j]; + b[i*(n+1) + j] = LCS_UP; + } else { + c[i*(n+1) + j] = c[i*(n+1) + j-1]; + b[i*(n+1) + j] = LCS_LEFT; + } + } + } + *result = b; + free(c); + *l1 = m; + *l2 = n; +} + +int SuggestMgr::lcslen(const char * s, const char* s2) { + int m; + int n; + int i; + int j; + char * result; + int len = 0; + lcs(s, s2, &m, &n, &result); + if (!result) return 0; + i = m; + j = n; + while ((i != 0) && (j != 0)) { + if (result[i*(n+1) + j] == LCS_UPLEFT) { + len++; + i--; + j--; + } else if (result[i*(n+1) + j] == LCS_UP) { + i--; + } else j--; + } + free(result); + return len; +} diff --git a/src/lib/hunspell/src/suggestmgr.hxx b/src/lib/hunspell/src/suggestmgr.hxx new file mode 100644 index 0000000..5f043fd --- /dev/null +++ b/src/lib/hunspell/src/suggestmgr.hxx @@ -0,0 +1,111 @@ +#ifndef _SUGGESTMGR_HXX_ +#define _SUGGESTMGR_HXX_ + +#define MAXSWL 100 +#define MAXSWUTF8L (MAXSWL * 4) +#define MAX_ROOTS 100 +#define MAX_WORDS 100 +#define MAX_GUESS 200 +#define MAXNGRAMSUGS 4 +#define MAXPHONSUGS 2 +#define MAXCOMPOUNDSUGS 3 + +// timelimit: max ~1/4 sec (process time on Linux) for a time consuming function +#define TIMELIMIT (CLOCKS_PER_SEC >> 2) +#define MINTIMER 100 +#define MAXPLUSTIMER 100 + +#define NGRAM_LONGER_WORSE (1 << 0) +#define NGRAM_ANY_MISMATCH (1 << 1) +#define NGRAM_LOWERING (1 << 2) +#define NGRAM_WEIGHTED (1 << 3) + +#include "hunvisapi.h" + +#include "atypes.hxx" +#include "affixmgr.hxx" +#include "hashmgr.hxx" +#include "langnum.hxx" +#include + +enum { LCS_UP, LCS_LEFT, LCS_UPLEFT }; + +class LIBHUNSPELL_DLL_EXPORTED SuggestMgr +{ + char * ckey; + int ckeyl; + w_char * ckey_utf; + + char * ctry; + int ctryl; + w_char * ctry_utf; + + AffixMgr* pAMgr; + int maxSug; + struct cs_info * csconv; + int utf8; + int langnum; + int nosplitsugs; + int maxngramsugs; + int maxcpdsugs; + int complexprefixes; + + +public: + SuggestMgr(const char * tryme, int maxn, AffixMgr *aptr); + ~SuggestMgr(); + + int suggest(char*** slst, const char * word, int nsug, int * onlycmpdsug); + int ngsuggest(char ** wlst, char * word, int ns, HashMgr** pHMgr, int md); + int suggest_auto(char*** slst, const char * word, int nsug); + int suggest_stems(char*** slst, const char * word, int nsug); + int suggest_pos_stems(char*** slst, const char * word, int nsug); + + char * suggest_morph(const char * word); + char * suggest_gen(char ** pl, int pln, char * pattern); + char * suggest_morph_for_spelling_error(const char * word); + +private: + int testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest, + int * timer, clock_t * timelimit); + int checkword(const char *, int, int, int *, clock_t *); + int check_forbidden(const char *, int); + + int capchars(char **, const char *, int, int); + int replchars(char**, const char *, int, int); + int doubletwochars(char**, const char *, int, int); + int forgotchar(char **, const char *, int, int); + int swapchar(char **, const char *, int, int); + int longswapchar(char **, const char *, int, int); + int movechar(char **, const char *, int, int); + int extrachar(char **, const char *, int, int); + int badcharkey(char **, const char *, int, int); + int badchar(char **, const char *, int, int); + int twowords(char **, const char *, int, int); + int fixstems(char **, const char *, int); + + int capchars_utf(char **, const w_char *, int wl, int, int); + int doubletwochars_utf(char**, const w_char *, int wl, int, int); + int forgotchar_utf(char**, const w_char *, int wl, int, int); + int extrachar_utf(char**, const w_char *, int wl, int, int); + int badcharkey_utf(char **, const w_char *, int wl, int, int); + int badchar_utf(char **, const w_char *, int wl, int, int); + int swapchar_utf(char **, const w_char *, int wl, int, int); + int longswapchar_utf(char **, const w_char *, int, int, int); + int movechar_utf(char **, const w_char *, int, int, int); + + int mapchars(char**, const char *, int, int); + int map_related(const char *, char *, int, int, char ** wlst, int, int, const mapentry*, int, int *, clock_t *); + int ngram(int n, char * s1, const char * s2, int opt); + int mystrlen(const char * word); + int leftcommonsubstring(char * s1, const char * s2); + int commoncharacterpositions(char * s1, const char * s2, int * is_swap); + void bubblesort( char ** rwd, char ** rwd2, int * rsc, int n); + void lcs(const char * s, const char * s2, int * l1, int * l2, char ** result); + int lcslen(const char * s, const char* s2); + char * suggest_hentry_gen(hentry * rv, char * pattern); + +}; + +#endif + diff --git a/src/lib/hunspell/src/utf_info.cxx b/src/lib/hunspell/src/utf_info.cxx new file mode 100644 index 0000000..4a8e203 --- /dev/null +++ b/src/lib/hunspell/src/utf_info.cxx @@ -0,0 +1,19676 @@ +#include "csutil.hxx" +/* fields: Unicode letter, toupper, tolower */ +static struct unicode_info utf_lst[] = { +{ 0x0041, 0x0041, 0x0061 }, +{ 0x0042, 0x0042, 0x0062 }, +{ 0x0043, 0x0043, 0x0063 }, +{ 0x0044, 0x0044, 0x0064 }, +{ 0x0045, 0x0045, 0x0065 }, +{ 0x0046, 0x0046, 0x0066 }, +{ 0x0047, 0x0047, 0x0067 }, +{ 0x0048, 0x0048, 0x0068 }, +{ 0x0049, 0x0049, 0x0069 }, +{ 0x004A, 0x004A, 0x006A }, +{ 0x004B, 0x004B, 0x006B }, +{ 0x004C, 0x004C, 0x006C }, +{ 0x004D, 0x004D, 0x006D }, +{ 0x004E, 0x004E, 0x006E }, +{ 0x004F, 0x004F, 0x006F }, +{ 0x0050, 0x0050, 0x0070 }, +{ 0x0051, 0x0051, 0x0071 }, +{ 0x0052, 0x0052, 0x0072 }, +{ 0x0053, 0x0053, 0x0073 }, +{ 0x0054, 0x0054, 0x0074 }, +{ 0x0055, 0x0055, 0x0075 }, +{ 0x0056, 0x0056, 0x0076 }, +{ 0x0057, 0x0057, 0x0077 }, +{ 0x0058, 0x0058, 0x0078 }, +{ 0x0059, 0x0059, 0x0079 }, +{ 0x005A, 0x005A, 0x007A }, +{ 0x0061, 0x0041, 0x0061 }, +{ 0x0062, 0x0042, 0x0062 }, +{ 0x0063, 0x0043, 0x0063 }, +{ 0x0064, 0x0044, 0x0064 }, +{ 0x0065, 0x0045, 0x0065 }, +{ 0x0066, 0x0046, 0x0066 }, +{ 0x0067, 0x0047, 0x0067 }, +{ 0x0068, 0x0048, 0x0068 }, +{ 0x0069, 0x0049, 0x0069 }, +{ 0x006A, 0x004A, 0x006A }, +{ 0x006B, 0x004B, 0x006B }, +{ 0x006C, 0x004C, 0x006C }, +{ 0x006D, 0x004D, 0x006D }, +{ 0x006E, 0x004E, 0x006E }, +{ 0x006F, 0x004F, 0x006F }, +{ 0x0070, 0x0050, 0x0070 }, +{ 0x0071, 0x0051, 0x0071 }, +{ 0x0072, 0x0052, 0x0072 }, +{ 0x0073, 0x0053, 0x0073 }, +{ 0x0074, 0x0054, 0x0074 }, +{ 0x0075, 0x0055, 0x0075 }, +{ 0x0076, 0x0056, 0x0076 }, +{ 0x0077, 0x0057, 0x0077 }, +{ 0x0078, 0x0058, 0x0078 }, +{ 0x0079, 0x0059, 0x0079 }, +{ 0x007A, 0x005A, 0x007A }, +{ 0x00AA, 0x00AA, 0x00AA }, +{ 0x00B5, 0x039C, 0x00B5 }, +{ 0x00BA, 0x00BA, 0x00BA }, +{ 0x00C0, 0x00C0, 0x00E0 }, +{ 0x00C1, 0x00C1, 0x00E1 }, +{ 0x00C2, 0x00C2, 0x00E2 }, +{ 0x00C3, 0x00C3, 0x00E3 }, +{ 0x00C4, 0x00C4, 0x00E4 }, +{ 0x00C5, 0x00C5, 0x00E5 }, +{ 0x00C6, 0x00C6, 0x00E6 }, +{ 0x00C7, 0x00C7, 0x00E7 }, +{ 0x00C8, 0x00C8, 0x00E8 }, +{ 0x00C9, 0x00C9, 0x00E9 }, +{ 0x00CA, 0x00CA, 0x00EA }, +{ 0x00CB, 0x00CB, 0x00EB }, +{ 0x00CC, 0x00CC, 0x00EC }, +{ 0x00CD, 0x00CD, 0x00ED }, +{ 0x00CE, 0x00CE, 0x00EE }, +{ 0x00CF, 0x00CF, 0x00EF }, +{ 0x00D0, 0x00D0, 0x00F0 }, +{ 0x00D1, 0x00D1, 0x00F1 }, +{ 0x00D2, 0x00D2, 0x00F2 }, +{ 0x00D3, 0x00D3, 0x00F3 }, +{ 0x00D4, 0x00D4, 0x00F4 }, +{ 0x00D5, 0x00D5, 0x00F5 }, +{ 0x00D6, 0x00D6, 0x00F6 }, +{ 0x00D8, 0x00D8, 0x00F8 }, +{ 0x00D9, 0x00D9, 0x00F9 }, +{ 0x00DA, 0x00DA, 0x00FA }, +{ 0x00DB, 0x00DB, 0x00FB }, +{ 0x00DC, 0x00DC, 0x00FC }, +{ 0x00DD, 0x00DD, 0x00FD }, +{ 0x00DE, 0x00DE, 0x00FE }, +{ 0x00DF, 0x00DF, 0x00DF }, +{ 0x00E0, 0x00C0, 0x00E0 }, +{ 0x00E1, 0x00C1, 0x00E1 }, +{ 0x00E2, 0x00C2, 0x00E2 }, +{ 0x00E3, 0x00C3, 0x00E3 }, +{ 0x00E4, 0x00C4, 0x00E4 }, +{ 0x00E5, 0x00C5, 0x00E5 }, +{ 0x00E6, 0x00C6, 0x00E6 }, +{ 0x00E7, 0x00C7, 0x00E7 }, +{ 0x00E8, 0x00C8, 0x00E8 }, +{ 0x00E9, 0x00C9, 0x00E9 }, +{ 0x00EA, 0x00CA, 0x00EA }, +{ 0x00EB, 0x00CB, 0x00EB }, +{ 0x00EC, 0x00CC, 0x00EC }, +{ 0x00ED, 0x00CD, 0x00ED }, +{ 0x00EE, 0x00CE, 0x00EE }, +{ 0x00EF, 0x00CF, 0x00EF }, +{ 0x00F0, 0x00D0, 0x00F0 }, +{ 0x00F1, 0x00D1, 0x00F1 }, +{ 0x00F2, 0x00D2, 0x00F2 }, +{ 0x00F3, 0x00D3, 0x00F3 }, +{ 0x00F4, 0x00D4, 0x00F4 }, +{ 0x00F5, 0x00D5, 0x00F5 }, +{ 0x00F6, 0x00D6, 0x00F6 }, +{ 0x00F8, 0x00D8, 0x00F8 }, +{ 0x00F9, 0x00D9, 0x00F9 }, +{ 0x00FA, 0x00DA, 0x00FA }, +{ 0x00FB, 0x00DB, 0x00FB }, +{ 0x00FC, 0x00DC, 0x00FC }, +{ 0x00FD, 0x00DD, 0x00FD }, +{ 0x00FE, 0x00DE, 0x00FE }, +{ 0x00FF, 0x0178, 0x00FF }, +{ 0x0100, 0x0100, 0x0101 }, +{ 0x0101, 0x0100, 0x0101 }, +{ 0x0102, 0x0102, 0x0103 }, +{ 0x0103, 0x0102, 0x0103 }, +{ 0x0104, 0x0104, 0x0105 }, +{ 0x0105, 0x0104, 0x0105 }, +{ 0x0106, 0x0106, 0x0107 }, +{ 0x0107, 0x0106, 0x0107 }, +{ 0x0108, 0x0108, 0x0109 }, +{ 0x0109, 0x0108, 0x0109 }, +{ 0x010A, 0x010A, 0x010B }, +{ 0x010B, 0x010A, 0x010B }, +{ 0x010C, 0x010C, 0x010D }, +{ 0x010D, 0x010C, 0x010D }, +{ 0x010E, 0x010E, 0x010F }, +{ 0x010F, 0x010E, 0x010F }, +{ 0x0110, 0x0110, 0x0111 }, +{ 0x0111, 0x0110, 0x0111 }, +{ 0x0112, 0x0112, 0x0113 }, +{ 0x0113, 0x0112, 0x0113 }, +{ 0x0114, 0x0114, 0x0115 }, +{ 0x0115, 0x0114, 0x0115 }, +{ 0x0116, 0x0116, 0x0117 }, +{ 0x0117, 0x0116, 0x0117 }, +{ 0x0118, 0x0118, 0x0119 }, +{ 0x0119, 0x0118, 0x0119 }, +{ 0x011A, 0x011A, 0x011B }, +{ 0x011B, 0x011A, 0x011B }, +{ 0x011C, 0x011C, 0x011D }, +{ 0x011D, 0x011C, 0x011D }, +{ 0x011E, 0x011E, 0x011F }, +{ 0x011F, 0x011E, 0x011F }, +{ 0x0120, 0x0120, 0x0121 }, +{ 0x0121, 0x0120, 0x0121 }, +{ 0x0122, 0x0122, 0x0123 }, +{ 0x0123, 0x0122, 0x0123 }, +{ 0x0124, 0x0124, 0x0125 }, +{ 0x0125, 0x0124, 0x0125 }, +{ 0x0126, 0x0126, 0x0127 }, +{ 0x0127, 0x0126, 0x0127 }, +{ 0x0128, 0x0128, 0x0129 }, +{ 0x0129, 0x0128, 0x0129 }, +{ 0x012A, 0x012A, 0x012B }, +{ 0x012B, 0x012A, 0x012B }, +{ 0x012C, 0x012C, 0x012D }, +{ 0x012D, 0x012C, 0x012D }, +{ 0x012E, 0x012E, 0x012F }, +{ 0x012F, 0x012E, 0x012F }, +{ 0x0130, 0x0130, 0x0069 }, +{ 0x0131, 0x0049, 0x0131 }, +{ 0x0132, 0x0132, 0x0133 }, +{ 0x0133, 0x0132, 0x0133 }, +{ 0x0134, 0x0134, 0x0135 }, +{ 0x0135, 0x0134, 0x0135 }, +{ 0x0136, 0x0136, 0x0137 }, +{ 0x0137, 0x0136, 0x0137 }, +{ 0x0138, 0x0138, 0x0138 }, +{ 0x0139, 0x0139, 0x013A }, +{ 0x013A, 0x0139, 0x013A }, +{ 0x013B, 0x013B, 0x013C }, +{ 0x013C, 0x013B, 0x013C }, +{ 0x013D, 0x013D, 0x013E }, +{ 0x013E, 0x013D, 0x013E }, +{ 0x013F, 0x013F, 0x0140 }, +{ 0x0140, 0x013F, 0x0140 }, +{ 0x0141, 0x0141, 0x0142 }, +{ 0x0142, 0x0141, 0x0142 }, +{ 0x0143, 0x0143, 0x0144 }, +{ 0x0144, 0x0143, 0x0144 }, +{ 0x0145, 0x0145, 0x0146 }, +{ 0x0146, 0x0145, 0x0146 }, +{ 0x0147, 0x0147, 0x0148 }, +{ 0x0148, 0x0147, 0x0148 }, +{ 0x0149, 0x0149, 0x0149 }, +{ 0x014A, 0x014A, 0x014B }, +{ 0x014B, 0x014A, 0x014B }, +{ 0x014C, 0x014C, 0x014D }, +{ 0x014D, 0x014C, 0x014D }, +{ 0x014E, 0x014E, 0x014F }, +{ 0x014F, 0x014E, 0x014F }, +{ 0x0150, 0x0150, 0x0151 }, +{ 0x0151, 0x0150, 0x0151 }, +{ 0x0152, 0x0152, 0x0153 }, +{ 0x0153, 0x0152, 0x0153 }, +{ 0x0154, 0x0154, 0x0155 }, +{ 0x0155, 0x0154, 0x0155 }, +{ 0x0156, 0x0156, 0x0157 }, +{ 0x0157, 0x0156, 0x0157 }, +{ 0x0158, 0x0158, 0x0159 }, +{ 0x0159, 0x0158, 0x0159 }, +{ 0x015A, 0x015A, 0x015B }, +{ 0x015B, 0x015A, 0x015B }, +{ 0x015C, 0x015C, 0x015D }, +{ 0x015D, 0x015C, 0x015D }, +{ 0x015E, 0x015E, 0x015F }, +{ 0x015F, 0x015E, 0x015F }, +{ 0x0160, 0x0160, 0x0161 }, +{ 0x0161, 0x0160, 0x0161 }, +{ 0x0162, 0x0162, 0x0163 }, +{ 0x0163, 0x0162, 0x0163 }, +{ 0x0164, 0x0164, 0x0165 }, +{ 0x0165, 0x0164, 0x0165 }, +{ 0x0166, 0x0166, 0x0167 }, +{ 0x0167, 0x0166, 0x0167 }, +{ 0x0168, 0x0168, 0x0169 }, +{ 0x0169, 0x0168, 0x0169 }, +{ 0x016A, 0x016A, 0x016B }, +{ 0x016B, 0x016A, 0x016B }, +{ 0x016C, 0x016C, 0x016D }, +{ 0x016D, 0x016C, 0x016D }, +{ 0x016E, 0x016E, 0x016F }, +{ 0x016F, 0x016E, 0x016F }, +{ 0x0170, 0x0170, 0x0171 }, +{ 0x0171, 0x0170, 0x0171 }, +{ 0x0172, 0x0172, 0x0173 }, +{ 0x0173, 0x0172, 0x0173 }, +{ 0x0174, 0x0174, 0x0175 }, +{ 0x0175, 0x0174, 0x0175 }, +{ 0x0176, 0x0176, 0x0177 }, +{ 0x0177, 0x0176, 0x0177 }, +{ 0x0178, 0x0178, 0x00FF }, +{ 0x0179, 0x0179, 0x017A }, +{ 0x017A, 0x0179, 0x017A }, +{ 0x017B, 0x017B, 0x017C }, +{ 0x017C, 0x017B, 0x017C }, +{ 0x017D, 0x017D, 0x017E }, +{ 0x017E, 0x017D, 0x017E }, +{ 0x017F, 0x0053, 0x017F }, +{ 0x0180, 0x0180, 0x0180 }, +{ 0x0181, 0x0181, 0x0253 }, +{ 0x0182, 0x0182, 0x0183 }, +{ 0x0183, 0x0182, 0x0183 }, +{ 0x0184, 0x0184, 0x0185 }, +{ 0x0185, 0x0184, 0x0185 }, +{ 0x0186, 0x0186, 0x0254 }, +{ 0x0187, 0x0187, 0x0188 }, +{ 0x0188, 0x0187, 0x0188 }, +{ 0x0189, 0x0189, 0x0256 }, +{ 0x018A, 0x018A, 0x0257 }, +{ 0x018B, 0x018B, 0x018C }, +{ 0x018C, 0x018B, 0x018C }, +{ 0x018D, 0x018D, 0x018D }, +{ 0x018E, 0x018E, 0x01DD }, +{ 0x018F, 0x018F, 0x0259 }, +{ 0x0190, 0x0190, 0x025B }, +{ 0x0191, 0x0191, 0x0192 }, +{ 0x0192, 0x0191, 0x0192 }, +{ 0x0193, 0x0193, 0x0260 }, +{ 0x0194, 0x0194, 0x0263 }, +{ 0x0195, 0x01F6, 0x0195 }, +{ 0x0196, 0x0196, 0x0269 }, +{ 0x0197, 0x0197, 0x0268 }, +{ 0x0198, 0x0198, 0x0199 }, +{ 0x0199, 0x0198, 0x0199 }, +{ 0x019A, 0x023D, 0x019A }, +{ 0x019B, 0x019B, 0x019B }, +{ 0x019C, 0x019C, 0x026F }, +{ 0x019D, 0x019D, 0x0272 }, +{ 0x019E, 0x0220, 0x019E }, +{ 0x019F, 0x019F, 0x0275 }, +{ 0x01A0, 0x01A0, 0x01A1 }, +{ 0x01A1, 0x01A0, 0x01A1 }, +{ 0x01A2, 0x01A2, 0x01A3 }, +{ 0x01A3, 0x01A2, 0x01A3 }, +{ 0x01A4, 0x01A4, 0x01A5 }, +{ 0x01A5, 0x01A4, 0x01A5 }, +{ 0x01A6, 0x01A6, 0x0280 }, +{ 0x01A7, 0x01A7, 0x01A8 }, +{ 0x01A8, 0x01A7, 0x01A8 }, +{ 0x01A9, 0x01A9, 0x0283 }, +{ 0x01AA, 0x01AA, 0x01AA }, +{ 0x01AB, 0x01AB, 0x01AB }, +{ 0x01AC, 0x01AC, 0x01AD }, +{ 0x01AD, 0x01AC, 0x01AD }, +{ 0x01AE, 0x01AE, 0x0288 }, +{ 0x01AF, 0x01AF, 0x01B0 }, +{ 0x01B0, 0x01AF, 0x01B0 }, +{ 0x01B1, 0x01B1, 0x028A }, +{ 0x01B2, 0x01B2, 0x028B }, +{ 0x01B3, 0x01B3, 0x01B4 }, +{ 0x01B4, 0x01B3, 0x01B4 }, +{ 0x01B5, 0x01B5, 0x01B6 }, +{ 0x01B6, 0x01B5, 0x01B6 }, +{ 0x01B7, 0x01B7, 0x0292 }, +{ 0x01B8, 0x01B8, 0x01B9 }, +{ 0x01B9, 0x01B8, 0x01B9 }, +{ 0x01BA, 0x01BA, 0x01BA }, +{ 0x01BB, 0x01BB, 0x01BB }, +{ 0x01BC, 0x01BC, 0x01BD }, +{ 0x01BD, 0x01BC, 0x01BD }, +{ 0x01BE, 0x01BE, 0x01BE }, +{ 0x01BF, 0x01F7, 0x01BF }, +{ 0x01C0, 0x01C0, 0x01C0 }, +{ 0x01C1, 0x01C1, 0x01C1 }, +{ 0x01C2, 0x01C2, 0x01C2 }, +{ 0x01C3, 0x01C3, 0x01C3 }, +{ 0x01C4, 0x01C4, 0x01C6 }, +{ 0x01C5, 0x01C4, 0x01C6 }, +{ 0x01C6, 0x01C4, 0x01C6 }, +{ 0x01C7, 0x01C7, 0x01C9 }, +{ 0x01C8, 0x01C7, 0x01C9 }, +{ 0x01C9, 0x01C7, 0x01C9 }, +{ 0x01CA, 0x01CA, 0x01CC }, +{ 0x01CB, 0x01CA, 0x01CC }, +{ 0x01CC, 0x01CA, 0x01CC }, +{ 0x01CD, 0x01CD, 0x01CE }, +{ 0x01CE, 0x01CD, 0x01CE }, +{ 0x01CF, 0x01CF, 0x01D0 }, +{ 0x01D0, 0x01CF, 0x01D0 }, +{ 0x01D1, 0x01D1, 0x01D2 }, +{ 0x01D2, 0x01D1, 0x01D2 }, +{ 0x01D3, 0x01D3, 0x01D4 }, +{ 0x01D4, 0x01D3, 0x01D4 }, +{ 0x01D5, 0x01D5, 0x01D6 }, +{ 0x01D6, 0x01D5, 0x01D6 }, +{ 0x01D7, 0x01D7, 0x01D8 }, +{ 0x01D8, 0x01D7, 0x01D8 }, +{ 0x01D9, 0x01D9, 0x01DA }, +{ 0x01DA, 0x01D9, 0x01DA }, +{ 0x01DB, 0x01DB, 0x01DC }, +{ 0x01DC, 0x01DB, 0x01DC }, +{ 0x01DD, 0x018E, 0x01DD }, +{ 0x01DE, 0x01DE, 0x01DF }, +{ 0x01DF, 0x01DE, 0x01DF }, +{ 0x01E0, 0x01E0, 0x01E1 }, +{ 0x01E1, 0x01E0, 0x01E1 }, +{ 0x01E2, 0x01E2, 0x01E3 }, +{ 0x01E3, 0x01E2, 0x01E3 }, +{ 0x01E4, 0x01E4, 0x01E5 }, +{ 0x01E5, 0x01E4, 0x01E5 }, +{ 0x01E6, 0x01E6, 0x01E7 }, +{ 0x01E7, 0x01E6, 0x01E7 }, +{ 0x01E8, 0x01E8, 0x01E9 }, +{ 0x01E9, 0x01E8, 0x01E9 }, +{ 0x01EA, 0x01EA, 0x01EB }, +{ 0x01EB, 0x01EA, 0x01EB }, +{ 0x01EC, 0x01EC, 0x01ED }, +{ 0x01ED, 0x01EC, 0x01ED }, +{ 0x01EE, 0x01EE, 0x01EF }, +{ 0x01EF, 0x01EE, 0x01EF }, +{ 0x01F0, 0x01F0, 0x01F0 }, +{ 0x01F1, 0x01F1, 0x01F3 }, +{ 0x01F2, 0x01F1, 0x01F3 }, +{ 0x01F3, 0x01F1, 0x01F3 }, +{ 0x01F4, 0x01F4, 0x01F5 }, +{ 0x01F5, 0x01F4, 0x01F5 }, +{ 0x01F6, 0x01F6, 0x0195 }, +{ 0x01F7, 0x01F7, 0x01BF }, +{ 0x01F8, 0x01F8, 0x01F9 }, +{ 0x01F9, 0x01F8, 0x01F9 }, +{ 0x01FA, 0x01FA, 0x01FB }, +{ 0x01FB, 0x01FA, 0x01FB }, +{ 0x01FC, 0x01FC, 0x01FD }, +{ 0x01FD, 0x01FC, 0x01FD }, +{ 0x01FE, 0x01FE, 0x01FF }, +{ 0x01FF, 0x01FE, 0x01FF }, +{ 0x0200, 0x0200, 0x0201 }, +{ 0x0201, 0x0200, 0x0201 }, +{ 0x0202, 0x0202, 0x0203 }, +{ 0x0203, 0x0202, 0x0203 }, +{ 0x0204, 0x0204, 0x0205 }, +{ 0x0205, 0x0204, 0x0205 }, +{ 0x0206, 0x0206, 0x0207 }, +{ 0x0207, 0x0206, 0x0207 }, +{ 0x0208, 0x0208, 0x0209 }, +{ 0x0209, 0x0208, 0x0209 }, +{ 0x020A, 0x020A, 0x020B }, +{ 0x020B, 0x020A, 0x020B }, +{ 0x020C, 0x020C, 0x020D }, +{ 0x020D, 0x020C, 0x020D }, +{ 0x020E, 0x020E, 0x020F }, +{ 0x020F, 0x020E, 0x020F }, +{ 0x0210, 0x0210, 0x0211 }, +{ 0x0211, 0x0210, 0x0211 }, +{ 0x0212, 0x0212, 0x0213 }, +{ 0x0213, 0x0212, 0x0213 }, +{ 0x0214, 0x0214, 0x0215 }, +{ 0x0215, 0x0214, 0x0215 }, +{ 0x0216, 0x0216, 0x0217 }, +{ 0x0217, 0x0216, 0x0217 }, +{ 0x0218, 0x0218, 0x0219 }, +{ 0x0219, 0x0218, 0x0219 }, +{ 0x021A, 0x021A, 0x021B }, +{ 0x021B, 0x021A, 0x021B }, +{ 0x021C, 0x021C, 0x021D }, +{ 0x021D, 0x021C, 0x021D }, +{ 0x021E, 0x021E, 0x021F }, +{ 0x021F, 0x021E, 0x021F }, +{ 0x0220, 0x0220, 0x019E }, +{ 0x0221, 0x0221, 0x0221 }, +{ 0x0222, 0x0222, 0x0223 }, +{ 0x0223, 0x0222, 0x0223 }, +{ 0x0224, 0x0224, 0x0225 }, +{ 0x0225, 0x0224, 0x0225 }, +{ 0x0226, 0x0226, 0x0227 }, +{ 0x0227, 0x0226, 0x0227 }, +{ 0x0228, 0x0228, 0x0229 }, +{ 0x0229, 0x0228, 0x0229 }, +{ 0x022A, 0x022A, 0x022B }, +{ 0x022B, 0x022A, 0x022B }, +{ 0x022C, 0x022C, 0x022D }, +{ 0x022D, 0x022C, 0x022D }, +{ 0x022E, 0x022E, 0x022F }, +{ 0x022F, 0x022E, 0x022F }, +{ 0x0230, 0x0230, 0x0231 }, +{ 0x0231, 0x0230, 0x0231 }, +{ 0x0232, 0x0232, 0x0233 }, +{ 0x0233, 0x0232, 0x0233 }, +{ 0x0234, 0x0234, 0x0234 }, +{ 0x0235, 0x0235, 0x0235 }, +{ 0x0236, 0x0236, 0x0236 }, +{ 0x0237, 0x0237, 0x0237 }, +{ 0x0238, 0x0238, 0x0238 }, +{ 0x0239, 0x0239, 0x0239 }, +{ 0x023A, 0x023A, 0x023A }, +{ 0x023B, 0x023B, 0x023C }, +{ 0x023C, 0x023B, 0x023C }, +{ 0x023D, 0x023D, 0x019A }, +{ 0x023E, 0x023E, 0x023E }, +{ 0x023F, 0x023F, 0x023F }, +{ 0x0240, 0x0240, 0x0240 }, +{ 0x0241, 0x0241, 0x0294 }, +{ 0x0250, 0x0250, 0x0250 }, +{ 0x0251, 0x0251, 0x0251 }, +{ 0x0252, 0x0252, 0x0252 }, +{ 0x0253, 0x0181, 0x0253 }, +{ 0x0254, 0x0186, 0x0254 }, +{ 0x0255, 0x0255, 0x0255 }, +{ 0x0256, 0x0189, 0x0256 }, +{ 0x0257, 0x018A, 0x0257 }, +{ 0x0258, 0x0258, 0x0258 }, +{ 0x0259, 0x018F, 0x0259 }, +{ 0x025A, 0x025A, 0x025A }, +{ 0x025B, 0x0190, 0x025B }, +{ 0x025C, 0x025C, 0x025C }, +{ 0x025D, 0x025D, 0x025D }, +{ 0x025E, 0x025E, 0x025E }, +{ 0x025F, 0x025F, 0x025F }, +{ 0x0260, 0x0193, 0x0260 }, +{ 0x0261, 0x0261, 0x0261 }, +{ 0x0262, 0x0262, 0x0262 }, +{ 0x0263, 0x0194, 0x0263 }, +{ 0x0264, 0x0264, 0x0264 }, +{ 0x0265, 0x0265, 0x0265 }, +{ 0x0266, 0x0266, 0x0266 }, +{ 0x0267, 0x0267, 0x0267 }, +{ 0x0268, 0x0197, 0x0268 }, +{ 0x0269, 0x0196, 0x0269 }, +{ 0x026A, 0x026A, 0x026A }, +{ 0x026B, 0x026B, 0x026B }, +{ 0x026C, 0x026C, 0x026C }, +{ 0x026D, 0x026D, 0x026D }, +{ 0x026E, 0x026E, 0x026E }, +{ 0x026F, 0x019C, 0x026F }, +{ 0x0270, 0x0270, 0x0270 }, +{ 0x0271, 0x0271, 0x0271 }, +{ 0x0272, 0x019D, 0x0272 }, +{ 0x0273, 0x0273, 0x0273 }, +{ 0x0274, 0x0274, 0x0274 }, +{ 0x0275, 0x019F, 0x0275 }, +{ 0x0276, 0x0276, 0x0276 }, +{ 0x0277, 0x0277, 0x0277 }, +{ 0x0278, 0x0278, 0x0278 }, +{ 0x0279, 0x0279, 0x0279 }, +{ 0x027A, 0x027A, 0x027A }, +{ 0x027B, 0x027B, 0x027B }, +{ 0x027C, 0x027C, 0x027C }, +{ 0x027D, 0x027D, 0x027D }, +{ 0x027E, 0x027E, 0x027E }, +{ 0x027F, 0x027F, 0x027F }, +{ 0x0280, 0x01A6, 0x0280 }, +{ 0x0281, 0x0281, 0x0281 }, +{ 0x0282, 0x0282, 0x0282 }, +{ 0x0283, 0x01A9, 0x0283 }, +{ 0x0284, 0x0284, 0x0284 }, +{ 0x0285, 0x0285, 0x0285 }, +{ 0x0286, 0x0286, 0x0286 }, +{ 0x0287, 0x0287, 0x0287 }, +{ 0x0288, 0x01AE, 0x0288 }, +{ 0x0289, 0x0289, 0x0289 }, +{ 0x028A, 0x01B1, 0x028A }, +{ 0x028B, 0x01B2, 0x028B }, +{ 0x028C, 0x028C, 0x028C }, +{ 0x028D, 0x028D, 0x028D }, +{ 0x028E, 0x028E, 0x028E }, +{ 0x028F, 0x028F, 0x028F }, +{ 0x0290, 0x0290, 0x0290 }, +{ 0x0291, 0x0291, 0x0291 }, +{ 0x0292, 0x01B7, 0x0292 }, +{ 0x0293, 0x0293, 0x0293 }, +{ 0x0294, 0x0241, 0x0294 }, +{ 0x0295, 0x0295, 0x0295 }, +{ 0x0296, 0x0296, 0x0296 }, +{ 0x0297, 0x0297, 0x0297 }, +{ 0x0298, 0x0298, 0x0298 }, +{ 0x0299, 0x0299, 0x0299 }, +{ 0x029A, 0x029A, 0x029A }, +{ 0x029B, 0x029B, 0x029B }, +{ 0x029C, 0x029C, 0x029C }, +{ 0x029D, 0x029D, 0x029D }, +{ 0x029E, 0x029E, 0x029E }, +{ 0x029F, 0x029F, 0x029F }, +{ 0x02A0, 0x02A0, 0x02A0 }, +{ 0x02A1, 0x02A1, 0x02A1 }, +{ 0x02A2, 0x02A2, 0x02A2 }, +{ 0x02A3, 0x02A3, 0x02A3 }, +{ 0x02A4, 0x02A4, 0x02A4 }, +{ 0x02A5, 0x02A5, 0x02A5 }, +{ 0x02A6, 0x02A6, 0x02A6 }, +{ 0x02A7, 0x02A7, 0x02A7 }, +{ 0x02A8, 0x02A8, 0x02A8 }, +{ 0x02A9, 0x02A9, 0x02A9 }, +{ 0x02AA, 0x02AA, 0x02AA }, +{ 0x02AB, 0x02AB, 0x02AB }, +{ 0x02AC, 0x02AC, 0x02AC }, +{ 0x02AD, 0x02AD, 0x02AD }, +{ 0x02AE, 0x02AE, 0x02AE }, +{ 0x02AF, 0x02AF, 0x02AF }, +{ 0x02B0, 0x02B0, 0x02B0 }, +{ 0x02B1, 0x02B1, 0x02B1 }, +{ 0x02B2, 0x02B2, 0x02B2 }, +{ 0x02B3, 0x02B3, 0x02B3 }, +{ 0x02B4, 0x02B4, 0x02B4 }, +{ 0x02B5, 0x02B5, 0x02B5 }, +{ 0x02B6, 0x02B6, 0x02B6 }, +{ 0x02B7, 0x02B7, 0x02B7 }, +{ 0x02B8, 0x02B8, 0x02B8 }, +{ 0x02B9, 0x02B9, 0x02B9 }, +{ 0x02BA, 0x02BA, 0x02BA }, +{ 0x02BB, 0x02BB, 0x02BB }, +{ 0x02BC, 0x02BC, 0x02BC }, +{ 0x02BD, 0x02BD, 0x02BD }, +{ 0x02BE, 0x02BE, 0x02BE }, +{ 0x02BF, 0x02BF, 0x02BF }, +{ 0x02C0, 0x02C0, 0x02C0 }, +{ 0x02C1, 0x02C1, 0x02C1 }, +{ 0x02C6, 0x02C6, 0x02C6 }, +{ 0x02C7, 0x02C7, 0x02C7 }, +{ 0x02C8, 0x02C8, 0x02C8 }, +{ 0x02C9, 0x02C9, 0x02C9 }, +{ 0x02CA, 0x02CA, 0x02CA }, +{ 0x02CB, 0x02CB, 0x02CB }, +{ 0x02CC, 0x02CC, 0x02CC }, +{ 0x02CD, 0x02CD, 0x02CD }, +{ 0x02CE, 0x02CE, 0x02CE }, +{ 0x02CF, 0x02CF, 0x02CF }, +{ 0x02D0, 0x02D0, 0x02D0 }, +{ 0x02D1, 0x02D1, 0x02D1 }, +{ 0x02E0, 0x02E0, 0x02E0 }, +{ 0x02E1, 0x02E1, 0x02E1 }, +{ 0x02E2, 0x02E2, 0x02E2 }, +{ 0x02E3, 0x02E3, 0x02E3 }, +{ 0x02E4, 0x02E4, 0x02E4 }, +{ 0x02EE, 0x02EE, 0x02EE }, +{ 0x0300, 0x0300, 0x0300 }, +{ 0x0301, 0x0301, 0x0301 }, +{ 0x0302, 0x0302, 0x0302 }, +{ 0x0303, 0x0303, 0x0303 }, +{ 0x0304, 0x0304, 0x0304 }, +{ 0x0305, 0x0305, 0x0305 }, +{ 0x0306, 0x0306, 0x0306 }, +{ 0x0307, 0x0307, 0x0307 }, +{ 0x0308, 0x0308, 0x0308 }, +{ 0x0309, 0x0309, 0x0309 }, +{ 0x030A, 0x030A, 0x030A }, +{ 0x030B, 0x030B, 0x030B }, +{ 0x030C, 0x030C, 0x030C }, +{ 0x030D, 0x030D, 0x030D }, +{ 0x030E, 0x030E, 0x030E }, +{ 0x030F, 0x030F, 0x030F }, +{ 0x0310, 0x0310, 0x0310 }, +{ 0x0311, 0x0311, 0x0311 }, +{ 0x0312, 0x0312, 0x0312 }, +{ 0x0313, 0x0313, 0x0313 }, +{ 0x0314, 0x0314, 0x0314 }, +{ 0x0315, 0x0315, 0x0315 }, +{ 0x0316, 0x0316, 0x0316 }, +{ 0x0317, 0x0317, 0x0317 }, +{ 0x0318, 0x0318, 0x0318 }, +{ 0x0319, 0x0319, 0x0319 }, +{ 0x031A, 0x031A, 0x031A }, +{ 0x031B, 0x031B, 0x031B }, +{ 0x031C, 0x031C, 0x031C }, +{ 0x031D, 0x031D, 0x031D }, +{ 0x031E, 0x031E, 0x031E }, +{ 0x031F, 0x031F, 0x031F }, +{ 0x0320, 0x0320, 0x0320 }, +{ 0x0321, 0x0321, 0x0321 }, +{ 0x0322, 0x0322, 0x0322 }, +{ 0x0323, 0x0323, 0x0323 }, +{ 0x0324, 0x0324, 0x0324 }, +{ 0x0325, 0x0325, 0x0325 }, +{ 0x0326, 0x0326, 0x0326 }, +{ 0x0327, 0x0327, 0x0327 }, +{ 0x0328, 0x0328, 0x0328 }, +{ 0x0329, 0x0329, 0x0329 }, +{ 0x032A, 0x032A, 0x032A }, +{ 0x032B, 0x032B, 0x032B }, +{ 0x032C, 0x032C, 0x032C }, +{ 0x032D, 0x032D, 0x032D }, +{ 0x032E, 0x032E, 0x032E }, +{ 0x032F, 0x032F, 0x032F }, +{ 0x0330, 0x0330, 0x0330 }, +{ 0x0331, 0x0331, 0x0331 }, +{ 0x0332, 0x0332, 0x0332 }, +{ 0x0333, 0x0333, 0x0333 }, +{ 0x0334, 0x0334, 0x0334 }, +{ 0x0335, 0x0335, 0x0335 }, +{ 0x0336, 0x0336, 0x0336 }, +{ 0x0337, 0x0337, 0x0337 }, +{ 0x0338, 0x0338, 0x0338 }, +{ 0x0339, 0x0339, 0x0339 }, +{ 0x033A, 0x033A, 0x033A }, +{ 0x033B, 0x033B, 0x033B }, +{ 0x033C, 0x033C, 0x033C }, +{ 0x033D, 0x033D, 0x033D }, +{ 0x033E, 0x033E, 0x033E }, +{ 0x033F, 0x033F, 0x033F }, +{ 0x0340, 0x0340, 0x0340 }, +{ 0x0341, 0x0341, 0x0341 }, +{ 0x0342, 0x0342, 0x0342 }, +{ 0x0343, 0x0343, 0x0343 }, +{ 0x0344, 0x0344, 0x0344 }, +{ 0x0345, 0x0399, 0x0345 }, +{ 0x0346, 0x0346, 0x0346 }, +{ 0x0347, 0x0347, 0x0347 }, +{ 0x0348, 0x0348, 0x0348 }, +{ 0x0349, 0x0349, 0x0349 }, +{ 0x034A, 0x034A, 0x034A }, +{ 0x034B, 0x034B, 0x034B }, +{ 0x034C, 0x034C, 0x034C }, +{ 0x034D, 0x034D, 0x034D }, +{ 0x034E, 0x034E, 0x034E }, +{ 0x034F, 0x034F, 0x034F }, +{ 0x0350, 0x0350, 0x0350 }, +{ 0x0351, 0x0351, 0x0351 }, +{ 0x0352, 0x0352, 0x0352 }, +{ 0x0353, 0x0353, 0x0353 }, +{ 0x0354, 0x0354, 0x0354 }, +{ 0x0355, 0x0355, 0x0355 }, +{ 0x0356, 0x0356, 0x0356 }, +{ 0x0357, 0x0357, 0x0357 }, +{ 0x0358, 0x0358, 0x0358 }, +{ 0x0359, 0x0359, 0x0359 }, +{ 0x035A, 0x035A, 0x035A }, +{ 0x035B, 0x035B, 0x035B }, +{ 0x035C, 0x035C, 0x035C }, +{ 0x035D, 0x035D, 0x035D }, +{ 0x035E, 0x035E, 0x035E }, +{ 0x035F, 0x035F, 0x035F }, +{ 0x0360, 0x0360, 0x0360 }, +{ 0x0361, 0x0361, 0x0361 }, +{ 0x0362, 0x0362, 0x0362 }, +{ 0x0363, 0x0363, 0x0363 }, +{ 0x0364, 0x0364, 0x0364 }, +{ 0x0365, 0x0365, 0x0365 }, +{ 0x0366, 0x0366, 0x0366 }, +{ 0x0367, 0x0367, 0x0367 }, +{ 0x0368, 0x0368, 0x0368 }, +{ 0x0369, 0x0369, 0x0369 }, +{ 0x036A, 0x036A, 0x036A }, +{ 0x036B, 0x036B, 0x036B }, +{ 0x036C, 0x036C, 0x036C }, +{ 0x036D, 0x036D, 0x036D }, +{ 0x036E, 0x036E, 0x036E }, +{ 0x036F, 0x036F, 0x036F }, +{ 0x037A, 0x037A, 0x037A }, +{ 0x0386, 0x0386, 0x03AC }, +{ 0x0388, 0x0388, 0x03AD }, +{ 0x0389, 0x0389, 0x03AE }, +{ 0x038A, 0x038A, 0x03AF }, +{ 0x038C, 0x038C, 0x03CC }, +{ 0x038E, 0x038E, 0x03CD }, +{ 0x038F, 0x038F, 0x03CE }, +{ 0x0390, 0x0390, 0x0390 }, +{ 0x0391, 0x0391, 0x03B1 }, +{ 0x0392, 0x0392, 0x03B2 }, +{ 0x0393, 0x0393, 0x03B3 }, +{ 0x0394, 0x0394, 0x03B4 }, +{ 0x0395, 0x0395, 0x03B5 }, +{ 0x0396, 0x0396, 0x03B6 }, +{ 0x0397, 0x0397, 0x03B7 }, +{ 0x0398, 0x0398, 0x03B8 }, +{ 0x0399, 0x0399, 0x03B9 }, +{ 0x039A, 0x039A, 0x03BA }, +{ 0x039B, 0x039B, 0x03BB }, +{ 0x039C, 0x039C, 0x03BC }, +{ 0x039D, 0x039D, 0x03BD }, +{ 0x039E, 0x039E, 0x03BE }, +{ 0x039F, 0x039F, 0x03BF }, +{ 0x03A0, 0x03A0, 0x03C0 }, +{ 0x03A1, 0x03A1, 0x03C1 }, +{ 0x03A3, 0x03A3, 0x03C3 }, +{ 0x03A4, 0x03A4, 0x03C4 }, +{ 0x03A5, 0x03A5, 0x03C5 }, +{ 0x03A6, 0x03A6, 0x03C6 }, +{ 0x03A7, 0x03A7, 0x03C7 }, +{ 0x03A8, 0x03A8, 0x03C8 }, +{ 0x03A9, 0x03A9, 0x03C9 }, +{ 0x03AA, 0x03AA, 0x03CA }, +{ 0x03AB, 0x03AB, 0x03CB }, +{ 0x03AC, 0x0386, 0x03AC }, +{ 0x03AD, 0x0388, 0x03AD }, +{ 0x03AE, 0x0389, 0x03AE }, +{ 0x03AF, 0x038A, 0x03AF }, +{ 0x03B0, 0x03B0, 0x03B0 }, +{ 0x03B1, 0x0391, 0x03B1 }, +{ 0x03B2, 0x0392, 0x03B2 }, +{ 0x03B3, 0x0393, 0x03B3 }, +{ 0x03B4, 0x0394, 0x03B4 }, +{ 0x03B5, 0x0395, 0x03B5 }, +{ 0x03B6, 0x0396, 0x03B6 }, +{ 0x03B7, 0x0397, 0x03B7 }, +{ 0x03B8, 0x0398, 0x03B8 }, +{ 0x03B9, 0x0399, 0x03B9 }, +{ 0x03BA, 0x039A, 0x03BA }, +{ 0x03BB, 0x039B, 0x03BB }, +{ 0x03BC, 0x039C, 0x03BC }, +{ 0x03BD, 0x039D, 0x03BD }, +{ 0x03BE, 0x039E, 0x03BE }, +{ 0x03BF, 0x039F, 0x03BF }, +{ 0x03C0, 0x03A0, 0x03C0 }, +{ 0x03C1, 0x03A1, 0x03C1 }, +{ 0x03C2, 0x03A3, 0x03C2 }, +{ 0x03C3, 0x03A3, 0x03C3 }, +{ 0x03C4, 0x03A4, 0x03C4 }, +{ 0x03C5, 0x03A5, 0x03C5 }, +{ 0x03C6, 0x03A6, 0x03C6 }, +{ 0x03C7, 0x03A7, 0x03C7 }, +{ 0x03C8, 0x03A8, 0x03C8 }, +{ 0x03C9, 0x03A9, 0x03C9 }, +{ 0x03CA, 0x03AA, 0x03CA }, +{ 0x03CB, 0x03AB, 0x03CB }, +{ 0x03CC, 0x038C, 0x03CC }, +{ 0x03CD, 0x038E, 0x03CD }, +{ 0x03CE, 0x038F, 0x03CE }, +{ 0x03D0, 0x0392, 0x03D0 }, +{ 0x03D1, 0x0398, 0x03D1 }, +{ 0x03D2, 0x03D2, 0x03D2 }, +{ 0x03D3, 0x03D3, 0x03D3 }, +{ 0x03D4, 0x03D4, 0x03D4 }, +{ 0x03D5, 0x03A6, 0x03D5 }, +{ 0x03D6, 0x03A0, 0x03D6 }, +{ 0x03D7, 0x03D7, 0x03D7 }, +{ 0x03D8, 0x03D8, 0x03D9 }, +{ 0x03D9, 0x03D8, 0x03D9 }, +{ 0x03DA, 0x03DA, 0x03DB }, +{ 0x03DB, 0x03DA, 0x03DB }, +{ 0x03DC, 0x03DC, 0x03DD }, +{ 0x03DD, 0x03DC, 0x03DD }, +{ 0x03DE, 0x03DE, 0x03DF }, +{ 0x03DF, 0x03DE, 0x03DF }, +{ 0x03E0, 0x03E0, 0x03E1 }, +{ 0x03E1, 0x03E0, 0x03E1 }, +{ 0x03E2, 0x03E2, 0x03E3 }, +{ 0x03E3, 0x03E2, 0x03E3 }, +{ 0x03E4, 0x03E4, 0x03E5 }, +{ 0x03E5, 0x03E4, 0x03E5 }, +{ 0x03E6, 0x03E6, 0x03E7 }, +{ 0x03E7, 0x03E6, 0x03E7 }, +{ 0x03E8, 0x03E8, 0x03E9 }, +{ 0x03E9, 0x03E8, 0x03E9 }, +{ 0x03EA, 0x03EA, 0x03EB }, +{ 0x03EB, 0x03EA, 0x03EB }, +{ 0x03EC, 0x03EC, 0x03ED }, +{ 0x03ED, 0x03EC, 0x03ED }, +{ 0x03EE, 0x03EE, 0x03EF }, +{ 0x03EF, 0x03EE, 0x03EF }, +{ 0x03F0, 0x039A, 0x03F0 }, +{ 0x03F1, 0x03A1, 0x03F1 }, +{ 0x03F2, 0x03F9, 0x03F2 }, +{ 0x03F3, 0x03F3, 0x03F3 }, +{ 0x03F4, 0x03F4, 0x03B8 }, +{ 0x03F5, 0x0395, 0x03F5 }, +{ 0x03F7, 0x03F7, 0x03F8 }, +{ 0x03F8, 0x03F7, 0x03F8 }, +{ 0x03F9, 0x03F9, 0x03F2 }, +{ 0x03FA, 0x03FA, 0x03FB }, +{ 0x03FB, 0x03FA, 0x03FB }, +{ 0x03FC, 0x03FC, 0x03FC }, +{ 0x03FD, 0x03FD, 0x03FD }, +{ 0x03FE, 0x03FE, 0x03FE }, +{ 0x03FF, 0x03FF, 0x03FF }, +{ 0x0400, 0x0400, 0x0450 }, +{ 0x0401, 0x0401, 0x0451 }, +{ 0x0402, 0x0402, 0x0452 }, +{ 0x0403, 0x0403, 0x0453 }, +{ 0x0404, 0x0404, 0x0454 }, +{ 0x0405, 0x0405, 0x0455 }, +{ 0x0406, 0x0406, 0x0456 }, +{ 0x0407, 0x0407, 0x0457 }, +{ 0x0408, 0x0408, 0x0458 }, +{ 0x0409, 0x0409, 0x0459 }, +{ 0x040A, 0x040A, 0x045A }, +{ 0x040B, 0x040B, 0x045B }, +{ 0x040C, 0x040C, 0x045C }, +{ 0x040D, 0x040D, 0x045D }, +{ 0x040E, 0x040E, 0x045E }, +{ 0x040F, 0x040F, 0x045F }, +{ 0x0410, 0x0410, 0x0430 }, +{ 0x0411, 0x0411, 0x0431 }, +{ 0x0412, 0x0412, 0x0432 }, +{ 0x0413, 0x0413, 0x0433 }, +{ 0x0414, 0x0414, 0x0434 }, +{ 0x0415, 0x0415, 0x0435 }, +{ 0x0416, 0x0416, 0x0436 }, +{ 0x0417, 0x0417, 0x0437 }, +{ 0x0418, 0x0418, 0x0438 }, +{ 0x0419, 0x0419, 0x0439 }, +{ 0x041A, 0x041A, 0x043A }, +{ 0x041B, 0x041B, 0x043B }, +{ 0x041C, 0x041C, 0x043C }, +{ 0x041D, 0x041D, 0x043D }, +{ 0x041E, 0x041E, 0x043E }, +{ 0x041F, 0x041F, 0x043F }, +{ 0x0420, 0x0420, 0x0440 }, +{ 0x0421, 0x0421, 0x0441 }, +{ 0x0422, 0x0422, 0x0442 }, +{ 0x0423, 0x0423, 0x0443 }, +{ 0x0424, 0x0424, 0x0444 }, +{ 0x0425, 0x0425, 0x0445 }, +{ 0x0426, 0x0426, 0x0446 }, +{ 0x0427, 0x0427, 0x0447 }, +{ 0x0428, 0x0428, 0x0448 }, +{ 0x0429, 0x0429, 0x0449 }, +{ 0x042A, 0x042A, 0x044A }, +{ 0x042B, 0x042B, 0x044B }, +{ 0x042C, 0x042C, 0x044C }, +{ 0x042D, 0x042D, 0x044D }, +{ 0x042E, 0x042E, 0x044E }, +{ 0x042F, 0x042F, 0x044F }, +{ 0x0430, 0x0410, 0x0430 }, +{ 0x0431, 0x0411, 0x0431 }, +{ 0x0432, 0x0412, 0x0432 }, +{ 0x0433, 0x0413, 0x0433 }, +{ 0x0434, 0x0414, 0x0434 }, +{ 0x0435, 0x0415, 0x0435 }, +{ 0x0436, 0x0416, 0x0436 }, +{ 0x0437, 0x0417, 0x0437 }, +{ 0x0438, 0x0418, 0x0438 }, +{ 0x0439, 0x0419, 0x0439 }, +{ 0x043A, 0x041A, 0x043A }, +{ 0x043B, 0x041B, 0x043B }, +{ 0x043C, 0x041C, 0x043C }, +{ 0x043D, 0x041D, 0x043D }, +{ 0x043E, 0x041E, 0x043E }, +{ 0x043F, 0x041F, 0x043F }, +{ 0x0440, 0x0420, 0x0440 }, +{ 0x0441, 0x0421, 0x0441 }, +{ 0x0442, 0x0422, 0x0442 }, +{ 0x0443, 0x0423, 0x0443 }, +{ 0x0444, 0x0424, 0x0444 }, +{ 0x0445, 0x0425, 0x0445 }, +{ 0x0446, 0x0426, 0x0446 }, +{ 0x0447, 0x0427, 0x0447 }, +{ 0x0448, 0x0428, 0x0448 }, +{ 0x0449, 0x0429, 0x0449 }, +{ 0x044A, 0x042A, 0x044A }, +{ 0x044B, 0x042B, 0x044B }, +{ 0x044C, 0x042C, 0x044C }, +{ 0x044D, 0x042D, 0x044D }, +{ 0x044E, 0x042E, 0x044E }, +{ 0x044F, 0x042F, 0x044F }, +{ 0x0450, 0x0400, 0x0450 }, +{ 0x0451, 0x0401, 0x0451 }, +{ 0x0452, 0x0402, 0x0452 }, +{ 0x0453, 0x0403, 0x0453 }, +{ 0x0454, 0x0404, 0x0454 }, +{ 0x0455, 0x0405, 0x0455 }, +{ 0x0456, 0x0406, 0x0456 }, +{ 0x0457, 0x0407, 0x0457 }, +{ 0x0458, 0x0408, 0x0458 }, +{ 0x0459, 0x0409, 0x0459 }, +{ 0x045A, 0x040A, 0x045A }, +{ 0x045B, 0x040B, 0x045B }, +{ 0x045C, 0x040C, 0x045C }, +{ 0x045D, 0x040D, 0x045D }, +{ 0x045E, 0x040E, 0x045E }, +{ 0x045F, 0x040F, 0x045F }, +{ 0x0460, 0x0460, 0x0461 }, +{ 0x0461, 0x0460, 0x0461 }, +{ 0x0462, 0x0462, 0x0463 }, +{ 0x0463, 0x0462, 0x0463 }, +{ 0x0464, 0x0464, 0x0465 }, +{ 0x0465, 0x0464, 0x0465 }, +{ 0x0466, 0x0466, 0x0467 }, +{ 0x0467, 0x0466, 0x0467 }, +{ 0x0468, 0x0468, 0x0469 }, +{ 0x0469, 0x0468, 0x0469 }, +{ 0x046A, 0x046A, 0x046B }, +{ 0x046B, 0x046A, 0x046B }, +{ 0x046C, 0x046C, 0x046D }, +{ 0x046D, 0x046C, 0x046D }, +{ 0x046E, 0x046E, 0x046F }, +{ 0x046F, 0x046E, 0x046F }, +{ 0x0470, 0x0470, 0x0471 }, +{ 0x0471, 0x0470, 0x0471 }, +{ 0x0472, 0x0472, 0x0473 }, +{ 0x0473, 0x0472, 0x0473 }, +{ 0x0474, 0x0474, 0x0475 }, +{ 0x0475, 0x0474, 0x0475 }, +{ 0x0476, 0x0476, 0x0477 }, +{ 0x0477, 0x0476, 0x0477 }, +{ 0x0478, 0x0478, 0x0479 }, +{ 0x0479, 0x0478, 0x0479 }, +{ 0x047A, 0x047A, 0x047B }, +{ 0x047B, 0x047A, 0x047B }, +{ 0x047C, 0x047C, 0x047D }, +{ 0x047D, 0x047C, 0x047D }, +{ 0x047E, 0x047E, 0x047F }, +{ 0x047F, 0x047E, 0x047F }, +{ 0x0480, 0x0480, 0x0481 }, +{ 0x0481, 0x0480, 0x0481 }, +{ 0x0483, 0x0483, 0x0483 }, +{ 0x0484, 0x0484, 0x0484 }, +{ 0x0485, 0x0485, 0x0485 }, +{ 0x0486, 0x0486, 0x0486 }, +{ 0x048A, 0x048A, 0x048B }, +{ 0x048B, 0x048A, 0x048B }, +{ 0x048C, 0x048C, 0x048D }, +{ 0x048D, 0x048C, 0x048D }, +{ 0x048E, 0x048E, 0x048F }, +{ 0x048F, 0x048E, 0x048F }, +{ 0x0490, 0x0490, 0x0491 }, +{ 0x0491, 0x0490, 0x0491 }, +{ 0x0492, 0x0492, 0x0493 }, +{ 0x0493, 0x0492, 0x0493 }, +{ 0x0494, 0x0494, 0x0495 }, +{ 0x0495, 0x0494, 0x0495 }, +{ 0x0496, 0x0496, 0x0497 }, +{ 0x0497, 0x0496, 0x0497 }, +{ 0x0498, 0x0498, 0x0499 }, +{ 0x0499, 0x0498, 0x0499 }, +{ 0x049A, 0x049A, 0x049B }, +{ 0x049B, 0x049A, 0x049B }, +{ 0x049C, 0x049C, 0x049D }, +{ 0x049D, 0x049C, 0x049D }, +{ 0x049E, 0x049E, 0x049F }, +{ 0x049F, 0x049E, 0x049F }, +{ 0x04A0, 0x04A0, 0x04A1 }, +{ 0x04A1, 0x04A0, 0x04A1 }, +{ 0x04A2, 0x04A2, 0x04A3 }, +{ 0x04A3, 0x04A2, 0x04A3 }, +{ 0x04A4, 0x04A4, 0x04A5 }, +{ 0x04A5, 0x04A4, 0x04A5 }, +{ 0x04A6, 0x04A6, 0x04A7 }, +{ 0x04A7, 0x04A6, 0x04A7 }, +{ 0x04A8, 0x04A8, 0x04A9 }, +{ 0x04A9, 0x04A8, 0x04A9 }, +{ 0x04AA, 0x04AA, 0x04AB }, +{ 0x04AB, 0x04AA, 0x04AB }, +{ 0x04AC, 0x04AC, 0x04AD }, +{ 0x04AD, 0x04AC, 0x04AD }, +{ 0x04AE, 0x04AE, 0x04AF }, +{ 0x04AF, 0x04AE, 0x04AF }, +{ 0x04B0, 0x04B0, 0x04B1 }, +{ 0x04B1, 0x04B0, 0x04B1 }, +{ 0x04B2, 0x04B2, 0x04B3 }, +{ 0x04B3, 0x04B2, 0x04B3 }, +{ 0x04B4, 0x04B4, 0x04B5 }, +{ 0x04B5, 0x04B4, 0x04B5 }, +{ 0x04B6, 0x04B6, 0x04B7 }, +{ 0x04B7, 0x04B6, 0x04B7 }, +{ 0x04B8, 0x04B8, 0x04B9 }, +{ 0x04B9, 0x04B8, 0x04B9 }, +{ 0x04BA, 0x04BA, 0x04BB }, +{ 0x04BB, 0x04BA, 0x04BB }, +{ 0x04BC, 0x04BC, 0x04BD }, +{ 0x04BD, 0x04BC, 0x04BD }, +{ 0x04BE, 0x04BE, 0x04BF }, +{ 0x04BF, 0x04BE, 0x04BF }, +{ 0x04C0, 0x04C0, 0x04C0 }, +{ 0x04C1, 0x04C1, 0x04C2 }, +{ 0x04C2, 0x04C1, 0x04C2 }, +{ 0x04C3, 0x04C3, 0x04C4 }, +{ 0x04C4, 0x04C3, 0x04C4 }, +{ 0x04C5, 0x04C5, 0x04C6 }, +{ 0x04C6, 0x04C5, 0x04C6 }, +{ 0x04C7, 0x04C7, 0x04C8 }, +{ 0x04C8, 0x04C7, 0x04C8 }, +{ 0x04C9, 0x04C9, 0x04CA }, +{ 0x04CA, 0x04C9, 0x04CA }, +{ 0x04CB, 0x04CB, 0x04CC }, +{ 0x04CC, 0x04CB, 0x04CC }, +{ 0x04CD, 0x04CD, 0x04CE }, +{ 0x04CE, 0x04CD, 0x04CE }, +{ 0x04D0, 0x04D0, 0x04D1 }, +{ 0x04D1, 0x04D0, 0x04D1 }, +{ 0x04D2, 0x04D2, 0x04D3 }, +{ 0x04D3, 0x04D2, 0x04D3 }, +{ 0x04D4, 0x04D4, 0x04D5 }, +{ 0x04D5, 0x04D4, 0x04D5 }, +{ 0x04D6, 0x04D6, 0x04D7 }, +{ 0x04D7, 0x04D6, 0x04D7 }, +{ 0x04D8, 0x04D8, 0x04D9 }, +{ 0x04D9, 0x04D8, 0x04D9 }, +{ 0x04DA, 0x04DA, 0x04DB }, +{ 0x04DB, 0x04DA, 0x04DB }, +{ 0x04DC, 0x04DC, 0x04DD }, +{ 0x04DD, 0x04DC, 0x04DD }, +{ 0x04DE, 0x04DE, 0x04DF }, +{ 0x04DF, 0x04DE, 0x04DF }, +{ 0x04E0, 0x04E0, 0x04E1 }, +{ 0x04E1, 0x04E0, 0x04E1 }, +{ 0x04E2, 0x04E2, 0x04E3 }, +{ 0x04E3, 0x04E2, 0x04E3 }, +{ 0x04E4, 0x04E4, 0x04E5 }, +{ 0x04E5, 0x04E4, 0x04E5 }, +{ 0x04E6, 0x04E6, 0x04E7 }, +{ 0x04E7, 0x04E6, 0x04E7 }, +{ 0x04E8, 0x04E8, 0x04E9 }, +{ 0x04E9, 0x04E8, 0x04E9 }, +{ 0x04EA, 0x04EA, 0x04EB }, +{ 0x04EB, 0x04EA, 0x04EB }, +{ 0x04EC, 0x04EC, 0x04ED }, +{ 0x04ED, 0x04EC, 0x04ED }, +{ 0x04EE, 0x04EE, 0x04EF }, +{ 0x04EF, 0x04EE, 0x04EF }, +{ 0x04F0, 0x04F0, 0x04F1 }, +{ 0x04F1, 0x04F0, 0x04F1 }, +{ 0x04F2, 0x04F2, 0x04F3 }, +{ 0x04F3, 0x04F2, 0x04F3 }, +{ 0x04F4, 0x04F4, 0x04F5 }, +{ 0x04F5, 0x04F4, 0x04F5 }, +{ 0x04F6, 0x04F6, 0x04F7 }, +{ 0x04F7, 0x04F6, 0x04F7 }, +{ 0x04F8, 0x04F8, 0x04F9 }, +{ 0x04F9, 0x04F8, 0x04F9 }, +{ 0x0500, 0x0500, 0x0501 }, +{ 0x0501, 0x0500, 0x0501 }, +{ 0x0502, 0x0502, 0x0503 }, +{ 0x0503, 0x0502, 0x0503 }, +{ 0x0504, 0x0504, 0x0505 }, +{ 0x0505, 0x0504, 0x0505 }, +{ 0x0506, 0x0506, 0x0507 }, +{ 0x0507, 0x0506, 0x0507 }, +{ 0x0508, 0x0508, 0x0509 }, +{ 0x0509, 0x0508, 0x0509 }, +{ 0x050A, 0x050A, 0x050B }, +{ 0x050B, 0x050A, 0x050B }, +{ 0x050C, 0x050C, 0x050D }, +{ 0x050D, 0x050C, 0x050D }, +{ 0x050E, 0x050E, 0x050F }, +{ 0x050F, 0x050E, 0x050F }, +{ 0x0531, 0x0531, 0x0561 }, +{ 0x0532, 0x0532, 0x0562 }, +{ 0x0533, 0x0533, 0x0563 }, +{ 0x0534, 0x0534, 0x0564 }, +{ 0x0535, 0x0535, 0x0565 }, +{ 0x0536, 0x0536, 0x0566 }, +{ 0x0537, 0x0537, 0x0567 }, +{ 0x0538, 0x0538, 0x0568 }, +{ 0x0539, 0x0539, 0x0569 }, +{ 0x053A, 0x053A, 0x056A }, +{ 0x053B, 0x053B, 0x056B }, +{ 0x053C, 0x053C, 0x056C }, +{ 0x053D, 0x053D, 0x056D }, +{ 0x053E, 0x053E, 0x056E }, +{ 0x053F, 0x053F, 0x056F }, +{ 0x0540, 0x0540, 0x0570 }, +{ 0x0541, 0x0541, 0x0571 }, +{ 0x0542, 0x0542, 0x0572 }, +{ 0x0543, 0x0543, 0x0573 }, +{ 0x0544, 0x0544, 0x0574 }, +{ 0x0545, 0x0545, 0x0575 }, +{ 0x0546, 0x0546, 0x0576 }, +{ 0x0547, 0x0547, 0x0577 }, +{ 0x0548, 0x0548, 0x0578 }, +{ 0x0549, 0x0549, 0x0579 }, +{ 0x054A, 0x054A, 0x057A }, +{ 0x054B, 0x054B, 0x057B }, +{ 0x054C, 0x054C, 0x057C }, +{ 0x054D, 0x054D, 0x057D }, +{ 0x054E, 0x054E, 0x057E }, +{ 0x054F, 0x054F, 0x057F }, +{ 0x0550, 0x0550, 0x0580 }, +{ 0x0551, 0x0551, 0x0581 }, +{ 0x0552, 0x0552, 0x0582 }, +{ 0x0553, 0x0553, 0x0583 }, +{ 0x0554, 0x0554, 0x0584 }, +{ 0x0555, 0x0555, 0x0585 }, +{ 0x0556, 0x0556, 0x0586 }, +{ 0x0559, 0x0559, 0x0559 }, +{ 0x0561, 0x0531, 0x0561 }, +{ 0x0562, 0x0532, 0x0562 }, +{ 0x0563, 0x0533, 0x0563 }, +{ 0x0564, 0x0534, 0x0564 }, +{ 0x0565, 0x0535, 0x0565 }, +{ 0x0566, 0x0536, 0x0566 }, +{ 0x0567, 0x0537, 0x0567 }, +{ 0x0568, 0x0538, 0x0568 }, +{ 0x0569, 0x0539, 0x0569 }, +{ 0x056A, 0x053A, 0x056A }, +{ 0x056B, 0x053B, 0x056B }, +{ 0x056C, 0x053C, 0x056C }, +{ 0x056D, 0x053D, 0x056D }, +{ 0x056E, 0x053E, 0x056E }, +{ 0x056F, 0x053F, 0x056F }, +{ 0x0570, 0x0540, 0x0570 }, +{ 0x0571, 0x0541, 0x0571 }, +{ 0x0572, 0x0542, 0x0572 }, +{ 0x0573, 0x0543, 0x0573 }, +{ 0x0574, 0x0544, 0x0574 }, +{ 0x0575, 0x0545, 0x0575 }, +{ 0x0576, 0x0546, 0x0576 }, +{ 0x0577, 0x0547, 0x0577 }, +{ 0x0578, 0x0548, 0x0578 }, +{ 0x0579, 0x0549, 0x0579 }, +{ 0x057A, 0x054A, 0x057A }, +{ 0x057B, 0x054B, 0x057B }, +{ 0x057C, 0x054C, 0x057C }, +{ 0x057D, 0x054D, 0x057D }, +{ 0x057E, 0x054E, 0x057E }, +{ 0x057F, 0x054F, 0x057F }, +{ 0x0580, 0x0550, 0x0580 }, +{ 0x0581, 0x0551, 0x0581 }, +{ 0x0582, 0x0552, 0x0582 }, +{ 0x0583, 0x0553, 0x0583 }, +{ 0x0584, 0x0554, 0x0584 }, +{ 0x0585, 0x0555, 0x0585 }, +{ 0x0586, 0x0556, 0x0586 }, +{ 0x0587, 0x0587, 0x0587 }, +{ 0x0591, 0x0591, 0x0591 }, +{ 0x0592, 0x0592, 0x0592 }, +{ 0x0593, 0x0593, 0x0593 }, +{ 0x0594, 0x0594, 0x0594 }, +{ 0x0595, 0x0595, 0x0595 }, +{ 0x0596, 0x0596, 0x0596 }, +{ 0x0597, 0x0597, 0x0597 }, +{ 0x0598, 0x0598, 0x0598 }, +{ 0x0599, 0x0599, 0x0599 }, +{ 0x059A, 0x059A, 0x059A }, +{ 0x059B, 0x059B, 0x059B }, +{ 0x059C, 0x059C, 0x059C }, +{ 0x059D, 0x059D, 0x059D }, +{ 0x059E, 0x059E, 0x059E }, +{ 0x059F, 0x059F, 0x059F }, +{ 0x05A0, 0x05A0, 0x05A0 }, +{ 0x05A1, 0x05A1, 0x05A1 }, +{ 0x05A2, 0x05A2, 0x05A2 }, +{ 0x05A3, 0x05A3, 0x05A3 }, +{ 0x05A4, 0x05A4, 0x05A4 }, +{ 0x05A5, 0x05A5, 0x05A5 }, +{ 0x05A6, 0x05A6, 0x05A6 }, +{ 0x05A7, 0x05A7, 0x05A7 }, +{ 0x05A8, 0x05A8, 0x05A8 }, +{ 0x05A9, 0x05A9, 0x05A9 }, +{ 0x05AA, 0x05AA, 0x05AA }, +{ 0x05AB, 0x05AB, 0x05AB }, +{ 0x05AC, 0x05AC, 0x05AC }, +{ 0x05AD, 0x05AD, 0x05AD }, +{ 0x05AE, 0x05AE, 0x05AE }, +{ 0x05AF, 0x05AF, 0x05AF }, +{ 0x05B0, 0x05B0, 0x05B0 }, +{ 0x05B1, 0x05B1, 0x05B1 }, +{ 0x05B2, 0x05B2, 0x05B2 }, +{ 0x05B3, 0x05B3, 0x05B3 }, +{ 0x05B4, 0x05B4, 0x05B4 }, +{ 0x05B5, 0x05B5, 0x05B5 }, +{ 0x05B6, 0x05B6, 0x05B6 }, +{ 0x05B7, 0x05B7, 0x05B7 }, +{ 0x05B8, 0x05B8, 0x05B8 }, +{ 0x05B9, 0x05B9, 0x05B9 }, +{ 0x05BB, 0x05BB, 0x05BB }, +{ 0x05BC, 0x05BC, 0x05BC }, +{ 0x05BD, 0x05BD, 0x05BD }, +{ 0x05BF, 0x05BF, 0x05BF }, +{ 0x05C1, 0x05C1, 0x05C1 }, +{ 0x05C2, 0x05C2, 0x05C2 }, +{ 0x05C4, 0x05C4, 0x05C4 }, +{ 0x05C5, 0x05C5, 0x05C5 }, +{ 0x05C7, 0x05C7, 0x05C7 }, +{ 0x05D0, 0x05D0, 0x05D0 }, +{ 0x05D1, 0x05D1, 0x05D1 }, +{ 0x05D2, 0x05D2, 0x05D2 }, +{ 0x05D3, 0x05D3, 0x05D3 }, +{ 0x05D4, 0x05D4, 0x05D4 }, +{ 0x05D5, 0x05D5, 0x05D5 }, +{ 0x05D6, 0x05D6, 0x05D6 }, +{ 0x05D7, 0x05D7, 0x05D7 }, +{ 0x05D8, 0x05D8, 0x05D8 }, +{ 0x05D9, 0x05D9, 0x05D9 }, +{ 0x05DA, 0x05DA, 0x05DA }, +{ 0x05DB, 0x05DB, 0x05DB }, +{ 0x05DC, 0x05DC, 0x05DC }, +{ 0x05DD, 0x05DD, 0x05DD }, +{ 0x05DE, 0x05DE, 0x05DE }, +{ 0x05DF, 0x05DF, 0x05DF }, +{ 0x05E0, 0x05E0, 0x05E0 }, +{ 0x05E1, 0x05E1, 0x05E1 }, +{ 0x05E2, 0x05E2, 0x05E2 }, +{ 0x05E3, 0x05E3, 0x05E3 }, +{ 0x05E4, 0x05E4, 0x05E4 }, +{ 0x05E5, 0x05E5, 0x05E5 }, +{ 0x05E6, 0x05E6, 0x05E6 }, +{ 0x05E7, 0x05E7, 0x05E7 }, +{ 0x05E8, 0x05E8, 0x05E8 }, +{ 0x05E9, 0x05E9, 0x05E9 }, +{ 0x05EA, 0x05EA, 0x05EA }, +{ 0x05F0, 0x05F0, 0x05F0 }, +{ 0x05F1, 0x05F1, 0x05F1 }, +{ 0x05F2, 0x05F2, 0x05F2 }, +{ 0x0610, 0x0610, 0x0610 }, +{ 0x0611, 0x0611, 0x0611 }, +{ 0x0612, 0x0612, 0x0612 }, +{ 0x0613, 0x0613, 0x0613 }, +{ 0x0614, 0x0614, 0x0614 }, +{ 0x0615, 0x0615, 0x0615 }, +{ 0x0621, 0x0621, 0x0621 }, +{ 0x0622, 0x0622, 0x0622 }, +{ 0x0623, 0x0623, 0x0623 }, +{ 0x0624, 0x0624, 0x0624 }, +{ 0x0625, 0x0625, 0x0625 }, +{ 0x0626, 0x0626, 0x0626 }, +{ 0x0627, 0x0627, 0x0627 }, +{ 0x0628, 0x0628, 0x0628 }, +{ 0x0629, 0x0629, 0x0629 }, +{ 0x062A, 0x062A, 0x062A }, +{ 0x062B, 0x062B, 0x062B }, +{ 0x062C, 0x062C, 0x062C }, +{ 0x062D, 0x062D, 0x062D }, +{ 0x062E, 0x062E, 0x062E }, +{ 0x062F, 0x062F, 0x062F }, +{ 0x0630, 0x0630, 0x0630 }, +{ 0x0631, 0x0631, 0x0631 }, +{ 0x0632, 0x0632, 0x0632 }, +{ 0x0633, 0x0633, 0x0633 }, +{ 0x0634, 0x0634, 0x0634 }, +{ 0x0635, 0x0635, 0x0635 }, +{ 0x0636, 0x0636, 0x0636 }, +{ 0x0637, 0x0637, 0x0637 }, +{ 0x0638, 0x0638, 0x0638 }, +{ 0x0639, 0x0639, 0x0639 }, +{ 0x063A, 0x063A, 0x063A }, +{ 0x0640, 0x0640, 0x0640 }, +{ 0x0641, 0x0641, 0x0641 }, +{ 0x0642, 0x0642, 0x0642 }, +{ 0x0643, 0x0643, 0x0643 }, +{ 0x0644, 0x0644, 0x0644 }, +{ 0x0645, 0x0645, 0x0645 }, +{ 0x0646, 0x0646, 0x0646 }, +{ 0x0647, 0x0647, 0x0647 }, +{ 0x0648, 0x0648, 0x0648 }, +{ 0x0649, 0x0649, 0x0649 }, +{ 0x064A, 0x064A, 0x064A }, +{ 0x064B, 0x064B, 0x064B }, +{ 0x064C, 0x064C, 0x064C }, +{ 0x064D, 0x064D, 0x064D }, +{ 0x064E, 0x064E, 0x064E }, +{ 0x064F, 0x064F, 0x064F }, +{ 0x0650, 0x0650, 0x0650 }, +{ 0x0651, 0x0651, 0x0651 }, +{ 0x0652, 0x0652, 0x0652 }, +{ 0x0653, 0x0653, 0x0653 }, +{ 0x0654, 0x0654, 0x0654 }, +{ 0x0655, 0x0655, 0x0655 }, +{ 0x0656, 0x0656, 0x0656 }, +{ 0x0657, 0x0657, 0x0657 }, +{ 0x0658, 0x0658, 0x0658 }, +{ 0x0659, 0x0659, 0x0659 }, +{ 0x065A, 0x065A, 0x065A }, +{ 0x065B, 0x065B, 0x065B }, +{ 0x065C, 0x065C, 0x065C }, +{ 0x065D, 0x065D, 0x065D }, +{ 0x065E, 0x065E, 0x065E }, +{ 0x066E, 0x066E, 0x066E }, +{ 0x066F, 0x066F, 0x066F }, +{ 0x0670, 0x0670, 0x0670 }, +{ 0x0671, 0x0671, 0x0671 }, +{ 0x0672, 0x0672, 0x0672 }, +{ 0x0673, 0x0673, 0x0673 }, +{ 0x0674, 0x0674, 0x0674 }, +{ 0x0675, 0x0675, 0x0675 }, +{ 0x0676, 0x0676, 0x0676 }, +{ 0x0677, 0x0677, 0x0677 }, +{ 0x0678, 0x0678, 0x0678 }, +{ 0x0679, 0x0679, 0x0679 }, +{ 0x067A, 0x067A, 0x067A }, +{ 0x067B, 0x067B, 0x067B }, +{ 0x067C, 0x067C, 0x067C }, +{ 0x067D, 0x067D, 0x067D }, +{ 0x067E, 0x067E, 0x067E }, +{ 0x067F, 0x067F, 0x067F }, +{ 0x0680, 0x0680, 0x0680 }, +{ 0x0681, 0x0681, 0x0681 }, +{ 0x0682, 0x0682, 0x0682 }, +{ 0x0683, 0x0683, 0x0683 }, +{ 0x0684, 0x0684, 0x0684 }, +{ 0x0685, 0x0685, 0x0685 }, +{ 0x0686, 0x0686, 0x0686 }, +{ 0x0687, 0x0687, 0x0687 }, +{ 0x0688, 0x0688, 0x0688 }, +{ 0x0689, 0x0689, 0x0689 }, +{ 0x068A, 0x068A, 0x068A }, +{ 0x068B, 0x068B, 0x068B }, +{ 0x068C, 0x068C, 0x068C }, +{ 0x068D, 0x068D, 0x068D }, +{ 0x068E, 0x068E, 0x068E }, +{ 0x068F, 0x068F, 0x068F }, +{ 0x0690, 0x0690, 0x0690 }, +{ 0x0691, 0x0691, 0x0691 }, +{ 0x0692, 0x0692, 0x0692 }, +{ 0x0693, 0x0693, 0x0693 }, +{ 0x0694, 0x0694, 0x0694 }, +{ 0x0695, 0x0695, 0x0695 }, +{ 0x0696, 0x0696, 0x0696 }, +{ 0x0697, 0x0697, 0x0697 }, +{ 0x0698, 0x0698, 0x0698 }, +{ 0x0699, 0x0699, 0x0699 }, +{ 0x069A, 0x069A, 0x069A }, +{ 0x069B, 0x069B, 0x069B }, +{ 0x069C, 0x069C, 0x069C }, +{ 0x069D, 0x069D, 0x069D }, +{ 0x069E, 0x069E, 0x069E }, +{ 0x069F, 0x069F, 0x069F }, +{ 0x06A0, 0x06A0, 0x06A0 }, +{ 0x06A1, 0x06A1, 0x06A1 }, +{ 0x06A2, 0x06A2, 0x06A2 }, +{ 0x06A3, 0x06A3, 0x06A3 }, +{ 0x06A4, 0x06A4, 0x06A4 }, +{ 0x06A5, 0x06A5, 0x06A5 }, +{ 0x06A6, 0x06A6, 0x06A6 }, +{ 0x06A7, 0x06A7, 0x06A7 }, +{ 0x06A8, 0x06A8, 0x06A8 }, +{ 0x06A9, 0x06A9, 0x06A9 }, +{ 0x06AA, 0x06AA, 0x06AA }, +{ 0x06AB, 0x06AB, 0x06AB }, +{ 0x06AC, 0x06AC, 0x06AC }, +{ 0x06AD, 0x06AD, 0x06AD }, +{ 0x06AE, 0x06AE, 0x06AE }, +{ 0x06AF, 0x06AF, 0x06AF }, +{ 0x06B0, 0x06B0, 0x06B0 }, +{ 0x06B1, 0x06B1, 0x06B1 }, +{ 0x06B2, 0x06B2, 0x06B2 }, +{ 0x06B3, 0x06B3, 0x06B3 }, +{ 0x06B4, 0x06B4, 0x06B4 }, +{ 0x06B5, 0x06B5, 0x06B5 }, +{ 0x06B6, 0x06B6, 0x06B6 }, +{ 0x06B7, 0x06B7, 0x06B7 }, +{ 0x06B8, 0x06B8, 0x06B8 }, +{ 0x06B9, 0x06B9, 0x06B9 }, +{ 0x06BA, 0x06BA, 0x06BA }, +{ 0x06BB, 0x06BB, 0x06BB }, +{ 0x06BC, 0x06BC, 0x06BC }, +{ 0x06BD, 0x06BD, 0x06BD }, +{ 0x06BE, 0x06BE, 0x06BE }, +{ 0x06BF, 0x06BF, 0x06BF }, +{ 0x06C0, 0x06C0, 0x06C0 }, +{ 0x06C1, 0x06C1, 0x06C1 }, +{ 0x06C2, 0x06C2, 0x06C2 }, +{ 0x06C3, 0x06C3, 0x06C3 }, +{ 0x06C4, 0x06C4, 0x06C4 }, +{ 0x06C5, 0x06C5, 0x06C5 }, +{ 0x06C6, 0x06C6, 0x06C6 }, +{ 0x06C7, 0x06C7, 0x06C7 }, +{ 0x06C8, 0x06C8, 0x06C8 }, +{ 0x06C9, 0x06C9, 0x06C9 }, +{ 0x06CA, 0x06CA, 0x06CA }, +{ 0x06CB, 0x06CB, 0x06CB }, +{ 0x06CC, 0x06CC, 0x06CC }, +{ 0x06CD, 0x06CD, 0x06CD }, +{ 0x06CE, 0x06CE, 0x06CE }, +{ 0x06CF, 0x06CF, 0x06CF }, +{ 0x06D0, 0x06D0, 0x06D0 }, +{ 0x06D1, 0x06D1, 0x06D1 }, +{ 0x06D2, 0x06D2, 0x06D2 }, +{ 0x06D3, 0x06D3, 0x06D3 }, +{ 0x06D5, 0x06D5, 0x06D5 }, +{ 0x06D6, 0x06D6, 0x06D6 }, +{ 0x06D7, 0x06D7, 0x06D7 }, +{ 0x06D8, 0x06D8, 0x06D8 }, +{ 0x06D9, 0x06D9, 0x06D9 }, +{ 0x06DA, 0x06DA, 0x06DA }, +{ 0x06DB, 0x06DB, 0x06DB }, +{ 0x06DC, 0x06DC, 0x06DC }, +{ 0x06DF, 0x06DF, 0x06DF }, +{ 0x06E0, 0x06E0, 0x06E0 }, +{ 0x06E1, 0x06E1, 0x06E1 }, +{ 0x06E2, 0x06E2, 0x06E2 }, +{ 0x06E3, 0x06E3, 0x06E3 }, +{ 0x06E4, 0x06E4, 0x06E4 }, +{ 0x06E5, 0x06E5, 0x06E5 }, +{ 0x06E6, 0x06E6, 0x06E6 }, +{ 0x06E7, 0x06E7, 0x06E7 }, +{ 0x06E8, 0x06E8, 0x06E8 }, +{ 0x06EA, 0x06EA, 0x06EA }, +{ 0x06EB, 0x06EB, 0x06EB }, +{ 0x06EC, 0x06EC, 0x06EC }, +{ 0x06ED, 0x06ED, 0x06ED }, +{ 0x06EE, 0x06EE, 0x06EE }, +{ 0x06EF, 0x06EF, 0x06EF }, +{ 0x06FA, 0x06FA, 0x06FA }, +{ 0x06FB, 0x06FB, 0x06FB }, +{ 0x06FC, 0x06FC, 0x06FC }, +{ 0x06FF, 0x06FF, 0x06FF }, +{ 0x0710, 0x0710, 0x0710 }, +{ 0x0711, 0x0711, 0x0711 }, +{ 0x0712, 0x0712, 0x0712 }, +{ 0x0713, 0x0713, 0x0713 }, +{ 0x0714, 0x0714, 0x0714 }, +{ 0x0715, 0x0715, 0x0715 }, +{ 0x0716, 0x0716, 0x0716 }, +{ 0x0717, 0x0717, 0x0717 }, +{ 0x0718, 0x0718, 0x0718 }, +{ 0x0719, 0x0719, 0x0719 }, +{ 0x071A, 0x071A, 0x071A }, +{ 0x071B, 0x071B, 0x071B }, +{ 0x071C, 0x071C, 0x071C }, +{ 0x071D, 0x071D, 0x071D }, +{ 0x071E, 0x071E, 0x071E }, +{ 0x071F, 0x071F, 0x071F }, +{ 0x0720, 0x0720, 0x0720 }, +{ 0x0721, 0x0721, 0x0721 }, +{ 0x0722, 0x0722, 0x0722 }, +{ 0x0723, 0x0723, 0x0723 }, +{ 0x0724, 0x0724, 0x0724 }, +{ 0x0725, 0x0725, 0x0725 }, +{ 0x0726, 0x0726, 0x0726 }, +{ 0x0727, 0x0727, 0x0727 }, +{ 0x0728, 0x0728, 0x0728 }, +{ 0x0729, 0x0729, 0x0729 }, +{ 0x072A, 0x072A, 0x072A }, +{ 0x072B, 0x072B, 0x072B }, +{ 0x072C, 0x072C, 0x072C }, +{ 0x072D, 0x072D, 0x072D }, +{ 0x072E, 0x072E, 0x072E }, +{ 0x072F, 0x072F, 0x072F }, +{ 0x0730, 0x0730, 0x0730 }, +{ 0x0731, 0x0731, 0x0731 }, +{ 0x0732, 0x0732, 0x0732 }, +{ 0x0733, 0x0733, 0x0733 }, +{ 0x0734, 0x0734, 0x0734 }, +{ 0x0735, 0x0735, 0x0735 }, +{ 0x0736, 0x0736, 0x0736 }, +{ 0x0737, 0x0737, 0x0737 }, +{ 0x0738, 0x0738, 0x0738 }, +{ 0x0739, 0x0739, 0x0739 }, +{ 0x073A, 0x073A, 0x073A }, +{ 0x073B, 0x073B, 0x073B }, +{ 0x073C, 0x073C, 0x073C }, +{ 0x073D, 0x073D, 0x073D }, +{ 0x073E, 0x073E, 0x073E }, +{ 0x073F, 0x073F, 0x073F }, +{ 0x0740, 0x0740, 0x0740 }, +{ 0x0741, 0x0741, 0x0741 }, +{ 0x0742, 0x0742, 0x0742 }, +{ 0x0743, 0x0743, 0x0743 }, +{ 0x0744, 0x0744, 0x0744 }, +{ 0x0745, 0x0745, 0x0745 }, +{ 0x0746, 0x0746, 0x0746 }, +{ 0x0747, 0x0747, 0x0747 }, +{ 0x0748, 0x0748, 0x0748 }, +{ 0x0749, 0x0749, 0x0749 }, +{ 0x074A, 0x074A, 0x074A }, +{ 0x074D, 0x074D, 0x074D }, +{ 0x074E, 0x074E, 0x074E }, +{ 0x074F, 0x074F, 0x074F }, +{ 0x0750, 0x0750, 0x0750 }, +{ 0x0751, 0x0751, 0x0751 }, +{ 0x0752, 0x0752, 0x0752 }, +{ 0x0753, 0x0753, 0x0753 }, +{ 0x0754, 0x0754, 0x0754 }, +{ 0x0755, 0x0755, 0x0755 }, +{ 0x0756, 0x0756, 0x0756 }, +{ 0x0757, 0x0757, 0x0757 }, +{ 0x0758, 0x0758, 0x0758 }, +{ 0x0759, 0x0759, 0x0759 }, +{ 0x075A, 0x075A, 0x075A }, +{ 0x075B, 0x075B, 0x075B }, +{ 0x075C, 0x075C, 0x075C }, +{ 0x075D, 0x075D, 0x075D }, +{ 0x075E, 0x075E, 0x075E }, +{ 0x075F, 0x075F, 0x075F }, +{ 0x0760, 0x0760, 0x0760 }, +{ 0x0761, 0x0761, 0x0761 }, +{ 0x0762, 0x0762, 0x0762 }, +{ 0x0763, 0x0763, 0x0763 }, +{ 0x0764, 0x0764, 0x0764 }, +{ 0x0765, 0x0765, 0x0765 }, +{ 0x0766, 0x0766, 0x0766 }, +{ 0x0767, 0x0767, 0x0767 }, +{ 0x0768, 0x0768, 0x0768 }, +{ 0x0769, 0x0769, 0x0769 }, +{ 0x076A, 0x076A, 0x076A }, +{ 0x076B, 0x076B, 0x076B }, +{ 0x076C, 0x076C, 0x076C }, +{ 0x076D, 0x076D, 0x076D }, +{ 0x0780, 0x0780, 0x0780 }, +{ 0x0781, 0x0781, 0x0781 }, +{ 0x0782, 0x0782, 0x0782 }, +{ 0x0783, 0x0783, 0x0783 }, +{ 0x0784, 0x0784, 0x0784 }, +{ 0x0785, 0x0785, 0x0785 }, +{ 0x0786, 0x0786, 0x0786 }, +{ 0x0787, 0x0787, 0x0787 }, +{ 0x0788, 0x0788, 0x0788 }, +{ 0x0789, 0x0789, 0x0789 }, +{ 0x078A, 0x078A, 0x078A }, +{ 0x078B, 0x078B, 0x078B }, +{ 0x078C, 0x078C, 0x078C }, +{ 0x078D, 0x078D, 0x078D }, +{ 0x078E, 0x078E, 0x078E }, +{ 0x078F, 0x078F, 0x078F }, +{ 0x0790, 0x0790, 0x0790 }, +{ 0x0791, 0x0791, 0x0791 }, +{ 0x0792, 0x0792, 0x0792 }, +{ 0x0793, 0x0793, 0x0793 }, +{ 0x0794, 0x0794, 0x0794 }, +{ 0x0795, 0x0795, 0x0795 }, +{ 0x0796, 0x0796, 0x0796 }, +{ 0x0797, 0x0797, 0x0797 }, +{ 0x0798, 0x0798, 0x0798 }, +{ 0x0799, 0x0799, 0x0799 }, +{ 0x079A, 0x079A, 0x079A }, +{ 0x079B, 0x079B, 0x079B }, +{ 0x079C, 0x079C, 0x079C }, +{ 0x079D, 0x079D, 0x079D }, +{ 0x079E, 0x079E, 0x079E }, +{ 0x079F, 0x079F, 0x079F }, +{ 0x07A0, 0x07A0, 0x07A0 }, +{ 0x07A1, 0x07A1, 0x07A1 }, +{ 0x07A2, 0x07A2, 0x07A2 }, +{ 0x07A3, 0x07A3, 0x07A3 }, +{ 0x07A4, 0x07A4, 0x07A4 }, +{ 0x07A5, 0x07A5, 0x07A5 }, +{ 0x07A6, 0x07A6, 0x07A6 }, +{ 0x07A7, 0x07A7, 0x07A7 }, +{ 0x07A8, 0x07A8, 0x07A8 }, +{ 0x07A9, 0x07A9, 0x07A9 }, +{ 0x07AA, 0x07AA, 0x07AA }, +{ 0x07AB, 0x07AB, 0x07AB }, +{ 0x07AC, 0x07AC, 0x07AC }, +{ 0x07AD, 0x07AD, 0x07AD }, +{ 0x07AE, 0x07AE, 0x07AE }, +{ 0x07AF, 0x07AF, 0x07AF }, +{ 0x07B0, 0x07B0, 0x07B0 }, +{ 0x07B1, 0x07B1, 0x07B1 }, +{ 0x0901, 0x0901, 0x0901 }, +{ 0x0902, 0x0902, 0x0902 }, +{ 0x0904, 0x0904, 0x0904 }, +{ 0x0905, 0x0905, 0x0905 }, +{ 0x0906, 0x0906, 0x0906 }, +{ 0x0907, 0x0907, 0x0907 }, +{ 0x0908, 0x0908, 0x0908 }, +{ 0x0909, 0x0909, 0x0909 }, +{ 0x090A, 0x090A, 0x090A }, +{ 0x090B, 0x090B, 0x090B }, +{ 0x090C, 0x090C, 0x090C }, +{ 0x090D, 0x090D, 0x090D }, +{ 0x090E, 0x090E, 0x090E }, +{ 0x090F, 0x090F, 0x090F }, +{ 0x0910, 0x0910, 0x0910 }, +{ 0x0911, 0x0911, 0x0911 }, +{ 0x0912, 0x0912, 0x0912 }, +{ 0x0913, 0x0913, 0x0913 }, +{ 0x0914, 0x0914, 0x0914 }, +{ 0x0915, 0x0915, 0x0915 }, +{ 0x0916, 0x0916, 0x0916 }, +{ 0x0917, 0x0917, 0x0917 }, +{ 0x0918, 0x0918, 0x0918 }, +{ 0x0919, 0x0919, 0x0919 }, +{ 0x091A, 0x091A, 0x091A }, +{ 0x091B, 0x091B, 0x091B }, +{ 0x091C, 0x091C, 0x091C }, +{ 0x091D, 0x091D, 0x091D }, +{ 0x091E, 0x091E, 0x091E }, +{ 0x091F, 0x091F, 0x091F }, +{ 0x0920, 0x0920, 0x0920 }, +{ 0x0921, 0x0921, 0x0921 }, +{ 0x0922, 0x0922, 0x0922 }, +{ 0x0923, 0x0923, 0x0923 }, +{ 0x0924, 0x0924, 0x0924 }, +{ 0x0925, 0x0925, 0x0925 }, +{ 0x0926, 0x0926, 0x0926 }, +{ 0x0927, 0x0927, 0x0927 }, +{ 0x0928, 0x0928, 0x0928 }, +{ 0x0929, 0x0929, 0x0929 }, +{ 0x092A, 0x092A, 0x092A }, +{ 0x092B, 0x092B, 0x092B }, +{ 0x092C, 0x092C, 0x092C }, +{ 0x092D, 0x092D, 0x092D }, +{ 0x092E, 0x092E, 0x092E }, +{ 0x092F, 0x092F, 0x092F }, +{ 0x0930, 0x0930, 0x0930 }, +{ 0x0931, 0x0931, 0x0931 }, +{ 0x0932, 0x0932, 0x0932 }, +{ 0x0933, 0x0933, 0x0933 }, +{ 0x0934, 0x0934, 0x0934 }, +{ 0x0935, 0x0935, 0x0935 }, +{ 0x0936, 0x0936, 0x0936 }, +{ 0x0937, 0x0937, 0x0937 }, +{ 0x0938, 0x0938, 0x0938 }, +{ 0x0939, 0x0939, 0x0939 }, +{ 0x093C, 0x093C, 0x093C }, +{ 0x093D, 0x093D, 0x093D }, +{ 0x0941, 0x0941, 0x0941 }, +{ 0x0942, 0x0942, 0x0942 }, +{ 0x0943, 0x0943, 0x0943 }, +{ 0x0944, 0x0944, 0x0944 }, +{ 0x0945, 0x0945, 0x0945 }, +{ 0x0946, 0x0946, 0x0946 }, +{ 0x0947, 0x0947, 0x0947 }, +{ 0x0948, 0x0948, 0x0948 }, +{ 0x094D, 0x094D, 0x094D }, +{ 0x0950, 0x0950, 0x0950 }, +{ 0x0951, 0x0951, 0x0951 }, +{ 0x0952, 0x0952, 0x0952 }, +{ 0x0953, 0x0953, 0x0953 }, +{ 0x0954, 0x0954, 0x0954 }, +{ 0x0958, 0x0958, 0x0958 }, +{ 0x0959, 0x0959, 0x0959 }, +{ 0x095A, 0x095A, 0x095A }, +{ 0x095B, 0x095B, 0x095B }, +{ 0x095C, 0x095C, 0x095C }, +{ 0x095D, 0x095D, 0x095D }, +{ 0x095E, 0x095E, 0x095E }, +{ 0x095F, 0x095F, 0x095F }, +{ 0x0960, 0x0960, 0x0960 }, +{ 0x0961, 0x0961, 0x0961 }, +{ 0x0962, 0x0962, 0x0962 }, +{ 0x0963, 0x0963, 0x0963 }, +{ 0x097D, 0x097D, 0x097D }, +{ 0x0981, 0x0981, 0x0981 }, +{ 0x0985, 0x0985, 0x0985 }, +{ 0x0986, 0x0986, 0x0986 }, +{ 0x0987, 0x0987, 0x0987 }, +{ 0x0988, 0x0988, 0x0988 }, +{ 0x0989, 0x0989, 0x0989 }, +{ 0x098A, 0x098A, 0x098A }, +{ 0x098B, 0x098B, 0x098B }, +{ 0x098C, 0x098C, 0x098C }, +{ 0x098F, 0x098F, 0x098F }, +{ 0x0990, 0x0990, 0x0990 }, +{ 0x0993, 0x0993, 0x0993 }, +{ 0x0994, 0x0994, 0x0994 }, +{ 0x0995, 0x0995, 0x0995 }, +{ 0x0996, 0x0996, 0x0996 }, +{ 0x0997, 0x0997, 0x0997 }, +{ 0x0998, 0x0998, 0x0998 }, +{ 0x0999, 0x0999, 0x0999 }, +{ 0x099A, 0x099A, 0x099A }, +{ 0x099B, 0x099B, 0x099B }, +{ 0x099C, 0x099C, 0x099C }, +{ 0x099D, 0x099D, 0x099D }, +{ 0x099E, 0x099E, 0x099E }, +{ 0x099F, 0x099F, 0x099F }, +{ 0x09A0, 0x09A0, 0x09A0 }, +{ 0x09A1, 0x09A1, 0x09A1 }, +{ 0x09A2, 0x09A2, 0x09A2 }, +{ 0x09A3, 0x09A3, 0x09A3 }, +{ 0x09A4, 0x09A4, 0x09A4 }, +{ 0x09A5, 0x09A5, 0x09A5 }, +{ 0x09A6, 0x09A6, 0x09A6 }, +{ 0x09A7, 0x09A7, 0x09A7 }, +{ 0x09A8, 0x09A8, 0x09A8 }, +{ 0x09AA, 0x09AA, 0x09AA }, +{ 0x09AB, 0x09AB, 0x09AB }, +{ 0x09AC, 0x09AC, 0x09AC }, +{ 0x09AD, 0x09AD, 0x09AD }, +{ 0x09AE, 0x09AE, 0x09AE }, +{ 0x09AF, 0x09AF, 0x09AF }, +{ 0x09B0, 0x09B0, 0x09B0 }, +{ 0x09B2, 0x09B2, 0x09B2 }, +{ 0x09B6, 0x09B6, 0x09B6 }, +{ 0x09B7, 0x09B7, 0x09B7 }, +{ 0x09B8, 0x09B8, 0x09B8 }, +{ 0x09B9, 0x09B9, 0x09B9 }, +{ 0x09BC, 0x09BC, 0x09BC }, +{ 0x09BD, 0x09BD, 0x09BD }, +{ 0x09C1, 0x09C1, 0x09C1 }, +{ 0x09C2, 0x09C2, 0x09C2 }, +{ 0x09C3, 0x09C3, 0x09C3 }, +{ 0x09C4, 0x09C4, 0x09C4 }, +{ 0x09CD, 0x09CD, 0x09CD }, +{ 0x09CE, 0x09CE, 0x09CE }, +{ 0x09DC, 0x09DC, 0x09DC }, +{ 0x09DD, 0x09DD, 0x09DD }, +{ 0x09DF, 0x09DF, 0x09DF }, +{ 0x09E0, 0x09E0, 0x09E0 }, +{ 0x09E1, 0x09E1, 0x09E1 }, +{ 0x09E2, 0x09E2, 0x09E2 }, +{ 0x09E3, 0x09E3, 0x09E3 }, +{ 0x09F0, 0x09F0, 0x09F0 }, +{ 0x09F1, 0x09F1, 0x09F1 }, +{ 0x0A01, 0x0A01, 0x0A01 }, +{ 0x0A02, 0x0A02, 0x0A02 }, +{ 0x0A05, 0x0A05, 0x0A05 }, +{ 0x0A06, 0x0A06, 0x0A06 }, +{ 0x0A07, 0x0A07, 0x0A07 }, +{ 0x0A08, 0x0A08, 0x0A08 }, +{ 0x0A09, 0x0A09, 0x0A09 }, +{ 0x0A0A, 0x0A0A, 0x0A0A }, +{ 0x0A0F, 0x0A0F, 0x0A0F }, +{ 0x0A10, 0x0A10, 0x0A10 }, +{ 0x0A13, 0x0A13, 0x0A13 }, +{ 0x0A14, 0x0A14, 0x0A14 }, +{ 0x0A15, 0x0A15, 0x0A15 }, +{ 0x0A16, 0x0A16, 0x0A16 }, +{ 0x0A17, 0x0A17, 0x0A17 }, +{ 0x0A18, 0x0A18, 0x0A18 }, +{ 0x0A19, 0x0A19, 0x0A19 }, +{ 0x0A1A, 0x0A1A, 0x0A1A }, +{ 0x0A1B, 0x0A1B, 0x0A1B }, +{ 0x0A1C, 0x0A1C, 0x0A1C }, +{ 0x0A1D, 0x0A1D, 0x0A1D }, +{ 0x0A1E, 0x0A1E, 0x0A1E }, +{ 0x0A1F, 0x0A1F, 0x0A1F }, +{ 0x0A20, 0x0A20, 0x0A20 }, +{ 0x0A21, 0x0A21, 0x0A21 }, +{ 0x0A22, 0x0A22, 0x0A22 }, +{ 0x0A23, 0x0A23, 0x0A23 }, +{ 0x0A24, 0x0A24, 0x0A24 }, +{ 0x0A25, 0x0A25, 0x0A25 }, +{ 0x0A26, 0x0A26, 0x0A26 }, +{ 0x0A27, 0x0A27, 0x0A27 }, +{ 0x0A28, 0x0A28, 0x0A28 }, +{ 0x0A2A, 0x0A2A, 0x0A2A }, +{ 0x0A2B, 0x0A2B, 0x0A2B }, +{ 0x0A2C, 0x0A2C, 0x0A2C }, +{ 0x0A2D, 0x0A2D, 0x0A2D }, +{ 0x0A2E, 0x0A2E, 0x0A2E }, +{ 0x0A2F, 0x0A2F, 0x0A2F }, +{ 0x0A30, 0x0A30, 0x0A30 }, +{ 0x0A32, 0x0A32, 0x0A32 }, +{ 0x0A33, 0x0A33, 0x0A33 }, +{ 0x0A35, 0x0A35, 0x0A35 }, +{ 0x0A36, 0x0A36, 0x0A36 }, +{ 0x0A38, 0x0A38, 0x0A38 }, +{ 0x0A39, 0x0A39, 0x0A39 }, +{ 0x0A3C, 0x0A3C, 0x0A3C }, +{ 0x0A41, 0x0A41, 0x0A41 }, +{ 0x0A42, 0x0A42, 0x0A42 }, +{ 0x0A47, 0x0A47, 0x0A47 }, +{ 0x0A48, 0x0A48, 0x0A48 }, +{ 0x0A4B, 0x0A4B, 0x0A4B }, +{ 0x0A4C, 0x0A4C, 0x0A4C }, +{ 0x0A4D, 0x0A4D, 0x0A4D }, +{ 0x0A59, 0x0A59, 0x0A59 }, +{ 0x0A5A, 0x0A5A, 0x0A5A }, +{ 0x0A5B, 0x0A5B, 0x0A5B }, +{ 0x0A5C, 0x0A5C, 0x0A5C }, +{ 0x0A5E, 0x0A5E, 0x0A5E }, +{ 0x0A70, 0x0A70, 0x0A70 }, +{ 0x0A71, 0x0A71, 0x0A71 }, +{ 0x0A72, 0x0A72, 0x0A72 }, +{ 0x0A73, 0x0A73, 0x0A73 }, +{ 0x0A74, 0x0A74, 0x0A74 }, +{ 0x0A81, 0x0A81, 0x0A81 }, +{ 0x0A82, 0x0A82, 0x0A82 }, +{ 0x0A85, 0x0A85, 0x0A85 }, +{ 0x0A86, 0x0A86, 0x0A86 }, +{ 0x0A87, 0x0A87, 0x0A87 }, +{ 0x0A88, 0x0A88, 0x0A88 }, +{ 0x0A89, 0x0A89, 0x0A89 }, +{ 0x0A8A, 0x0A8A, 0x0A8A }, +{ 0x0A8B, 0x0A8B, 0x0A8B }, +{ 0x0A8C, 0x0A8C, 0x0A8C }, +{ 0x0A8D, 0x0A8D, 0x0A8D }, +{ 0x0A8F, 0x0A8F, 0x0A8F }, +{ 0x0A90, 0x0A90, 0x0A90 }, +{ 0x0A91, 0x0A91, 0x0A91 }, +{ 0x0A93, 0x0A93, 0x0A93 }, +{ 0x0A94, 0x0A94, 0x0A94 }, +{ 0x0A95, 0x0A95, 0x0A95 }, +{ 0x0A96, 0x0A96, 0x0A96 }, +{ 0x0A97, 0x0A97, 0x0A97 }, +{ 0x0A98, 0x0A98, 0x0A98 }, +{ 0x0A99, 0x0A99, 0x0A99 }, +{ 0x0A9A, 0x0A9A, 0x0A9A }, +{ 0x0A9B, 0x0A9B, 0x0A9B }, +{ 0x0A9C, 0x0A9C, 0x0A9C }, +{ 0x0A9D, 0x0A9D, 0x0A9D }, +{ 0x0A9E, 0x0A9E, 0x0A9E }, +{ 0x0A9F, 0x0A9F, 0x0A9F }, +{ 0x0AA0, 0x0AA0, 0x0AA0 }, +{ 0x0AA1, 0x0AA1, 0x0AA1 }, +{ 0x0AA2, 0x0AA2, 0x0AA2 }, +{ 0x0AA3, 0x0AA3, 0x0AA3 }, +{ 0x0AA4, 0x0AA4, 0x0AA4 }, +{ 0x0AA5, 0x0AA5, 0x0AA5 }, +{ 0x0AA6, 0x0AA6, 0x0AA6 }, +{ 0x0AA7, 0x0AA7, 0x0AA7 }, +{ 0x0AA8, 0x0AA8, 0x0AA8 }, +{ 0x0AAA, 0x0AAA, 0x0AAA }, +{ 0x0AAB, 0x0AAB, 0x0AAB }, +{ 0x0AAC, 0x0AAC, 0x0AAC }, +{ 0x0AAD, 0x0AAD, 0x0AAD }, +{ 0x0AAE, 0x0AAE, 0x0AAE }, +{ 0x0AAF, 0x0AAF, 0x0AAF }, +{ 0x0AB0, 0x0AB0, 0x0AB0 }, +{ 0x0AB2, 0x0AB2, 0x0AB2 }, +{ 0x0AB3, 0x0AB3, 0x0AB3 }, +{ 0x0AB5, 0x0AB5, 0x0AB5 }, +{ 0x0AB6, 0x0AB6, 0x0AB6 }, +{ 0x0AB7, 0x0AB7, 0x0AB7 }, +{ 0x0AB8, 0x0AB8, 0x0AB8 }, +{ 0x0AB9, 0x0AB9, 0x0AB9 }, +{ 0x0ABC, 0x0ABC, 0x0ABC }, +{ 0x0ABD, 0x0ABD, 0x0ABD }, +{ 0x0AC1, 0x0AC1, 0x0AC1 }, +{ 0x0AC2, 0x0AC2, 0x0AC2 }, +{ 0x0AC3, 0x0AC3, 0x0AC3 }, +{ 0x0AC4, 0x0AC4, 0x0AC4 }, +{ 0x0AC5, 0x0AC5, 0x0AC5 }, +{ 0x0AC7, 0x0AC7, 0x0AC7 }, +{ 0x0AC8, 0x0AC8, 0x0AC8 }, +{ 0x0ACD, 0x0ACD, 0x0ACD }, +{ 0x0AD0, 0x0AD0, 0x0AD0 }, +{ 0x0AE0, 0x0AE0, 0x0AE0 }, +{ 0x0AE1, 0x0AE1, 0x0AE1 }, +{ 0x0AE2, 0x0AE2, 0x0AE2 }, +{ 0x0AE3, 0x0AE3, 0x0AE3 }, +{ 0x0B01, 0x0B01, 0x0B01 }, +{ 0x0B05, 0x0B05, 0x0B05 }, +{ 0x0B06, 0x0B06, 0x0B06 }, +{ 0x0B07, 0x0B07, 0x0B07 }, +{ 0x0B08, 0x0B08, 0x0B08 }, +{ 0x0B09, 0x0B09, 0x0B09 }, +{ 0x0B0A, 0x0B0A, 0x0B0A }, +{ 0x0B0B, 0x0B0B, 0x0B0B }, +{ 0x0B0C, 0x0B0C, 0x0B0C }, +{ 0x0B0F, 0x0B0F, 0x0B0F }, +{ 0x0B10, 0x0B10, 0x0B10 }, +{ 0x0B13, 0x0B13, 0x0B13 }, +{ 0x0B14, 0x0B14, 0x0B14 }, +{ 0x0B15, 0x0B15, 0x0B15 }, +{ 0x0B16, 0x0B16, 0x0B16 }, +{ 0x0B17, 0x0B17, 0x0B17 }, +{ 0x0B18, 0x0B18, 0x0B18 }, +{ 0x0B19, 0x0B19, 0x0B19 }, +{ 0x0B1A, 0x0B1A, 0x0B1A }, +{ 0x0B1B, 0x0B1B, 0x0B1B }, +{ 0x0B1C, 0x0B1C, 0x0B1C }, +{ 0x0B1D, 0x0B1D, 0x0B1D }, +{ 0x0B1E, 0x0B1E, 0x0B1E }, +{ 0x0B1F, 0x0B1F, 0x0B1F }, +{ 0x0B20, 0x0B20, 0x0B20 }, +{ 0x0B21, 0x0B21, 0x0B21 }, +{ 0x0B22, 0x0B22, 0x0B22 }, +{ 0x0B23, 0x0B23, 0x0B23 }, +{ 0x0B24, 0x0B24, 0x0B24 }, +{ 0x0B25, 0x0B25, 0x0B25 }, +{ 0x0B26, 0x0B26, 0x0B26 }, +{ 0x0B27, 0x0B27, 0x0B27 }, +{ 0x0B28, 0x0B28, 0x0B28 }, +{ 0x0B2A, 0x0B2A, 0x0B2A }, +{ 0x0B2B, 0x0B2B, 0x0B2B }, +{ 0x0B2C, 0x0B2C, 0x0B2C }, +{ 0x0B2D, 0x0B2D, 0x0B2D }, +{ 0x0B2E, 0x0B2E, 0x0B2E }, +{ 0x0B2F, 0x0B2F, 0x0B2F }, +{ 0x0B30, 0x0B30, 0x0B30 }, +{ 0x0B32, 0x0B32, 0x0B32 }, +{ 0x0B33, 0x0B33, 0x0B33 }, +{ 0x0B35, 0x0B35, 0x0B35 }, +{ 0x0B36, 0x0B36, 0x0B36 }, +{ 0x0B37, 0x0B37, 0x0B37 }, +{ 0x0B38, 0x0B38, 0x0B38 }, +{ 0x0B39, 0x0B39, 0x0B39 }, +{ 0x0B3C, 0x0B3C, 0x0B3C }, +{ 0x0B3D, 0x0B3D, 0x0B3D }, +{ 0x0B3F, 0x0B3F, 0x0B3F }, +{ 0x0B41, 0x0B41, 0x0B41 }, +{ 0x0B42, 0x0B42, 0x0B42 }, +{ 0x0B43, 0x0B43, 0x0B43 }, +{ 0x0B4D, 0x0B4D, 0x0B4D }, +{ 0x0B56, 0x0B56, 0x0B56 }, +{ 0x0B5C, 0x0B5C, 0x0B5C }, +{ 0x0B5D, 0x0B5D, 0x0B5D }, +{ 0x0B5F, 0x0B5F, 0x0B5F }, +{ 0x0B60, 0x0B60, 0x0B60 }, +{ 0x0B61, 0x0B61, 0x0B61 }, +{ 0x0B71, 0x0B71, 0x0B71 }, +{ 0x0B82, 0x0B82, 0x0B82 }, +{ 0x0B83, 0x0B83, 0x0B83 }, +{ 0x0B85, 0x0B85, 0x0B85 }, +{ 0x0B86, 0x0B86, 0x0B86 }, +{ 0x0B87, 0x0B87, 0x0B87 }, +{ 0x0B88, 0x0B88, 0x0B88 }, +{ 0x0B89, 0x0B89, 0x0B89 }, +{ 0x0B8A, 0x0B8A, 0x0B8A }, +{ 0x0B8E, 0x0B8E, 0x0B8E }, +{ 0x0B8F, 0x0B8F, 0x0B8F }, +{ 0x0B90, 0x0B90, 0x0B90 }, +{ 0x0B92, 0x0B92, 0x0B92 }, +{ 0x0B93, 0x0B93, 0x0B93 }, +{ 0x0B94, 0x0B94, 0x0B94 }, +{ 0x0B95, 0x0B95, 0x0B95 }, +{ 0x0B99, 0x0B99, 0x0B99 }, +{ 0x0B9A, 0x0B9A, 0x0B9A }, +{ 0x0B9C, 0x0B9C, 0x0B9C }, +{ 0x0B9E, 0x0B9E, 0x0B9E }, +{ 0x0B9F, 0x0B9F, 0x0B9F }, +{ 0x0BA3, 0x0BA3, 0x0BA3 }, +{ 0x0BA4, 0x0BA4, 0x0BA4 }, +{ 0x0BA8, 0x0BA8, 0x0BA8 }, +{ 0x0BA9, 0x0BA9, 0x0BA9 }, +{ 0x0BAA, 0x0BAA, 0x0BAA }, +{ 0x0BAE, 0x0BAE, 0x0BAE }, +{ 0x0BAF, 0x0BAF, 0x0BAF }, +{ 0x0BB0, 0x0BB0, 0x0BB0 }, +{ 0x0BB1, 0x0BB1, 0x0BB1 }, +{ 0x0BB2, 0x0BB2, 0x0BB2 }, +{ 0x0BB3, 0x0BB3, 0x0BB3 }, +{ 0x0BB4, 0x0BB4, 0x0BB4 }, +{ 0x0BB5, 0x0BB5, 0x0BB5 }, +{ 0x0BB6, 0x0BB6, 0x0BB6 }, +{ 0x0BB7, 0x0BB7, 0x0BB7 }, +{ 0x0BB8, 0x0BB8, 0x0BB8 }, +{ 0x0BB9, 0x0BB9, 0x0BB9 }, +{ 0x0BC0, 0x0BC0, 0x0BC0 }, +{ 0x0BCD, 0x0BCD, 0x0BCD }, +{ 0x0C05, 0x0C05, 0x0C05 }, +{ 0x0C06, 0x0C06, 0x0C06 }, +{ 0x0C07, 0x0C07, 0x0C07 }, +{ 0x0C08, 0x0C08, 0x0C08 }, +{ 0x0C09, 0x0C09, 0x0C09 }, +{ 0x0C0A, 0x0C0A, 0x0C0A }, +{ 0x0C0B, 0x0C0B, 0x0C0B }, +{ 0x0C0C, 0x0C0C, 0x0C0C }, +{ 0x0C0E, 0x0C0E, 0x0C0E }, +{ 0x0C0F, 0x0C0F, 0x0C0F }, +{ 0x0C10, 0x0C10, 0x0C10 }, +{ 0x0C12, 0x0C12, 0x0C12 }, +{ 0x0C13, 0x0C13, 0x0C13 }, +{ 0x0C14, 0x0C14, 0x0C14 }, +{ 0x0C15, 0x0C15, 0x0C15 }, +{ 0x0C16, 0x0C16, 0x0C16 }, +{ 0x0C17, 0x0C17, 0x0C17 }, +{ 0x0C18, 0x0C18, 0x0C18 }, +{ 0x0C19, 0x0C19, 0x0C19 }, +{ 0x0C1A, 0x0C1A, 0x0C1A }, +{ 0x0C1B, 0x0C1B, 0x0C1B }, +{ 0x0C1C, 0x0C1C, 0x0C1C }, +{ 0x0C1D, 0x0C1D, 0x0C1D }, +{ 0x0C1E, 0x0C1E, 0x0C1E }, +{ 0x0C1F, 0x0C1F, 0x0C1F }, +{ 0x0C20, 0x0C20, 0x0C20 }, +{ 0x0C21, 0x0C21, 0x0C21 }, +{ 0x0C22, 0x0C22, 0x0C22 }, +{ 0x0C23, 0x0C23, 0x0C23 }, +{ 0x0C24, 0x0C24, 0x0C24 }, +{ 0x0C25, 0x0C25, 0x0C25 }, +{ 0x0C26, 0x0C26, 0x0C26 }, +{ 0x0C27, 0x0C27, 0x0C27 }, +{ 0x0C28, 0x0C28, 0x0C28 }, +{ 0x0C2A, 0x0C2A, 0x0C2A }, +{ 0x0C2B, 0x0C2B, 0x0C2B }, +{ 0x0C2C, 0x0C2C, 0x0C2C }, +{ 0x0C2D, 0x0C2D, 0x0C2D }, +{ 0x0C2E, 0x0C2E, 0x0C2E }, +{ 0x0C2F, 0x0C2F, 0x0C2F }, +{ 0x0C30, 0x0C30, 0x0C30 }, +{ 0x0C31, 0x0C31, 0x0C31 }, +{ 0x0C32, 0x0C32, 0x0C32 }, +{ 0x0C33, 0x0C33, 0x0C33 }, +{ 0x0C35, 0x0C35, 0x0C35 }, +{ 0x0C36, 0x0C36, 0x0C36 }, +{ 0x0C37, 0x0C37, 0x0C37 }, +{ 0x0C38, 0x0C38, 0x0C38 }, +{ 0x0C39, 0x0C39, 0x0C39 }, +{ 0x0C3E, 0x0C3E, 0x0C3E }, +{ 0x0C3F, 0x0C3F, 0x0C3F }, +{ 0x0C40, 0x0C40, 0x0C40 }, +{ 0x0C46, 0x0C46, 0x0C46 }, +{ 0x0C47, 0x0C47, 0x0C47 }, +{ 0x0C48, 0x0C48, 0x0C48 }, +{ 0x0C4A, 0x0C4A, 0x0C4A }, +{ 0x0C4B, 0x0C4B, 0x0C4B }, +{ 0x0C4C, 0x0C4C, 0x0C4C }, +{ 0x0C4D, 0x0C4D, 0x0C4D }, +{ 0x0C55, 0x0C55, 0x0C55 }, +{ 0x0C56, 0x0C56, 0x0C56 }, +{ 0x0C60, 0x0C60, 0x0C60 }, +{ 0x0C61, 0x0C61, 0x0C61 }, +{ 0x0C85, 0x0C85, 0x0C85 }, +{ 0x0C86, 0x0C86, 0x0C86 }, +{ 0x0C87, 0x0C87, 0x0C87 }, +{ 0x0C88, 0x0C88, 0x0C88 }, +{ 0x0C89, 0x0C89, 0x0C89 }, +{ 0x0C8A, 0x0C8A, 0x0C8A }, +{ 0x0C8B, 0x0C8B, 0x0C8B }, +{ 0x0C8C, 0x0C8C, 0x0C8C }, +{ 0x0C8E, 0x0C8E, 0x0C8E }, +{ 0x0C8F, 0x0C8F, 0x0C8F }, +{ 0x0C90, 0x0C90, 0x0C90 }, +{ 0x0C92, 0x0C92, 0x0C92 }, +{ 0x0C93, 0x0C93, 0x0C93 }, +{ 0x0C94, 0x0C94, 0x0C94 }, +{ 0x0C95, 0x0C95, 0x0C95 }, +{ 0x0C96, 0x0C96, 0x0C96 }, +{ 0x0C97, 0x0C97, 0x0C97 }, +{ 0x0C98, 0x0C98, 0x0C98 }, +{ 0x0C99, 0x0C99, 0x0C99 }, +{ 0x0C9A, 0x0C9A, 0x0C9A }, +{ 0x0C9B, 0x0C9B, 0x0C9B }, +{ 0x0C9C, 0x0C9C, 0x0C9C }, +{ 0x0C9D, 0x0C9D, 0x0C9D }, +{ 0x0C9E, 0x0C9E, 0x0C9E }, +{ 0x0C9F, 0x0C9F, 0x0C9F }, +{ 0x0CA0, 0x0CA0, 0x0CA0 }, +{ 0x0CA1, 0x0CA1, 0x0CA1 }, +{ 0x0CA2, 0x0CA2, 0x0CA2 }, +{ 0x0CA3, 0x0CA3, 0x0CA3 }, +{ 0x0CA4, 0x0CA4, 0x0CA4 }, +{ 0x0CA5, 0x0CA5, 0x0CA5 }, +{ 0x0CA6, 0x0CA6, 0x0CA6 }, +{ 0x0CA7, 0x0CA7, 0x0CA7 }, +{ 0x0CA8, 0x0CA8, 0x0CA8 }, +{ 0x0CAA, 0x0CAA, 0x0CAA }, +{ 0x0CAB, 0x0CAB, 0x0CAB }, +{ 0x0CAC, 0x0CAC, 0x0CAC }, +{ 0x0CAD, 0x0CAD, 0x0CAD }, +{ 0x0CAE, 0x0CAE, 0x0CAE }, +{ 0x0CAF, 0x0CAF, 0x0CAF }, +{ 0x0CB0, 0x0CB0, 0x0CB0 }, +{ 0x0CB1, 0x0CB1, 0x0CB1 }, +{ 0x0CB2, 0x0CB2, 0x0CB2 }, +{ 0x0CB3, 0x0CB3, 0x0CB3 }, +{ 0x0CB5, 0x0CB5, 0x0CB5 }, +{ 0x0CB6, 0x0CB6, 0x0CB6 }, +{ 0x0CB7, 0x0CB7, 0x0CB7 }, +{ 0x0CB8, 0x0CB8, 0x0CB8 }, +{ 0x0CB9, 0x0CB9, 0x0CB9 }, +{ 0x0CBC, 0x0CBC, 0x0CBC }, +{ 0x0CBD, 0x0CBD, 0x0CBD }, +{ 0x0CBF, 0x0CBF, 0x0CBF }, +{ 0x0CC6, 0x0CC6, 0x0CC6 }, +{ 0x0CCC, 0x0CCC, 0x0CCC }, +{ 0x0CCD, 0x0CCD, 0x0CCD }, +{ 0x0CDE, 0x0CDE, 0x0CDE }, +{ 0x0CE0, 0x0CE0, 0x0CE0 }, +{ 0x0CE1, 0x0CE1, 0x0CE1 }, +{ 0x0D05, 0x0D05, 0x0D05 }, +{ 0x0D06, 0x0D06, 0x0D06 }, +{ 0x0D07, 0x0D07, 0x0D07 }, +{ 0x0D08, 0x0D08, 0x0D08 }, +{ 0x0D09, 0x0D09, 0x0D09 }, +{ 0x0D0A, 0x0D0A, 0x0D0A }, +{ 0x0D0B, 0x0D0B, 0x0D0B }, +{ 0x0D0C, 0x0D0C, 0x0D0C }, +{ 0x0D0E, 0x0D0E, 0x0D0E }, +{ 0x0D0F, 0x0D0F, 0x0D0F }, +{ 0x0D10, 0x0D10, 0x0D10 }, +{ 0x0D12, 0x0D12, 0x0D12 }, +{ 0x0D13, 0x0D13, 0x0D13 }, +{ 0x0D14, 0x0D14, 0x0D14 }, +{ 0x0D15, 0x0D15, 0x0D15 }, +{ 0x0D16, 0x0D16, 0x0D16 }, +{ 0x0D17, 0x0D17, 0x0D17 }, +{ 0x0D18, 0x0D18, 0x0D18 }, +{ 0x0D19, 0x0D19, 0x0D19 }, +{ 0x0D1A, 0x0D1A, 0x0D1A }, +{ 0x0D1B, 0x0D1B, 0x0D1B }, +{ 0x0D1C, 0x0D1C, 0x0D1C }, +{ 0x0D1D, 0x0D1D, 0x0D1D }, +{ 0x0D1E, 0x0D1E, 0x0D1E }, +{ 0x0D1F, 0x0D1F, 0x0D1F }, +{ 0x0D20, 0x0D20, 0x0D20 }, +{ 0x0D21, 0x0D21, 0x0D21 }, +{ 0x0D22, 0x0D22, 0x0D22 }, +{ 0x0D23, 0x0D23, 0x0D23 }, +{ 0x0D24, 0x0D24, 0x0D24 }, +{ 0x0D25, 0x0D25, 0x0D25 }, +{ 0x0D26, 0x0D26, 0x0D26 }, +{ 0x0D27, 0x0D27, 0x0D27 }, +{ 0x0D28, 0x0D28, 0x0D28 }, +{ 0x0D2A, 0x0D2A, 0x0D2A }, +{ 0x0D2B, 0x0D2B, 0x0D2B }, +{ 0x0D2C, 0x0D2C, 0x0D2C }, +{ 0x0D2D, 0x0D2D, 0x0D2D }, +{ 0x0D2E, 0x0D2E, 0x0D2E }, +{ 0x0D2F, 0x0D2F, 0x0D2F }, +{ 0x0D30, 0x0D30, 0x0D30 }, +{ 0x0D31, 0x0D31, 0x0D31 }, +{ 0x0D32, 0x0D32, 0x0D32 }, +{ 0x0D33, 0x0D33, 0x0D33 }, +{ 0x0D34, 0x0D34, 0x0D34 }, +{ 0x0D35, 0x0D35, 0x0D35 }, +{ 0x0D36, 0x0D36, 0x0D36 }, +{ 0x0D37, 0x0D37, 0x0D37 }, +{ 0x0D38, 0x0D38, 0x0D38 }, +{ 0x0D39, 0x0D39, 0x0D39 }, +{ 0x0D41, 0x0D41, 0x0D41 }, +{ 0x0D42, 0x0D42, 0x0D42 }, +{ 0x0D43, 0x0D43, 0x0D43 }, +{ 0x0D4D, 0x0D4D, 0x0D4D }, +{ 0x0D60, 0x0D60, 0x0D60 }, +{ 0x0D61, 0x0D61, 0x0D61 }, +{ 0x0D85, 0x0D85, 0x0D85 }, +{ 0x0D86, 0x0D86, 0x0D86 }, +{ 0x0D87, 0x0D87, 0x0D87 }, +{ 0x0D88, 0x0D88, 0x0D88 }, +{ 0x0D89, 0x0D89, 0x0D89 }, +{ 0x0D8A, 0x0D8A, 0x0D8A }, +{ 0x0D8B, 0x0D8B, 0x0D8B }, +{ 0x0D8C, 0x0D8C, 0x0D8C }, +{ 0x0D8D, 0x0D8D, 0x0D8D }, +{ 0x0D8E, 0x0D8E, 0x0D8E }, +{ 0x0D8F, 0x0D8F, 0x0D8F }, +{ 0x0D90, 0x0D90, 0x0D90 }, +{ 0x0D91, 0x0D91, 0x0D91 }, +{ 0x0D92, 0x0D92, 0x0D92 }, +{ 0x0D93, 0x0D93, 0x0D93 }, +{ 0x0D94, 0x0D94, 0x0D94 }, +{ 0x0D95, 0x0D95, 0x0D95 }, +{ 0x0D96, 0x0D96, 0x0D96 }, +{ 0x0D9A, 0x0D9A, 0x0D9A }, +{ 0x0D9B, 0x0D9B, 0x0D9B }, +{ 0x0D9C, 0x0D9C, 0x0D9C }, +{ 0x0D9D, 0x0D9D, 0x0D9D }, +{ 0x0D9E, 0x0D9E, 0x0D9E }, +{ 0x0D9F, 0x0D9F, 0x0D9F }, +{ 0x0DA0, 0x0DA0, 0x0DA0 }, +{ 0x0DA1, 0x0DA1, 0x0DA1 }, +{ 0x0DA2, 0x0DA2, 0x0DA2 }, +{ 0x0DA3, 0x0DA3, 0x0DA3 }, +{ 0x0DA4, 0x0DA4, 0x0DA4 }, +{ 0x0DA5, 0x0DA5, 0x0DA5 }, +{ 0x0DA6, 0x0DA6, 0x0DA6 }, +{ 0x0DA7, 0x0DA7, 0x0DA7 }, +{ 0x0DA8, 0x0DA8, 0x0DA8 }, +{ 0x0DA9, 0x0DA9, 0x0DA9 }, +{ 0x0DAA, 0x0DAA, 0x0DAA }, +{ 0x0DAB, 0x0DAB, 0x0DAB }, +{ 0x0DAC, 0x0DAC, 0x0DAC }, +{ 0x0DAD, 0x0DAD, 0x0DAD }, +{ 0x0DAE, 0x0DAE, 0x0DAE }, +{ 0x0DAF, 0x0DAF, 0x0DAF }, +{ 0x0DB0, 0x0DB0, 0x0DB0 }, +{ 0x0DB1, 0x0DB1, 0x0DB1 }, +{ 0x0DB3, 0x0DB3, 0x0DB3 }, +{ 0x0DB4, 0x0DB4, 0x0DB4 }, +{ 0x0DB5, 0x0DB5, 0x0DB5 }, +{ 0x0DB6, 0x0DB6, 0x0DB6 }, +{ 0x0DB7, 0x0DB7, 0x0DB7 }, +{ 0x0DB8, 0x0DB8, 0x0DB8 }, +{ 0x0DB9, 0x0DB9, 0x0DB9 }, +{ 0x0DBA, 0x0DBA, 0x0DBA }, +{ 0x0DBB, 0x0DBB, 0x0DBB }, +{ 0x0DBD, 0x0DBD, 0x0DBD }, +{ 0x0DC0, 0x0DC0, 0x0DC0 }, +{ 0x0DC1, 0x0DC1, 0x0DC1 }, +{ 0x0DC2, 0x0DC2, 0x0DC2 }, +{ 0x0DC3, 0x0DC3, 0x0DC3 }, +{ 0x0DC4, 0x0DC4, 0x0DC4 }, +{ 0x0DC5, 0x0DC5, 0x0DC5 }, +{ 0x0DC6, 0x0DC6, 0x0DC6 }, +{ 0x0DCA, 0x0DCA, 0x0DCA }, +{ 0x0DD2, 0x0DD2, 0x0DD2 }, +{ 0x0DD3, 0x0DD3, 0x0DD3 }, +{ 0x0DD4, 0x0DD4, 0x0DD4 }, +{ 0x0DD6, 0x0DD6, 0x0DD6 }, +{ 0x0E01, 0x0E01, 0x0E01 }, +{ 0x0E02, 0x0E02, 0x0E02 }, +{ 0x0E03, 0x0E03, 0x0E03 }, +{ 0x0E04, 0x0E04, 0x0E04 }, +{ 0x0E05, 0x0E05, 0x0E05 }, +{ 0x0E06, 0x0E06, 0x0E06 }, +{ 0x0E07, 0x0E07, 0x0E07 }, +{ 0x0E08, 0x0E08, 0x0E08 }, +{ 0x0E09, 0x0E09, 0x0E09 }, +{ 0x0E0A, 0x0E0A, 0x0E0A }, +{ 0x0E0B, 0x0E0B, 0x0E0B }, +{ 0x0E0C, 0x0E0C, 0x0E0C }, +{ 0x0E0D, 0x0E0D, 0x0E0D }, +{ 0x0E0E, 0x0E0E, 0x0E0E }, +{ 0x0E0F, 0x0E0F, 0x0E0F }, +{ 0x0E10, 0x0E10, 0x0E10 }, +{ 0x0E11, 0x0E11, 0x0E11 }, +{ 0x0E12, 0x0E12, 0x0E12 }, +{ 0x0E13, 0x0E13, 0x0E13 }, +{ 0x0E14, 0x0E14, 0x0E14 }, +{ 0x0E15, 0x0E15, 0x0E15 }, +{ 0x0E16, 0x0E16, 0x0E16 }, +{ 0x0E17, 0x0E17, 0x0E17 }, +{ 0x0E18, 0x0E18, 0x0E18 }, +{ 0x0E19, 0x0E19, 0x0E19 }, +{ 0x0E1A, 0x0E1A, 0x0E1A }, +{ 0x0E1B, 0x0E1B, 0x0E1B }, +{ 0x0E1C, 0x0E1C, 0x0E1C }, +{ 0x0E1D, 0x0E1D, 0x0E1D }, +{ 0x0E1E, 0x0E1E, 0x0E1E }, +{ 0x0E1F, 0x0E1F, 0x0E1F }, +{ 0x0E20, 0x0E20, 0x0E20 }, +{ 0x0E21, 0x0E21, 0x0E21 }, +{ 0x0E22, 0x0E22, 0x0E22 }, +{ 0x0E23, 0x0E23, 0x0E23 }, +{ 0x0E24, 0x0E24, 0x0E24 }, +{ 0x0E25, 0x0E25, 0x0E25 }, +{ 0x0E26, 0x0E26, 0x0E26 }, +{ 0x0E27, 0x0E27, 0x0E27 }, +{ 0x0E28, 0x0E28, 0x0E28 }, +{ 0x0E29, 0x0E29, 0x0E29 }, +{ 0x0E2A, 0x0E2A, 0x0E2A }, +{ 0x0E2B, 0x0E2B, 0x0E2B }, +{ 0x0E2C, 0x0E2C, 0x0E2C }, +{ 0x0E2D, 0x0E2D, 0x0E2D }, +{ 0x0E2E, 0x0E2E, 0x0E2E }, +{ 0x0E2F, 0x0E2F, 0x0E2F }, +{ 0x0E30, 0x0E30, 0x0E30 }, +{ 0x0E31, 0x0E31, 0x0E31 }, +{ 0x0E32, 0x0E32, 0x0E32 }, +{ 0x0E33, 0x0E33, 0x0E33 }, +{ 0x0E34, 0x0E34, 0x0E34 }, +{ 0x0E35, 0x0E35, 0x0E35 }, +{ 0x0E36, 0x0E36, 0x0E36 }, +{ 0x0E37, 0x0E37, 0x0E37 }, +{ 0x0E38, 0x0E38, 0x0E38 }, +{ 0x0E39, 0x0E39, 0x0E39 }, +{ 0x0E3A, 0x0E3A, 0x0E3A }, +{ 0x0E40, 0x0E40, 0x0E40 }, +{ 0x0E41, 0x0E41, 0x0E41 }, +{ 0x0E42, 0x0E42, 0x0E42 }, +{ 0x0E43, 0x0E43, 0x0E43 }, +{ 0x0E44, 0x0E44, 0x0E44 }, +{ 0x0E45, 0x0E45, 0x0E45 }, +{ 0x0E46, 0x0E46, 0x0E46 }, +{ 0x0E47, 0x0E47, 0x0E47 }, +{ 0x0E48, 0x0E48, 0x0E48 }, +{ 0x0E49, 0x0E49, 0x0E49 }, +{ 0x0E4A, 0x0E4A, 0x0E4A }, +{ 0x0E4B, 0x0E4B, 0x0E4B }, +{ 0x0E4C, 0x0E4C, 0x0E4C }, +{ 0x0E4D, 0x0E4D, 0x0E4D }, +{ 0x0E4E, 0x0E4E, 0x0E4E }, +{ 0x0E81, 0x0E81, 0x0E81 }, +{ 0x0E82, 0x0E82, 0x0E82 }, +{ 0x0E84, 0x0E84, 0x0E84 }, +{ 0x0E87, 0x0E87, 0x0E87 }, +{ 0x0E88, 0x0E88, 0x0E88 }, +{ 0x0E8A, 0x0E8A, 0x0E8A }, +{ 0x0E8D, 0x0E8D, 0x0E8D }, +{ 0x0E94, 0x0E94, 0x0E94 }, +{ 0x0E95, 0x0E95, 0x0E95 }, +{ 0x0E96, 0x0E96, 0x0E96 }, +{ 0x0E97, 0x0E97, 0x0E97 }, +{ 0x0E99, 0x0E99, 0x0E99 }, +{ 0x0E9A, 0x0E9A, 0x0E9A }, +{ 0x0E9B, 0x0E9B, 0x0E9B }, +{ 0x0E9C, 0x0E9C, 0x0E9C }, +{ 0x0E9D, 0x0E9D, 0x0E9D }, +{ 0x0E9E, 0x0E9E, 0x0E9E }, +{ 0x0E9F, 0x0E9F, 0x0E9F }, +{ 0x0EA1, 0x0EA1, 0x0EA1 }, +{ 0x0EA2, 0x0EA2, 0x0EA2 }, +{ 0x0EA3, 0x0EA3, 0x0EA3 }, +{ 0x0EA5, 0x0EA5, 0x0EA5 }, +{ 0x0EA7, 0x0EA7, 0x0EA7 }, +{ 0x0EAA, 0x0EAA, 0x0EAA }, +{ 0x0EAB, 0x0EAB, 0x0EAB }, +{ 0x0EAD, 0x0EAD, 0x0EAD }, +{ 0x0EAE, 0x0EAE, 0x0EAE }, +{ 0x0EAF, 0x0EAF, 0x0EAF }, +{ 0x0EB0, 0x0EB0, 0x0EB0 }, +{ 0x0EB1, 0x0EB1, 0x0EB1 }, +{ 0x0EB2, 0x0EB2, 0x0EB2 }, +{ 0x0EB3, 0x0EB3, 0x0EB3 }, +{ 0x0EB4, 0x0EB4, 0x0EB4 }, +{ 0x0EB5, 0x0EB5, 0x0EB5 }, +{ 0x0EB6, 0x0EB6, 0x0EB6 }, +{ 0x0EB7, 0x0EB7, 0x0EB7 }, +{ 0x0EB8, 0x0EB8, 0x0EB8 }, +{ 0x0EB9, 0x0EB9, 0x0EB9 }, +{ 0x0EBB, 0x0EBB, 0x0EBB }, +{ 0x0EBC, 0x0EBC, 0x0EBC }, +{ 0x0EBD, 0x0EBD, 0x0EBD }, +{ 0x0EC0, 0x0EC0, 0x0EC0 }, +{ 0x0EC1, 0x0EC1, 0x0EC1 }, +{ 0x0EC2, 0x0EC2, 0x0EC2 }, +{ 0x0EC3, 0x0EC3, 0x0EC3 }, +{ 0x0EC4, 0x0EC4, 0x0EC4 }, +{ 0x0EC6, 0x0EC6, 0x0EC6 }, +{ 0x0EC8, 0x0EC8, 0x0EC8 }, +{ 0x0EC9, 0x0EC9, 0x0EC9 }, +{ 0x0ECA, 0x0ECA, 0x0ECA }, +{ 0x0ECB, 0x0ECB, 0x0ECB }, +{ 0x0ECC, 0x0ECC, 0x0ECC }, +{ 0x0ECD, 0x0ECD, 0x0ECD }, +{ 0x0EDC, 0x0EDC, 0x0EDC }, +{ 0x0EDD, 0x0EDD, 0x0EDD }, +{ 0x0F00, 0x0F00, 0x0F00 }, +{ 0x0F18, 0x0F18, 0x0F18 }, +{ 0x0F19, 0x0F19, 0x0F19 }, +{ 0x0F35, 0x0F35, 0x0F35 }, +{ 0x0F37, 0x0F37, 0x0F37 }, +{ 0x0F39, 0x0F39, 0x0F39 }, +{ 0x0F40, 0x0F40, 0x0F40 }, +{ 0x0F41, 0x0F41, 0x0F41 }, +{ 0x0F42, 0x0F42, 0x0F42 }, +{ 0x0F43, 0x0F43, 0x0F43 }, +{ 0x0F44, 0x0F44, 0x0F44 }, +{ 0x0F45, 0x0F45, 0x0F45 }, +{ 0x0F46, 0x0F46, 0x0F46 }, +{ 0x0F47, 0x0F47, 0x0F47 }, +{ 0x0F49, 0x0F49, 0x0F49 }, +{ 0x0F4A, 0x0F4A, 0x0F4A }, +{ 0x0F4B, 0x0F4B, 0x0F4B }, +{ 0x0F4C, 0x0F4C, 0x0F4C }, +{ 0x0F4D, 0x0F4D, 0x0F4D }, +{ 0x0F4E, 0x0F4E, 0x0F4E }, +{ 0x0F4F, 0x0F4F, 0x0F4F }, +{ 0x0F50, 0x0F50, 0x0F50 }, +{ 0x0F51, 0x0F51, 0x0F51 }, +{ 0x0F52, 0x0F52, 0x0F52 }, +{ 0x0F53, 0x0F53, 0x0F53 }, +{ 0x0F54, 0x0F54, 0x0F54 }, +{ 0x0F55, 0x0F55, 0x0F55 }, +{ 0x0F56, 0x0F56, 0x0F56 }, +{ 0x0F57, 0x0F57, 0x0F57 }, +{ 0x0F58, 0x0F58, 0x0F58 }, +{ 0x0F59, 0x0F59, 0x0F59 }, +{ 0x0F5A, 0x0F5A, 0x0F5A }, +{ 0x0F5B, 0x0F5B, 0x0F5B }, +{ 0x0F5C, 0x0F5C, 0x0F5C }, +{ 0x0F5D, 0x0F5D, 0x0F5D }, +{ 0x0F5E, 0x0F5E, 0x0F5E }, +{ 0x0F5F, 0x0F5F, 0x0F5F }, +{ 0x0F60, 0x0F60, 0x0F60 }, +{ 0x0F61, 0x0F61, 0x0F61 }, +{ 0x0F62, 0x0F62, 0x0F62 }, +{ 0x0F63, 0x0F63, 0x0F63 }, +{ 0x0F64, 0x0F64, 0x0F64 }, +{ 0x0F65, 0x0F65, 0x0F65 }, +{ 0x0F66, 0x0F66, 0x0F66 }, +{ 0x0F67, 0x0F67, 0x0F67 }, +{ 0x0F68, 0x0F68, 0x0F68 }, +{ 0x0F69, 0x0F69, 0x0F69 }, +{ 0x0F6A, 0x0F6A, 0x0F6A }, +{ 0x0F71, 0x0F71, 0x0F71 }, +{ 0x0F72, 0x0F72, 0x0F72 }, +{ 0x0F73, 0x0F73, 0x0F73 }, +{ 0x0F74, 0x0F74, 0x0F74 }, +{ 0x0F75, 0x0F75, 0x0F75 }, +{ 0x0F76, 0x0F76, 0x0F76 }, +{ 0x0F77, 0x0F77, 0x0F77 }, +{ 0x0F78, 0x0F78, 0x0F78 }, +{ 0x0F79, 0x0F79, 0x0F79 }, +{ 0x0F7A, 0x0F7A, 0x0F7A }, +{ 0x0F7B, 0x0F7B, 0x0F7B }, +{ 0x0F7C, 0x0F7C, 0x0F7C }, +{ 0x0F7D, 0x0F7D, 0x0F7D }, +{ 0x0F7E, 0x0F7E, 0x0F7E }, +{ 0x0F80, 0x0F80, 0x0F80 }, +{ 0x0F81, 0x0F81, 0x0F81 }, +{ 0x0F82, 0x0F82, 0x0F82 }, +{ 0x0F83, 0x0F83, 0x0F83 }, +{ 0x0F84, 0x0F84, 0x0F84 }, +{ 0x0F86, 0x0F86, 0x0F86 }, +{ 0x0F87, 0x0F87, 0x0F87 }, +{ 0x0F88, 0x0F88, 0x0F88 }, +{ 0x0F89, 0x0F89, 0x0F89 }, +{ 0x0F8A, 0x0F8A, 0x0F8A }, +{ 0x0F8B, 0x0F8B, 0x0F8B }, +{ 0x0F90, 0x0F90, 0x0F90 }, +{ 0x0F91, 0x0F91, 0x0F91 }, +{ 0x0F92, 0x0F92, 0x0F92 }, +{ 0x0F93, 0x0F93, 0x0F93 }, +{ 0x0F94, 0x0F94, 0x0F94 }, +{ 0x0F95, 0x0F95, 0x0F95 }, +{ 0x0F96, 0x0F96, 0x0F96 }, +{ 0x0F97, 0x0F97, 0x0F97 }, +{ 0x0F99, 0x0F99, 0x0F99 }, +{ 0x0F9A, 0x0F9A, 0x0F9A }, +{ 0x0F9B, 0x0F9B, 0x0F9B }, +{ 0x0F9C, 0x0F9C, 0x0F9C }, +{ 0x0F9D, 0x0F9D, 0x0F9D }, +{ 0x0F9E, 0x0F9E, 0x0F9E }, +{ 0x0F9F, 0x0F9F, 0x0F9F }, +{ 0x0FA0, 0x0FA0, 0x0FA0 }, +{ 0x0FA1, 0x0FA1, 0x0FA1 }, +{ 0x0FA2, 0x0FA2, 0x0FA2 }, +{ 0x0FA3, 0x0FA3, 0x0FA3 }, +{ 0x0FA4, 0x0FA4, 0x0FA4 }, +{ 0x0FA5, 0x0FA5, 0x0FA5 }, +{ 0x0FA6, 0x0FA6, 0x0FA6 }, +{ 0x0FA7, 0x0FA7, 0x0FA7 }, +{ 0x0FA8, 0x0FA8, 0x0FA8 }, +{ 0x0FA9, 0x0FA9, 0x0FA9 }, +{ 0x0FAA, 0x0FAA, 0x0FAA }, +{ 0x0FAB, 0x0FAB, 0x0FAB }, +{ 0x0FAC, 0x0FAC, 0x0FAC }, +{ 0x0FAD, 0x0FAD, 0x0FAD }, +{ 0x0FAE, 0x0FAE, 0x0FAE }, +{ 0x0FAF, 0x0FAF, 0x0FAF }, +{ 0x0FB0, 0x0FB0, 0x0FB0 }, +{ 0x0FB1, 0x0FB1, 0x0FB1 }, +{ 0x0FB2, 0x0FB2, 0x0FB2 }, +{ 0x0FB3, 0x0FB3, 0x0FB3 }, +{ 0x0FB4, 0x0FB4, 0x0FB4 }, +{ 0x0FB5, 0x0FB5, 0x0FB5 }, +{ 0x0FB6, 0x0FB6, 0x0FB6 }, +{ 0x0FB7, 0x0FB7, 0x0FB7 }, +{ 0x0FB8, 0x0FB8, 0x0FB8 }, +{ 0x0FB9, 0x0FB9, 0x0FB9 }, +{ 0x0FBA, 0x0FBA, 0x0FBA }, +{ 0x0FBB, 0x0FBB, 0x0FBB }, +{ 0x0FBC, 0x0FBC, 0x0FBC }, +{ 0x0FC6, 0x0FC6, 0x0FC6 }, +{ 0x1000, 0x1000, 0x1000 }, +{ 0x1001, 0x1001, 0x1001 }, +{ 0x1002, 0x1002, 0x1002 }, +{ 0x1003, 0x1003, 0x1003 }, +{ 0x1004, 0x1004, 0x1004 }, +{ 0x1005, 0x1005, 0x1005 }, +{ 0x1006, 0x1006, 0x1006 }, +{ 0x1007, 0x1007, 0x1007 }, +{ 0x1008, 0x1008, 0x1008 }, +{ 0x1009, 0x1009, 0x1009 }, +{ 0x100A, 0x100A, 0x100A }, +{ 0x100B, 0x100B, 0x100B }, +{ 0x100C, 0x100C, 0x100C }, +{ 0x100D, 0x100D, 0x100D }, +{ 0x100E, 0x100E, 0x100E }, +{ 0x100F, 0x100F, 0x100F }, +{ 0x1010, 0x1010, 0x1010 }, +{ 0x1011, 0x1011, 0x1011 }, +{ 0x1012, 0x1012, 0x1012 }, +{ 0x1013, 0x1013, 0x1013 }, +{ 0x1014, 0x1014, 0x1014 }, +{ 0x1015, 0x1015, 0x1015 }, +{ 0x1016, 0x1016, 0x1016 }, +{ 0x1017, 0x1017, 0x1017 }, +{ 0x1018, 0x1018, 0x1018 }, +{ 0x1019, 0x1019, 0x1019 }, +{ 0x101A, 0x101A, 0x101A }, +{ 0x101B, 0x101B, 0x101B }, +{ 0x101C, 0x101C, 0x101C }, +{ 0x101D, 0x101D, 0x101D }, +{ 0x101E, 0x101E, 0x101E }, +{ 0x101F, 0x101F, 0x101F }, +{ 0x1020, 0x1020, 0x1020 }, +{ 0x1021, 0x1021, 0x1021 }, +{ 0x1023, 0x1023, 0x1023 }, +{ 0x1024, 0x1024, 0x1024 }, +{ 0x1025, 0x1025, 0x1025 }, +{ 0x1026, 0x1026, 0x1026 }, +{ 0x1027, 0x1027, 0x1027 }, +{ 0x1029, 0x1029, 0x1029 }, +{ 0x102A, 0x102A, 0x102A }, +{ 0x102D, 0x102D, 0x102D }, +{ 0x102E, 0x102E, 0x102E }, +{ 0x102F, 0x102F, 0x102F }, +{ 0x1030, 0x1030, 0x1030 }, +{ 0x1032, 0x1032, 0x1032 }, +{ 0x1036, 0x1036, 0x1036 }, +{ 0x1037, 0x1037, 0x1037 }, +{ 0x1039, 0x1039, 0x1039 }, +{ 0x1050, 0x1050, 0x1050 }, +{ 0x1051, 0x1051, 0x1051 }, +{ 0x1052, 0x1052, 0x1052 }, +{ 0x1053, 0x1053, 0x1053 }, +{ 0x1054, 0x1054, 0x1054 }, +{ 0x1055, 0x1055, 0x1055 }, +{ 0x1058, 0x1058, 0x1058 }, +{ 0x1059, 0x1059, 0x1059 }, +{ 0x10A0, 0x10A0, 0x2D00 }, +{ 0x10A1, 0x10A1, 0x2D01 }, +{ 0x10A2, 0x10A2, 0x2D02 }, +{ 0x10A3, 0x10A3, 0x2D03 }, +{ 0x10A4, 0x10A4, 0x2D04 }, +{ 0x10A5, 0x10A5, 0x2D05 }, +{ 0x10A6, 0x10A6, 0x2D06 }, +{ 0x10A7, 0x10A7, 0x2D07 }, +{ 0x10A8, 0x10A8, 0x2D08 }, +{ 0x10A9, 0x10A9, 0x2D09 }, +{ 0x10AA, 0x10AA, 0x2D0A }, +{ 0x10AB, 0x10AB, 0x2D0B }, +{ 0x10AC, 0x10AC, 0x2D0C }, +{ 0x10AD, 0x10AD, 0x2D0D }, +{ 0x10AE, 0x10AE, 0x2D0E }, +{ 0x10AF, 0x10AF, 0x2D0F }, +{ 0x10B0, 0x10B0, 0x2D10 }, +{ 0x10B1, 0x10B1, 0x2D11 }, +{ 0x10B2, 0x10B2, 0x2D12 }, +{ 0x10B3, 0x10B3, 0x2D13 }, +{ 0x10B4, 0x10B4, 0x2D14 }, +{ 0x10B5, 0x10B5, 0x2D15 }, +{ 0x10B6, 0x10B6, 0x2D16 }, +{ 0x10B7, 0x10B7, 0x2D17 }, +{ 0x10B8, 0x10B8, 0x2D18 }, +{ 0x10B9, 0x10B9, 0x2D19 }, +{ 0x10BA, 0x10BA, 0x2D1A }, +{ 0x10BB, 0x10BB, 0x2D1B }, +{ 0x10BC, 0x10BC, 0x2D1C }, +{ 0x10BD, 0x10BD, 0x2D1D }, +{ 0x10BE, 0x10BE, 0x2D1E }, +{ 0x10BF, 0x10BF, 0x2D1F }, +{ 0x10C0, 0x10C0, 0x2D20 }, +{ 0x10C1, 0x10C1, 0x2D21 }, +{ 0x10C2, 0x10C2, 0x2D22 }, +{ 0x10C3, 0x10C3, 0x2D23 }, +{ 0x10C4, 0x10C4, 0x2D24 }, +{ 0x10C5, 0x10C5, 0x2D25 }, +{ 0x10D0, 0x10D0, 0x10D0 }, +{ 0x10D1, 0x10D1, 0x10D1 }, +{ 0x10D2, 0x10D2, 0x10D2 }, +{ 0x10D3, 0x10D3, 0x10D3 }, +{ 0x10D4, 0x10D4, 0x10D4 }, +{ 0x10D5, 0x10D5, 0x10D5 }, +{ 0x10D6, 0x10D6, 0x10D6 }, +{ 0x10D7, 0x10D7, 0x10D7 }, +{ 0x10D8, 0x10D8, 0x10D8 }, +{ 0x10D9, 0x10D9, 0x10D9 }, +{ 0x10DA, 0x10DA, 0x10DA }, +{ 0x10DB, 0x10DB, 0x10DB }, +{ 0x10DC, 0x10DC, 0x10DC }, +{ 0x10DD, 0x10DD, 0x10DD }, +{ 0x10DE, 0x10DE, 0x10DE }, +{ 0x10DF, 0x10DF, 0x10DF }, +{ 0x10E0, 0x10E0, 0x10E0 }, +{ 0x10E1, 0x10E1, 0x10E1 }, +{ 0x10E2, 0x10E2, 0x10E2 }, +{ 0x10E3, 0x10E3, 0x10E3 }, +{ 0x10E4, 0x10E4, 0x10E4 }, +{ 0x10E5, 0x10E5, 0x10E5 }, +{ 0x10E6, 0x10E6, 0x10E6 }, +{ 0x10E7, 0x10E7, 0x10E7 }, +{ 0x10E8, 0x10E8, 0x10E8 }, +{ 0x10E9, 0x10E9, 0x10E9 }, +{ 0x10EA, 0x10EA, 0x10EA }, +{ 0x10EB, 0x10EB, 0x10EB }, +{ 0x10EC, 0x10EC, 0x10EC }, +{ 0x10ED, 0x10ED, 0x10ED }, +{ 0x10EE, 0x10EE, 0x10EE }, +{ 0x10EF, 0x10EF, 0x10EF }, +{ 0x10F0, 0x10F0, 0x10F0 }, +{ 0x10F1, 0x10F1, 0x10F1 }, +{ 0x10F2, 0x10F2, 0x10F2 }, +{ 0x10F3, 0x10F3, 0x10F3 }, +{ 0x10F4, 0x10F4, 0x10F4 }, +{ 0x10F5, 0x10F5, 0x10F5 }, +{ 0x10F6, 0x10F6, 0x10F6 }, +{ 0x10F7, 0x10F7, 0x10F7 }, +{ 0x10F8, 0x10F8, 0x10F8 }, +{ 0x10F9, 0x10F9, 0x10F9 }, +{ 0x10FA, 0x10FA, 0x10FA }, +{ 0x10FC, 0x10FC, 0x10FC }, +{ 0x1100, 0x1100, 0x1100 }, +{ 0x1101, 0x1101, 0x1101 }, +{ 0x1102, 0x1102, 0x1102 }, +{ 0x1103, 0x1103, 0x1103 }, +{ 0x1104, 0x1104, 0x1104 }, +{ 0x1105, 0x1105, 0x1105 }, +{ 0x1106, 0x1106, 0x1106 }, +{ 0x1107, 0x1107, 0x1107 }, +{ 0x1108, 0x1108, 0x1108 }, +{ 0x1109, 0x1109, 0x1109 }, +{ 0x110A, 0x110A, 0x110A }, +{ 0x110B, 0x110B, 0x110B }, +{ 0x110C, 0x110C, 0x110C }, +{ 0x110D, 0x110D, 0x110D }, +{ 0x110E, 0x110E, 0x110E }, +{ 0x110F, 0x110F, 0x110F }, +{ 0x1110, 0x1110, 0x1110 }, +{ 0x1111, 0x1111, 0x1111 }, +{ 0x1112, 0x1112, 0x1112 }, +{ 0x1113, 0x1113, 0x1113 }, +{ 0x1114, 0x1114, 0x1114 }, +{ 0x1115, 0x1115, 0x1115 }, +{ 0x1116, 0x1116, 0x1116 }, +{ 0x1117, 0x1117, 0x1117 }, +{ 0x1118, 0x1118, 0x1118 }, +{ 0x1119, 0x1119, 0x1119 }, +{ 0x111A, 0x111A, 0x111A }, +{ 0x111B, 0x111B, 0x111B }, +{ 0x111C, 0x111C, 0x111C }, +{ 0x111D, 0x111D, 0x111D }, +{ 0x111E, 0x111E, 0x111E }, +{ 0x111F, 0x111F, 0x111F }, +{ 0x1120, 0x1120, 0x1120 }, +{ 0x1121, 0x1121, 0x1121 }, +{ 0x1122, 0x1122, 0x1122 }, +{ 0x1123, 0x1123, 0x1123 }, +{ 0x1124, 0x1124, 0x1124 }, +{ 0x1125, 0x1125, 0x1125 }, +{ 0x1126, 0x1126, 0x1126 }, +{ 0x1127, 0x1127, 0x1127 }, +{ 0x1128, 0x1128, 0x1128 }, +{ 0x1129, 0x1129, 0x1129 }, +{ 0x112A, 0x112A, 0x112A }, +{ 0x112B, 0x112B, 0x112B }, +{ 0x112C, 0x112C, 0x112C }, +{ 0x112D, 0x112D, 0x112D }, +{ 0x112E, 0x112E, 0x112E }, +{ 0x112F, 0x112F, 0x112F }, +{ 0x1130, 0x1130, 0x1130 }, +{ 0x1131, 0x1131, 0x1131 }, +{ 0x1132, 0x1132, 0x1132 }, +{ 0x1133, 0x1133, 0x1133 }, +{ 0x1134, 0x1134, 0x1134 }, +{ 0x1135, 0x1135, 0x1135 }, +{ 0x1136, 0x1136, 0x1136 }, +{ 0x1137, 0x1137, 0x1137 }, +{ 0x1138, 0x1138, 0x1138 }, +{ 0x1139, 0x1139, 0x1139 }, +{ 0x113A, 0x113A, 0x113A }, +{ 0x113B, 0x113B, 0x113B }, +{ 0x113C, 0x113C, 0x113C }, +{ 0x113D, 0x113D, 0x113D }, +{ 0x113E, 0x113E, 0x113E }, +{ 0x113F, 0x113F, 0x113F }, +{ 0x1140, 0x1140, 0x1140 }, +{ 0x1141, 0x1141, 0x1141 }, +{ 0x1142, 0x1142, 0x1142 }, +{ 0x1143, 0x1143, 0x1143 }, +{ 0x1144, 0x1144, 0x1144 }, +{ 0x1145, 0x1145, 0x1145 }, +{ 0x1146, 0x1146, 0x1146 }, +{ 0x1147, 0x1147, 0x1147 }, +{ 0x1148, 0x1148, 0x1148 }, +{ 0x1149, 0x1149, 0x1149 }, +{ 0x114A, 0x114A, 0x114A }, +{ 0x114B, 0x114B, 0x114B }, +{ 0x114C, 0x114C, 0x114C }, +{ 0x114D, 0x114D, 0x114D }, +{ 0x114E, 0x114E, 0x114E }, +{ 0x114F, 0x114F, 0x114F }, +{ 0x1150, 0x1150, 0x1150 }, +{ 0x1151, 0x1151, 0x1151 }, +{ 0x1152, 0x1152, 0x1152 }, +{ 0x1153, 0x1153, 0x1153 }, +{ 0x1154, 0x1154, 0x1154 }, +{ 0x1155, 0x1155, 0x1155 }, +{ 0x1156, 0x1156, 0x1156 }, +{ 0x1157, 0x1157, 0x1157 }, +{ 0x1158, 0x1158, 0x1158 }, +{ 0x1159, 0x1159, 0x1159 }, +{ 0x115F, 0x115F, 0x115F }, +{ 0x1160, 0x1160, 0x1160 }, +{ 0x1161, 0x1161, 0x1161 }, +{ 0x1162, 0x1162, 0x1162 }, +{ 0x1163, 0x1163, 0x1163 }, +{ 0x1164, 0x1164, 0x1164 }, +{ 0x1165, 0x1165, 0x1165 }, +{ 0x1166, 0x1166, 0x1166 }, +{ 0x1167, 0x1167, 0x1167 }, +{ 0x1168, 0x1168, 0x1168 }, +{ 0x1169, 0x1169, 0x1169 }, +{ 0x116A, 0x116A, 0x116A }, +{ 0x116B, 0x116B, 0x116B }, +{ 0x116C, 0x116C, 0x116C }, +{ 0x116D, 0x116D, 0x116D }, +{ 0x116E, 0x116E, 0x116E }, +{ 0x116F, 0x116F, 0x116F }, +{ 0x1170, 0x1170, 0x1170 }, +{ 0x1171, 0x1171, 0x1171 }, +{ 0x1172, 0x1172, 0x1172 }, +{ 0x1173, 0x1173, 0x1173 }, +{ 0x1174, 0x1174, 0x1174 }, +{ 0x1175, 0x1175, 0x1175 }, +{ 0x1176, 0x1176, 0x1176 }, +{ 0x1177, 0x1177, 0x1177 }, +{ 0x1178, 0x1178, 0x1178 }, +{ 0x1179, 0x1179, 0x1179 }, +{ 0x117A, 0x117A, 0x117A }, +{ 0x117B, 0x117B, 0x117B }, +{ 0x117C, 0x117C, 0x117C }, +{ 0x117D, 0x117D, 0x117D }, +{ 0x117E, 0x117E, 0x117E }, +{ 0x117F, 0x117F, 0x117F }, +{ 0x1180, 0x1180, 0x1180 }, +{ 0x1181, 0x1181, 0x1181 }, +{ 0x1182, 0x1182, 0x1182 }, +{ 0x1183, 0x1183, 0x1183 }, +{ 0x1184, 0x1184, 0x1184 }, +{ 0x1185, 0x1185, 0x1185 }, +{ 0x1186, 0x1186, 0x1186 }, +{ 0x1187, 0x1187, 0x1187 }, +{ 0x1188, 0x1188, 0x1188 }, +{ 0x1189, 0x1189, 0x1189 }, +{ 0x118A, 0x118A, 0x118A }, +{ 0x118B, 0x118B, 0x118B }, +{ 0x118C, 0x118C, 0x118C }, +{ 0x118D, 0x118D, 0x118D }, +{ 0x118E, 0x118E, 0x118E }, +{ 0x118F, 0x118F, 0x118F }, +{ 0x1190, 0x1190, 0x1190 }, +{ 0x1191, 0x1191, 0x1191 }, +{ 0x1192, 0x1192, 0x1192 }, +{ 0x1193, 0x1193, 0x1193 }, +{ 0x1194, 0x1194, 0x1194 }, +{ 0x1195, 0x1195, 0x1195 }, +{ 0x1196, 0x1196, 0x1196 }, +{ 0x1197, 0x1197, 0x1197 }, +{ 0x1198, 0x1198, 0x1198 }, +{ 0x1199, 0x1199, 0x1199 }, +{ 0x119A, 0x119A, 0x119A }, +{ 0x119B, 0x119B, 0x119B }, +{ 0x119C, 0x119C, 0x119C }, +{ 0x119D, 0x119D, 0x119D }, +{ 0x119E, 0x119E, 0x119E }, +{ 0x119F, 0x119F, 0x119F }, +{ 0x11A0, 0x11A0, 0x11A0 }, +{ 0x11A1, 0x11A1, 0x11A1 }, +{ 0x11A2, 0x11A2, 0x11A2 }, +{ 0x11A8, 0x11A8, 0x11A8 }, +{ 0x11A9, 0x11A9, 0x11A9 }, +{ 0x11AA, 0x11AA, 0x11AA }, +{ 0x11AB, 0x11AB, 0x11AB }, +{ 0x11AC, 0x11AC, 0x11AC }, +{ 0x11AD, 0x11AD, 0x11AD }, +{ 0x11AE, 0x11AE, 0x11AE }, +{ 0x11AF, 0x11AF, 0x11AF }, +{ 0x11B0, 0x11B0, 0x11B0 }, +{ 0x11B1, 0x11B1, 0x11B1 }, +{ 0x11B2, 0x11B2, 0x11B2 }, +{ 0x11B3, 0x11B3, 0x11B3 }, +{ 0x11B4, 0x11B4, 0x11B4 }, +{ 0x11B5, 0x11B5, 0x11B5 }, +{ 0x11B6, 0x11B6, 0x11B6 }, +{ 0x11B7, 0x11B7, 0x11B7 }, +{ 0x11B8, 0x11B8, 0x11B8 }, +{ 0x11B9, 0x11B9, 0x11B9 }, +{ 0x11BA, 0x11BA, 0x11BA }, +{ 0x11BB, 0x11BB, 0x11BB }, +{ 0x11BC, 0x11BC, 0x11BC }, +{ 0x11BD, 0x11BD, 0x11BD }, +{ 0x11BE, 0x11BE, 0x11BE }, +{ 0x11BF, 0x11BF, 0x11BF }, +{ 0x11C0, 0x11C0, 0x11C0 }, +{ 0x11C1, 0x11C1, 0x11C1 }, +{ 0x11C2, 0x11C2, 0x11C2 }, +{ 0x11C3, 0x11C3, 0x11C3 }, +{ 0x11C4, 0x11C4, 0x11C4 }, +{ 0x11C5, 0x11C5, 0x11C5 }, +{ 0x11C6, 0x11C6, 0x11C6 }, +{ 0x11C7, 0x11C7, 0x11C7 }, +{ 0x11C8, 0x11C8, 0x11C8 }, +{ 0x11C9, 0x11C9, 0x11C9 }, +{ 0x11CA, 0x11CA, 0x11CA }, +{ 0x11CB, 0x11CB, 0x11CB }, +{ 0x11CC, 0x11CC, 0x11CC }, +{ 0x11CD, 0x11CD, 0x11CD }, +{ 0x11CE, 0x11CE, 0x11CE }, +{ 0x11CF, 0x11CF, 0x11CF }, +{ 0x11D0, 0x11D0, 0x11D0 }, +{ 0x11D1, 0x11D1, 0x11D1 }, +{ 0x11D2, 0x11D2, 0x11D2 }, +{ 0x11D3, 0x11D3, 0x11D3 }, +{ 0x11D4, 0x11D4, 0x11D4 }, +{ 0x11D5, 0x11D5, 0x11D5 }, +{ 0x11D6, 0x11D6, 0x11D6 }, +{ 0x11D7, 0x11D7, 0x11D7 }, +{ 0x11D8, 0x11D8, 0x11D8 }, +{ 0x11D9, 0x11D9, 0x11D9 }, +{ 0x11DA, 0x11DA, 0x11DA }, +{ 0x11DB, 0x11DB, 0x11DB }, +{ 0x11DC, 0x11DC, 0x11DC }, +{ 0x11DD, 0x11DD, 0x11DD }, +{ 0x11DE, 0x11DE, 0x11DE }, +{ 0x11DF, 0x11DF, 0x11DF }, +{ 0x11E0, 0x11E0, 0x11E0 }, +{ 0x11E1, 0x11E1, 0x11E1 }, +{ 0x11E2, 0x11E2, 0x11E2 }, +{ 0x11E3, 0x11E3, 0x11E3 }, +{ 0x11E4, 0x11E4, 0x11E4 }, +{ 0x11E5, 0x11E5, 0x11E5 }, +{ 0x11E6, 0x11E6, 0x11E6 }, +{ 0x11E7, 0x11E7, 0x11E7 }, +{ 0x11E8, 0x11E8, 0x11E8 }, +{ 0x11E9, 0x11E9, 0x11E9 }, +{ 0x11EA, 0x11EA, 0x11EA }, +{ 0x11EB, 0x11EB, 0x11EB }, +{ 0x11EC, 0x11EC, 0x11EC }, +{ 0x11ED, 0x11ED, 0x11ED }, +{ 0x11EE, 0x11EE, 0x11EE }, +{ 0x11EF, 0x11EF, 0x11EF }, +{ 0x11F0, 0x11F0, 0x11F0 }, +{ 0x11F1, 0x11F1, 0x11F1 }, +{ 0x11F2, 0x11F2, 0x11F2 }, +{ 0x11F3, 0x11F3, 0x11F3 }, +{ 0x11F4, 0x11F4, 0x11F4 }, +{ 0x11F5, 0x11F5, 0x11F5 }, +{ 0x11F6, 0x11F6, 0x11F6 }, +{ 0x11F7, 0x11F7, 0x11F7 }, +{ 0x11F8, 0x11F8, 0x11F8 }, +{ 0x11F9, 0x11F9, 0x11F9 }, +{ 0x1200, 0x1200, 0x1200 }, +{ 0x1201, 0x1201, 0x1201 }, +{ 0x1202, 0x1202, 0x1202 }, +{ 0x1203, 0x1203, 0x1203 }, +{ 0x1204, 0x1204, 0x1204 }, +{ 0x1205, 0x1205, 0x1205 }, +{ 0x1206, 0x1206, 0x1206 }, +{ 0x1207, 0x1207, 0x1207 }, +{ 0x1208, 0x1208, 0x1208 }, +{ 0x1209, 0x1209, 0x1209 }, +{ 0x120A, 0x120A, 0x120A }, +{ 0x120B, 0x120B, 0x120B }, +{ 0x120C, 0x120C, 0x120C }, +{ 0x120D, 0x120D, 0x120D }, +{ 0x120E, 0x120E, 0x120E }, +{ 0x120F, 0x120F, 0x120F }, +{ 0x1210, 0x1210, 0x1210 }, +{ 0x1211, 0x1211, 0x1211 }, +{ 0x1212, 0x1212, 0x1212 }, +{ 0x1213, 0x1213, 0x1213 }, +{ 0x1214, 0x1214, 0x1214 }, +{ 0x1215, 0x1215, 0x1215 }, +{ 0x1216, 0x1216, 0x1216 }, +{ 0x1217, 0x1217, 0x1217 }, +{ 0x1218, 0x1218, 0x1218 }, +{ 0x1219, 0x1219, 0x1219 }, +{ 0x121A, 0x121A, 0x121A }, +{ 0x121B, 0x121B, 0x121B }, +{ 0x121C, 0x121C, 0x121C }, +{ 0x121D, 0x121D, 0x121D }, +{ 0x121E, 0x121E, 0x121E }, +{ 0x121F, 0x121F, 0x121F }, +{ 0x1220, 0x1220, 0x1220 }, +{ 0x1221, 0x1221, 0x1221 }, +{ 0x1222, 0x1222, 0x1222 }, +{ 0x1223, 0x1223, 0x1223 }, +{ 0x1224, 0x1224, 0x1224 }, +{ 0x1225, 0x1225, 0x1225 }, +{ 0x1226, 0x1226, 0x1226 }, +{ 0x1227, 0x1227, 0x1227 }, +{ 0x1228, 0x1228, 0x1228 }, +{ 0x1229, 0x1229, 0x1229 }, +{ 0x122A, 0x122A, 0x122A }, +{ 0x122B, 0x122B, 0x122B }, +{ 0x122C, 0x122C, 0x122C }, +{ 0x122D, 0x122D, 0x122D }, +{ 0x122E, 0x122E, 0x122E }, +{ 0x122F, 0x122F, 0x122F }, +{ 0x1230, 0x1230, 0x1230 }, +{ 0x1231, 0x1231, 0x1231 }, +{ 0x1232, 0x1232, 0x1232 }, +{ 0x1233, 0x1233, 0x1233 }, +{ 0x1234, 0x1234, 0x1234 }, +{ 0x1235, 0x1235, 0x1235 }, +{ 0x1236, 0x1236, 0x1236 }, +{ 0x1237, 0x1237, 0x1237 }, +{ 0x1238, 0x1238, 0x1238 }, +{ 0x1239, 0x1239, 0x1239 }, +{ 0x123A, 0x123A, 0x123A }, +{ 0x123B, 0x123B, 0x123B }, +{ 0x123C, 0x123C, 0x123C }, +{ 0x123D, 0x123D, 0x123D }, +{ 0x123E, 0x123E, 0x123E }, +{ 0x123F, 0x123F, 0x123F }, +{ 0x1240, 0x1240, 0x1240 }, +{ 0x1241, 0x1241, 0x1241 }, +{ 0x1242, 0x1242, 0x1242 }, +{ 0x1243, 0x1243, 0x1243 }, +{ 0x1244, 0x1244, 0x1244 }, +{ 0x1245, 0x1245, 0x1245 }, +{ 0x1246, 0x1246, 0x1246 }, +{ 0x1247, 0x1247, 0x1247 }, +{ 0x1248, 0x1248, 0x1248 }, +{ 0x124A, 0x124A, 0x124A }, +{ 0x124B, 0x124B, 0x124B }, +{ 0x124C, 0x124C, 0x124C }, +{ 0x124D, 0x124D, 0x124D }, +{ 0x1250, 0x1250, 0x1250 }, +{ 0x1251, 0x1251, 0x1251 }, +{ 0x1252, 0x1252, 0x1252 }, +{ 0x1253, 0x1253, 0x1253 }, +{ 0x1254, 0x1254, 0x1254 }, +{ 0x1255, 0x1255, 0x1255 }, +{ 0x1256, 0x1256, 0x1256 }, +{ 0x1258, 0x1258, 0x1258 }, +{ 0x125A, 0x125A, 0x125A }, +{ 0x125B, 0x125B, 0x125B }, +{ 0x125C, 0x125C, 0x125C }, +{ 0x125D, 0x125D, 0x125D }, +{ 0x1260, 0x1260, 0x1260 }, +{ 0x1261, 0x1261, 0x1261 }, +{ 0x1262, 0x1262, 0x1262 }, +{ 0x1263, 0x1263, 0x1263 }, +{ 0x1264, 0x1264, 0x1264 }, +{ 0x1265, 0x1265, 0x1265 }, +{ 0x1266, 0x1266, 0x1266 }, +{ 0x1267, 0x1267, 0x1267 }, +{ 0x1268, 0x1268, 0x1268 }, +{ 0x1269, 0x1269, 0x1269 }, +{ 0x126A, 0x126A, 0x126A }, +{ 0x126B, 0x126B, 0x126B }, +{ 0x126C, 0x126C, 0x126C }, +{ 0x126D, 0x126D, 0x126D }, +{ 0x126E, 0x126E, 0x126E }, +{ 0x126F, 0x126F, 0x126F }, +{ 0x1270, 0x1270, 0x1270 }, +{ 0x1271, 0x1271, 0x1271 }, +{ 0x1272, 0x1272, 0x1272 }, +{ 0x1273, 0x1273, 0x1273 }, +{ 0x1274, 0x1274, 0x1274 }, +{ 0x1275, 0x1275, 0x1275 }, +{ 0x1276, 0x1276, 0x1276 }, +{ 0x1277, 0x1277, 0x1277 }, +{ 0x1278, 0x1278, 0x1278 }, +{ 0x1279, 0x1279, 0x1279 }, +{ 0x127A, 0x127A, 0x127A }, +{ 0x127B, 0x127B, 0x127B }, +{ 0x127C, 0x127C, 0x127C }, +{ 0x127D, 0x127D, 0x127D }, +{ 0x127E, 0x127E, 0x127E }, +{ 0x127F, 0x127F, 0x127F }, +{ 0x1280, 0x1280, 0x1280 }, +{ 0x1281, 0x1281, 0x1281 }, +{ 0x1282, 0x1282, 0x1282 }, +{ 0x1283, 0x1283, 0x1283 }, +{ 0x1284, 0x1284, 0x1284 }, +{ 0x1285, 0x1285, 0x1285 }, +{ 0x1286, 0x1286, 0x1286 }, +{ 0x1287, 0x1287, 0x1287 }, +{ 0x1288, 0x1288, 0x1288 }, +{ 0x128A, 0x128A, 0x128A }, +{ 0x128B, 0x128B, 0x128B }, +{ 0x128C, 0x128C, 0x128C }, +{ 0x128D, 0x128D, 0x128D }, +{ 0x1290, 0x1290, 0x1290 }, +{ 0x1291, 0x1291, 0x1291 }, +{ 0x1292, 0x1292, 0x1292 }, +{ 0x1293, 0x1293, 0x1293 }, +{ 0x1294, 0x1294, 0x1294 }, +{ 0x1295, 0x1295, 0x1295 }, +{ 0x1296, 0x1296, 0x1296 }, +{ 0x1297, 0x1297, 0x1297 }, +{ 0x1298, 0x1298, 0x1298 }, +{ 0x1299, 0x1299, 0x1299 }, +{ 0x129A, 0x129A, 0x129A }, +{ 0x129B, 0x129B, 0x129B }, +{ 0x129C, 0x129C, 0x129C }, +{ 0x129D, 0x129D, 0x129D }, +{ 0x129E, 0x129E, 0x129E }, +{ 0x129F, 0x129F, 0x129F }, +{ 0x12A0, 0x12A0, 0x12A0 }, +{ 0x12A1, 0x12A1, 0x12A1 }, +{ 0x12A2, 0x12A2, 0x12A2 }, +{ 0x12A3, 0x12A3, 0x12A3 }, +{ 0x12A4, 0x12A4, 0x12A4 }, +{ 0x12A5, 0x12A5, 0x12A5 }, +{ 0x12A6, 0x12A6, 0x12A6 }, +{ 0x12A7, 0x12A7, 0x12A7 }, +{ 0x12A8, 0x12A8, 0x12A8 }, +{ 0x12A9, 0x12A9, 0x12A9 }, +{ 0x12AA, 0x12AA, 0x12AA }, +{ 0x12AB, 0x12AB, 0x12AB }, +{ 0x12AC, 0x12AC, 0x12AC }, +{ 0x12AD, 0x12AD, 0x12AD }, +{ 0x12AE, 0x12AE, 0x12AE }, +{ 0x12AF, 0x12AF, 0x12AF }, +{ 0x12B0, 0x12B0, 0x12B0 }, +{ 0x12B2, 0x12B2, 0x12B2 }, +{ 0x12B3, 0x12B3, 0x12B3 }, +{ 0x12B4, 0x12B4, 0x12B4 }, +{ 0x12B5, 0x12B5, 0x12B5 }, +{ 0x12B8, 0x12B8, 0x12B8 }, +{ 0x12B9, 0x12B9, 0x12B9 }, +{ 0x12BA, 0x12BA, 0x12BA }, +{ 0x12BB, 0x12BB, 0x12BB }, +{ 0x12BC, 0x12BC, 0x12BC }, +{ 0x12BD, 0x12BD, 0x12BD }, +{ 0x12BE, 0x12BE, 0x12BE }, +{ 0x12C0, 0x12C0, 0x12C0 }, +{ 0x12C2, 0x12C2, 0x12C2 }, +{ 0x12C3, 0x12C3, 0x12C3 }, +{ 0x12C4, 0x12C4, 0x12C4 }, +{ 0x12C5, 0x12C5, 0x12C5 }, +{ 0x12C8, 0x12C8, 0x12C8 }, +{ 0x12C9, 0x12C9, 0x12C9 }, +{ 0x12CA, 0x12CA, 0x12CA }, +{ 0x12CB, 0x12CB, 0x12CB }, +{ 0x12CC, 0x12CC, 0x12CC }, +{ 0x12CD, 0x12CD, 0x12CD }, +{ 0x12CE, 0x12CE, 0x12CE }, +{ 0x12CF, 0x12CF, 0x12CF }, +{ 0x12D0, 0x12D0, 0x12D0 }, +{ 0x12D1, 0x12D1, 0x12D1 }, +{ 0x12D2, 0x12D2, 0x12D2 }, +{ 0x12D3, 0x12D3, 0x12D3 }, +{ 0x12D4, 0x12D4, 0x12D4 }, +{ 0x12D5, 0x12D5, 0x12D5 }, +{ 0x12D6, 0x12D6, 0x12D6 }, +{ 0x12D8, 0x12D8, 0x12D8 }, +{ 0x12D9, 0x12D9, 0x12D9 }, +{ 0x12DA, 0x12DA, 0x12DA }, +{ 0x12DB, 0x12DB, 0x12DB }, +{ 0x12DC, 0x12DC, 0x12DC }, +{ 0x12DD, 0x12DD, 0x12DD }, +{ 0x12DE, 0x12DE, 0x12DE }, +{ 0x12DF, 0x12DF, 0x12DF }, +{ 0x12E0, 0x12E0, 0x12E0 }, +{ 0x12E1, 0x12E1, 0x12E1 }, +{ 0x12E2, 0x12E2, 0x12E2 }, +{ 0x12E3, 0x12E3, 0x12E3 }, +{ 0x12E4, 0x12E4, 0x12E4 }, +{ 0x12E5, 0x12E5, 0x12E5 }, +{ 0x12E6, 0x12E6, 0x12E6 }, +{ 0x12E7, 0x12E7, 0x12E7 }, +{ 0x12E8, 0x12E8, 0x12E8 }, +{ 0x12E9, 0x12E9, 0x12E9 }, +{ 0x12EA, 0x12EA, 0x12EA }, +{ 0x12EB, 0x12EB, 0x12EB }, +{ 0x12EC, 0x12EC, 0x12EC }, +{ 0x12ED, 0x12ED, 0x12ED }, +{ 0x12EE, 0x12EE, 0x12EE }, +{ 0x12EF, 0x12EF, 0x12EF }, +{ 0x12F0, 0x12F0, 0x12F0 }, +{ 0x12F1, 0x12F1, 0x12F1 }, +{ 0x12F2, 0x12F2, 0x12F2 }, +{ 0x12F3, 0x12F3, 0x12F3 }, +{ 0x12F4, 0x12F4, 0x12F4 }, +{ 0x12F5, 0x12F5, 0x12F5 }, +{ 0x12F6, 0x12F6, 0x12F6 }, +{ 0x12F7, 0x12F7, 0x12F7 }, +{ 0x12F8, 0x12F8, 0x12F8 }, +{ 0x12F9, 0x12F9, 0x12F9 }, +{ 0x12FA, 0x12FA, 0x12FA }, +{ 0x12FB, 0x12FB, 0x12FB }, +{ 0x12FC, 0x12FC, 0x12FC }, +{ 0x12FD, 0x12FD, 0x12FD }, +{ 0x12FE, 0x12FE, 0x12FE }, +{ 0x12FF, 0x12FF, 0x12FF }, +{ 0x1300, 0x1300, 0x1300 }, +{ 0x1301, 0x1301, 0x1301 }, +{ 0x1302, 0x1302, 0x1302 }, +{ 0x1303, 0x1303, 0x1303 }, +{ 0x1304, 0x1304, 0x1304 }, +{ 0x1305, 0x1305, 0x1305 }, +{ 0x1306, 0x1306, 0x1306 }, +{ 0x1307, 0x1307, 0x1307 }, +{ 0x1308, 0x1308, 0x1308 }, +{ 0x1309, 0x1309, 0x1309 }, +{ 0x130A, 0x130A, 0x130A }, +{ 0x130B, 0x130B, 0x130B }, +{ 0x130C, 0x130C, 0x130C }, +{ 0x130D, 0x130D, 0x130D }, +{ 0x130E, 0x130E, 0x130E }, +{ 0x130F, 0x130F, 0x130F }, +{ 0x1310, 0x1310, 0x1310 }, +{ 0x1312, 0x1312, 0x1312 }, +{ 0x1313, 0x1313, 0x1313 }, +{ 0x1314, 0x1314, 0x1314 }, +{ 0x1315, 0x1315, 0x1315 }, +{ 0x1318, 0x1318, 0x1318 }, +{ 0x1319, 0x1319, 0x1319 }, +{ 0x131A, 0x131A, 0x131A }, +{ 0x131B, 0x131B, 0x131B }, +{ 0x131C, 0x131C, 0x131C }, +{ 0x131D, 0x131D, 0x131D }, +{ 0x131E, 0x131E, 0x131E }, +{ 0x131F, 0x131F, 0x131F }, +{ 0x1320, 0x1320, 0x1320 }, +{ 0x1321, 0x1321, 0x1321 }, +{ 0x1322, 0x1322, 0x1322 }, +{ 0x1323, 0x1323, 0x1323 }, +{ 0x1324, 0x1324, 0x1324 }, +{ 0x1325, 0x1325, 0x1325 }, +{ 0x1326, 0x1326, 0x1326 }, +{ 0x1327, 0x1327, 0x1327 }, +{ 0x1328, 0x1328, 0x1328 }, +{ 0x1329, 0x1329, 0x1329 }, +{ 0x132A, 0x132A, 0x132A }, +{ 0x132B, 0x132B, 0x132B }, +{ 0x132C, 0x132C, 0x132C }, +{ 0x132D, 0x132D, 0x132D }, +{ 0x132E, 0x132E, 0x132E }, +{ 0x132F, 0x132F, 0x132F }, +{ 0x1330, 0x1330, 0x1330 }, +{ 0x1331, 0x1331, 0x1331 }, +{ 0x1332, 0x1332, 0x1332 }, +{ 0x1333, 0x1333, 0x1333 }, +{ 0x1334, 0x1334, 0x1334 }, +{ 0x1335, 0x1335, 0x1335 }, +{ 0x1336, 0x1336, 0x1336 }, +{ 0x1337, 0x1337, 0x1337 }, +{ 0x1338, 0x1338, 0x1338 }, +{ 0x1339, 0x1339, 0x1339 }, +{ 0x133A, 0x133A, 0x133A }, +{ 0x133B, 0x133B, 0x133B }, +{ 0x133C, 0x133C, 0x133C }, +{ 0x133D, 0x133D, 0x133D }, +{ 0x133E, 0x133E, 0x133E }, +{ 0x133F, 0x133F, 0x133F }, +{ 0x1340, 0x1340, 0x1340 }, +{ 0x1341, 0x1341, 0x1341 }, +{ 0x1342, 0x1342, 0x1342 }, +{ 0x1343, 0x1343, 0x1343 }, +{ 0x1344, 0x1344, 0x1344 }, +{ 0x1345, 0x1345, 0x1345 }, +{ 0x1346, 0x1346, 0x1346 }, +{ 0x1347, 0x1347, 0x1347 }, +{ 0x1348, 0x1348, 0x1348 }, +{ 0x1349, 0x1349, 0x1349 }, +{ 0x134A, 0x134A, 0x134A }, +{ 0x134B, 0x134B, 0x134B }, +{ 0x134C, 0x134C, 0x134C }, +{ 0x134D, 0x134D, 0x134D }, +{ 0x134E, 0x134E, 0x134E }, +{ 0x134F, 0x134F, 0x134F }, +{ 0x1350, 0x1350, 0x1350 }, +{ 0x1351, 0x1351, 0x1351 }, +{ 0x1352, 0x1352, 0x1352 }, +{ 0x1353, 0x1353, 0x1353 }, +{ 0x1354, 0x1354, 0x1354 }, +{ 0x1355, 0x1355, 0x1355 }, +{ 0x1356, 0x1356, 0x1356 }, +{ 0x1357, 0x1357, 0x1357 }, +{ 0x1358, 0x1358, 0x1358 }, +{ 0x1359, 0x1359, 0x1359 }, +{ 0x135A, 0x135A, 0x135A }, +{ 0x135F, 0x135F, 0x135F }, +{ 0x1380, 0x1380, 0x1380 }, +{ 0x1381, 0x1381, 0x1381 }, +{ 0x1382, 0x1382, 0x1382 }, +{ 0x1383, 0x1383, 0x1383 }, +{ 0x1384, 0x1384, 0x1384 }, +{ 0x1385, 0x1385, 0x1385 }, +{ 0x1386, 0x1386, 0x1386 }, +{ 0x1387, 0x1387, 0x1387 }, +{ 0x1388, 0x1388, 0x1388 }, +{ 0x1389, 0x1389, 0x1389 }, +{ 0x138A, 0x138A, 0x138A }, +{ 0x138B, 0x138B, 0x138B }, +{ 0x138C, 0x138C, 0x138C }, +{ 0x138D, 0x138D, 0x138D }, +{ 0x138E, 0x138E, 0x138E }, +{ 0x138F, 0x138F, 0x138F }, +{ 0x13A0, 0x13A0, 0x13A0 }, +{ 0x13A1, 0x13A1, 0x13A1 }, +{ 0x13A2, 0x13A2, 0x13A2 }, +{ 0x13A3, 0x13A3, 0x13A3 }, +{ 0x13A4, 0x13A4, 0x13A4 }, +{ 0x13A5, 0x13A5, 0x13A5 }, +{ 0x13A6, 0x13A6, 0x13A6 }, +{ 0x13A7, 0x13A7, 0x13A7 }, +{ 0x13A8, 0x13A8, 0x13A8 }, +{ 0x13A9, 0x13A9, 0x13A9 }, +{ 0x13AA, 0x13AA, 0x13AA }, +{ 0x13AB, 0x13AB, 0x13AB }, +{ 0x13AC, 0x13AC, 0x13AC }, +{ 0x13AD, 0x13AD, 0x13AD }, +{ 0x13AE, 0x13AE, 0x13AE }, +{ 0x13AF, 0x13AF, 0x13AF }, +{ 0x13B0, 0x13B0, 0x13B0 }, +{ 0x13B1, 0x13B1, 0x13B1 }, +{ 0x13B2, 0x13B2, 0x13B2 }, +{ 0x13B3, 0x13B3, 0x13B3 }, +{ 0x13B4, 0x13B4, 0x13B4 }, +{ 0x13B5, 0x13B5, 0x13B5 }, +{ 0x13B6, 0x13B6, 0x13B6 }, +{ 0x13B7, 0x13B7, 0x13B7 }, +{ 0x13B8, 0x13B8, 0x13B8 }, +{ 0x13B9, 0x13B9, 0x13B9 }, +{ 0x13BA, 0x13BA, 0x13BA }, +{ 0x13BB, 0x13BB, 0x13BB }, +{ 0x13BC, 0x13BC, 0x13BC }, +{ 0x13BD, 0x13BD, 0x13BD }, +{ 0x13BE, 0x13BE, 0x13BE }, +{ 0x13BF, 0x13BF, 0x13BF }, +{ 0x13C0, 0x13C0, 0x13C0 }, +{ 0x13C1, 0x13C1, 0x13C1 }, +{ 0x13C2, 0x13C2, 0x13C2 }, +{ 0x13C3, 0x13C3, 0x13C3 }, +{ 0x13C4, 0x13C4, 0x13C4 }, +{ 0x13C5, 0x13C5, 0x13C5 }, +{ 0x13C6, 0x13C6, 0x13C6 }, +{ 0x13C7, 0x13C7, 0x13C7 }, +{ 0x13C8, 0x13C8, 0x13C8 }, +{ 0x13C9, 0x13C9, 0x13C9 }, +{ 0x13CA, 0x13CA, 0x13CA }, +{ 0x13CB, 0x13CB, 0x13CB }, +{ 0x13CC, 0x13CC, 0x13CC }, +{ 0x13CD, 0x13CD, 0x13CD }, +{ 0x13CE, 0x13CE, 0x13CE }, +{ 0x13CF, 0x13CF, 0x13CF }, +{ 0x13D0, 0x13D0, 0x13D0 }, +{ 0x13D1, 0x13D1, 0x13D1 }, +{ 0x13D2, 0x13D2, 0x13D2 }, +{ 0x13D3, 0x13D3, 0x13D3 }, +{ 0x13D4, 0x13D4, 0x13D4 }, +{ 0x13D5, 0x13D5, 0x13D5 }, +{ 0x13D6, 0x13D6, 0x13D6 }, +{ 0x13D7, 0x13D7, 0x13D7 }, +{ 0x13D8, 0x13D8, 0x13D8 }, +{ 0x13D9, 0x13D9, 0x13D9 }, +{ 0x13DA, 0x13DA, 0x13DA }, +{ 0x13DB, 0x13DB, 0x13DB }, +{ 0x13DC, 0x13DC, 0x13DC }, +{ 0x13DD, 0x13DD, 0x13DD }, +{ 0x13DE, 0x13DE, 0x13DE }, +{ 0x13DF, 0x13DF, 0x13DF }, +{ 0x13E0, 0x13E0, 0x13E0 }, +{ 0x13E1, 0x13E1, 0x13E1 }, +{ 0x13E2, 0x13E2, 0x13E2 }, +{ 0x13E3, 0x13E3, 0x13E3 }, +{ 0x13E4, 0x13E4, 0x13E4 }, +{ 0x13E5, 0x13E5, 0x13E5 }, +{ 0x13E6, 0x13E6, 0x13E6 }, +{ 0x13E7, 0x13E7, 0x13E7 }, +{ 0x13E8, 0x13E8, 0x13E8 }, +{ 0x13E9, 0x13E9, 0x13E9 }, +{ 0x13EA, 0x13EA, 0x13EA }, +{ 0x13EB, 0x13EB, 0x13EB }, +{ 0x13EC, 0x13EC, 0x13EC }, +{ 0x13ED, 0x13ED, 0x13ED }, +{ 0x13EE, 0x13EE, 0x13EE }, +{ 0x13EF, 0x13EF, 0x13EF }, +{ 0x13F0, 0x13F0, 0x13F0 }, +{ 0x13F1, 0x13F1, 0x13F1 }, +{ 0x13F2, 0x13F2, 0x13F2 }, +{ 0x13F3, 0x13F3, 0x13F3 }, +{ 0x13F4, 0x13F4, 0x13F4 }, +{ 0x1401, 0x1401, 0x1401 }, +{ 0x1402, 0x1402, 0x1402 }, +{ 0x1403, 0x1403, 0x1403 }, +{ 0x1404, 0x1404, 0x1404 }, +{ 0x1405, 0x1405, 0x1405 }, +{ 0x1406, 0x1406, 0x1406 }, +{ 0x1407, 0x1407, 0x1407 }, +{ 0x1408, 0x1408, 0x1408 }, +{ 0x1409, 0x1409, 0x1409 }, +{ 0x140A, 0x140A, 0x140A }, +{ 0x140B, 0x140B, 0x140B }, +{ 0x140C, 0x140C, 0x140C }, +{ 0x140D, 0x140D, 0x140D }, +{ 0x140E, 0x140E, 0x140E }, +{ 0x140F, 0x140F, 0x140F }, +{ 0x1410, 0x1410, 0x1410 }, +{ 0x1411, 0x1411, 0x1411 }, +{ 0x1412, 0x1412, 0x1412 }, +{ 0x1413, 0x1413, 0x1413 }, +{ 0x1414, 0x1414, 0x1414 }, +{ 0x1415, 0x1415, 0x1415 }, +{ 0x1416, 0x1416, 0x1416 }, +{ 0x1417, 0x1417, 0x1417 }, +{ 0x1418, 0x1418, 0x1418 }, +{ 0x1419, 0x1419, 0x1419 }, +{ 0x141A, 0x141A, 0x141A }, +{ 0x141B, 0x141B, 0x141B }, +{ 0x141C, 0x141C, 0x141C }, +{ 0x141D, 0x141D, 0x141D }, +{ 0x141E, 0x141E, 0x141E }, +{ 0x141F, 0x141F, 0x141F }, +{ 0x1420, 0x1420, 0x1420 }, +{ 0x1421, 0x1421, 0x1421 }, +{ 0x1422, 0x1422, 0x1422 }, +{ 0x1423, 0x1423, 0x1423 }, +{ 0x1424, 0x1424, 0x1424 }, +{ 0x1425, 0x1425, 0x1425 }, +{ 0x1426, 0x1426, 0x1426 }, +{ 0x1427, 0x1427, 0x1427 }, +{ 0x1428, 0x1428, 0x1428 }, +{ 0x1429, 0x1429, 0x1429 }, +{ 0x142A, 0x142A, 0x142A }, +{ 0x142B, 0x142B, 0x142B }, +{ 0x142C, 0x142C, 0x142C }, +{ 0x142D, 0x142D, 0x142D }, +{ 0x142E, 0x142E, 0x142E }, +{ 0x142F, 0x142F, 0x142F }, +{ 0x1430, 0x1430, 0x1430 }, +{ 0x1431, 0x1431, 0x1431 }, +{ 0x1432, 0x1432, 0x1432 }, +{ 0x1433, 0x1433, 0x1433 }, +{ 0x1434, 0x1434, 0x1434 }, +{ 0x1435, 0x1435, 0x1435 }, +{ 0x1436, 0x1436, 0x1436 }, +{ 0x1437, 0x1437, 0x1437 }, +{ 0x1438, 0x1438, 0x1438 }, +{ 0x1439, 0x1439, 0x1439 }, +{ 0x143A, 0x143A, 0x143A }, +{ 0x143B, 0x143B, 0x143B }, +{ 0x143C, 0x143C, 0x143C }, +{ 0x143D, 0x143D, 0x143D }, +{ 0x143E, 0x143E, 0x143E }, +{ 0x143F, 0x143F, 0x143F }, +{ 0x1440, 0x1440, 0x1440 }, +{ 0x1441, 0x1441, 0x1441 }, +{ 0x1442, 0x1442, 0x1442 }, +{ 0x1443, 0x1443, 0x1443 }, +{ 0x1444, 0x1444, 0x1444 }, +{ 0x1445, 0x1445, 0x1445 }, +{ 0x1446, 0x1446, 0x1446 }, +{ 0x1447, 0x1447, 0x1447 }, +{ 0x1448, 0x1448, 0x1448 }, +{ 0x1449, 0x1449, 0x1449 }, +{ 0x144A, 0x144A, 0x144A }, +{ 0x144B, 0x144B, 0x144B }, +{ 0x144C, 0x144C, 0x144C }, +{ 0x144D, 0x144D, 0x144D }, +{ 0x144E, 0x144E, 0x144E }, +{ 0x144F, 0x144F, 0x144F }, +{ 0x1450, 0x1450, 0x1450 }, +{ 0x1451, 0x1451, 0x1451 }, +{ 0x1452, 0x1452, 0x1452 }, +{ 0x1453, 0x1453, 0x1453 }, +{ 0x1454, 0x1454, 0x1454 }, +{ 0x1455, 0x1455, 0x1455 }, +{ 0x1456, 0x1456, 0x1456 }, +{ 0x1457, 0x1457, 0x1457 }, +{ 0x1458, 0x1458, 0x1458 }, +{ 0x1459, 0x1459, 0x1459 }, +{ 0x145A, 0x145A, 0x145A }, +{ 0x145B, 0x145B, 0x145B }, +{ 0x145C, 0x145C, 0x145C }, +{ 0x145D, 0x145D, 0x145D }, +{ 0x145E, 0x145E, 0x145E }, +{ 0x145F, 0x145F, 0x145F }, +{ 0x1460, 0x1460, 0x1460 }, +{ 0x1461, 0x1461, 0x1461 }, +{ 0x1462, 0x1462, 0x1462 }, +{ 0x1463, 0x1463, 0x1463 }, +{ 0x1464, 0x1464, 0x1464 }, +{ 0x1465, 0x1465, 0x1465 }, +{ 0x1466, 0x1466, 0x1466 }, +{ 0x1467, 0x1467, 0x1467 }, +{ 0x1468, 0x1468, 0x1468 }, +{ 0x1469, 0x1469, 0x1469 }, +{ 0x146A, 0x146A, 0x146A }, +{ 0x146B, 0x146B, 0x146B }, +{ 0x146C, 0x146C, 0x146C }, +{ 0x146D, 0x146D, 0x146D }, +{ 0x146E, 0x146E, 0x146E }, +{ 0x146F, 0x146F, 0x146F }, +{ 0x1470, 0x1470, 0x1470 }, +{ 0x1471, 0x1471, 0x1471 }, +{ 0x1472, 0x1472, 0x1472 }, +{ 0x1473, 0x1473, 0x1473 }, +{ 0x1474, 0x1474, 0x1474 }, +{ 0x1475, 0x1475, 0x1475 }, +{ 0x1476, 0x1476, 0x1476 }, +{ 0x1477, 0x1477, 0x1477 }, +{ 0x1478, 0x1478, 0x1478 }, +{ 0x1479, 0x1479, 0x1479 }, +{ 0x147A, 0x147A, 0x147A }, +{ 0x147B, 0x147B, 0x147B }, +{ 0x147C, 0x147C, 0x147C }, +{ 0x147D, 0x147D, 0x147D }, +{ 0x147E, 0x147E, 0x147E }, +{ 0x147F, 0x147F, 0x147F }, +{ 0x1480, 0x1480, 0x1480 }, +{ 0x1481, 0x1481, 0x1481 }, +{ 0x1482, 0x1482, 0x1482 }, +{ 0x1483, 0x1483, 0x1483 }, +{ 0x1484, 0x1484, 0x1484 }, +{ 0x1485, 0x1485, 0x1485 }, +{ 0x1486, 0x1486, 0x1486 }, +{ 0x1487, 0x1487, 0x1487 }, +{ 0x1488, 0x1488, 0x1488 }, +{ 0x1489, 0x1489, 0x1489 }, +{ 0x148A, 0x148A, 0x148A }, +{ 0x148B, 0x148B, 0x148B }, +{ 0x148C, 0x148C, 0x148C }, +{ 0x148D, 0x148D, 0x148D }, +{ 0x148E, 0x148E, 0x148E }, +{ 0x148F, 0x148F, 0x148F }, +{ 0x1490, 0x1490, 0x1490 }, +{ 0x1491, 0x1491, 0x1491 }, +{ 0x1492, 0x1492, 0x1492 }, +{ 0x1493, 0x1493, 0x1493 }, +{ 0x1494, 0x1494, 0x1494 }, +{ 0x1495, 0x1495, 0x1495 }, +{ 0x1496, 0x1496, 0x1496 }, +{ 0x1497, 0x1497, 0x1497 }, +{ 0x1498, 0x1498, 0x1498 }, +{ 0x1499, 0x1499, 0x1499 }, +{ 0x149A, 0x149A, 0x149A }, +{ 0x149B, 0x149B, 0x149B }, +{ 0x149C, 0x149C, 0x149C }, +{ 0x149D, 0x149D, 0x149D }, +{ 0x149E, 0x149E, 0x149E }, +{ 0x149F, 0x149F, 0x149F }, +{ 0x14A0, 0x14A0, 0x14A0 }, +{ 0x14A1, 0x14A1, 0x14A1 }, +{ 0x14A2, 0x14A2, 0x14A2 }, +{ 0x14A3, 0x14A3, 0x14A3 }, +{ 0x14A4, 0x14A4, 0x14A4 }, +{ 0x14A5, 0x14A5, 0x14A5 }, +{ 0x14A6, 0x14A6, 0x14A6 }, +{ 0x14A7, 0x14A7, 0x14A7 }, +{ 0x14A8, 0x14A8, 0x14A8 }, +{ 0x14A9, 0x14A9, 0x14A9 }, +{ 0x14AA, 0x14AA, 0x14AA }, +{ 0x14AB, 0x14AB, 0x14AB }, +{ 0x14AC, 0x14AC, 0x14AC }, +{ 0x14AD, 0x14AD, 0x14AD }, +{ 0x14AE, 0x14AE, 0x14AE }, +{ 0x14AF, 0x14AF, 0x14AF }, +{ 0x14B0, 0x14B0, 0x14B0 }, +{ 0x14B1, 0x14B1, 0x14B1 }, +{ 0x14B2, 0x14B2, 0x14B2 }, +{ 0x14B3, 0x14B3, 0x14B3 }, +{ 0x14B4, 0x14B4, 0x14B4 }, +{ 0x14B5, 0x14B5, 0x14B5 }, +{ 0x14B6, 0x14B6, 0x14B6 }, +{ 0x14B7, 0x14B7, 0x14B7 }, +{ 0x14B8, 0x14B8, 0x14B8 }, +{ 0x14B9, 0x14B9, 0x14B9 }, +{ 0x14BA, 0x14BA, 0x14BA }, +{ 0x14BB, 0x14BB, 0x14BB }, +{ 0x14BC, 0x14BC, 0x14BC }, +{ 0x14BD, 0x14BD, 0x14BD }, +{ 0x14BE, 0x14BE, 0x14BE }, +{ 0x14BF, 0x14BF, 0x14BF }, +{ 0x14C0, 0x14C0, 0x14C0 }, +{ 0x14C1, 0x14C1, 0x14C1 }, +{ 0x14C2, 0x14C2, 0x14C2 }, +{ 0x14C3, 0x14C3, 0x14C3 }, +{ 0x14C4, 0x14C4, 0x14C4 }, +{ 0x14C5, 0x14C5, 0x14C5 }, +{ 0x14C6, 0x14C6, 0x14C6 }, +{ 0x14C7, 0x14C7, 0x14C7 }, +{ 0x14C8, 0x14C8, 0x14C8 }, +{ 0x14C9, 0x14C9, 0x14C9 }, +{ 0x14CA, 0x14CA, 0x14CA }, +{ 0x14CB, 0x14CB, 0x14CB }, +{ 0x14CC, 0x14CC, 0x14CC }, +{ 0x14CD, 0x14CD, 0x14CD }, +{ 0x14CE, 0x14CE, 0x14CE }, +{ 0x14CF, 0x14CF, 0x14CF }, +{ 0x14D0, 0x14D0, 0x14D0 }, +{ 0x14D1, 0x14D1, 0x14D1 }, +{ 0x14D2, 0x14D2, 0x14D2 }, +{ 0x14D3, 0x14D3, 0x14D3 }, +{ 0x14D4, 0x14D4, 0x14D4 }, +{ 0x14D5, 0x14D5, 0x14D5 }, +{ 0x14D6, 0x14D6, 0x14D6 }, +{ 0x14D7, 0x14D7, 0x14D7 }, +{ 0x14D8, 0x14D8, 0x14D8 }, +{ 0x14D9, 0x14D9, 0x14D9 }, +{ 0x14DA, 0x14DA, 0x14DA }, +{ 0x14DB, 0x14DB, 0x14DB }, +{ 0x14DC, 0x14DC, 0x14DC }, +{ 0x14DD, 0x14DD, 0x14DD }, +{ 0x14DE, 0x14DE, 0x14DE }, +{ 0x14DF, 0x14DF, 0x14DF }, +{ 0x14E0, 0x14E0, 0x14E0 }, +{ 0x14E1, 0x14E1, 0x14E1 }, +{ 0x14E2, 0x14E2, 0x14E2 }, +{ 0x14E3, 0x14E3, 0x14E3 }, +{ 0x14E4, 0x14E4, 0x14E4 }, +{ 0x14E5, 0x14E5, 0x14E5 }, +{ 0x14E6, 0x14E6, 0x14E6 }, +{ 0x14E7, 0x14E7, 0x14E7 }, +{ 0x14E8, 0x14E8, 0x14E8 }, +{ 0x14E9, 0x14E9, 0x14E9 }, +{ 0x14EA, 0x14EA, 0x14EA }, +{ 0x14EB, 0x14EB, 0x14EB }, +{ 0x14EC, 0x14EC, 0x14EC }, +{ 0x14ED, 0x14ED, 0x14ED }, +{ 0x14EE, 0x14EE, 0x14EE }, +{ 0x14EF, 0x14EF, 0x14EF }, +{ 0x14F0, 0x14F0, 0x14F0 }, +{ 0x14F1, 0x14F1, 0x14F1 }, +{ 0x14F2, 0x14F2, 0x14F2 }, +{ 0x14F3, 0x14F3, 0x14F3 }, +{ 0x14F4, 0x14F4, 0x14F4 }, +{ 0x14F5, 0x14F5, 0x14F5 }, +{ 0x14F6, 0x14F6, 0x14F6 }, +{ 0x14F7, 0x14F7, 0x14F7 }, +{ 0x14F8, 0x14F8, 0x14F8 }, +{ 0x14F9, 0x14F9, 0x14F9 }, +{ 0x14FA, 0x14FA, 0x14FA }, +{ 0x14FB, 0x14FB, 0x14FB }, +{ 0x14FC, 0x14FC, 0x14FC }, +{ 0x14FD, 0x14FD, 0x14FD }, +{ 0x14FE, 0x14FE, 0x14FE }, +{ 0x14FF, 0x14FF, 0x14FF }, +{ 0x1500, 0x1500, 0x1500 }, +{ 0x1501, 0x1501, 0x1501 }, +{ 0x1502, 0x1502, 0x1502 }, +{ 0x1503, 0x1503, 0x1503 }, +{ 0x1504, 0x1504, 0x1504 }, +{ 0x1505, 0x1505, 0x1505 }, +{ 0x1506, 0x1506, 0x1506 }, +{ 0x1507, 0x1507, 0x1507 }, +{ 0x1508, 0x1508, 0x1508 }, +{ 0x1509, 0x1509, 0x1509 }, +{ 0x150A, 0x150A, 0x150A }, +{ 0x150B, 0x150B, 0x150B }, +{ 0x150C, 0x150C, 0x150C }, +{ 0x150D, 0x150D, 0x150D }, +{ 0x150E, 0x150E, 0x150E }, +{ 0x150F, 0x150F, 0x150F }, +{ 0x1510, 0x1510, 0x1510 }, +{ 0x1511, 0x1511, 0x1511 }, +{ 0x1512, 0x1512, 0x1512 }, +{ 0x1513, 0x1513, 0x1513 }, +{ 0x1514, 0x1514, 0x1514 }, +{ 0x1515, 0x1515, 0x1515 }, +{ 0x1516, 0x1516, 0x1516 }, +{ 0x1517, 0x1517, 0x1517 }, +{ 0x1518, 0x1518, 0x1518 }, +{ 0x1519, 0x1519, 0x1519 }, +{ 0x151A, 0x151A, 0x151A }, +{ 0x151B, 0x151B, 0x151B }, +{ 0x151C, 0x151C, 0x151C }, +{ 0x151D, 0x151D, 0x151D }, +{ 0x151E, 0x151E, 0x151E }, +{ 0x151F, 0x151F, 0x151F }, +{ 0x1520, 0x1520, 0x1520 }, +{ 0x1521, 0x1521, 0x1521 }, +{ 0x1522, 0x1522, 0x1522 }, +{ 0x1523, 0x1523, 0x1523 }, +{ 0x1524, 0x1524, 0x1524 }, +{ 0x1525, 0x1525, 0x1525 }, +{ 0x1526, 0x1526, 0x1526 }, +{ 0x1527, 0x1527, 0x1527 }, +{ 0x1528, 0x1528, 0x1528 }, +{ 0x1529, 0x1529, 0x1529 }, +{ 0x152A, 0x152A, 0x152A }, +{ 0x152B, 0x152B, 0x152B }, +{ 0x152C, 0x152C, 0x152C }, +{ 0x152D, 0x152D, 0x152D }, +{ 0x152E, 0x152E, 0x152E }, +{ 0x152F, 0x152F, 0x152F }, +{ 0x1530, 0x1530, 0x1530 }, +{ 0x1531, 0x1531, 0x1531 }, +{ 0x1532, 0x1532, 0x1532 }, +{ 0x1533, 0x1533, 0x1533 }, +{ 0x1534, 0x1534, 0x1534 }, +{ 0x1535, 0x1535, 0x1535 }, +{ 0x1536, 0x1536, 0x1536 }, +{ 0x1537, 0x1537, 0x1537 }, +{ 0x1538, 0x1538, 0x1538 }, +{ 0x1539, 0x1539, 0x1539 }, +{ 0x153A, 0x153A, 0x153A }, +{ 0x153B, 0x153B, 0x153B }, +{ 0x153C, 0x153C, 0x153C }, +{ 0x153D, 0x153D, 0x153D }, +{ 0x153E, 0x153E, 0x153E }, +{ 0x153F, 0x153F, 0x153F }, +{ 0x1540, 0x1540, 0x1540 }, +{ 0x1541, 0x1541, 0x1541 }, +{ 0x1542, 0x1542, 0x1542 }, +{ 0x1543, 0x1543, 0x1543 }, +{ 0x1544, 0x1544, 0x1544 }, +{ 0x1545, 0x1545, 0x1545 }, +{ 0x1546, 0x1546, 0x1546 }, +{ 0x1547, 0x1547, 0x1547 }, +{ 0x1548, 0x1548, 0x1548 }, +{ 0x1549, 0x1549, 0x1549 }, +{ 0x154A, 0x154A, 0x154A }, +{ 0x154B, 0x154B, 0x154B }, +{ 0x154C, 0x154C, 0x154C }, +{ 0x154D, 0x154D, 0x154D }, +{ 0x154E, 0x154E, 0x154E }, +{ 0x154F, 0x154F, 0x154F }, +{ 0x1550, 0x1550, 0x1550 }, +{ 0x1551, 0x1551, 0x1551 }, +{ 0x1552, 0x1552, 0x1552 }, +{ 0x1553, 0x1553, 0x1553 }, +{ 0x1554, 0x1554, 0x1554 }, +{ 0x1555, 0x1555, 0x1555 }, +{ 0x1556, 0x1556, 0x1556 }, +{ 0x1557, 0x1557, 0x1557 }, +{ 0x1558, 0x1558, 0x1558 }, +{ 0x1559, 0x1559, 0x1559 }, +{ 0x155A, 0x155A, 0x155A }, +{ 0x155B, 0x155B, 0x155B }, +{ 0x155C, 0x155C, 0x155C }, +{ 0x155D, 0x155D, 0x155D }, +{ 0x155E, 0x155E, 0x155E }, +{ 0x155F, 0x155F, 0x155F }, +{ 0x1560, 0x1560, 0x1560 }, +{ 0x1561, 0x1561, 0x1561 }, +{ 0x1562, 0x1562, 0x1562 }, +{ 0x1563, 0x1563, 0x1563 }, +{ 0x1564, 0x1564, 0x1564 }, +{ 0x1565, 0x1565, 0x1565 }, +{ 0x1566, 0x1566, 0x1566 }, +{ 0x1567, 0x1567, 0x1567 }, +{ 0x1568, 0x1568, 0x1568 }, +{ 0x1569, 0x1569, 0x1569 }, +{ 0x156A, 0x156A, 0x156A }, +{ 0x156B, 0x156B, 0x156B }, +{ 0x156C, 0x156C, 0x156C }, +{ 0x156D, 0x156D, 0x156D }, +{ 0x156E, 0x156E, 0x156E }, +{ 0x156F, 0x156F, 0x156F }, +{ 0x1570, 0x1570, 0x1570 }, +{ 0x1571, 0x1571, 0x1571 }, +{ 0x1572, 0x1572, 0x1572 }, +{ 0x1573, 0x1573, 0x1573 }, +{ 0x1574, 0x1574, 0x1574 }, +{ 0x1575, 0x1575, 0x1575 }, +{ 0x1576, 0x1576, 0x1576 }, +{ 0x1577, 0x1577, 0x1577 }, +{ 0x1578, 0x1578, 0x1578 }, +{ 0x1579, 0x1579, 0x1579 }, +{ 0x157A, 0x157A, 0x157A }, +{ 0x157B, 0x157B, 0x157B }, +{ 0x157C, 0x157C, 0x157C }, +{ 0x157D, 0x157D, 0x157D }, +{ 0x157E, 0x157E, 0x157E }, +{ 0x157F, 0x157F, 0x157F }, +{ 0x1580, 0x1580, 0x1580 }, +{ 0x1581, 0x1581, 0x1581 }, +{ 0x1582, 0x1582, 0x1582 }, +{ 0x1583, 0x1583, 0x1583 }, +{ 0x1584, 0x1584, 0x1584 }, +{ 0x1585, 0x1585, 0x1585 }, +{ 0x1586, 0x1586, 0x1586 }, +{ 0x1587, 0x1587, 0x1587 }, +{ 0x1588, 0x1588, 0x1588 }, +{ 0x1589, 0x1589, 0x1589 }, +{ 0x158A, 0x158A, 0x158A }, +{ 0x158B, 0x158B, 0x158B }, +{ 0x158C, 0x158C, 0x158C }, +{ 0x158D, 0x158D, 0x158D }, +{ 0x158E, 0x158E, 0x158E }, +{ 0x158F, 0x158F, 0x158F }, +{ 0x1590, 0x1590, 0x1590 }, +{ 0x1591, 0x1591, 0x1591 }, +{ 0x1592, 0x1592, 0x1592 }, +{ 0x1593, 0x1593, 0x1593 }, +{ 0x1594, 0x1594, 0x1594 }, +{ 0x1595, 0x1595, 0x1595 }, +{ 0x1596, 0x1596, 0x1596 }, +{ 0x1597, 0x1597, 0x1597 }, +{ 0x1598, 0x1598, 0x1598 }, +{ 0x1599, 0x1599, 0x1599 }, +{ 0x159A, 0x159A, 0x159A }, +{ 0x159B, 0x159B, 0x159B }, +{ 0x159C, 0x159C, 0x159C }, +{ 0x159D, 0x159D, 0x159D }, +{ 0x159E, 0x159E, 0x159E }, +{ 0x159F, 0x159F, 0x159F }, +{ 0x15A0, 0x15A0, 0x15A0 }, +{ 0x15A1, 0x15A1, 0x15A1 }, +{ 0x15A2, 0x15A2, 0x15A2 }, +{ 0x15A3, 0x15A3, 0x15A3 }, +{ 0x15A4, 0x15A4, 0x15A4 }, +{ 0x15A5, 0x15A5, 0x15A5 }, +{ 0x15A6, 0x15A6, 0x15A6 }, +{ 0x15A7, 0x15A7, 0x15A7 }, +{ 0x15A8, 0x15A8, 0x15A8 }, +{ 0x15A9, 0x15A9, 0x15A9 }, +{ 0x15AA, 0x15AA, 0x15AA }, +{ 0x15AB, 0x15AB, 0x15AB }, +{ 0x15AC, 0x15AC, 0x15AC }, +{ 0x15AD, 0x15AD, 0x15AD }, +{ 0x15AE, 0x15AE, 0x15AE }, +{ 0x15AF, 0x15AF, 0x15AF }, +{ 0x15B0, 0x15B0, 0x15B0 }, +{ 0x15B1, 0x15B1, 0x15B1 }, +{ 0x15B2, 0x15B2, 0x15B2 }, +{ 0x15B3, 0x15B3, 0x15B3 }, +{ 0x15B4, 0x15B4, 0x15B4 }, +{ 0x15B5, 0x15B5, 0x15B5 }, +{ 0x15B6, 0x15B6, 0x15B6 }, +{ 0x15B7, 0x15B7, 0x15B7 }, +{ 0x15B8, 0x15B8, 0x15B8 }, +{ 0x15B9, 0x15B9, 0x15B9 }, +{ 0x15BA, 0x15BA, 0x15BA }, +{ 0x15BB, 0x15BB, 0x15BB }, +{ 0x15BC, 0x15BC, 0x15BC }, +{ 0x15BD, 0x15BD, 0x15BD }, +{ 0x15BE, 0x15BE, 0x15BE }, +{ 0x15BF, 0x15BF, 0x15BF }, +{ 0x15C0, 0x15C0, 0x15C0 }, +{ 0x15C1, 0x15C1, 0x15C1 }, +{ 0x15C2, 0x15C2, 0x15C2 }, +{ 0x15C3, 0x15C3, 0x15C3 }, +{ 0x15C4, 0x15C4, 0x15C4 }, +{ 0x15C5, 0x15C5, 0x15C5 }, +{ 0x15C6, 0x15C6, 0x15C6 }, +{ 0x15C7, 0x15C7, 0x15C7 }, +{ 0x15C8, 0x15C8, 0x15C8 }, +{ 0x15C9, 0x15C9, 0x15C9 }, +{ 0x15CA, 0x15CA, 0x15CA }, +{ 0x15CB, 0x15CB, 0x15CB }, +{ 0x15CC, 0x15CC, 0x15CC }, +{ 0x15CD, 0x15CD, 0x15CD }, +{ 0x15CE, 0x15CE, 0x15CE }, +{ 0x15CF, 0x15CF, 0x15CF }, +{ 0x15D0, 0x15D0, 0x15D0 }, +{ 0x15D1, 0x15D1, 0x15D1 }, +{ 0x15D2, 0x15D2, 0x15D2 }, +{ 0x15D3, 0x15D3, 0x15D3 }, +{ 0x15D4, 0x15D4, 0x15D4 }, +{ 0x15D5, 0x15D5, 0x15D5 }, +{ 0x15D6, 0x15D6, 0x15D6 }, +{ 0x15D7, 0x15D7, 0x15D7 }, +{ 0x15D8, 0x15D8, 0x15D8 }, +{ 0x15D9, 0x15D9, 0x15D9 }, +{ 0x15DA, 0x15DA, 0x15DA }, +{ 0x15DB, 0x15DB, 0x15DB }, +{ 0x15DC, 0x15DC, 0x15DC }, +{ 0x15DD, 0x15DD, 0x15DD }, +{ 0x15DE, 0x15DE, 0x15DE }, +{ 0x15DF, 0x15DF, 0x15DF }, +{ 0x15E0, 0x15E0, 0x15E0 }, +{ 0x15E1, 0x15E1, 0x15E1 }, +{ 0x15E2, 0x15E2, 0x15E2 }, +{ 0x15E3, 0x15E3, 0x15E3 }, +{ 0x15E4, 0x15E4, 0x15E4 }, +{ 0x15E5, 0x15E5, 0x15E5 }, +{ 0x15E6, 0x15E6, 0x15E6 }, +{ 0x15E7, 0x15E7, 0x15E7 }, +{ 0x15E8, 0x15E8, 0x15E8 }, +{ 0x15E9, 0x15E9, 0x15E9 }, +{ 0x15EA, 0x15EA, 0x15EA }, +{ 0x15EB, 0x15EB, 0x15EB }, +{ 0x15EC, 0x15EC, 0x15EC }, +{ 0x15ED, 0x15ED, 0x15ED }, +{ 0x15EE, 0x15EE, 0x15EE }, +{ 0x15EF, 0x15EF, 0x15EF }, +{ 0x15F0, 0x15F0, 0x15F0 }, +{ 0x15F1, 0x15F1, 0x15F1 }, +{ 0x15F2, 0x15F2, 0x15F2 }, +{ 0x15F3, 0x15F3, 0x15F3 }, +{ 0x15F4, 0x15F4, 0x15F4 }, +{ 0x15F5, 0x15F5, 0x15F5 }, +{ 0x15F6, 0x15F6, 0x15F6 }, +{ 0x15F7, 0x15F7, 0x15F7 }, +{ 0x15F8, 0x15F8, 0x15F8 }, +{ 0x15F9, 0x15F9, 0x15F9 }, +{ 0x15FA, 0x15FA, 0x15FA }, +{ 0x15FB, 0x15FB, 0x15FB }, +{ 0x15FC, 0x15FC, 0x15FC }, +{ 0x15FD, 0x15FD, 0x15FD }, +{ 0x15FE, 0x15FE, 0x15FE }, +{ 0x15FF, 0x15FF, 0x15FF }, +{ 0x1600, 0x1600, 0x1600 }, +{ 0x1601, 0x1601, 0x1601 }, +{ 0x1602, 0x1602, 0x1602 }, +{ 0x1603, 0x1603, 0x1603 }, +{ 0x1604, 0x1604, 0x1604 }, +{ 0x1605, 0x1605, 0x1605 }, +{ 0x1606, 0x1606, 0x1606 }, +{ 0x1607, 0x1607, 0x1607 }, +{ 0x1608, 0x1608, 0x1608 }, +{ 0x1609, 0x1609, 0x1609 }, +{ 0x160A, 0x160A, 0x160A }, +{ 0x160B, 0x160B, 0x160B }, +{ 0x160C, 0x160C, 0x160C }, +{ 0x160D, 0x160D, 0x160D }, +{ 0x160E, 0x160E, 0x160E }, +{ 0x160F, 0x160F, 0x160F }, +{ 0x1610, 0x1610, 0x1610 }, +{ 0x1611, 0x1611, 0x1611 }, +{ 0x1612, 0x1612, 0x1612 }, +{ 0x1613, 0x1613, 0x1613 }, +{ 0x1614, 0x1614, 0x1614 }, +{ 0x1615, 0x1615, 0x1615 }, +{ 0x1616, 0x1616, 0x1616 }, +{ 0x1617, 0x1617, 0x1617 }, +{ 0x1618, 0x1618, 0x1618 }, +{ 0x1619, 0x1619, 0x1619 }, +{ 0x161A, 0x161A, 0x161A }, +{ 0x161B, 0x161B, 0x161B }, +{ 0x161C, 0x161C, 0x161C }, +{ 0x161D, 0x161D, 0x161D }, +{ 0x161E, 0x161E, 0x161E }, +{ 0x161F, 0x161F, 0x161F }, +{ 0x1620, 0x1620, 0x1620 }, +{ 0x1621, 0x1621, 0x1621 }, +{ 0x1622, 0x1622, 0x1622 }, +{ 0x1623, 0x1623, 0x1623 }, +{ 0x1624, 0x1624, 0x1624 }, +{ 0x1625, 0x1625, 0x1625 }, +{ 0x1626, 0x1626, 0x1626 }, +{ 0x1627, 0x1627, 0x1627 }, +{ 0x1628, 0x1628, 0x1628 }, +{ 0x1629, 0x1629, 0x1629 }, +{ 0x162A, 0x162A, 0x162A }, +{ 0x162B, 0x162B, 0x162B }, +{ 0x162C, 0x162C, 0x162C }, +{ 0x162D, 0x162D, 0x162D }, +{ 0x162E, 0x162E, 0x162E }, +{ 0x162F, 0x162F, 0x162F }, +{ 0x1630, 0x1630, 0x1630 }, +{ 0x1631, 0x1631, 0x1631 }, +{ 0x1632, 0x1632, 0x1632 }, +{ 0x1633, 0x1633, 0x1633 }, +{ 0x1634, 0x1634, 0x1634 }, +{ 0x1635, 0x1635, 0x1635 }, +{ 0x1636, 0x1636, 0x1636 }, +{ 0x1637, 0x1637, 0x1637 }, +{ 0x1638, 0x1638, 0x1638 }, +{ 0x1639, 0x1639, 0x1639 }, +{ 0x163A, 0x163A, 0x163A }, +{ 0x163B, 0x163B, 0x163B }, +{ 0x163C, 0x163C, 0x163C }, +{ 0x163D, 0x163D, 0x163D }, +{ 0x163E, 0x163E, 0x163E }, +{ 0x163F, 0x163F, 0x163F }, +{ 0x1640, 0x1640, 0x1640 }, +{ 0x1641, 0x1641, 0x1641 }, +{ 0x1642, 0x1642, 0x1642 }, +{ 0x1643, 0x1643, 0x1643 }, +{ 0x1644, 0x1644, 0x1644 }, +{ 0x1645, 0x1645, 0x1645 }, +{ 0x1646, 0x1646, 0x1646 }, +{ 0x1647, 0x1647, 0x1647 }, +{ 0x1648, 0x1648, 0x1648 }, +{ 0x1649, 0x1649, 0x1649 }, +{ 0x164A, 0x164A, 0x164A }, +{ 0x164B, 0x164B, 0x164B }, +{ 0x164C, 0x164C, 0x164C }, +{ 0x164D, 0x164D, 0x164D }, +{ 0x164E, 0x164E, 0x164E }, +{ 0x164F, 0x164F, 0x164F }, +{ 0x1650, 0x1650, 0x1650 }, +{ 0x1651, 0x1651, 0x1651 }, +{ 0x1652, 0x1652, 0x1652 }, +{ 0x1653, 0x1653, 0x1653 }, +{ 0x1654, 0x1654, 0x1654 }, +{ 0x1655, 0x1655, 0x1655 }, +{ 0x1656, 0x1656, 0x1656 }, +{ 0x1657, 0x1657, 0x1657 }, +{ 0x1658, 0x1658, 0x1658 }, +{ 0x1659, 0x1659, 0x1659 }, +{ 0x165A, 0x165A, 0x165A }, +{ 0x165B, 0x165B, 0x165B }, +{ 0x165C, 0x165C, 0x165C }, +{ 0x165D, 0x165D, 0x165D }, +{ 0x165E, 0x165E, 0x165E }, +{ 0x165F, 0x165F, 0x165F }, +{ 0x1660, 0x1660, 0x1660 }, +{ 0x1661, 0x1661, 0x1661 }, +{ 0x1662, 0x1662, 0x1662 }, +{ 0x1663, 0x1663, 0x1663 }, +{ 0x1664, 0x1664, 0x1664 }, +{ 0x1665, 0x1665, 0x1665 }, +{ 0x1666, 0x1666, 0x1666 }, +{ 0x1667, 0x1667, 0x1667 }, +{ 0x1668, 0x1668, 0x1668 }, +{ 0x1669, 0x1669, 0x1669 }, +{ 0x166A, 0x166A, 0x166A }, +{ 0x166B, 0x166B, 0x166B }, +{ 0x166C, 0x166C, 0x166C }, +{ 0x166F, 0x166F, 0x166F }, +{ 0x1670, 0x1670, 0x1670 }, +{ 0x1671, 0x1671, 0x1671 }, +{ 0x1672, 0x1672, 0x1672 }, +{ 0x1673, 0x1673, 0x1673 }, +{ 0x1674, 0x1674, 0x1674 }, +{ 0x1675, 0x1675, 0x1675 }, +{ 0x1676, 0x1676, 0x1676 }, +{ 0x1681, 0x1681, 0x1681 }, +{ 0x1682, 0x1682, 0x1682 }, +{ 0x1683, 0x1683, 0x1683 }, +{ 0x1684, 0x1684, 0x1684 }, +{ 0x1685, 0x1685, 0x1685 }, +{ 0x1686, 0x1686, 0x1686 }, +{ 0x1687, 0x1687, 0x1687 }, +{ 0x1688, 0x1688, 0x1688 }, +{ 0x1689, 0x1689, 0x1689 }, +{ 0x168A, 0x168A, 0x168A }, +{ 0x168B, 0x168B, 0x168B }, +{ 0x168C, 0x168C, 0x168C }, +{ 0x168D, 0x168D, 0x168D }, +{ 0x168E, 0x168E, 0x168E }, +{ 0x168F, 0x168F, 0x168F }, +{ 0x1690, 0x1690, 0x1690 }, +{ 0x1691, 0x1691, 0x1691 }, +{ 0x1692, 0x1692, 0x1692 }, +{ 0x1693, 0x1693, 0x1693 }, +{ 0x1694, 0x1694, 0x1694 }, +{ 0x1695, 0x1695, 0x1695 }, +{ 0x1696, 0x1696, 0x1696 }, +{ 0x1697, 0x1697, 0x1697 }, +{ 0x1698, 0x1698, 0x1698 }, +{ 0x1699, 0x1699, 0x1699 }, +{ 0x169A, 0x169A, 0x169A }, +{ 0x16A0, 0x16A0, 0x16A0 }, +{ 0x16A1, 0x16A1, 0x16A1 }, +{ 0x16A2, 0x16A2, 0x16A2 }, +{ 0x16A3, 0x16A3, 0x16A3 }, +{ 0x16A4, 0x16A4, 0x16A4 }, +{ 0x16A5, 0x16A5, 0x16A5 }, +{ 0x16A6, 0x16A6, 0x16A6 }, +{ 0x16A7, 0x16A7, 0x16A7 }, +{ 0x16A8, 0x16A8, 0x16A8 }, +{ 0x16A9, 0x16A9, 0x16A9 }, +{ 0x16AA, 0x16AA, 0x16AA }, +{ 0x16AB, 0x16AB, 0x16AB }, +{ 0x16AC, 0x16AC, 0x16AC }, +{ 0x16AD, 0x16AD, 0x16AD }, +{ 0x16AE, 0x16AE, 0x16AE }, +{ 0x16AF, 0x16AF, 0x16AF }, +{ 0x16B0, 0x16B0, 0x16B0 }, +{ 0x16B1, 0x16B1, 0x16B1 }, +{ 0x16B2, 0x16B2, 0x16B2 }, +{ 0x16B3, 0x16B3, 0x16B3 }, +{ 0x16B4, 0x16B4, 0x16B4 }, +{ 0x16B5, 0x16B5, 0x16B5 }, +{ 0x16B6, 0x16B6, 0x16B6 }, +{ 0x16B7, 0x16B7, 0x16B7 }, +{ 0x16B8, 0x16B8, 0x16B8 }, +{ 0x16B9, 0x16B9, 0x16B9 }, +{ 0x16BA, 0x16BA, 0x16BA }, +{ 0x16BB, 0x16BB, 0x16BB }, +{ 0x16BC, 0x16BC, 0x16BC }, +{ 0x16BD, 0x16BD, 0x16BD }, +{ 0x16BE, 0x16BE, 0x16BE }, +{ 0x16BF, 0x16BF, 0x16BF }, +{ 0x16C0, 0x16C0, 0x16C0 }, +{ 0x16C1, 0x16C1, 0x16C1 }, +{ 0x16C2, 0x16C2, 0x16C2 }, +{ 0x16C3, 0x16C3, 0x16C3 }, +{ 0x16C4, 0x16C4, 0x16C4 }, +{ 0x16C5, 0x16C5, 0x16C5 }, +{ 0x16C6, 0x16C6, 0x16C6 }, +{ 0x16C7, 0x16C7, 0x16C7 }, +{ 0x16C8, 0x16C8, 0x16C8 }, +{ 0x16C9, 0x16C9, 0x16C9 }, +{ 0x16CA, 0x16CA, 0x16CA }, +{ 0x16CB, 0x16CB, 0x16CB }, +{ 0x16CC, 0x16CC, 0x16CC }, +{ 0x16CD, 0x16CD, 0x16CD }, +{ 0x16CE, 0x16CE, 0x16CE }, +{ 0x16CF, 0x16CF, 0x16CF }, +{ 0x16D0, 0x16D0, 0x16D0 }, +{ 0x16D1, 0x16D1, 0x16D1 }, +{ 0x16D2, 0x16D2, 0x16D2 }, +{ 0x16D3, 0x16D3, 0x16D3 }, +{ 0x16D4, 0x16D4, 0x16D4 }, +{ 0x16D5, 0x16D5, 0x16D5 }, +{ 0x16D6, 0x16D6, 0x16D6 }, +{ 0x16D7, 0x16D7, 0x16D7 }, +{ 0x16D8, 0x16D8, 0x16D8 }, +{ 0x16D9, 0x16D9, 0x16D9 }, +{ 0x16DA, 0x16DA, 0x16DA }, +{ 0x16DB, 0x16DB, 0x16DB }, +{ 0x16DC, 0x16DC, 0x16DC }, +{ 0x16DD, 0x16DD, 0x16DD }, +{ 0x16DE, 0x16DE, 0x16DE }, +{ 0x16DF, 0x16DF, 0x16DF }, +{ 0x16E0, 0x16E0, 0x16E0 }, +{ 0x16E1, 0x16E1, 0x16E1 }, +{ 0x16E2, 0x16E2, 0x16E2 }, +{ 0x16E3, 0x16E3, 0x16E3 }, +{ 0x16E4, 0x16E4, 0x16E4 }, +{ 0x16E5, 0x16E5, 0x16E5 }, +{ 0x16E6, 0x16E6, 0x16E6 }, +{ 0x16E7, 0x16E7, 0x16E7 }, +{ 0x16E8, 0x16E8, 0x16E8 }, +{ 0x16E9, 0x16E9, 0x16E9 }, +{ 0x16EA, 0x16EA, 0x16EA }, +{ 0x1700, 0x1700, 0x1700 }, +{ 0x1701, 0x1701, 0x1701 }, +{ 0x1702, 0x1702, 0x1702 }, +{ 0x1703, 0x1703, 0x1703 }, +{ 0x1704, 0x1704, 0x1704 }, +{ 0x1705, 0x1705, 0x1705 }, +{ 0x1706, 0x1706, 0x1706 }, +{ 0x1707, 0x1707, 0x1707 }, +{ 0x1708, 0x1708, 0x1708 }, +{ 0x1709, 0x1709, 0x1709 }, +{ 0x170A, 0x170A, 0x170A }, +{ 0x170B, 0x170B, 0x170B }, +{ 0x170C, 0x170C, 0x170C }, +{ 0x170E, 0x170E, 0x170E }, +{ 0x170F, 0x170F, 0x170F }, +{ 0x1710, 0x1710, 0x1710 }, +{ 0x1711, 0x1711, 0x1711 }, +{ 0x1712, 0x1712, 0x1712 }, +{ 0x1713, 0x1713, 0x1713 }, +{ 0x1714, 0x1714, 0x1714 }, +{ 0x1720, 0x1720, 0x1720 }, +{ 0x1721, 0x1721, 0x1721 }, +{ 0x1722, 0x1722, 0x1722 }, +{ 0x1723, 0x1723, 0x1723 }, +{ 0x1724, 0x1724, 0x1724 }, +{ 0x1725, 0x1725, 0x1725 }, +{ 0x1726, 0x1726, 0x1726 }, +{ 0x1727, 0x1727, 0x1727 }, +{ 0x1728, 0x1728, 0x1728 }, +{ 0x1729, 0x1729, 0x1729 }, +{ 0x172A, 0x172A, 0x172A }, +{ 0x172B, 0x172B, 0x172B }, +{ 0x172C, 0x172C, 0x172C }, +{ 0x172D, 0x172D, 0x172D }, +{ 0x172E, 0x172E, 0x172E }, +{ 0x172F, 0x172F, 0x172F }, +{ 0x1730, 0x1730, 0x1730 }, +{ 0x1731, 0x1731, 0x1731 }, +{ 0x1732, 0x1732, 0x1732 }, +{ 0x1733, 0x1733, 0x1733 }, +{ 0x1734, 0x1734, 0x1734 }, +{ 0x1740, 0x1740, 0x1740 }, +{ 0x1741, 0x1741, 0x1741 }, +{ 0x1742, 0x1742, 0x1742 }, +{ 0x1743, 0x1743, 0x1743 }, +{ 0x1744, 0x1744, 0x1744 }, +{ 0x1745, 0x1745, 0x1745 }, +{ 0x1746, 0x1746, 0x1746 }, +{ 0x1747, 0x1747, 0x1747 }, +{ 0x1748, 0x1748, 0x1748 }, +{ 0x1749, 0x1749, 0x1749 }, +{ 0x174A, 0x174A, 0x174A }, +{ 0x174B, 0x174B, 0x174B }, +{ 0x174C, 0x174C, 0x174C }, +{ 0x174D, 0x174D, 0x174D }, +{ 0x174E, 0x174E, 0x174E }, +{ 0x174F, 0x174F, 0x174F }, +{ 0x1750, 0x1750, 0x1750 }, +{ 0x1751, 0x1751, 0x1751 }, +{ 0x1752, 0x1752, 0x1752 }, +{ 0x1753, 0x1753, 0x1753 }, +{ 0x1760, 0x1760, 0x1760 }, +{ 0x1761, 0x1761, 0x1761 }, +{ 0x1762, 0x1762, 0x1762 }, +{ 0x1763, 0x1763, 0x1763 }, +{ 0x1764, 0x1764, 0x1764 }, +{ 0x1765, 0x1765, 0x1765 }, +{ 0x1766, 0x1766, 0x1766 }, +{ 0x1767, 0x1767, 0x1767 }, +{ 0x1768, 0x1768, 0x1768 }, +{ 0x1769, 0x1769, 0x1769 }, +{ 0x176A, 0x176A, 0x176A }, +{ 0x176B, 0x176B, 0x176B }, +{ 0x176C, 0x176C, 0x176C }, +{ 0x176E, 0x176E, 0x176E }, +{ 0x176F, 0x176F, 0x176F }, +{ 0x1770, 0x1770, 0x1770 }, +{ 0x1772, 0x1772, 0x1772 }, +{ 0x1773, 0x1773, 0x1773 }, +{ 0x1780, 0x1780, 0x1780 }, +{ 0x1781, 0x1781, 0x1781 }, +{ 0x1782, 0x1782, 0x1782 }, +{ 0x1783, 0x1783, 0x1783 }, +{ 0x1784, 0x1784, 0x1784 }, +{ 0x1785, 0x1785, 0x1785 }, +{ 0x1786, 0x1786, 0x1786 }, +{ 0x1787, 0x1787, 0x1787 }, +{ 0x1788, 0x1788, 0x1788 }, +{ 0x1789, 0x1789, 0x1789 }, +{ 0x178A, 0x178A, 0x178A }, +{ 0x178B, 0x178B, 0x178B }, +{ 0x178C, 0x178C, 0x178C }, +{ 0x178D, 0x178D, 0x178D }, +{ 0x178E, 0x178E, 0x178E }, +{ 0x178F, 0x178F, 0x178F }, +{ 0x1790, 0x1790, 0x1790 }, +{ 0x1791, 0x1791, 0x1791 }, +{ 0x1792, 0x1792, 0x1792 }, +{ 0x1793, 0x1793, 0x1793 }, +{ 0x1794, 0x1794, 0x1794 }, +{ 0x1795, 0x1795, 0x1795 }, +{ 0x1796, 0x1796, 0x1796 }, +{ 0x1797, 0x1797, 0x1797 }, +{ 0x1798, 0x1798, 0x1798 }, +{ 0x1799, 0x1799, 0x1799 }, +{ 0x179A, 0x179A, 0x179A }, +{ 0x179B, 0x179B, 0x179B }, +{ 0x179C, 0x179C, 0x179C }, +{ 0x179D, 0x179D, 0x179D }, +{ 0x179E, 0x179E, 0x179E }, +{ 0x179F, 0x179F, 0x179F }, +{ 0x17A0, 0x17A0, 0x17A0 }, +{ 0x17A1, 0x17A1, 0x17A1 }, +{ 0x17A2, 0x17A2, 0x17A2 }, +{ 0x17A3, 0x17A3, 0x17A3 }, +{ 0x17A4, 0x17A4, 0x17A4 }, +{ 0x17A5, 0x17A5, 0x17A5 }, +{ 0x17A6, 0x17A6, 0x17A6 }, +{ 0x17A7, 0x17A7, 0x17A7 }, +{ 0x17A8, 0x17A8, 0x17A8 }, +{ 0x17A9, 0x17A9, 0x17A9 }, +{ 0x17AA, 0x17AA, 0x17AA }, +{ 0x17AB, 0x17AB, 0x17AB }, +{ 0x17AC, 0x17AC, 0x17AC }, +{ 0x17AD, 0x17AD, 0x17AD }, +{ 0x17AE, 0x17AE, 0x17AE }, +{ 0x17AF, 0x17AF, 0x17AF }, +{ 0x17B0, 0x17B0, 0x17B0 }, +{ 0x17B1, 0x17B1, 0x17B1 }, +{ 0x17B2, 0x17B2, 0x17B2 }, +{ 0x17B3, 0x17B3, 0x17B3 }, +{ 0x17B7, 0x17B7, 0x17B7 }, +{ 0x17B8, 0x17B8, 0x17B8 }, +{ 0x17B9, 0x17B9, 0x17B9 }, +{ 0x17BA, 0x17BA, 0x17BA }, +{ 0x17BB, 0x17BB, 0x17BB }, +{ 0x17BC, 0x17BC, 0x17BC }, +{ 0x17BD, 0x17BD, 0x17BD }, +{ 0x17C6, 0x17C6, 0x17C6 }, +{ 0x17C9, 0x17C9, 0x17C9 }, +{ 0x17CA, 0x17CA, 0x17CA }, +{ 0x17CB, 0x17CB, 0x17CB }, +{ 0x17CC, 0x17CC, 0x17CC }, +{ 0x17CD, 0x17CD, 0x17CD }, +{ 0x17CE, 0x17CE, 0x17CE }, +{ 0x17CF, 0x17CF, 0x17CF }, +{ 0x17D0, 0x17D0, 0x17D0 }, +{ 0x17D1, 0x17D1, 0x17D1 }, +{ 0x17D2, 0x17D2, 0x17D2 }, +{ 0x17D3, 0x17D3, 0x17D3 }, +{ 0x17D7, 0x17D7, 0x17D7 }, +{ 0x17DC, 0x17DC, 0x17DC }, +{ 0x17DD, 0x17DD, 0x17DD }, +{ 0x180B, 0x180B, 0x180B }, +{ 0x180C, 0x180C, 0x180C }, +{ 0x180D, 0x180D, 0x180D }, +{ 0x1820, 0x1820, 0x1820 }, +{ 0x1821, 0x1821, 0x1821 }, +{ 0x1822, 0x1822, 0x1822 }, +{ 0x1823, 0x1823, 0x1823 }, +{ 0x1824, 0x1824, 0x1824 }, +{ 0x1825, 0x1825, 0x1825 }, +{ 0x1826, 0x1826, 0x1826 }, +{ 0x1827, 0x1827, 0x1827 }, +{ 0x1828, 0x1828, 0x1828 }, +{ 0x1829, 0x1829, 0x1829 }, +{ 0x182A, 0x182A, 0x182A }, +{ 0x182B, 0x182B, 0x182B }, +{ 0x182C, 0x182C, 0x182C }, +{ 0x182D, 0x182D, 0x182D }, +{ 0x182E, 0x182E, 0x182E }, +{ 0x182F, 0x182F, 0x182F }, +{ 0x1830, 0x1830, 0x1830 }, +{ 0x1831, 0x1831, 0x1831 }, +{ 0x1832, 0x1832, 0x1832 }, +{ 0x1833, 0x1833, 0x1833 }, +{ 0x1834, 0x1834, 0x1834 }, +{ 0x1835, 0x1835, 0x1835 }, +{ 0x1836, 0x1836, 0x1836 }, +{ 0x1837, 0x1837, 0x1837 }, +{ 0x1838, 0x1838, 0x1838 }, +{ 0x1839, 0x1839, 0x1839 }, +{ 0x183A, 0x183A, 0x183A }, +{ 0x183B, 0x183B, 0x183B }, +{ 0x183C, 0x183C, 0x183C }, +{ 0x183D, 0x183D, 0x183D }, +{ 0x183E, 0x183E, 0x183E }, +{ 0x183F, 0x183F, 0x183F }, +{ 0x1840, 0x1840, 0x1840 }, +{ 0x1841, 0x1841, 0x1841 }, +{ 0x1842, 0x1842, 0x1842 }, +{ 0x1843, 0x1843, 0x1843 }, +{ 0x1844, 0x1844, 0x1844 }, +{ 0x1845, 0x1845, 0x1845 }, +{ 0x1846, 0x1846, 0x1846 }, +{ 0x1847, 0x1847, 0x1847 }, +{ 0x1848, 0x1848, 0x1848 }, +{ 0x1849, 0x1849, 0x1849 }, +{ 0x184A, 0x184A, 0x184A }, +{ 0x184B, 0x184B, 0x184B }, +{ 0x184C, 0x184C, 0x184C }, +{ 0x184D, 0x184D, 0x184D }, +{ 0x184E, 0x184E, 0x184E }, +{ 0x184F, 0x184F, 0x184F }, +{ 0x1850, 0x1850, 0x1850 }, +{ 0x1851, 0x1851, 0x1851 }, +{ 0x1852, 0x1852, 0x1852 }, +{ 0x1853, 0x1853, 0x1853 }, +{ 0x1854, 0x1854, 0x1854 }, +{ 0x1855, 0x1855, 0x1855 }, +{ 0x1856, 0x1856, 0x1856 }, +{ 0x1857, 0x1857, 0x1857 }, +{ 0x1858, 0x1858, 0x1858 }, +{ 0x1859, 0x1859, 0x1859 }, +{ 0x185A, 0x185A, 0x185A }, +{ 0x185B, 0x185B, 0x185B }, +{ 0x185C, 0x185C, 0x185C }, +{ 0x185D, 0x185D, 0x185D }, +{ 0x185E, 0x185E, 0x185E }, +{ 0x185F, 0x185F, 0x185F }, +{ 0x1860, 0x1860, 0x1860 }, +{ 0x1861, 0x1861, 0x1861 }, +{ 0x1862, 0x1862, 0x1862 }, +{ 0x1863, 0x1863, 0x1863 }, +{ 0x1864, 0x1864, 0x1864 }, +{ 0x1865, 0x1865, 0x1865 }, +{ 0x1866, 0x1866, 0x1866 }, +{ 0x1867, 0x1867, 0x1867 }, +{ 0x1868, 0x1868, 0x1868 }, +{ 0x1869, 0x1869, 0x1869 }, +{ 0x186A, 0x186A, 0x186A }, +{ 0x186B, 0x186B, 0x186B }, +{ 0x186C, 0x186C, 0x186C }, +{ 0x186D, 0x186D, 0x186D }, +{ 0x186E, 0x186E, 0x186E }, +{ 0x186F, 0x186F, 0x186F }, +{ 0x1870, 0x1870, 0x1870 }, +{ 0x1871, 0x1871, 0x1871 }, +{ 0x1872, 0x1872, 0x1872 }, +{ 0x1873, 0x1873, 0x1873 }, +{ 0x1874, 0x1874, 0x1874 }, +{ 0x1875, 0x1875, 0x1875 }, +{ 0x1876, 0x1876, 0x1876 }, +{ 0x1877, 0x1877, 0x1877 }, +{ 0x1880, 0x1880, 0x1880 }, +{ 0x1881, 0x1881, 0x1881 }, +{ 0x1882, 0x1882, 0x1882 }, +{ 0x1883, 0x1883, 0x1883 }, +{ 0x1884, 0x1884, 0x1884 }, +{ 0x1885, 0x1885, 0x1885 }, +{ 0x1886, 0x1886, 0x1886 }, +{ 0x1887, 0x1887, 0x1887 }, +{ 0x1888, 0x1888, 0x1888 }, +{ 0x1889, 0x1889, 0x1889 }, +{ 0x188A, 0x188A, 0x188A }, +{ 0x188B, 0x188B, 0x188B }, +{ 0x188C, 0x188C, 0x188C }, +{ 0x188D, 0x188D, 0x188D }, +{ 0x188E, 0x188E, 0x188E }, +{ 0x188F, 0x188F, 0x188F }, +{ 0x1890, 0x1890, 0x1890 }, +{ 0x1891, 0x1891, 0x1891 }, +{ 0x1892, 0x1892, 0x1892 }, +{ 0x1893, 0x1893, 0x1893 }, +{ 0x1894, 0x1894, 0x1894 }, +{ 0x1895, 0x1895, 0x1895 }, +{ 0x1896, 0x1896, 0x1896 }, +{ 0x1897, 0x1897, 0x1897 }, +{ 0x1898, 0x1898, 0x1898 }, +{ 0x1899, 0x1899, 0x1899 }, +{ 0x189A, 0x189A, 0x189A }, +{ 0x189B, 0x189B, 0x189B }, +{ 0x189C, 0x189C, 0x189C }, +{ 0x189D, 0x189D, 0x189D }, +{ 0x189E, 0x189E, 0x189E }, +{ 0x189F, 0x189F, 0x189F }, +{ 0x18A0, 0x18A0, 0x18A0 }, +{ 0x18A1, 0x18A1, 0x18A1 }, +{ 0x18A2, 0x18A2, 0x18A2 }, +{ 0x18A3, 0x18A3, 0x18A3 }, +{ 0x18A4, 0x18A4, 0x18A4 }, +{ 0x18A5, 0x18A5, 0x18A5 }, +{ 0x18A6, 0x18A6, 0x18A6 }, +{ 0x18A7, 0x18A7, 0x18A7 }, +{ 0x18A8, 0x18A8, 0x18A8 }, +{ 0x18A9, 0x18A9, 0x18A9 }, +{ 0x1900, 0x1900, 0x1900 }, +{ 0x1901, 0x1901, 0x1901 }, +{ 0x1902, 0x1902, 0x1902 }, +{ 0x1903, 0x1903, 0x1903 }, +{ 0x1904, 0x1904, 0x1904 }, +{ 0x1905, 0x1905, 0x1905 }, +{ 0x1906, 0x1906, 0x1906 }, +{ 0x1907, 0x1907, 0x1907 }, +{ 0x1908, 0x1908, 0x1908 }, +{ 0x1909, 0x1909, 0x1909 }, +{ 0x190A, 0x190A, 0x190A }, +{ 0x190B, 0x190B, 0x190B }, +{ 0x190C, 0x190C, 0x190C }, +{ 0x190D, 0x190D, 0x190D }, +{ 0x190E, 0x190E, 0x190E }, +{ 0x190F, 0x190F, 0x190F }, +{ 0x1910, 0x1910, 0x1910 }, +{ 0x1911, 0x1911, 0x1911 }, +{ 0x1912, 0x1912, 0x1912 }, +{ 0x1913, 0x1913, 0x1913 }, +{ 0x1914, 0x1914, 0x1914 }, +{ 0x1915, 0x1915, 0x1915 }, +{ 0x1916, 0x1916, 0x1916 }, +{ 0x1917, 0x1917, 0x1917 }, +{ 0x1918, 0x1918, 0x1918 }, +{ 0x1919, 0x1919, 0x1919 }, +{ 0x191A, 0x191A, 0x191A }, +{ 0x191B, 0x191B, 0x191B }, +{ 0x191C, 0x191C, 0x191C }, +{ 0x1920, 0x1920, 0x1920 }, +{ 0x1921, 0x1921, 0x1921 }, +{ 0x1922, 0x1922, 0x1922 }, +{ 0x1927, 0x1927, 0x1927 }, +{ 0x1928, 0x1928, 0x1928 }, +{ 0x1932, 0x1932, 0x1932 }, +{ 0x1939, 0x1939, 0x1939 }, +{ 0x193A, 0x193A, 0x193A }, +{ 0x193B, 0x193B, 0x193B }, +{ 0x1950, 0x1950, 0x1950 }, +{ 0x1951, 0x1951, 0x1951 }, +{ 0x1952, 0x1952, 0x1952 }, +{ 0x1953, 0x1953, 0x1953 }, +{ 0x1954, 0x1954, 0x1954 }, +{ 0x1955, 0x1955, 0x1955 }, +{ 0x1956, 0x1956, 0x1956 }, +{ 0x1957, 0x1957, 0x1957 }, +{ 0x1958, 0x1958, 0x1958 }, +{ 0x1959, 0x1959, 0x1959 }, +{ 0x195A, 0x195A, 0x195A }, +{ 0x195B, 0x195B, 0x195B }, +{ 0x195C, 0x195C, 0x195C }, +{ 0x195D, 0x195D, 0x195D }, +{ 0x195E, 0x195E, 0x195E }, +{ 0x195F, 0x195F, 0x195F }, +{ 0x1960, 0x1960, 0x1960 }, +{ 0x1961, 0x1961, 0x1961 }, +{ 0x1962, 0x1962, 0x1962 }, +{ 0x1963, 0x1963, 0x1963 }, +{ 0x1964, 0x1964, 0x1964 }, +{ 0x1965, 0x1965, 0x1965 }, +{ 0x1966, 0x1966, 0x1966 }, +{ 0x1967, 0x1967, 0x1967 }, +{ 0x1968, 0x1968, 0x1968 }, +{ 0x1969, 0x1969, 0x1969 }, +{ 0x196A, 0x196A, 0x196A }, +{ 0x196B, 0x196B, 0x196B }, +{ 0x196C, 0x196C, 0x196C }, +{ 0x196D, 0x196D, 0x196D }, +{ 0x1970, 0x1970, 0x1970 }, +{ 0x1971, 0x1971, 0x1971 }, +{ 0x1972, 0x1972, 0x1972 }, +{ 0x1973, 0x1973, 0x1973 }, +{ 0x1974, 0x1974, 0x1974 }, +{ 0x1980, 0x1980, 0x1980 }, +{ 0x1981, 0x1981, 0x1981 }, +{ 0x1982, 0x1982, 0x1982 }, +{ 0x1983, 0x1983, 0x1983 }, +{ 0x1984, 0x1984, 0x1984 }, +{ 0x1985, 0x1985, 0x1985 }, +{ 0x1986, 0x1986, 0x1986 }, +{ 0x1987, 0x1987, 0x1987 }, +{ 0x1988, 0x1988, 0x1988 }, +{ 0x1989, 0x1989, 0x1989 }, +{ 0x198A, 0x198A, 0x198A }, +{ 0x198B, 0x198B, 0x198B }, +{ 0x198C, 0x198C, 0x198C }, +{ 0x198D, 0x198D, 0x198D }, +{ 0x198E, 0x198E, 0x198E }, +{ 0x198F, 0x198F, 0x198F }, +{ 0x1990, 0x1990, 0x1990 }, +{ 0x1991, 0x1991, 0x1991 }, +{ 0x1992, 0x1992, 0x1992 }, +{ 0x1993, 0x1993, 0x1993 }, +{ 0x1994, 0x1994, 0x1994 }, +{ 0x1995, 0x1995, 0x1995 }, +{ 0x1996, 0x1996, 0x1996 }, +{ 0x1997, 0x1997, 0x1997 }, +{ 0x1998, 0x1998, 0x1998 }, +{ 0x1999, 0x1999, 0x1999 }, +{ 0x199A, 0x199A, 0x199A }, +{ 0x199B, 0x199B, 0x199B }, +{ 0x199C, 0x199C, 0x199C }, +{ 0x199D, 0x199D, 0x199D }, +{ 0x199E, 0x199E, 0x199E }, +{ 0x199F, 0x199F, 0x199F }, +{ 0x19A0, 0x19A0, 0x19A0 }, +{ 0x19A1, 0x19A1, 0x19A1 }, +{ 0x19A2, 0x19A2, 0x19A2 }, +{ 0x19A3, 0x19A3, 0x19A3 }, +{ 0x19A4, 0x19A4, 0x19A4 }, +{ 0x19A5, 0x19A5, 0x19A5 }, +{ 0x19A6, 0x19A6, 0x19A6 }, +{ 0x19A7, 0x19A7, 0x19A7 }, +{ 0x19A8, 0x19A8, 0x19A8 }, +{ 0x19A9, 0x19A9, 0x19A9 }, +{ 0x19C1, 0x19C1, 0x19C1 }, +{ 0x19C2, 0x19C2, 0x19C2 }, +{ 0x19C3, 0x19C3, 0x19C3 }, +{ 0x19C4, 0x19C4, 0x19C4 }, +{ 0x19C5, 0x19C5, 0x19C5 }, +{ 0x19C6, 0x19C6, 0x19C6 }, +{ 0x19C7, 0x19C7, 0x19C7 }, +{ 0x1A00, 0x1A00, 0x1A00 }, +{ 0x1A01, 0x1A01, 0x1A01 }, +{ 0x1A02, 0x1A02, 0x1A02 }, +{ 0x1A03, 0x1A03, 0x1A03 }, +{ 0x1A04, 0x1A04, 0x1A04 }, +{ 0x1A05, 0x1A05, 0x1A05 }, +{ 0x1A06, 0x1A06, 0x1A06 }, +{ 0x1A07, 0x1A07, 0x1A07 }, +{ 0x1A08, 0x1A08, 0x1A08 }, +{ 0x1A09, 0x1A09, 0x1A09 }, +{ 0x1A0A, 0x1A0A, 0x1A0A }, +{ 0x1A0B, 0x1A0B, 0x1A0B }, +{ 0x1A0C, 0x1A0C, 0x1A0C }, +{ 0x1A0D, 0x1A0D, 0x1A0D }, +{ 0x1A0E, 0x1A0E, 0x1A0E }, +{ 0x1A0F, 0x1A0F, 0x1A0F }, +{ 0x1A10, 0x1A10, 0x1A10 }, +{ 0x1A11, 0x1A11, 0x1A11 }, +{ 0x1A12, 0x1A12, 0x1A12 }, +{ 0x1A13, 0x1A13, 0x1A13 }, +{ 0x1A14, 0x1A14, 0x1A14 }, +{ 0x1A15, 0x1A15, 0x1A15 }, +{ 0x1A16, 0x1A16, 0x1A16 }, +{ 0x1A17, 0x1A17, 0x1A17 }, +{ 0x1A18, 0x1A18, 0x1A18 }, +{ 0x1D00, 0x1D00, 0x1D00 }, +{ 0x1D01, 0x1D01, 0x1D01 }, +{ 0x1D02, 0x1D02, 0x1D02 }, +{ 0x1D03, 0x1D03, 0x1D03 }, +{ 0x1D04, 0x1D04, 0x1D04 }, +{ 0x1D05, 0x1D05, 0x1D05 }, +{ 0x1D06, 0x1D06, 0x1D06 }, +{ 0x1D07, 0x1D07, 0x1D07 }, +{ 0x1D08, 0x1D08, 0x1D08 }, +{ 0x1D09, 0x1D09, 0x1D09 }, +{ 0x1D0A, 0x1D0A, 0x1D0A }, +{ 0x1D0B, 0x1D0B, 0x1D0B }, +{ 0x1D0C, 0x1D0C, 0x1D0C }, +{ 0x1D0D, 0x1D0D, 0x1D0D }, +{ 0x1D0E, 0x1D0E, 0x1D0E }, +{ 0x1D0F, 0x1D0F, 0x1D0F }, +{ 0x1D10, 0x1D10, 0x1D10 }, +{ 0x1D11, 0x1D11, 0x1D11 }, +{ 0x1D12, 0x1D12, 0x1D12 }, +{ 0x1D13, 0x1D13, 0x1D13 }, +{ 0x1D14, 0x1D14, 0x1D14 }, +{ 0x1D15, 0x1D15, 0x1D15 }, +{ 0x1D16, 0x1D16, 0x1D16 }, +{ 0x1D17, 0x1D17, 0x1D17 }, +{ 0x1D18, 0x1D18, 0x1D18 }, +{ 0x1D19, 0x1D19, 0x1D19 }, +{ 0x1D1A, 0x1D1A, 0x1D1A }, +{ 0x1D1B, 0x1D1B, 0x1D1B }, +{ 0x1D1C, 0x1D1C, 0x1D1C }, +{ 0x1D1D, 0x1D1D, 0x1D1D }, +{ 0x1D1E, 0x1D1E, 0x1D1E }, +{ 0x1D1F, 0x1D1F, 0x1D1F }, +{ 0x1D20, 0x1D20, 0x1D20 }, +{ 0x1D21, 0x1D21, 0x1D21 }, +{ 0x1D22, 0x1D22, 0x1D22 }, +{ 0x1D23, 0x1D23, 0x1D23 }, +{ 0x1D24, 0x1D24, 0x1D24 }, +{ 0x1D25, 0x1D25, 0x1D25 }, +{ 0x1D26, 0x1D26, 0x1D26 }, +{ 0x1D27, 0x1D27, 0x1D27 }, +{ 0x1D28, 0x1D28, 0x1D28 }, +{ 0x1D29, 0x1D29, 0x1D29 }, +{ 0x1D2A, 0x1D2A, 0x1D2A }, +{ 0x1D2B, 0x1D2B, 0x1D2B }, +{ 0x1D2C, 0x1D2C, 0x1D2C }, +{ 0x1D2D, 0x1D2D, 0x1D2D }, +{ 0x1D2E, 0x1D2E, 0x1D2E }, +{ 0x1D2F, 0x1D2F, 0x1D2F }, +{ 0x1D30, 0x1D30, 0x1D30 }, +{ 0x1D31, 0x1D31, 0x1D31 }, +{ 0x1D32, 0x1D32, 0x1D32 }, +{ 0x1D33, 0x1D33, 0x1D33 }, +{ 0x1D34, 0x1D34, 0x1D34 }, +{ 0x1D35, 0x1D35, 0x1D35 }, +{ 0x1D36, 0x1D36, 0x1D36 }, +{ 0x1D37, 0x1D37, 0x1D37 }, +{ 0x1D38, 0x1D38, 0x1D38 }, +{ 0x1D39, 0x1D39, 0x1D39 }, +{ 0x1D3A, 0x1D3A, 0x1D3A }, +{ 0x1D3B, 0x1D3B, 0x1D3B }, +{ 0x1D3C, 0x1D3C, 0x1D3C }, +{ 0x1D3D, 0x1D3D, 0x1D3D }, +{ 0x1D3E, 0x1D3E, 0x1D3E }, +{ 0x1D3F, 0x1D3F, 0x1D3F }, +{ 0x1D40, 0x1D40, 0x1D40 }, +{ 0x1D41, 0x1D41, 0x1D41 }, +{ 0x1D42, 0x1D42, 0x1D42 }, +{ 0x1D43, 0x1D43, 0x1D43 }, +{ 0x1D44, 0x1D44, 0x1D44 }, +{ 0x1D45, 0x1D45, 0x1D45 }, +{ 0x1D46, 0x1D46, 0x1D46 }, +{ 0x1D47, 0x1D47, 0x1D47 }, +{ 0x1D48, 0x1D48, 0x1D48 }, +{ 0x1D49, 0x1D49, 0x1D49 }, +{ 0x1D4A, 0x1D4A, 0x1D4A }, +{ 0x1D4B, 0x1D4B, 0x1D4B }, +{ 0x1D4C, 0x1D4C, 0x1D4C }, +{ 0x1D4D, 0x1D4D, 0x1D4D }, +{ 0x1D4E, 0x1D4E, 0x1D4E }, +{ 0x1D4F, 0x1D4F, 0x1D4F }, +{ 0x1D50, 0x1D50, 0x1D50 }, +{ 0x1D51, 0x1D51, 0x1D51 }, +{ 0x1D52, 0x1D52, 0x1D52 }, +{ 0x1D53, 0x1D53, 0x1D53 }, +{ 0x1D54, 0x1D54, 0x1D54 }, +{ 0x1D55, 0x1D55, 0x1D55 }, +{ 0x1D56, 0x1D56, 0x1D56 }, +{ 0x1D57, 0x1D57, 0x1D57 }, +{ 0x1D58, 0x1D58, 0x1D58 }, +{ 0x1D59, 0x1D59, 0x1D59 }, +{ 0x1D5A, 0x1D5A, 0x1D5A }, +{ 0x1D5B, 0x1D5B, 0x1D5B }, +{ 0x1D5C, 0x1D5C, 0x1D5C }, +{ 0x1D5D, 0x1D5D, 0x1D5D }, +{ 0x1D5E, 0x1D5E, 0x1D5E }, +{ 0x1D5F, 0x1D5F, 0x1D5F }, +{ 0x1D60, 0x1D60, 0x1D60 }, +{ 0x1D61, 0x1D61, 0x1D61 }, +{ 0x1D62, 0x1D62, 0x1D62 }, +{ 0x1D63, 0x1D63, 0x1D63 }, +{ 0x1D64, 0x1D64, 0x1D64 }, +{ 0x1D65, 0x1D65, 0x1D65 }, +{ 0x1D66, 0x1D66, 0x1D66 }, +{ 0x1D67, 0x1D67, 0x1D67 }, +{ 0x1D68, 0x1D68, 0x1D68 }, +{ 0x1D69, 0x1D69, 0x1D69 }, +{ 0x1D6A, 0x1D6A, 0x1D6A }, +{ 0x1D6B, 0x1D6B, 0x1D6B }, +{ 0x1D6C, 0x1D6C, 0x1D6C }, +{ 0x1D6D, 0x1D6D, 0x1D6D }, +{ 0x1D6E, 0x1D6E, 0x1D6E }, +{ 0x1D6F, 0x1D6F, 0x1D6F }, +{ 0x1D70, 0x1D70, 0x1D70 }, +{ 0x1D71, 0x1D71, 0x1D71 }, +{ 0x1D72, 0x1D72, 0x1D72 }, +{ 0x1D73, 0x1D73, 0x1D73 }, +{ 0x1D74, 0x1D74, 0x1D74 }, +{ 0x1D75, 0x1D75, 0x1D75 }, +{ 0x1D76, 0x1D76, 0x1D76 }, +{ 0x1D77, 0x1D77, 0x1D77 }, +{ 0x1D78, 0x1D78, 0x1D78 }, +{ 0x1D79, 0x1D79, 0x1D79 }, +{ 0x1D7A, 0x1D7A, 0x1D7A }, +{ 0x1D7B, 0x1D7B, 0x1D7B }, +{ 0x1D7C, 0x1D7C, 0x1D7C }, +{ 0x1D7D, 0x1D7D, 0x1D7D }, +{ 0x1D7E, 0x1D7E, 0x1D7E }, +{ 0x1D7F, 0x1D7F, 0x1D7F }, +{ 0x1D80, 0x1D80, 0x1D80 }, +{ 0x1D81, 0x1D81, 0x1D81 }, +{ 0x1D82, 0x1D82, 0x1D82 }, +{ 0x1D83, 0x1D83, 0x1D83 }, +{ 0x1D84, 0x1D84, 0x1D84 }, +{ 0x1D85, 0x1D85, 0x1D85 }, +{ 0x1D86, 0x1D86, 0x1D86 }, +{ 0x1D87, 0x1D87, 0x1D87 }, +{ 0x1D88, 0x1D88, 0x1D88 }, +{ 0x1D89, 0x1D89, 0x1D89 }, +{ 0x1D8A, 0x1D8A, 0x1D8A }, +{ 0x1D8B, 0x1D8B, 0x1D8B }, +{ 0x1D8C, 0x1D8C, 0x1D8C }, +{ 0x1D8D, 0x1D8D, 0x1D8D }, +{ 0x1D8E, 0x1D8E, 0x1D8E }, +{ 0x1D8F, 0x1D8F, 0x1D8F }, +{ 0x1D90, 0x1D90, 0x1D90 }, +{ 0x1D91, 0x1D91, 0x1D91 }, +{ 0x1D92, 0x1D92, 0x1D92 }, +{ 0x1D93, 0x1D93, 0x1D93 }, +{ 0x1D94, 0x1D94, 0x1D94 }, +{ 0x1D95, 0x1D95, 0x1D95 }, +{ 0x1D96, 0x1D96, 0x1D96 }, +{ 0x1D97, 0x1D97, 0x1D97 }, +{ 0x1D98, 0x1D98, 0x1D98 }, +{ 0x1D99, 0x1D99, 0x1D99 }, +{ 0x1D9A, 0x1D9A, 0x1D9A }, +{ 0x1D9B, 0x1D9B, 0x1D9B }, +{ 0x1D9C, 0x1D9C, 0x1D9C }, +{ 0x1D9D, 0x1D9D, 0x1D9D }, +{ 0x1D9E, 0x1D9E, 0x1D9E }, +{ 0x1D9F, 0x1D9F, 0x1D9F }, +{ 0x1DA0, 0x1DA0, 0x1DA0 }, +{ 0x1DA1, 0x1DA1, 0x1DA1 }, +{ 0x1DA2, 0x1DA2, 0x1DA2 }, +{ 0x1DA3, 0x1DA3, 0x1DA3 }, +{ 0x1DA4, 0x1DA4, 0x1DA4 }, +{ 0x1DA5, 0x1DA5, 0x1DA5 }, +{ 0x1DA6, 0x1DA6, 0x1DA6 }, +{ 0x1DA7, 0x1DA7, 0x1DA7 }, +{ 0x1DA8, 0x1DA8, 0x1DA8 }, +{ 0x1DA9, 0x1DA9, 0x1DA9 }, +{ 0x1DAA, 0x1DAA, 0x1DAA }, +{ 0x1DAB, 0x1DAB, 0x1DAB }, +{ 0x1DAC, 0x1DAC, 0x1DAC }, +{ 0x1DAD, 0x1DAD, 0x1DAD }, +{ 0x1DAE, 0x1DAE, 0x1DAE }, +{ 0x1DAF, 0x1DAF, 0x1DAF }, +{ 0x1DB0, 0x1DB0, 0x1DB0 }, +{ 0x1DB1, 0x1DB1, 0x1DB1 }, +{ 0x1DB2, 0x1DB2, 0x1DB2 }, +{ 0x1DB3, 0x1DB3, 0x1DB3 }, +{ 0x1DB4, 0x1DB4, 0x1DB4 }, +{ 0x1DB5, 0x1DB5, 0x1DB5 }, +{ 0x1DB6, 0x1DB6, 0x1DB6 }, +{ 0x1DB7, 0x1DB7, 0x1DB7 }, +{ 0x1DB8, 0x1DB8, 0x1DB8 }, +{ 0x1DB9, 0x1DB9, 0x1DB9 }, +{ 0x1DBA, 0x1DBA, 0x1DBA }, +{ 0x1DBB, 0x1DBB, 0x1DBB }, +{ 0x1DBC, 0x1DBC, 0x1DBC }, +{ 0x1DBD, 0x1DBD, 0x1DBD }, +{ 0x1DBE, 0x1DBE, 0x1DBE }, +{ 0x1DBF, 0x1DBF, 0x1DBF }, +{ 0x1DC0, 0x1DC0, 0x1DC0 }, +{ 0x1DC1, 0x1DC1, 0x1DC1 }, +{ 0x1DC2, 0x1DC2, 0x1DC2 }, +{ 0x1DC3, 0x1DC3, 0x1DC3 }, +{ 0x1E00, 0x1E00, 0x1E01 }, +{ 0x1E01, 0x1E00, 0x1E01 }, +{ 0x1E02, 0x1E02, 0x1E03 }, +{ 0x1E03, 0x1E02, 0x1E03 }, +{ 0x1E04, 0x1E04, 0x1E05 }, +{ 0x1E05, 0x1E04, 0x1E05 }, +{ 0x1E06, 0x1E06, 0x1E07 }, +{ 0x1E07, 0x1E06, 0x1E07 }, +{ 0x1E08, 0x1E08, 0x1E09 }, +{ 0x1E09, 0x1E08, 0x1E09 }, +{ 0x1E0A, 0x1E0A, 0x1E0B }, +{ 0x1E0B, 0x1E0A, 0x1E0B }, +{ 0x1E0C, 0x1E0C, 0x1E0D }, +{ 0x1E0D, 0x1E0C, 0x1E0D }, +{ 0x1E0E, 0x1E0E, 0x1E0F }, +{ 0x1E0F, 0x1E0E, 0x1E0F }, +{ 0x1E10, 0x1E10, 0x1E11 }, +{ 0x1E11, 0x1E10, 0x1E11 }, +{ 0x1E12, 0x1E12, 0x1E13 }, +{ 0x1E13, 0x1E12, 0x1E13 }, +{ 0x1E14, 0x1E14, 0x1E15 }, +{ 0x1E15, 0x1E14, 0x1E15 }, +{ 0x1E16, 0x1E16, 0x1E17 }, +{ 0x1E17, 0x1E16, 0x1E17 }, +{ 0x1E18, 0x1E18, 0x1E19 }, +{ 0x1E19, 0x1E18, 0x1E19 }, +{ 0x1E1A, 0x1E1A, 0x1E1B }, +{ 0x1E1B, 0x1E1A, 0x1E1B }, +{ 0x1E1C, 0x1E1C, 0x1E1D }, +{ 0x1E1D, 0x1E1C, 0x1E1D }, +{ 0x1E1E, 0x1E1E, 0x1E1F }, +{ 0x1E1F, 0x1E1E, 0x1E1F }, +{ 0x1E20, 0x1E20, 0x1E21 }, +{ 0x1E21, 0x1E20, 0x1E21 }, +{ 0x1E22, 0x1E22, 0x1E23 }, +{ 0x1E23, 0x1E22, 0x1E23 }, +{ 0x1E24, 0x1E24, 0x1E25 }, +{ 0x1E25, 0x1E24, 0x1E25 }, +{ 0x1E26, 0x1E26, 0x1E27 }, +{ 0x1E27, 0x1E26, 0x1E27 }, +{ 0x1E28, 0x1E28, 0x1E29 }, +{ 0x1E29, 0x1E28, 0x1E29 }, +{ 0x1E2A, 0x1E2A, 0x1E2B }, +{ 0x1E2B, 0x1E2A, 0x1E2B }, +{ 0x1E2C, 0x1E2C, 0x1E2D }, +{ 0x1E2D, 0x1E2C, 0x1E2D }, +{ 0x1E2E, 0x1E2E, 0x1E2F }, +{ 0x1E2F, 0x1E2E, 0x1E2F }, +{ 0x1E30, 0x1E30, 0x1E31 }, +{ 0x1E31, 0x1E30, 0x1E31 }, +{ 0x1E32, 0x1E32, 0x1E33 }, +{ 0x1E33, 0x1E32, 0x1E33 }, +{ 0x1E34, 0x1E34, 0x1E35 }, +{ 0x1E35, 0x1E34, 0x1E35 }, +{ 0x1E36, 0x1E36, 0x1E37 }, +{ 0x1E37, 0x1E36, 0x1E37 }, +{ 0x1E38, 0x1E38, 0x1E39 }, +{ 0x1E39, 0x1E38, 0x1E39 }, +{ 0x1E3A, 0x1E3A, 0x1E3B }, +{ 0x1E3B, 0x1E3A, 0x1E3B }, +{ 0x1E3C, 0x1E3C, 0x1E3D }, +{ 0x1E3D, 0x1E3C, 0x1E3D }, +{ 0x1E3E, 0x1E3E, 0x1E3F }, +{ 0x1E3F, 0x1E3E, 0x1E3F }, +{ 0x1E40, 0x1E40, 0x1E41 }, +{ 0x1E41, 0x1E40, 0x1E41 }, +{ 0x1E42, 0x1E42, 0x1E43 }, +{ 0x1E43, 0x1E42, 0x1E43 }, +{ 0x1E44, 0x1E44, 0x1E45 }, +{ 0x1E45, 0x1E44, 0x1E45 }, +{ 0x1E46, 0x1E46, 0x1E47 }, +{ 0x1E47, 0x1E46, 0x1E47 }, +{ 0x1E48, 0x1E48, 0x1E49 }, +{ 0x1E49, 0x1E48, 0x1E49 }, +{ 0x1E4A, 0x1E4A, 0x1E4B }, +{ 0x1E4B, 0x1E4A, 0x1E4B }, +{ 0x1E4C, 0x1E4C, 0x1E4D }, +{ 0x1E4D, 0x1E4C, 0x1E4D }, +{ 0x1E4E, 0x1E4E, 0x1E4F }, +{ 0x1E4F, 0x1E4E, 0x1E4F }, +{ 0x1E50, 0x1E50, 0x1E51 }, +{ 0x1E51, 0x1E50, 0x1E51 }, +{ 0x1E52, 0x1E52, 0x1E53 }, +{ 0x1E53, 0x1E52, 0x1E53 }, +{ 0x1E54, 0x1E54, 0x1E55 }, +{ 0x1E55, 0x1E54, 0x1E55 }, +{ 0x1E56, 0x1E56, 0x1E57 }, +{ 0x1E57, 0x1E56, 0x1E57 }, +{ 0x1E58, 0x1E58, 0x1E59 }, +{ 0x1E59, 0x1E58, 0x1E59 }, +{ 0x1E5A, 0x1E5A, 0x1E5B }, +{ 0x1E5B, 0x1E5A, 0x1E5B }, +{ 0x1E5C, 0x1E5C, 0x1E5D }, +{ 0x1E5D, 0x1E5C, 0x1E5D }, +{ 0x1E5E, 0x1E5E, 0x1E5F }, +{ 0x1E5F, 0x1E5E, 0x1E5F }, +{ 0x1E60, 0x1E60, 0x1E61 }, +{ 0x1E61, 0x1E60, 0x1E61 }, +{ 0x1E62, 0x1E62, 0x1E63 }, +{ 0x1E63, 0x1E62, 0x1E63 }, +{ 0x1E64, 0x1E64, 0x1E65 }, +{ 0x1E65, 0x1E64, 0x1E65 }, +{ 0x1E66, 0x1E66, 0x1E67 }, +{ 0x1E67, 0x1E66, 0x1E67 }, +{ 0x1E68, 0x1E68, 0x1E69 }, +{ 0x1E69, 0x1E68, 0x1E69 }, +{ 0x1E6A, 0x1E6A, 0x1E6B }, +{ 0x1E6B, 0x1E6A, 0x1E6B }, +{ 0x1E6C, 0x1E6C, 0x1E6D }, +{ 0x1E6D, 0x1E6C, 0x1E6D }, +{ 0x1E6E, 0x1E6E, 0x1E6F }, +{ 0x1E6F, 0x1E6E, 0x1E6F }, +{ 0x1E70, 0x1E70, 0x1E71 }, +{ 0x1E71, 0x1E70, 0x1E71 }, +{ 0x1E72, 0x1E72, 0x1E73 }, +{ 0x1E73, 0x1E72, 0x1E73 }, +{ 0x1E74, 0x1E74, 0x1E75 }, +{ 0x1E75, 0x1E74, 0x1E75 }, +{ 0x1E76, 0x1E76, 0x1E77 }, +{ 0x1E77, 0x1E76, 0x1E77 }, +{ 0x1E78, 0x1E78, 0x1E79 }, +{ 0x1E79, 0x1E78, 0x1E79 }, +{ 0x1E7A, 0x1E7A, 0x1E7B }, +{ 0x1E7B, 0x1E7A, 0x1E7B }, +{ 0x1E7C, 0x1E7C, 0x1E7D }, +{ 0x1E7D, 0x1E7C, 0x1E7D }, +{ 0x1E7E, 0x1E7E, 0x1E7F }, +{ 0x1E7F, 0x1E7E, 0x1E7F }, +{ 0x1E80, 0x1E80, 0x1E81 }, +{ 0x1E81, 0x1E80, 0x1E81 }, +{ 0x1E82, 0x1E82, 0x1E83 }, +{ 0x1E83, 0x1E82, 0x1E83 }, +{ 0x1E84, 0x1E84, 0x1E85 }, +{ 0x1E85, 0x1E84, 0x1E85 }, +{ 0x1E86, 0x1E86, 0x1E87 }, +{ 0x1E87, 0x1E86, 0x1E87 }, +{ 0x1E88, 0x1E88, 0x1E89 }, +{ 0x1E89, 0x1E88, 0x1E89 }, +{ 0x1E8A, 0x1E8A, 0x1E8B }, +{ 0x1E8B, 0x1E8A, 0x1E8B }, +{ 0x1E8C, 0x1E8C, 0x1E8D }, +{ 0x1E8D, 0x1E8C, 0x1E8D }, +{ 0x1E8E, 0x1E8E, 0x1E8F }, +{ 0x1E8F, 0x1E8E, 0x1E8F }, +{ 0x1E90, 0x1E90, 0x1E91 }, +{ 0x1E91, 0x1E90, 0x1E91 }, +{ 0x1E92, 0x1E92, 0x1E93 }, +{ 0x1E93, 0x1E92, 0x1E93 }, +{ 0x1E94, 0x1E94, 0x1E95 }, +{ 0x1E95, 0x1E94, 0x1E95 }, +{ 0x1E96, 0x1E96, 0x1E96 }, +{ 0x1E97, 0x1E97, 0x1E97 }, +{ 0x1E98, 0x1E98, 0x1E98 }, +{ 0x1E99, 0x1E99, 0x1E99 }, +{ 0x1E9A, 0x1E9A, 0x1E9A }, +{ 0x1E9B, 0x1E60, 0x1E9B }, +{ 0x1EA0, 0x1EA0, 0x1EA1 }, +{ 0x1EA1, 0x1EA0, 0x1EA1 }, +{ 0x1EA2, 0x1EA2, 0x1EA3 }, +{ 0x1EA3, 0x1EA2, 0x1EA3 }, +{ 0x1EA4, 0x1EA4, 0x1EA5 }, +{ 0x1EA5, 0x1EA4, 0x1EA5 }, +{ 0x1EA6, 0x1EA6, 0x1EA7 }, +{ 0x1EA7, 0x1EA6, 0x1EA7 }, +{ 0x1EA8, 0x1EA8, 0x1EA9 }, +{ 0x1EA9, 0x1EA8, 0x1EA9 }, +{ 0x1EAA, 0x1EAA, 0x1EAB }, +{ 0x1EAB, 0x1EAA, 0x1EAB }, +{ 0x1EAC, 0x1EAC, 0x1EAD }, +{ 0x1EAD, 0x1EAC, 0x1EAD }, +{ 0x1EAE, 0x1EAE, 0x1EAF }, +{ 0x1EAF, 0x1EAE, 0x1EAF }, +{ 0x1EB0, 0x1EB0, 0x1EB1 }, +{ 0x1EB1, 0x1EB0, 0x1EB1 }, +{ 0x1EB2, 0x1EB2, 0x1EB3 }, +{ 0x1EB3, 0x1EB2, 0x1EB3 }, +{ 0x1EB4, 0x1EB4, 0x1EB5 }, +{ 0x1EB5, 0x1EB4, 0x1EB5 }, +{ 0x1EB6, 0x1EB6, 0x1EB7 }, +{ 0x1EB7, 0x1EB6, 0x1EB7 }, +{ 0x1EB8, 0x1EB8, 0x1EB9 }, +{ 0x1EB9, 0x1EB8, 0x1EB9 }, +{ 0x1EBA, 0x1EBA, 0x1EBB }, +{ 0x1EBB, 0x1EBA, 0x1EBB }, +{ 0x1EBC, 0x1EBC, 0x1EBD }, +{ 0x1EBD, 0x1EBC, 0x1EBD }, +{ 0x1EBE, 0x1EBE, 0x1EBF }, +{ 0x1EBF, 0x1EBE, 0x1EBF }, +{ 0x1EC0, 0x1EC0, 0x1EC1 }, +{ 0x1EC1, 0x1EC0, 0x1EC1 }, +{ 0x1EC2, 0x1EC2, 0x1EC3 }, +{ 0x1EC3, 0x1EC2, 0x1EC3 }, +{ 0x1EC4, 0x1EC4, 0x1EC5 }, +{ 0x1EC5, 0x1EC4, 0x1EC5 }, +{ 0x1EC6, 0x1EC6, 0x1EC7 }, +{ 0x1EC7, 0x1EC6, 0x1EC7 }, +{ 0x1EC8, 0x1EC8, 0x1EC9 }, +{ 0x1EC9, 0x1EC8, 0x1EC9 }, +{ 0x1ECA, 0x1ECA, 0x1ECB }, +{ 0x1ECB, 0x1ECA, 0x1ECB }, +{ 0x1ECC, 0x1ECC, 0x1ECD }, +{ 0x1ECD, 0x1ECC, 0x1ECD }, +{ 0x1ECE, 0x1ECE, 0x1ECF }, +{ 0x1ECF, 0x1ECE, 0x1ECF }, +{ 0x1ED0, 0x1ED0, 0x1ED1 }, +{ 0x1ED1, 0x1ED0, 0x1ED1 }, +{ 0x1ED2, 0x1ED2, 0x1ED3 }, +{ 0x1ED3, 0x1ED2, 0x1ED3 }, +{ 0x1ED4, 0x1ED4, 0x1ED5 }, +{ 0x1ED5, 0x1ED4, 0x1ED5 }, +{ 0x1ED6, 0x1ED6, 0x1ED7 }, +{ 0x1ED7, 0x1ED6, 0x1ED7 }, +{ 0x1ED8, 0x1ED8, 0x1ED9 }, +{ 0x1ED9, 0x1ED8, 0x1ED9 }, +{ 0x1EDA, 0x1EDA, 0x1EDB }, +{ 0x1EDB, 0x1EDA, 0x1EDB }, +{ 0x1EDC, 0x1EDC, 0x1EDD }, +{ 0x1EDD, 0x1EDC, 0x1EDD }, +{ 0x1EDE, 0x1EDE, 0x1EDF }, +{ 0x1EDF, 0x1EDE, 0x1EDF }, +{ 0x1EE0, 0x1EE0, 0x1EE1 }, +{ 0x1EE1, 0x1EE0, 0x1EE1 }, +{ 0x1EE2, 0x1EE2, 0x1EE3 }, +{ 0x1EE3, 0x1EE2, 0x1EE3 }, +{ 0x1EE4, 0x1EE4, 0x1EE5 }, +{ 0x1EE5, 0x1EE4, 0x1EE5 }, +{ 0x1EE6, 0x1EE6, 0x1EE7 }, +{ 0x1EE7, 0x1EE6, 0x1EE7 }, +{ 0x1EE8, 0x1EE8, 0x1EE9 }, +{ 0x1EE9, 0x1EE8, 0x1EE9 }, +{ 0x1EEA, 0x1EEA, 0x1EEB }, +{ 0x1EEB, 0x1EEA, 0x1EEB }, +{ 0x1EEC, 0x1EEC, 0x1EED }, +{ 0x1EED, 0x1EEC, 0x1EED }, +{ 0x1EEE, 0x1EEE, 0x1EEF }, +{ 0x1EEF, 0x1EEE, 0x1EEF }, +{ 0x1EF0, 0x1EF0, 0x1EF1 }, +{ 0x1EF1, 0x1EF0, 0x1EF1 }, +{ 0x1EF2, 0x1EF2, 0x1EF3 }, +{ 0x1EF3, 0x1EF2, 0x1EF3 }, +{ 0x1EF4, 0x1EF4, 0x1EF5 }, +{ 0x1EF5, 0x1EF4, 0x1EF5 }, +{ 0x1EF6, 0x1EF6, 0x1EF7 }, +{ 0x1EF7, 0x1EF6, 0x1EF7 }, +{ 0x1EF8, 0x1EF8, 0x1EF9 }, +{ 0x1EF9, 0x1EF8, 0x1EF9 }, +{ 0x1F00, 0x1F08, 0x1F00 }, +{ 0x1F01, 0x1F09, 0x1F01 }, +{ 0x1F02, 0x1F0A, 0x1F02 }, +{ 0x1F03, 0x1F0B, 0x1F03 }, +{ 0x1F04, 0x1F0C, 0x1F04 }, +{ 0x1F05, 0x1F0D, 0x1F05 }, +{ 0x1F06, 0x1F0E, 0x1F06 }, +{ 0x1F07, 0x1F0F, 0x1F07 }, +{ 0x1F08, 0x1F08, 0x1F00 }, +{ 0x1F09, 0x1F09, 0x1F01 }, +{ 0x1F0A, 0x1F0A, 0x1F02 }, +{ 0x1F0B, 0x1F0B, 0x1F03 }, +{ 0x1F0C, 0x1F0C, 0x1F04 }, +{ 0x1F0D, 0x1F0D, 0x1F05 }, +{ 0x1F0E, 0x1F0E, 0x1F06 }, +{ 0x1F0F, 0x1F0F, 0x1F07 }, +{ 0x1F10, 0x1F18, 0x1F10 }, +{ 0x1F11, 0x1F19, 0x1F11 }, +{ 0x1F12, 0x1F1A, 0x1F12 }, +{ 0x1F13, 0x1F1B, 0x1F13 }, +{ 0x1F14, 0x1F1C, 0x1F14 }, +{ 0x1F15, 0x1F1D, 0x1F15 }, +{ 0x1F18, 0x1F18, 0x1F10 }, +{ 0x1F19, 0x1F19, 0x1F11 }, +{ 0x1F1A, 0x1F1A, 0x1F12 }, +{ 0x1F1B, 0x1F1B, 0x1F13 }, +{ 0x1F1C, 0x1F1C, 0x1F14 }, +{ 0x1F1D, 0x1F1D, 0x1F15 }, +{ 0x1F20, 0x1F28, 0x1F20 }, +{ 0x1F21, 0x1F29, 0x1F21 }, +{ 0x1F22, 0x1F2A, 0x1F22 }, +{ 0x1F23, 0x1F2B, 0x1F23 }, +{ 0x1F24, 0x1F2C, 0x1F24 }, +{ 0x1F25, 0x1F2D, 0x1F25 }, +{ 0x1F26, 0x1F2E, 0x1F26 }, +{ 0x1F27, 0x1F2F, 0x1F27 }, +{ 0x1F28, 0x1F28, 0x1F20 }, +{ 0x1F29, 0x1F29, 0x1F21 }, +{ 0x1F2A, 0x1F2A, 0x1F22 }, +{ 0x1F2B, 0x1F2B, 0x1F23 }, +{ 0x1F2C, 0x1F2C, 0x1F24 }, +{ 0x1F2D, 0x1F2D, 0x1F25 }, +{ 0x1F2E, 0x1F2E, 0x1F26 }, +{ 0x1F2F, 0x1F2F, 0x1F27 }, +{ 0x1F30, 0x1F38, 0x1F30 }, +{ 0x1F31, 0x1F39, 0x1F31 }, +{ 0x1F32, 0x1F3A, 0x1F32 }, +{ 0x1F33, 0x1F3B, 0x1F33 }, +{ 0x1F34, 0x1F3C, 0x1F34 }, +{ 0x1F35, 0x1F3D, 0x1F35 }, +{ 0x1F36, 0x1F3E, 0x1F36 }, +{ 0x1F37, 0x1F3F, 0x1F37 }, +{ 0x1F38, 0x1F38, 0x1F30 }, +{ 0x1F39, 0x1F39, 0x1F31 }, +{ 0x1F3A, 0x1F3A, 0x1F32 }, +{ 0x1F3B, 0x1F3B, 0x1F33 }, +{ 0x1F3C, 0x1F3C, 0x1F34 }, +{ 0x1F3D, 0x1F3D, 0x1F35 }, +{ 0x1F3E, 0x1F3E, 0x1F36 }, +{ 0x1F3F, 0x1F3F, 0x1F37 }, +{ 0x1F40, 0x1F48, 0x1F40 }, +{ 0x1F41, 0x1F49, 0x1F41 }, +{ 0x1F42, 0x1F4A, 0x1F42 }, +{ 0x1F43, 0x1F4B, 0x1F43 }, +{ 0x1F44, 0x1F4C, 0x1F44 }, +{ 0x1F45, 0x1F4D, 0x1F45 }, +{ 0x1F48, 0x1F48, 0x1F40 }, +{ 0x1F49, 0x1F49, 0x1F41 }, +{ 0x1F4A, 0x1F4A, 0x1F42 }, +{ 0x1F4B, 0x1F4B, 0x1F43 }, +{ 0x1F4C, 0x1F4C, 0x1F44 }, +{ 0x1F4D, 0x1F4D, 0x1F45 }, +{ 0x1F50, 0x1F50, 0x1F50 }, +{ 0x1F51, 0x1F59, 0x1F51 }, +{ 0x1F52, 0x1F52, 0x1F52 }, +{ 0x1F53, 0x1F5B, 0x1F53 }, +{ 0x1F54, 0x1F54, 0x1F54 }, +{ 0x1F55, 0x1F5D, 0x1F55 }, +{ 0x1F56, 0x1F56, 0x1F56 }, +{ 0x1F57, 0x1F5F, 0x1F57 }, +{ 0x1F59, 0x1F59, 0x1F51 }, +{ 0x1F5B, 0x1F5B, 0x1F53 }, +{ 0x1F5D, 0x1F5D, 0x1F55 }, +{ 0x1F5F, 0x1F5F, 0x1F57 }, +{ 0x1F60, 0x1F68, 0x1F60 }, +{ 0x1F61, 0x1F69, 0x1F61 }, +{ 0x1F62, 0x1F6A, 0x1F62 }, +{ 0x1F63, 0x1F6B, 0x1F63 }, +{ 0x1F64, 0x1F6C, 0x1F64 }, +{ 0x1F65, 0x1F6D, 0x1F65 }, +{ 0x1F66, 0x1F6E, 0x1F66 }, +{ 0x1F67, 0x1F6F, 0x1F67 }, +{ 0x1F68, 0x1F68, 0x1F60 }, +{ 0x1F69, 0x1F69, 0x1F61 }, +{ 0x1F6A, 0x1F6A, 0x1F62 }, +{ 0x1F6B, 0x1F6B, 0x1F63 }, +{ 0x1F6C, 0x1F6C, 0x1F64 }, +{ 0x1F6D, 0x1F6D, 0x1F65 }, +{ 0x1F6E, 0x1F6E, 0x1F66 }, +{ 0x1F6F, 0x1F6F, 0x1F67 }, +{ 0x1F70, 0x1FBA, 0x1F70 }, +{ 0x1F71, 0x1FBB, 0x1F71 }, +{ 0x1F72, 0x1FC8, 0x1F72 }, +{ 0x1F73, 0x1FC9, 0x1F73 }, +{ 0x1F74, 0x1FCA, 0x1F74 }, +{ 0x1F75, 0x1FCB, 0x1F75 }, +{ 0x1F76, 0x1FDA, 0x1F76 }, +{ 0x1F77, 0x1FDB, 0x1F77 }, +{ 0x1F78, 0x1FF8, 0x1F78 }, +{ 0x1F79, 0x1FF9, 0x1F79 }, +{ 0x1F7A, 0x1FEA, 0x1F7A }, +{ 0x1F7B, 0x1FEB, 0x1F7B }, +{ 0x1F7C, 0x1FFA, 0x1F7C }, +{ 0x1F7D, 0x1FFB, 0x1F7D }, +{ 0x1F80, 0x1F88, 0x1F80 }, +{ 0x1F81, 0x1F89, 0x1F81 }, +{ 0x1F82, 0x1F8A, 0x1F82 }, +{ 0x1F83, 0x1F8B, 0x1F83 }, +{ 0x1F84, 0x1F8C, 0x1F84 }, +{ 0x1F85, 0x1F8D, 0x1F85 }, +{ 0x1F86, 0x1F8E, 0x1F86 }, +{ 0x1F87, 0x1F8F, 0x1F87 }, +{ 0x1F88, 0x1F88, 0x1F80 }, +{ 0x1F89, 0x1F89, 0x1F81 }, +{ 0x1F8A, 0x1F8A, 0x1F82 }, +{ 0x1F8B, 0x1F8B, 0x1F83 }, +{ 0x1F8C, 0x1F8C, 0x1F84 }, +{ 0x1F8D, 0x1F8D, 0x1F85 }, +{ 0x1F8E, 0x1F8E, 0x1F86 }, +{ 0x1F8F, 0x1F8F, 0x1F87 }, +{ 0x1F90, 0x1F98, 0x1F90 }, +{ 0x1F91, 0x1F99, 0x1F91 }, +{ 0x1F92, 0x1F9A, 0x1F92 }, +{ 0x1F93, 0x1F9B, 0x1F93 }, +{ 0x1F94, 0x1F9C, 0x1F94 }, +{ 0x1F95, 0x1F9D, 0x1F95 }, +{ 0x1F96, 0x1F9E, 0x1F96 }, +{ 0x1F97, 0x1F9F, 0x1F97 }, +{ 0x1F98, 0x1F98, 0x1F90 }, +{ 0x1F99, 0x1F99, 0x1F91 }, +{ 0x1F9A, 0x1F9A, 0x1F92 }, +{ 0x1F9B, 0x1F9B, 0x1F93 }, +{ 0x1F9C, 0x1F9C, 0x1F94 }, +{ 0x1F9D, 0x1F9D, 0x1F95 }, +{ 0x1F9E, 0x1F9E, 0x1F96 }, +{ 0x1F9F, 0x1F9F, 0x1F97 }, +{ 0x1FA0, 0x1FA8, 0x1FA0 }, +{ 0x1FA1, 0x1FA9, 0x1FA1 }, +{ 0x1FA2, 0x1FAA, 0x1FA2 }, +{ 0x1FA3, 0x1FAB, 0x1FA3 }, +{ 0x1FA4, 0x1FAC, 0x1FA4 }, +{ 0x1FA5, 0x1FAD, 0x1FA5 }, +{ 0x1FA6, 0x1FAE, 0x1FA6 }, +{ 0x1FA7, 0x1FAF, 0x1FA7 }, +{ 0x1FA8, 0x1FA8, 0x1FA0 }, +{ 0x1FA9, 0x1FA9, 0x1FA1 }, +{ 0x1FAA, 0x1FAA, 0x1FA2 }, +{ 0x1FAB, 0x1FAB, 0x1FA3 }, +{ 0x1FAC, 0x1FAC, 0x1FA4 }, +{ 0x1FAD, 0x1FAD, 0x1FA5 }, +{ 0x1FAE, 0x1FAE, 0x1FA6 }, +{ 0x1FAF, 0x1FAF, 0x1FA7 }, +{ 0x1FB0, 0x1FB8, 0x1FB0 }, +{ 0x1FB1, 0x1FB9, 0x1FB1 }, +{ 0x1FB2, 0x1FB2, 0x1FB2 }, +{ 0x1FB3, 0x1FBC, 0x1FB3 }, +{ 0x1FB4, 0x1FB4, 0x1FB4 }, +{ 0x1FB6, 0x1FB6, 0x1FB6 }, +{ 0x1FB7, 0x1FB7, 0x1FB7 }, +{ 0x1FB8, 0x1FB8, 0x1FB0 }, +{ 0x1FB9, 0x1FB9, 0x1FB1 }, +{ 0x1FBA, 0x1FBA, 0x1F70 }, +{ 0x1FBB, 0x1FBB, 0x1F71 }, +{ 0x1FBC, 0x1FBC, 0x1FB3 }, +{ 0x1FBE, 0x0399, 0x1FBE }, +{ 0x1FC2, 0x1FC2, 0x1FC2 }, +{ 0x1FC3, 0x1FCC, 0x1FC3 }, +{ 0x1FC4, 0x1FC4, 0x1FC4 }, +{ 0x1FC6, 0x1FC6, 0x1FC6 }, +{ 0x1FC7, 0x1FC7, 0x1FC7 }, +{ 0x1FC8, 0x1FC8, 0x1F72 }, +{ 0x1FC9, 0x1FC9, 0x1F73 }, +{ 0x1FCA, 0x1FCA, 0x1F74 }, +{ 0x1FCB, 0x1FCB, 0x1F75 }, +{ 0x1FCC, 0x1FCC, 0x1FC3 }, +{ 0x1FD0, 0x1FD8, 0x1FD0 }, +{ 0x1FD1, 0x1FD9, 0x1FD1 }, +{ 0x1FD2, 0x1FD2, 0x1FD2 }, +{ 0x1FD3, 0x1FD3, 0x1FD3 }, +{ 0x1FD6, 0x1FD6, 0x1FD6 }, +{ 0x1FD7, 0x1FD7, 0x1FD7 }, +{ 0x1FD8, 0x1FD8, 0x1FD0 }, +{ 0x1FD9, 0x1FD9, 0x1FD1 }, +{ 0x1FDA, 0x1FDA, 0x1F76 }, +{ 0x1FDB, 0x1FDB, 0x1F77 }, +{ 0x1FE0, 0x1FE8, 0x1FE0 }, +{ 0x1FE1, 0x1FE9, 0x1FE1 }, +{ 0x1FE2, 0x1FE2, 0x1FE2 }, +{ 0x1FE3, 0x1FE3, 0x1FE3 }, +{ 0x1FE4, 0x1FE4, 0x1FE4 }, +{ 0x1FE5, 0x1FEC, 0x1FE5 }, +{ 0x1FE6, 0x1FE6, 0x1FE6 }, +{ 0x1FE7, 0x1FE7, 0x1FE7 }, +{ 0x1FE8, 0x1FE8, 0x1FE0 }, +{ 0x1FE9, 0x1FE9, 0x1FE1 }, +{ 0x1FEA, 0x1FEA, 0x1F7A }, +{ 0x1FEB, 0x1FEB, 0x1F7B }, +{ 0x1FEC, 0x1FEC, 0x1FE5 }, +{ 0x1FF2, 0x1FF2, 0x1FF2 }, +{ 0x1FF3, 0x1FFC, 0x1FF3 }, +{ 0x1FF4, 0x1FF4, 0x1FF4 }, +{ 0x1FF6, 0x1FF6, 0x1FF6 }, +{ 0x1FF7, 0x1FF7, 0x1FF7 }, +{ 0x1FF8, 0x1FF8, 0x1F78 }, +{ 0x1FF9, 0x1FF9, 0x1F79 }, +{ 0x1FFA, 0x1FFA, 0x1F7C }, +{ 0x1FFB, 0x1FFB, 0x1F7D }, +{ 0x1FFC, 0x1FFC, 0x1FF3 }, +{ 0x2071, 0x2071, 0x2071 }, +{ 0x207F, 0x207F, 0x207F }, +{ 0x2090, 0x2090, 0x2090 }, +{ 0x2091, 0x2091, 0x2091 }, +{ 0x2092, 0x2092, 0x2092 }, +{ 0x2093, 0x2093, 0x2093 }, +{ 0x2094, 0x2094, 0x2094 }, +{ 0x20D0, 0x20D0, 0x20D0 }, +{ 0x20D1, 0x20D1, 0x20D1 }, +{ 0x20D2, 0x20D2, 0x20D2 }, +{ 0x20D3, 0x20D3, 0x20D3 }, +{ 0x20D4, 0x20D4, 0x20D4 }, +{ 0x20D5, 0x20D5, 0x20D5 }, +{ 0x20D6, 0x20D6, 0x20D6 }, +{ 0x20D7, 0x20D7, 0x20D7 }, +{ 0x20D8, 0x20D8, 0x20D8 }, +{ 0x20D9, 0x20D9, 0x20D9 }, +{ 0x20DA, 0x20DA, 0x20DA }, +{ 0x20DB, 0x20DB, 0x20DB }, +{ 0x20DC, 0x20DC, 0x20DC }, +{ 0x20E1, 0x20E1, 0x20E1 }, +{ 0x20E5, 0x20E5, 0x20E5 }, +{ 0x20E6, 0x20E6, 0x20E6 }, +{ 0x20E7, 0x20E7, 0x20E7 }, +{ 0x20E8, 0x20E8, 0x20E8 }, +{ 0x20E9, 0x20E9, 0x20E9 }, +{ 0x20EA, 0x20EA, 0x20EA }, +{ 0x20EB, 0x20EB, 0x20EB }, +{ 0x2102, 0x2102, 0x2102 }, +{ 0x2107, 0x2107, 0x2107 }, +{ 0x210A, 0x210A, 0x210A }, +{ 0x210B, 0x210B, 0x210B }, +{ 0x210C, 0x210C, 0x210C }, +{ 0x210D, 0x210D, 0x210D }, +{ 0x210E, 0x210E, 0x210E }, +{ 0x210F, 0x210F, 0x210F }, +{ 0x2110, 0x2110, 0x2110 }, +{ 0x2111, 0x2111, 0x2111 }, +{ 0x2112, 0x2112, 0x2112 }, +{ 0x2113, 0x2113, 0x2113 }, +{ 0x2115, 0x2115, 0x2115 }, +{ 0x2119, 0x2119, 0x2119 }, +{ 0x211A, 0x211A, 0x211A }, +{ 0x211B, 0x211B, 0x211B }, +{ 0x211C, 0x211C, 0x211C }, +{ 0x211D, 0x211D, 0x211D }, +{ 0x2124, 0x2124, 0x2124 }, +{ 0x2126, 0x2126, 0x03C9 }, +{ 0x2128, 0x2128, 0x2128 }, +{ 0x212A, 0x212A, 0x006B }, +{ 0x212B, 0x212B, 0x00E5 }, +{ 0x212C, 0x212C, 0x212C }, +{ 0x212D, 0x212D, 0x212D }, +{ 0x212F, 0x212F, 0x212F }, +{ 0x2130, 0x2130, 0x2130 }, +{ 0x2131, 0x2131, 0x2131 }, +{ 0x2133, 0x2133, 0x2133 }, +{ 0x2134, 0x2134, 0x2134 }, +{ 0x2135, 0x2135, 0x2135 }, +{ 0x2136, 0x2136, 0x2136 }, +{ 0x2137, 0x2137, 0x2137 }, +{ 0x2138, 0x2138, 0x2138 }, +{ 0x2139, 0x2139, 0x2139 }, +{ 0x213C, 0x213C, 0x213C }, +{ 0x213D, 0x213D, 0x213D }, +{ 0x213E, 0x213E, 0x213E }, +{ 0x213F, 0x213F, 0x213F }, +{ 0x2145, 0x2145, 0x2145 }, +{ 0x2146, 0x2146, 0x2146 }, +{ 0x2147, 0x2147, 0x2147 }, +{ 0x2148, 0x2148, 0x2148 }, +{ 0x2149, 0x2149, 0x2149 }, +{ 0x2C00, 0x2C00, 0x2C30 }, +{ 0x2C01, 0x2C01, 0x2C31 }, +{ 0x2C02, 0x2C02, 0x2C32 }, +{ 0x2C03, 0x2C03, 0x2C33 }, +{ 0x2C04, 0x2C04, 0x2C34 }, +{ 0x2C05, 0x2C05, 0x2C35 }, +{ 0x2C06, 0x2C06, 0x2C36 }, +{ 0x2C07, 0x2C07, 0x2C37 }, +{ 0x2C08, 0x2C08, 0x2C38 }, +{ 0x2C09, 0x2C09, 0x2C39 }, +{ 0x2C0A, 0x2C0A, 0x2C3A }, +{ 0x2C0B, 0x2C0B, 0x2C3B }, +{ 0x2C0C, 0x2C0C, 0x2C3C }, +{ 0x2C0D, 0x2C0D, 0x2C3D }, +{ 0x2C0E, 0x2C0E, 0x2C3E }, +{ 0x2C0F, 0x2C0F, 0x2C3F }, +{ 0x2C10, 0x2C10, 0x2C40 }, +{ 0x2C11, 0x2C11, 0x2C41 }, +{ 0x2C12, 0x2C12, 0x2C42 }, +{ 0x2C13, 0x2C13, 0x2C43 }, +{ 0x2C14, 0x2C14, 0x2C44 }, +{ 0x2C15, 0x2C15, 0x2C45 }, +{ 0x2C16, 0x2C16, 0x2C46 }, +{ 0x2C17, 0x2C17, 0x2C47 }, +{ 0x2C18, 0x2C18, 0x2C48 }, +{ 0x2C19, 0x2C19, 0x2C49 }, +{ 0x2C1A, 0x2C1A, 0x2C4A }, +{ 0x2C1B, 0x2C1B, 0x2C4B }, +{ 0x2C1C, 0x2C1C, 0x2C4C }, +{ 0x2C1D, 0x2C1D, 0x2C4D }, +{ 0x2C1E, 0x2C1E, 0x2C4E }, +{ 0x2C1F, 0x2C1F, 0x2C4F }, +{ 0x2C20, 0x2C20, 0x2C50 }, +{ 0x2C21, 0x2C21, 0x2C51 }, +{ 0x2C22, 0x2C22, 0x2C52 }, +{ 0x2C23, 0x2C23, 0x2C53 }, +{ 0x2C24, 0x2C24, 0x2C54 }, +{ 0x2C25, 0x2C25, 0x2C55 }, +{ 0x2C26, 0x2C26, 0x2C56 }, +{ 0x2C27, 0x2C27, 0x2C57 }, +{ 0x2C28, 0x2C28, 0x2C58 }, +{ 0x2C29, 0x2C29, 0x2C59 }, +{ 0x2C2A, 0x2C2A, 0x2C5A }, +{ 0x2C2B, 0x2C2B, 0x2C5B }, +{ 0x2C2C, 0x2C2C, 0x2C5C }, +{ 0x2C2D, 0x2C2D, 0x2C5D }, +{ 0x2C2E, 0x2C2E, 0x2C5E }, +{ 0x2C30, 0x2C00, 0x2C30 }, +{ 0x2C31, 0x2C01, 0x2C31 }, +{ 0x2C32, 0x2C02, 0x2C32 }, +{ 0x2C33, 0x2C03, 0x2C33 }, +{ 0x2C34, 0x2C04, 0x2C34 }, +{ 0x2C35, 0x2C05, 0x2C35 }, +{ 0x2C36, 0x2C06, 0x2C36 }, +{ 0x2C37, 0x2C07, 0x2C37 }, +{ 0x2C38, 0x2C08, 0x2C38 }, +{ 0x2C39, 0x2C09, 0x2C39 }, +{ 0x2C3A, 0x2C0A, 0x2C3A }, +{ 0x2C3B, 0x2C0B, 0x2C3B }, +{ 0x2C3C, 0x2C0C, 0x2C3C }, +{ 0x2C3D, 0x2C0D, 0x2C3D }, +{ 0x2C3E, 0x2C0E, 0x2C3E }, +{ 0x2C3F, 0x2C0F, 0x2C3F }, +{ 0x2C40, 0x2C10, 0x2C40 }, +{ 0x2C41, 0x2C11, 0x2C41 }, +{ 0x2C42, 0x2C12, 0x2C42 }, +{ 0x2C43, 0x2C13, 0x2C43 }, +{ 0x2C44, 0x2C14, 0x2C44 }, +{ 0x2C45, 0x2C15, 0x2C45 }, +{ 0x2C46, 0x2C16, 0x2C46 }, +{ 0x2C47, 0x2C17, 0x2C47 }, +{ 0x2C48, 0x2C18, 0x2C48 }, +{ 0x2C49, 0x2C19, 0x2C49 }, +{ 0x2C4A, 0x2C1A, 0x2C4A }, +{ 0x2C4B, 0x2C1B, 0x2C4B }, +{ 0x2C4C, 0x2C1C, 0x2C4C }, +{ 0x2C4D, 0x2C1D, 0x2C4D }, +{ 0x2C4E, 0x2C1E, 0x2C4E }, +{ 0x2C4F, 0x2C1F, 0x2C4F }, +{ 0x2C50, 0x2C20, 0x2C50 }, +{ 0x2C51, 0x2C21, 0x2C51 }, +{ 0x2C52, 0x2C22, 0x2C52 }, +{ 0x2C53, 0x2C23, 0x2C53 }, +{ 0x2C54, 0x2C24, 0x2C54 }, +{ 0x2C55, 0x2C25, 0x2C55 }, +{ 0x2C56, 0x2C26, 0x2C56 }, +{ 0x2C57, 0x2C27, 0x2C57 }, +{ 0x2C58, 0x2C28, 0x2C58 }, +{ 0x2C59, 0x2C29, 0x2C59 }, +{ 0x2C5A, 0x2C2A, 0x2C5A }, +{ 0x2C5B, 0x2C2B, 0x2C5B }, +{ 0x2C5C, 0x2C2C, 0x2C5C }, +{ 0x2C5D, 0x2C2D, 0x2C5D }, +{ 0x2C5E, 0x2C2E, 0x2C5E }, +{ 0x2C80, 0x2C80, 0x2C81 }, +{ 0x2C81, 0x2C80, 0x2C81 }, +{ 0x2C82, 0x2C82, 0x2C83 }, +{ 0x2C83, 0x2C82, 0x2C83 }, +{ 0x2C84, 0x2C84, 0x2C85 }, +{ 0x2C85, 0x2C84, 0x2C85 }, +{ 0x2C86, 0x2C86, 0x2C87 }, +{ 0x2C87, 0x2C86, 0x2C87 }, +{ 0x2C88, 0x2C88, 0x2C89 }, +{ 0x2C89, 0x2C88, 0x2C89 }, +{ 0x2C8A, 0x2C8A, 0x2C8B }, +{ 0x2C8B, 0x2C8A, 0x2C8B }, +{ 0x2C8C, 0x2C8C, 0x2C8D }, +{ 0x2C8D, 0x2C8C, 0x2C8D }, +{ 0x2C8E, 0x2C8E, 0x2C8F }, +{ 0x2C8F, 0x2C8E, 0x2C8F }, +{ 0x2C90, 0x2C90, 0x2C91 }, +{ 0x2C91, 0x2C90, 0x2C91 }, +{ 0x2C92, 0x2C92, 0x2C93 }, +{ 0x2C93, 0x2C92, 0x2C93 }, +{ 0x2C94, 0x2C94, 0x2C95 }, +{ 0x2C95, 0x2C94, 0x2C95 }, +{ 0x2C96, 0x2C96, 0x2C97 }, +{ 0x2C97, 0x2C96, 0x2C97 }, +{ 0x2C98, 0x2C98, 0x2C99 }, +{ 0x2C99, 0x2C98, 0x2C99 }, +{ 0x2C9A, 0x2C9A, 0x2C9B }, +{ 0x2C9B, 0x2C9A, 0x2C9B }, +{ 0x2C9C, 0x2C9C, 0x2C9D }, +{ 0x2C9D, 0x2C9C, 0x2C9D }, +{ 0x2C9E, 0x2C9E, 0x2C9F }, +{ 0x2C9F, 0x2C9E, 0x2C9F }, +{ 0x2CA0, 0x2CA0, 0x2CA1 }, +{ 0x2CA1, 0x2CA0, 0x2CA1 }, +{ 0x2CA2, 0x2CA2, 0x2CA3 }, +{ 0x2CA3, 0x2CA2, 0x2CA3 }, +{ 0x2CA4, 0x2CA4, 0x2CA5 }, +{ 0x2CA5, 0x2CA4, 0x2CA5 }, +{ 0x2CA6, 0x2CA6, 0x2CA7 }, +{ 0x2CA7, 0x2CA6, 0x2CA7 }, +{ 0x2CA8, 0x2CA8, 0x2CA9 }, +{ 0x2CA9, 0x2CA8, 0x2CA9 }, +{ 0x2CAA, 0x2CAA, 0x2CAB }, +{ 0x2CAB, 0x2CAA, 0x2CAB }, +{ 0x2CAC, 0x2CAC, 0x2CAD }, +{ 0x2CAD, 0x2CAC, 0x2CAD }, +{ 0x2CAE, 0x2CAE, 0x2CAF }, +{ 0x2CAF, 0x2CAE, 0x2CAF }, +{ 0x2CB0, 0x2CB0, 0x2CB1 }, +{ 0x2CB1, 0x2CB0, 0x2CB1 }, +{ 0x2CB2, 0x2CB2, 0x2CB3 }, +{ 0x2CB3, 0x2CB2, 0x2CB3 }, +{ 0x2CB4, 0x2CB4, 0x2CB5 }, +{ 0x2CB5, 0x2CB4, 0x2CB5 }, +{ 0x2CB6, 0x2CB6, 0x2CB7 }, +{ 0x2CB7, 0x2CB6, 0x2CB7 }, +{ 0x2CB8, 0x2CB8, 0x2CB9 }, +{ 0x2CB9, 0x2CB8, 0x2CB9 }, +{ 0x2CBA, 0x2CBA, 0x2CBB }, +{ 0x2CBB, 0x2CBA, 0x2CBB }, +{ 0x2CBC, 0x2CBC, 0x2CBD }, +{ 0x2CBD, 0x2CBC, 0x2CBD }, +{ 0x2CBE, 0x2CBE, 0x2CBF }, +{ 0x2CBF, 0x2CBE, 0x2CBF }, +{ 0x2CC0, 0x2CC0, 0x2CC1 }, +{ 0x2CC1, 0x2CC0, 0x2CC1 }, +{ 0x2CC2, 0x2CC2, 0x2CC3 }, +{ 0x2CC3, 0x2CC2, 0x2CC3 }, +{ 0x2CC4, 0x2CC4, 0x2CC5 }, +{ 0x2CC5, 0x2CC4, 0x2CC5 }, +{ 0x2CC6, 0x2CC6, 0x2CC7 }, +{ 0x2CC7, 0x2CC6, 0x2CC7 }, +{ 0x2CC8, 0x2CC8, 0x2CC9 }, +{ 0x2CC9, 0x2CC8, 0x2CC9 }, +{ 0x2CCA, 0x2CCA, 0x2CCB }, +{ 0x2CCB, 0x2CCA, 0x2CCB }, +{ 0x2CCC, 0x2CCC, 0x2CCD }, +{ 0x2CCD, 0x2CCC, 0x2CCD }, +{ 0x2CCE, 0x2CCE, 0x2CCF }, +{ 0x2CCF, 0x2CCE, 0x2CCF }, +{ 0x2CD0, 0x2CD0, 0x2CD1 }, +{ 0x2CD1, 0x2CD0, 0x2CD1 }, +{ 0x2CD2, 0x2CD2, 0x2CD3 }, +{ 0x2CD3, 0x2CD2, 0x2CD3 }, +{ 0x2CD4, 0x2CD4, 0x2CD5 }, +{ 0x2CD5, 0x2CD4, 0x2CD5 }, +{ 0x2CD6, 0x2CD6, 0x2CD7 }, +{ 0x2CD7, 0x2CD6, 0x2CD7 }, +{ 0x2CD8, 0x2CD8, 0x2CD9 }, +{ 0x2CD9, 0x2CD8, 0x2CD9 }, +{ 0x2CDA, 0x2CDA, 0x2CDB }, +{ 0x2CDB, 0x2CDA, 0x2CDB }, +{ 0x2CDC, 0x2CDC, 0x2CDD }, +{ 0x2CDD, 0x2CDC, 0x2CDD }, +{ 0x2CDE, 0x2CDE, 0x2CDF }, +{ 0x2CDF, 0x2CDE, 0x2CDF }, +{ 0x2CE0, 0x2CE0, 0x2CE1 }, +{ 0x2CE1, 0x2CE0, 0x2CE1 }, +{ 0x2CE2, 0x2CE2, 0x2CE3 }, +{ 0x2CE3, 0x2CE2, 0x2CE3 }, +{ 0x2CE4, 0x2CE4, 0x2CE4 }, +{ 0x2D00, 0x10A0, 0x2D00 }, +{ 0x2D01, 0x10A1, 0x2D01 }, +{ 0x2D02, 0x10A2, 0x2D02 }, +{ 0x2D03, 0x10A3, 0x2D03 }, +{ 0x2D04, 0x10A4, 0x2D04 }, +{ 0x2D05, 0x10A5, 0x2D05 }, +{ 0x2D06, 0x10A6, 0x2D06 }, +{ 0x2D07, 0x10A7, 0x2D07 }, +{ 0x2D08, 0x10A8, 0x2D08 }, +{ 0x2D09, 0x10A9, 0x2D09 }, +{ 0x2D0A, 0x10AA, 0x2D0A }, +{ 0x2D0B, 0x10AB, 0x2D0B }, +{ 0x2D0C, 0x10AC, 0x2D0C }, +{ 0x2D0D, 0x10AD, 0x2D0D }, +{ 0x2D0E, 0x10AE, 0x2D0E }, +{ 0x2D0F, 0x10AF, 0x2D0F }, +{ 0x2D10, 0x10B0, 0x2D10 }, +{ 0x2D11, 0x10B1, 0x2D11 }, +{ 0x2D12, 0x10B2, 0x2D12 }, +{ 0x2D13, 0x10B3, 0x2D13 }, +{ 0x2D14, 0x10B4, 0x2D14 }, +{ 0x2D15, 0x10B5, 0x2D15 }, +{ 0x2D16, 0x10B6, 0x2D16 }, +{ 0x2D17, 0x10B7, 0x2D17 }, +{ 0x2D18, 0x10B8, 0x2D18 }, +{ 0x2D19, 0x10B9, 0x2D19 }, +{ 0x2D1A, 0x10BA, 0x2D1A }, +{ 0x2D1B, 0x10BB, 0x2D1B }, +{ 0x2D1C, 0x10BC, 0x2D1C }, +{ 0x2D1D, 0x10BD, 0x2D1D }, +{ 0x2D1E, 0x10BE, 0x2D1E }, +{ 0x2D1F, 0x10BF, 0x2D1F }, +{ 0x2D20, 0x10C0, 0x2D20 }, +{ 0x2D21, 0x10C1, 0x2D21 }, +{ 0x2D22, 0x10C2, 0x2D22 }, +{ 0x2D23, 0x10C3, 0x2D23 }, +{ 0x2D24, 0x10C4, 0x2D24 }, +{ 0x2D25, 0x10C5, 0x2D25 }, +{ 0x2D30, 0x2D30, 0x2D30 }, +{ 0x2D31, 0x2D31, 0x2D31 }, +{ 0x2D32, 0x2D32, 0x2D32 }, +{ 0x2D33, 0x2D33, 0x2D33 }, +{ 0x2D34, 0x2D34, 0x2D34 }, +{ 0x2D35, 0x2D35, 0x2D35 }, +{ 0x2D36, 0x2D36, 0x2D36 }, +{ 0x2D37, 0x2D37, 0x2D37 }, +{ 0x2D38, 0x2D38, 0x2D38 }, +{ 0x2D39, 0x2D39, 0x2D39 }, +{ 0x2D3A, 0x2D3A, 0x2D3A }, +{ 0x2D3B, 0x2D3B, 0x2D3B }, +{ 0x2D3C, 0x2D3C, 0x2D3C }, +{ 0x2D3D, 0x2D3D, 0x2D3D }, +{ 0x2D3E, 0x2D3E, 0x2D3E }, +{ 0x2D3F, 0x2D3F, 0x2D3F }, +{ 0x2D40, 0x2D40, 0x2D40 }, +{ 0x2D41, 0x2D41, 0x2D41 }, +{ 0x2D42, 0x2D42, 0x2D42 }, +{ 0x2D43, 0x2D43, 0x2D43 }, +{ 0x2D44, 0x2D44, 0x2D44 }, +{ 0x2D45, 0x2D45, 0x2D45 }, +{ 0x2D46, 0x2D46, 0x2D46 }, +{ 0x2D47, 0x2D47, 0x2D47 }, +{ 0x2D48, 0x2D48, 0x2D48 }, +{ 0x2D49, 0x2D49, 0x2D49 }, +{ 0x2D4A, 0x2D4A, 0x2D4A }, +{ 0x2D4B, 0x2D4B, 0x2D4B }, +{ 0x2D4C, 0x2D4C, 0x2D4C }, +{ 0x2D4D, 0x2D4D, 0x2D4D }, +{ 0x2D4E, 0x2D4E, 0x2D4E }, +{ 0x2D4F, 0x2D4F, 0x2D4F }, +{ 0x2D50, 0x2D50, 0x2D50 }, +{ 0x2D51, 0x2D51, 0x2D51 }, +{ 0x2D52, 0x2D52, 0x2D52 }, +{ 0x2D53, 0x2D53, 0x2D53 }, +{ 0x2D54, 0x2D54, 0x2D54 }, +{ 0x2D55, 0x2D55, 0x2D55 }, +{ 0x2D56, 0x2D56, 0x2D56 }, +{ 0x2D57, 0x2D57, 0x2D57 }, +{ 0x2D58, 0x2D58, 0x2D58 }, +{ 0x2D59, 0x2D59, 0x2D59 }, +{ 0x2D5A, 0x2D5A, 0x2D5A }, +{ 0x2D5B, 0x2D5B, 0x2D5B }, +{ 0x2D5C, 0x2D5C, 0x2D5C }, +{ 0x2D5D, 0x2D5D, 0x2D5D }, +{ 0x2D5E, 0x2D5E, 0x2D5E }, +{ 0x2D5F, 0x2D5F, 0x2D5F }, +{ 0x2D60, 0x2D60, 0x2D60 }, +{ 0x2D61, 0x2D61, 0x2D61 }, +{ 0x2D62, 0x2D62, 0x2D62 }, +{ 0x2D63, 0x2D63, 0x2D63 }, +{ 0x2D64, 0x2D64, 0x2D64 }, +{ 0x2D65, 0x2D65, 0x2D65 }, +{ 0x2D6F, 0x2D6F, 0x2D6F }, +{ 0x2D80, 0x2D80, 0x2D80 }, +{ 0x2D81, 0x2D81, 0x2D81 }, +{ 0x2D82, 0x2D82, 0x2D82 }, +{ 0x2D83, 0x2D83, 0x2D83 }, +{ 0x2D84, 0x2D84, 0x2D84 }, +{ 0x2D85, 0x2D85, 0x2D85 }, +{ 0x2D86, 0x2D86, 0x2D86 }, +{ 0x2D87, 0x2D87, 0x2D87 }, +{ 0x2D88, 0x2D88, 0x2D88 }, +{ 0x2D89, 0x2D89, 0x2D89 }, +{ 0x2D8A, 0x2D8A, 0x2D8A }, +{ 0x2D8B, 0x2D8B, 0x2D8B }, +{ 0x2D8C, 0x2D8C, 0x2D8C }, +{ 0x2D8D, 0x2D8D, 0x2D8D }, +{ 0x2D8E, 0x2D8E, 0x2D8E }, +{ 0x2D8F, 0x2D8F, 0x2D8F }, +{ 0x2D90, 0x2D90, 0x2D90 }, +{ 0x2D91, 0x2D91, 0x2D91 }, +{ 0x2D92, 0x2D92, 0x2D92 }, +{ 0x2D93, 0x2D93, 0x2D93 }, +{ 0x2D94, 0x2D94, 0x2D94 }, +{ 0x2D95, 0x2D95, 0x2D95 }, +{ 0x2D96, 0x2D96, 0x2D96 }, +{ 0x2DA0, 0x2DA0, 0x2DA0 }, +{ 0x2DA1, 0x2DA1, 0x2DA1 }, +{ 0x2DA2, 0x2DA2, 0x2DA2 }, +{ 0x2DA3, 0x2DA3, 0x2DA3 }, +{ 0x2DA4, 0x2DA4, 0x2DA4 }, +{ 0x2DA5, 0x2DA5, 0x2DA5 }, +{ 0x2DA6, 0x2DA6, 0x2DA6 }, +{ 0x2DA8, 0x2DA8, 0x2DA8 }, +{ 0x2DA9, 0x2DA9, 0x2DA9 }, +{ 0x2DAA, 0x2DAA, 0x2DAA }, +{ 0x2DAB, 0x2DAB, 0x2DAB }, +{ 0x2DAC, 0x2DAC, 0x2DAC }, +{ 0x2DAD, 0x2DAD, 0x2DAD }, +{ 0x2DAE, 0x2DAE, 0x2DAE }, +{ 0x2DB0, 0x2DB0, 0x2DB0 }, +{ 0x2DB1, 0x2DB1, 0x2DB1 }, +{ 0x2DB2, 0x2DB2, 0x2DB2 }, +{ 0x2DB3, 0x2DB3, 0x2DB3 }, +{ 0x2DB4, 0x2DB4, 0x2DB4 }, +{ 0x2DB5, 0x2DB5, 0x2DB5 }, +{ 0x2DB6, 0x2DB6, 0x2DB6 }, +{ 0x2DB8, 0x2DB8, 0x2DB8 }, +{ 0x2DB9, 0x2DB9, 0x2DB9 }, +{ 0x2DBA, 0x2DBA, 0x2DBA }, +{ 0x2DBB, 0x2DBB, 0x2DBB }, +{ 0x2DBC, 0x2DBC, 0x2DBC }, +{ 0x2DBD, 0x2DBD, 0x2DBD }, +{ 0x2DBE, 0x2DBE, 0x2DBE }, +{ 0x2DC0, 0x2DC0, 0x2DC0 }, +{ 0x2DC1, 0x2DC1, 0x2DC1 }, +{ 0x2DC2, 0x2DC2, 0x2DC2 }, +{ 0x2DC3, 0x2DC3, 0x2DC3 }, +{ 0x2DC4, 0x2DC4, 0x2DC4 }, +{ 0x2DC5, 0x2DC5, 0x2DC5 }, +{ 0x2DC6, 0x2DC6, 0x2DC6 }, +{ 0x2DC8, 0x2DC8, 0x2DC8 }, +{ 0x2DC9, 0x2DC9, 0x2DC9 }, +{ 0x2DCA, 0x2DCA, 0x2DCA }, +{ 0x2DCB, 0x2DCB, 0x2DCB }, +{ 0x2DCC, 0x2DCC, 0x2DCC }, +{ 0x2DCD, 0x2DCD, 0x2DCD }, +{ 0x2DCE, 0x2DCE, 0x2DCE }, +{ 0x2DD0, 0x2DD0, 0x2DD0 }, +{ 0x2DD1, 0x2DD1, 0x2DD1 }, +{ 0x2DD2, 0x2DD2, 0x2DD2 }, +{ 0x2DD3, 0x2DD3, 0x2DD3 }, +{ 0x2DD4, 0x2DD4, 0x2DD4 }, +{ 0x2DD5, 0x2DD5, 0x2DD5 }, +{ 0x2DD6, 0x2DD6, 0x2DD6 }, +{ 0x2DD8, 0x2DD8, 0x2DD8 }, +{ 0x2DD9, 0x2DD9, 0x2DD9 }, +{ 0x2DDA, 0x2DDA, 0x2DDA }, +{ 0x2DDB, 0x2DDB, 0x2DDB }, +{ 0x2DDC, 0x2DDC, 0x2DDC }, +{ 0x2DDD, 0x2DDD, 0x2DDD }, +{ 0x2DDE, 0x2DDE, 0x2DDE }, +{ 0x3005, 0x3005, 0x3005 }, +{ 0x3006, 0x3006, 0x3006 }, +{ 0x302A, 0x302A, 0x302A }, +{ 0x302B, 0x302B, 0x302B }, +{ 0x302C, 0x302C, 0x302C }, +{ 0x302D, 0x302D, 0x302D }, +{ 0x302E, 0x302E, 0x302E }, +{ 0x302F, 0x302F, 0x302F }, +{ 0x3031, 0x3031, 0x3031 }, +{ 0x3032, 0x3032, 0x3032 }, +{ 0x3033, 0x3033, 0x3033 }, +{ 0x3034, 0x3034, 0x3034 }, +{ 0x3035, 0x3035, 0x3035 }, +{ 0x303B, 0x303B, 0x303B }, +{ 0x303C, 0x303C, 0x303C }, +{ 0x3041, 0x3041, 0x3041 }, +{ 0x3042, 0x3042, 0x3042 }, +{ 0x3043, 0x3043, 0x3043 }, +{ 0x3044, 0x3044, 0x3044 }, +{ 0x3045, 0x3045, 0x3045 }, +{ 0x3046, 0x3046, 0x3046 }, +{ 0x3047, 0x3047, 0x3047 }, +{ 0x3048, 0x3048, 0x3048 }, +{ 0x3049, 0x3049, 0x3049 }, +{ 0x304A, 0x304A, 0x304A }, +{ 0x304B, 0x304B, 0x304B }, +{ 0x304C, 0x304C, 0x304C }, +{ 0x304D, 0x304D, 0x304D }, +{ 0x304E, 0x304E, 0x304E }, +{ 0x304F, 0x304F, 0x304F }, +{ 0x3050, 0x3050, 0x3050 }, +{ 0x3051, 0x3051, 0x3051 }, +{ 0x3052, 0x3052, 0x3052 }, +{ 0x3053, 0x3053, 0x3053 }, +{ 0x3054, 0x3054, 0x3054 }, +{ 0x3055, 0x3055, 0x3055 }, +{ 0x3056, 0x3056, 0x3056 }, +{ 0x3057, 0x3057, 0x3057 }, +{ 0x3058, 0x3058, 0x3058 }, +{ 0x3059, 0x3059, 0x3059 }, +{ 0x305A, 0x305A, 0x305A }, +{ 0x305B, 0x305B, 0x305B }, +{ 0x305C, 0x305C, 0x305C }, +{ 0x305D, 0x305D, 0x305D }, +{ 0x305E, 0x305E, 0x305E }, +{ 0x305F, 0x305F, 0x305F }, +{ 0x3060, 0x3060, 0x3060 }, +{ 0x3061, 0x3061, 0x3061 }, +{ 0x3062, 0x3062, 0x3062 }, +{ 0x3063, 0x3063, 0x3063 }, +{ 0x3064, 0x3064, 0x3064 }, +{ 0x3065, 0x3065, 0x3065 }, +{ 0x3066, 0x3066, 0x3066 }, +{ 0x3067, 0x3067, 0x3067 }, +{ 0x3068, 0x3068, 0x3068 }, +{ 0x3069, 0x3069, 0x3069 }, +{ 0x306A, 0x306A, 0x306A }, +{ 0x306B, 0x306B, 0x306B }, +{ 0x306C, 0x306C, 0x306C }, +{ 0x306D, 0x306D, 0x306D }, +{ 0x306E, 0x306E, 0x306E }, +{ 0x306F, 0x306F, 0x306F }, +{ 0x3070, 0x3070, 0x3070 }, +{ 0x3071, 0x3071, 0x3071 }, +{ 0x3072, 0x3072, 0x3072 }, +{ 0x3073, 0x3073, 0x3073 }, +{ 0x3074, 0x3074, 0x3074 }, +{ 0x3075, 0x3075, 0x3075 }, +{ 0x3076, 0x3076, 0x3076 }, +{ 0x3077, 0x3077, 0x3077 }, +{ 0x3078, 0x3078, 0x3078 }, +{ 0x3079, 0x3079, 0x3079 }, +{ 0x307A, 0x307A, 0x307A }, +{ 0x307B, 0x307B, 0x307B }, +{ 0x307C, 0x307C, 0x307C }, +{ 0x307D, 0x307D, 0x307D }, +{ 0x307E, 0x307E, 0x307E }, +{ 0x307F, 0x307F, 0x307F }, +{ 0x3080, 0x3080, 0x3080 }, +{ 0x3081, 0x3081, 0x3081 }, +{ 0x3082, 0x3082, 0x3082 }, +{ 0x3083, 0x3083, 0x3083 }, +{ 0x3084, 0x3084, 0x3084 }, +{ 0x3085, 0x3085, 0x3085 }, +{ 0x3086, 0x3086, 0x3086 }, +{ 0x3087, 0x3087, 0x3087 }, +{ 0x3088, 0x3088, 0x3088 }, +{ 0x3089, 0x3089, 0x3089 }, +{ 0x308A, 0x308A, 0x308A }, +{ 0x308B, 0x308B, 0x308B }, +{ 0x308C, 0x308C, 0x308C }, +{ 0x308D, 0x308D, 0x308D }, +{ 0x308E, 0x308E, 0x308E }, +{ 0x308F, 0x308F, 0x308F }, +{ 0x3090, 0x3090, 0x3090 }, +{ 0x3091, 0x3091, 0x3091 }, +{ 0x3092, 0x3092, 0x3092 }, +{ 0x3093, 0x3093, 0x3093 }, +{ 0x3094, 0x3094, 0x3094 }, +{ 0x3095, 0x3095, 0x3095 }, +{ 0x3096, 0x3096, 0x3096 }, +{ 0x3099, 0x3099, 0x3099 }, +{ 0x309A, 0x309A, 0x309A }, +{ 0x309D, 0x309D, 0x309D }, +{ 0x309E, 0x309E, 0x309E }, +{ 0x309F, 0x309F, 0x309F }, +{ 0x30A1, 0x30A1, 0x30A1 }, +{ 0x30A2, 0x30A2, 0x30A2 }, +{ 0x30A3, 0x30A3, 0x30A3 }, +{ 0x30A4, 0x30A4, 0x30A4 }, +{ 0x30A5, 0x30A5, 0x30A5 }, +{ 0x30A6, 0x30A6, 0x30A6 }, +{ 0x30A7, 0x30A7, 0x30A7 }, +{ 0x30A8, 0x30A8, 0x30A8 }, +{ 0x30A9, 0x30A9, 0x30A9 }, +{ 0x30AA, 0x30AA, 0x30AA }, +{ 0x30AB, 0x30AB, 0x30AB }, +{ 0x30AC, 0x30AC, 0x30AC }, +{ 0x30AD, 0x30AD, 0x30AD }, +{ 0x30AE, 0x30AE, 0x30AE }, +{ 0x30AF, 0x30AF, 0x30AF }, +{ 0x30B0, 0x30B0, 0x30B0 }, +{ 0x30B1, 0x30B1, 0x30B1 }, +{ 0x30B2, 0x30B2, 0x30B2 }, +{ 0x30B3, 0x30B3, 0x30B3 }, +{ 0x30B4, 0x30B4, 0x30B4 }, +{ 0x30B5, 0x30B5, 0x30B5 }, +{ 0x30B6, 0x30B6, 0x30B6 }, +{ 0x30B7, 0x30B7, 0x30B7 }, +{ 0x30B8, 0x30B8, 0x30B8 }, +{ 0x30B9, 0x30B9, 0x30B9 }, +{ 0x30BA, 0x30BA, 0x30BA }, +{ 0x30BB, 0x30BB, 0x30BB }, +{ 0x30BC, 0x30BC, 0x30BC }, +{ 0x30BD, 0x30BD, 0x30BD }, +{ 0x30BE, 0x30BE, 0x30BE }, +{ 0x30BF, 0x30BF, 0x30BF }, +{ 0x30C0, 0x30C0, 0x30C0 }, +{ 0x30C1, 0x30C1, 0x30C1 }, +{ 0x30C2, 0x30C2, 0x30C2 }, +{ 0x30C3, 0x30C3, 0x30C3 }, +{ 0x30C4, 0x30C4, 0x30C4 }, +{ 0x30C5, 0x30C5, 0x30C5 }, +{ 0x30C6, 0x30C6, 0x30C6 }, +{ 0x30C7, 0x30C7, 0x30C7 }, +{ 0x30C8, 0x30C8, 0x30C8 }, +{ 0x30C9, 0x30C9, 0x30C9 }, +{ 0x30CA, 0x30CA, 0x30CA }, +{ 0x30CB, 0x30CB, 0x30CB }, +{ 0x30CC, 0x30CC, 0x30CC }, +{ 0x30CD, 0x30CD, 0x30CD }, +{ 0x30CE, 0x30CE, 0x30CE }, +{ 0x30CF, 0x30CF, 0x30CF }, +{ 0x30D0, 0x30D0, 0x30D0 }, +{ 0x30D1, 0x30D1, 0x30D1 }, +{ 0x30D2, 0x30D2, 0x30D2 }, +{ 0x30D3, 0x30D3, 0x30D3 }, +{ 0x30D4, 0x30D4, 0x30D4 }, +{ 0x30D5, 0x30D5, 0x30D5 }, +{ 0x30D6, 0x30D6, 0x30D6 }, +{ 0x30D7, 0x30D7, 0x30D7 }, +{ 0x30D8, 0x30D8, 0x30D8 }, +{ 0x30D9, 0x30D9, 0x30D9 }, +{ 0x30DA, 0x30DA, 0x30DA }, +{ 0x30DB, 0x30DB, 0x30DB }, +{ 0x30DC, 0x30DC, 0x30DC }, +{ 0x30DD, 0x30DD, 0x30DD }, +{ 0x30DE, 0x30DE, 0x30DE }, +{ 0x30DF, 0x30DF, 0x30DF }, +{ 0x30E0, 0x30E0, 0x30E0 }, +{ 0x30E1, 0x30E1, 0x30E1 }, +{ 0x30E2, 0x30E2, 0x30E2 }, +{ 0x30E3, 0x30E3, 0x30E3 }, +{ 0x30E4, 0x30E4, 0x30E4 }, +{ 0x30E5, 0x30E5, 0x30E5 }, +{ 0x30E6, 0x30E6, 0x30E6 }, +{ 0x30E7, 0x30E7, 0x30E7 }, +{ 0x30E8, 0x30E8, 0x30E8 }, +{ 0x30E9, 0x30E9, 0x30E9 }, +{ 0x30EA, 0x30EA, 0x30EA }, +{ 0x30EB, 0x30EB, 0x30EB }, +{ 0x30EC, 0x30EC, 0x30EC }, +{ 0x30ED, 0x30ED, 0x30ED }, +{ 0x30EE, 0x30EE, 0x30EE }, +{ 0x30EF, 0x30EF, 0x30EF }, +{ 0x30F0, 0x30F0, 0x30F0 }, +{ 0x30F1, 0x30F1, 0x30F1 }, +{ 0x30F2, 0x30F2, 0x30F2 }, +{ 0x30F3, 0x30F3, 0x30F3 }, +{ 0x30F4, 0x30F4, 0x30F4 }, +{ 0x30F5, 0x30F5, 0x30F5 }, +{ 0x30F6, 0x30F6, 0x30F6 }, +{ 0x30F7, 0x30F7, 0x30F7 }, +{ 0x30F8, 0x30F8, 0x30F8 }, +{ 0x30F9, 0x30F9, 0x30F9 }, +{ 0x30FA, 0x30FA, 0x30FA }, +{ 0x30FC, 0x30FC, 0x30FC }, +{ 0x30FD, 0x30FD, 0x30FD }, +{ 0x30FE, 0x30FE, 0x30FE }, +{ 0x30FF, 0x30FF, 0x30FF }, +{ 0x3105, 0x3105, 0x3105 }, +{ 0x3106, 0x3106, 0x3106 }, +{ 0x3107, 0x3107, 0x3107 }, +{ 0x3108, 0x3108, 0x3108 }, +{ 0x3109, 0x3109, 0x3109 }, +{ 0x310A, 0x310A, 0x310A }, +{ 0x310B, 0x310B, 0x310B }, +{ 0x310C, 0x310C, 0x310C }, +{ 0x310D, 0x310D, 0x310D }, +{ 0x310E, 0x310E, 0x310E }, +{ 0x310F, 0x310F, 0x310F }, +{ 0x3110, 0x3110, 0x3110 }, +{ 0x3111, 0x3111, 0x3111 }, +{ 0x3112, 0x3112, 0x3112 }, +{ 0x3113, 0x3113, 0x3113 }, +{ 0x3114, 0x3114, 0x3114 }, +{ 0x3115, 0x3115, 0x3115 }, +{ 0x3116, 0x3116, 0x3116 }, +{ 0x3117, 0x3117, 0x3117 }, +{ 0x3118, 0x3118, 0x3118 }, +{ 0x3119, 0x3119, 0x3119 }, +{ 0x311A, 0x311A, 0x311A }, +{ 0x311B, 0x311B, 0x311B }, +{ 0x311C, 0x311C, 0x311C }, +{ 0x311D, 0x311D, 0x311D }, +{ 0x311E, 0x311E, 0x311E }, +{ 0x311F, 0x311F, 0x311F }, +{ 0x3120, 0x3120, 0x3120 }, +{ 0x3121, 0x3121, 0x3121 }, +{ 0x3122, 0x3122, 0x3122 }, +{ 0x3123, 0x3123, 0x3123 }, +{ 0x3124, 0x3124, 0x3124 }, +{ 0x3125, 0x3125, 0x3125 }, +{ 0x3126, 0x3126, 0x3126 }, +{ 0x3127, 0x3127, 0x3127 }, +{ 0x3128, 0x3128, 0x3128 }, +{ 0x3129, 0x3129, 0x3129 }, +{ 0x312A, 0x312A, 0x312A }, +{ 0x312B, 0x312B, 0x312B }, +{ 0x312C, 0x312C, 0x312C }, +{ 0x3131, 0x3131, 0x3131 }, +{ 0x3132, 0x3132, 0x3132 }, +{ 0x3133, 0x3133, 0x3133 }, +{ 0x3134, 0x3134, 0x3134 }, +{ 0x3135, 0x3135, 0x3135 }, +{ 0x3136, 0x3136, 0x3136 }, +{ 0x3137, 0x3137, 0x3137 }, +{ 0x3138, 0x3138, 0x3138 }, +{ 0x3139, 0x3139, 0x3139 }, +{ 0x313A, 0x313A, 0x313A }, +{ 0x313B, 0x313B, 0x313B }, +{ 0x313C, 0x313C, 0x313C }, +{ 0x313D, 0x313D, 0x313D }, +{ 0x313E, 0x313E, 0x313E }, +{ 0x313F, 0x313F, 0x313F }, +{ 0x3140, 0x3140, 0x3140 }, +{ 0x3141, 0x3141, 0x3141 }, +{ 0x3142, 0x3142, 0x3142 }, +{ 0x3143, 0x3143, 0x3143 }, +{ 0x3144, 0x3144, 0x3144 }, +{ 0x3145, 0x3145, 0x3145 }, +{ 0x3146, 0x3146, 0x3146 }, +{ 0x3147, 0x3147, 0x3147 }, +{ 0x3148, 0x3148, 0x3148 }, +{ 0x3149, 0x3149, 0x3149 }, +{ 0x314A, 0x314A, 0x314A }, +{ 0x314B, 0x314B, 0x314B }, +{ 0x314C, 0x314C, 0x314C }, +{ 0x314D, 0x314D, 0x314D }, +{ 0x314E, 0x314E, 0x314E }, +{ 0x314F, 0x314F, 0x314F }, +{ 0x3150, 0x3150, 0x3150 }, +{ 0x3151, 0x3151, 0x3151 }, +{ 0x3152, 0x3152, 0x3152 }, +{ 0x3153, 0x3153, 0x3153 }, +{ 0x3154, 0x3154, 0x3154 }, +{ 0x3155, 0x3155, 0x3155 }, +{ 0x3156, 0x3156, 0x3156 }, +{ 0x3157, 0x3157, 0x3157 }, +{ 0x3158, 0x3158, 0x3158 }, +{ 0x3159, 0x3159, 0x3159 }, +{ 0x315A, 0x315A, 0x315A }, +{ 0x315B, 0x315B, 0x315B }, +{ 0x315C, 0x315C, 0x315C }, +{ 0x315D, 0x315D, 0x315D }, +{ 0x315E, 0x315E, 0x315E }, +{ 0x315F, 0x315F, 0x315F }, +{ 0x3160, 0x3160, 0x3160 }, +{ 0x3161, 0x3161, 0x3161 }, +{ 0x3162, 0x3162, 0x3162 }, +{ 0x3163, 0x3163, 0x3163 }, +{ 0x3164, 0x3164, 0x3164 }, +{ 0x3165, 0x3165, 0x3165 }, +{ 0x3166, 0x3166, 0x3166 }, +{ 0x3167, 0x3167, 0x3167 }, +{ 0x3168, 0x3168, 0x3168 }, +{ 0x3169, 0x3169, 0x3169 }, +{ 0x316A, 0x316A, 0x316A }, +{ 0x316B, 0x316B, 0x316B }, +{ 0x316C, 0x316C, 0x316C }, +{ 0x316D, 0x316D, 0x316D }, +{ 0x316E, 0x316E, 0x316E }, +{ 0x316F, 0x316F, 0x316F }, +{ 0x3170, 0x3170, 0x3170 }, +{ 0x3171, 0x3171, 0x3171 }, +{ 0x3172, 0x3172, 0x3172 }, +{ 0x3173, 0x3173, 0x3173 }, +{ 0x3174, 0x3174, 0x3174 }, +{ 0x3175, 0x3175, 0x3175 }, +{ 0x3176, 0x3176, 0x3176 }, +{ 0x3177, 0x3177, 0x3177 }, +{ 0x3178, 0x3178, 0x3178 }, +{ 0x3179, 0x3179, 0x3179 }, +{ 0x317A, 0x317A, 0x317A }, +{ 0x317B, 0x317B, 0x317B }, +{ 0x317C, 0x317C, 0x317C }, +{ 0x317D, 0x317D, 0x317D }, +{ 0x317E, 0x317E, 0x317E }, +{ 0x317F, 0x317F, 0x317F }, +{ 0x3180, 0x3180, 0x3180 }, +{ 0x3181, 0x3181, 0x3181 }, +{ 0x3182, 0x3182, 0x3182 }, +{ 0x3183, 0x3183, 0x3183 }, +{ 0x3184, 0x3184, 0x3184 }, +{ 0x3185, 0x3185, 0x3185 }, +{ 0x3186, 0x3186, 0x3186 }, +{ 0x3187, 0x3187, 0x3187 }, +{ 0x3188, 0x3188, 0x3188 }, +{ 0x3189, 0x3189, 0x3189 }, +{ 0x318A, 0x318A, 0x318A }, +{ 0x318B, 0x318B, 0x318B }, +{ 0x318C, 0x318C, 0x318C }, +{ 0x318D, 0x318D, 0x318D }, +{ 0x318E, 0x318E, 0x318E }, +{ 0x31A0, 0x31A0, 0x31A0 }, +{ 0x31A1, 0x31A1, 0x31A1 }, +{ 0x31A2, 0x31A2, 0x31A2 }, +{ 0x31A3, 0x31A3, 0x31A3 }, +{ 0x31A4, 0x31A4, 0x31A4 }, +{ 0x31A5, 0x31A5, 0x31A5 }, +{ 0x31A6, 0x31A6, 0x31A6 }, +{ 0x31A7, 0x31A7, 0x31A7 }, +{ 0x31A8, 0x31A8, 0x31A8 }, +{ 0x31A9, 0x31A9, 0x31A9 }, +{ 0x31AA, 0x31AA, 0x31AA }, +{ 0x31AB, 0x31AB, 0x31AB }, +{ 0x31AC, 0x31AC, 0x31AC }, +{ 0x31AD, 0x31AD, 0x31AD }, +{ 0x31AE, 0x31AE, 0x31AE }, +{ 0x31AF, 0x31AF, 0x31AF }, +{ 0x31B0, 0x31B0, 0x31B0 }, +{ 0x31B1, 0x31B1, 0x31B1 }, +{ 0x31B2, 0x31B2, 0x31B2 }, +{ 0x31B3, 0x31B3, 0x31B3 }, +{ 0x31B4, 0x31B4, 0x31B4 }, +{ 0x31B5, 0x31B5, 0x31B5 }, +{ 0x31B6, 0x31B6, 0x31B6 }, +{ 0x31B7, 0x31B7, 0x31B7 }, +{ 0x31F0, 0x31F0, 0x31F0 }, +{ 0x31F1, 0x31F1, 0x31F1 }, +{ 0x31F2, 0x31F2, 0x31F2 }, +{ 0x31F3, 0x31F3, 0x31F3 }, +{ 0x31F4, 0x31F4, 0x31F4 }, +{ 0x31F5, 0x31F5, 0x31F5 }, +{ 0x31F6, 0x31F6, 0x31F6 }, +{ 0x31F7, 0x31F7, 0x31F7 }, +{ 0x31F8, 0x31F8, 0x31F8 }, +{ 0x31F9, 0x31F9, 0x31F9 }, +{ 0x31FA, 0x31FA, 0x31FA }, +{ 0x31FB, 0x31FB, 0x31FB }, +{ 0x31FC, 0x31FC, 0x31FC }, +{ 0x31FD, 0x31FD, 0x31FD }, +{ 0x31FE, 0x31FE, 0x31FE }, +{ 0x31FF, 0x31FF, 0x31FF }, +{ 0x3400, 0x3400, 0x3400 }, +{ 0x4DB5, 0x4DB5, 0x4DB5 }, +{ 0x4E00, 0x4E00, 0x4E00 }, +{ 0x9FBB, 0x9FBB, 0x9FBB }, +{ 0xA000, 0xA000, 0xA000 }, +{ 0xA001, 0xA001, 0xA001 }, +{ 0xA002, 0xA002, 0xA002 }, +{ 0xA003, 0xA003, 0xA003 }, +{ 0xA004, 0xA004, 0xA004 }, +{ 0xA005, 0xA005, 0xA005 }, +{ 0xA006, 0xA006, 0xA006 }, +{ 0xA007, 0xA007, 0xA007 }, +{ 0xA008, 0xA008, 0xA008 }, +{ 0xA009, 0xA009, 0xA009 }, +{ 0xA00A, 0xA00A, 0xA00A }, +{ 0xA00B, 0xA00B, 0xA00B }, +{ 0xA00C, 0xA00C, 0xA00C }, +{ 0xA00D, 0xA00D, 0xA00D }, +{ 0xA00E, 0xA00E, 0xA00E }, +{ 0xA00F, 0xA00F, 0xA00F }, +{ 0xA010, 0xA010, 0xA010 }, +{ 0xA011, 0xA011, 0xA011 }, +{ 0xA012, 0xA012, 0xA012 }, +{ 0xA013, 0xA013, 0xA013 }, +{ 0xA014, 0xA014, 0xA014 }, +{ 0xA015, 0xA015, 0xA015 }, +{ 0xA016, 0xA016, 0xA016 }, +{ 0xA017, 0xA017, 0xA017 }, +{ 0xA018, 0xA018, 0xA018 }, +{ 0xA019, 0xA019, 0xA019 }, +{ 0xA01A, 0xA01A, 0xA01A }, +{ 0xA01B, 0xA01B, 0xA01B }, +{ 0xA01C, 0xA01C, 0xA01C }, +{ 0xA01D, 0xA01D, 0xA01D }, +{ 0xA01E, 0xA01E, 0xA01E }, +{ 0xA01F, 0xA01F, 0xA01F }, +{ 0xA020, 0xA020, 0xA020 }, +{ 0xA021, 0xA021, 0xA021 }, +{ 0xA022, 0xA022, 0xA022 }, +{ 0xA023, 0xA023, 0xA023 }, +{ 0xA024, 0xA024, 0xA024 }, +{ 0xA025, 0xA025, 0xA025 }, +{ 0xA026, 0xA026, 0xA026 }, +{ 0xA027, 0xA027, 0xA027 }, +{ 0xA028, 0xA028, 0xA028 }, +{ 0xA029, 0xA029, 0xA029 }, +{ 0xA02A, 0xA02A, 0xA02A }, +{ 0xA02B, 0xA02B, 0xA02B }, +{ 0xA02C, 0xA02C, 0xA02C }, +{ 0xA02D, 0xA02D, 0xA02D }, +{ 0xA02E, 0xA02E, 0xA02E }, +{ 0xA02F, 0xA02F, 0xA02F }, +{ 0xA030, 0xA030, 0xA030 }, +{ 0xA031, 0xA031, 0xA031 }, +{ 0xA032, 0xA032, 0xA032 }, +{ 0xA033, 0xA033, 0xA033 }, +{ 0xA034, 0xA034, 0xA034 }, +{ 0xA035, 0xA035, 0xA035 }, +{ 0xA036, 0xA036, 0xA036 }, +{ 0xA037, 0xA037, 0xA037 }, +{ 0xA038, 0xA038, 0xA038 }, +{ 0xA039, 0xA039, 0xA039 }, +{ 0xA03A, 0xA03A, 0xA03A }, +{ 0xA03B, 0xA03B, 0xA03B }, +{ 0xA03C, 0xA03C, 0xA03C }, +{ 0xA03D, 0xA03D, 0xA03D }, +{ 0xA03E, 0xA03E, 0xA03E }, +{ 0xA03F, 0xA03F, 0xA03F }, +{ 0xA040, 0xA040, 0xA040 }, +{ 0xA041, 0xA041, 0xA041 }, +{ 0xA042, 0xA042, 0xA042 }, +{ 0xA043, 0xA043, 0xA043 }, +{ 0xA044, 0xA044, 0xA044 }, +{ 0xA045, 0xA045, 0xA045 }, +{ 0xA046, 0xA046, 0xA046 }, +{ 0xA047, 0xA047, 0xA047 }, +{ 0xA048, 0xA048, 0xA048 }, +{ 0xA049, 0xA049, 0xA049 }, +{ 0xA04A, 0xA04A, 0xA04A }, +{ 0xA04B, 0xA04B, 0xA04B }, +{ 0xA04C, 0xA04C, 0xA04C }, +{ 0xA04D, 0xA04D, 0xA04D }, +{ 0xA04E, 0xA04E, 0xA04E }, +{ 0xA04F, 0xA04F, 0xA04F }, +{ 0xA050, 0xA050, 0xA050 }, +{ 0xA051, 0xA051, 0xA051 }, +{ 0xA052, 0xA052, 0xA052 }, +{ 0xA053, 0xA053, 0xA053 }, +{ 0xA054, 0xA054, 0xA054 }, +{ 0xA055, 0xA055, 0xA055 }, +{ 0xA056, 0xA056, 0xA056 }, +{ 0xA057, 0xA057, 0xA057 }, +{ 0xA058, 0xA058, 0xA058 }, +{ 0xA059, 0xA059, 0xA059 }, +{ 0xA05A, 0xA05A, 0xA05A }, +{ 0xA05B, 0xA05B, 0xA05B }, +{ 0xA05C, 0xA05C, 0xA05C }, +{ 0xA05D, 0xA05D, 0xA05D }, +{ 0xA05E, 0xA05E, 0xA05E }, +{ 0xA05F, 0xA05F, 0xA05F }, +{ 0xA060, 0xA060, 0xA060 }, +{ 0xA061, 0xA061, 0xA061 }, +{ 0xA062, 0xA062, 0xA062 }, +{ 0xA063, 0xA063, 0xA063 }, +{ 0xA064, 0xA064, 0xA064 }, +{ 0xA065, 0xA065, 0xA065 }, +{ 0xA066, 0xA066, 0xA066 }, +{ 0xA067, 0xA067, 0xA067 }, +{ 0xA068, 0xA068, 0xA068 }, +{ 0xA069, 0xA069, 0xA069 }, +{ 0xA06A, 0xA06A, 0xA06A }, +{ 0xA06B, 0xA06B, 0xA06B }, +{ 0xA06C, 0xA06C, 0xA06C }, +{ 0xA06D, 0xA06D, 0xA06D }, +{ 0xA06E, 0xA06E, 0xA06E }, +{ 0xA06F, 0xA06F, 0xA06F }, +{ 0xA070, 0xA070, 0xA070 }, +{ 0xA071, 0xA071, 0xA071 }, +{ 0xA072, 0xA072, 0xA072 }, +{ 0xA073, 0xA073, 0xA073 }, +{ 0xA074, 0xA074, 0xA074 }, +{ 0xA075, 0xA075, 0xA075 }, +{ 0xA076, 0xA076, 0xA076 }, +{ 0xA077, 0xA077, 0xA077 }, +{ 0xA078, 0xA078, 0xA078 }, +{ 0xA079, 0xA079, 0xA079 }, +{ 0xA07A, 0xA07A, 0xA07A }, +{ 0xA07B, 0xA07B, 0xA07B }, +{ 0xA07C, 0xA07C, 0xA07C }, +{ 0xA07D, 0xA07D, 0xA07D }, +{ 0xA07E, 0xA07E, 0xA07E }, +{ 0xA07F, 0xA07F, 0xA07F }, +{ 0xA080, 0xA080, 0xA080 }, +{ 0xA081, 0xA081, 0xA081 }, +{ 0xA082, 0xA082, 0xA082 }, +{ 0xA083, 0xA083, 0xA083 }, +{ 0xA084, 0xA084, 0xA084 }, +{ 0xA085, 0xA085, 0xA085 }, +{ 0xA086, 0xA086, 0xA086 }, +{ 0xA087, 0xA087, 0xA087 }, +{ 0xA088, 0xA088, 0xA088 }, +{ 0xA089, 0xA089, 0xA089 }, +{ 0xA08A, 0xA08A, 0xA08A }, +{ 0xA08B, 0xA08B, 0xA08B }, +{ 0xA08C, 0xA08C, 0xA08C }, +{ 0xA08D, 0xA08D, 0xA08D }, +{ 0xA08E, 0xA08E, 0xA08E }, +{ 0xA08F, 0xA08F, 0xA08F }, +{ 0xA090, 0xA090, 0xA090 }, +{ 0xA091, 0xA091, 0xA091 }, +{ 0xA092, 0xA092, 0xA092 }, +{ 0xA093, 0xA093, 0xA093 }, +{ 0xA094, 0xA094, 0xA094 }, +{ 0xA095, 0xA095, 0xA095 }, +{ 0xA096, 0xA096, 0xA096 }, +{ 0xA097, 0xA097, 0xA097 }, +{ 0xA098, 0xA098, 0xA098 }, +{ 0xA099, 0xA099, 0xA099 }, +{ 0xA09A, 0xA09A, 0xA09A }, +{ 0xA09B, 0xA09B, 0xA09B }, +{ 0xA09C, 0xA09C, 0xA09C }, +{ 0xA09D, 0xA09D, 0xA09D }, +{ 0xA09E, 0xA09E, 0xA09E }, +{ 0xA09F, 0xA09F, 0xA09F }, +{ 0xA0A0, 0xA0A0, 0xA0A0 }, +{ 0xA0A1, 0xA0A1, 0xA0A1 }, +{ 0xA0A2, 0xA0A2, 0xA0A2 }, +{ 0xA0A3, 0xA0A3, 0xA0A3 }, +{ 0xA0A4, 0xA0A4, 0xA0A4 }, +{ 0xA0A5, 0xA0A5, 0xA0A5 }, +{ 0xA0A6, 0xA0A6, 0xA0A6 }, +{ 0xA0A7, 0xA0A7, 0xA0A7 }, +{ 0xA0A8, 0xA0A8, 0xA0A8 }, +{ 0xA0A9, 0xA0A9, 0xA0A9 }, +{ 0xA0AA, 0xA0AA, 0xA0AA }, +{ 0xA0AB, 0xA0AB, 0xA0AB }, +{ 0xA0AC, 0xA0AC, 0xA0AC }, +{ 0xA0AD, 0xA0AD, 0xA0AD }, +{ 0xA0AE, 0xA0AE, 0xA0AE }, +{ 0xA0AF, 0xA0AF, 0xA0AF }, +{ 0xA0B0, 0xA0B0, 0xA0B0 }, +{ 0xA0B1, 0xA0B1, 0xA0B1 }, +{ 0xA0B2, 0xA0B2, 0xA0B2 }, +{ 0xA0B3, 0xA0B3, 0xA0B3 }, +{ 0xA0B4, 0xA0B4, 0xA0B4 }, +{ 0xA0B5, 0xA0B5, 0xA0B5 }, +{ 0xA0B6, 0xA0B6, 0xA0B6 }, +{ 0xA0B7, 0xA0B7, 0xA0B7 }, +{ 0xA0B8, 0xA0B8, 0xA0B8 }, +{ 0xA0B9, 0xA0B9, 0xA0B9 }, +{ 0xA0BA, 0xA0BA, 0xA0BA }, +{ 0xA0BB, 0xA0BB, 0xA0BB }, +{ 0xA0BC, 0xA0BC, 0xA0BC }, +{ 0xA0BD, 0xA0BD, 0xA0BD }, +{ 0xA0BE, 0xA0BE, 0xA0BE }, +{ 0xA0BF, 0xA0BF, 0xA0BF }, +{ 0xA0C0, 0xA0C0, 0xA0C0 }, +{ 0xA0C1, 0xA0C1, 0xA0C1 }, +{ 0xA0C2, 0xA0C2, 0xA0C2 }, +{ 0xA0C3, 0xA0C3, 0xA0C3 }, +{ 0xA0C4, 0xA0C4, 0xA0C4 }, +{ 0xA0C5, 0xA0C5, 0xA0C5 }, +{ 0xA0C6, 0xA0C6, 0xA0C6 }, +{ 0xA0C7, 0xA0C7, 0xA0C7 }, +{ 0xA0C8, 0xA0C8, 0xA0C8 }, +{ 0xA0C9, 0xA0C9, 0xA0C9 }, +{ 0xA0CA, 0xA0CA, 0xA0CA }, +{ 0xA0CB, 0xA0CB, 0xA0CB }, +{ 0xA0CC, 0xA0CC, 0xA0CC }, +{ 0xA0CD, 0xA0CD, 0xA0CD }, +{ 0xA0CE, 0xA0CE, 0xA0CE }, +{ 0xA0CF, 0xA0CF, 0xA0CF }, +{ 0xA0D0, 0xA0D0, 0xA0D0 }, +{ 0xA0D1, 0xA0D1, 0xA0D1 }, +{ 0xA0D2, 0xA0D2, 0xA0D2 }, +{ 0xA0D3, 0xA0D3, 0xA0D3 }, +{ 0xA0D4, 0xA0D4, 0xA0D4 }, +{ 0xA0D5, 0xA0D5, 0xA0D5 }, +{ 0xA0D6, 0xA0D6, 0xA0D6 }, +{ 0xA0D7, 0xA0D7, 0xA0D7 }, +{ 0xA0D8, 0xA0D8, 0xA0D8 }, +{ 0xA0D9, 0xA0D9, 0xA0D9 }, +{ 0xA0DA, 0xA0DA, 0xA0DA }, +{ 0xA0DB, 0xA0DB, 0xA0DB }, +{ 0xA0DC, 0xA0DC, 0xA0DC }, +{ 0xA0DD, 0xA0DD, 0xA0DD }, +{ 0xA0DE, 0xA0DE, 0xA0DE }, +{ 0xA0DF, 0xA0DF, 0xA0DF }, +{ 0xA0E0, 0xA0E0, 0xA0E0 }, +{ 0xA0E1, 0xA0E1, 0xA0E1 }, +{ 0xA0E2, 0xA0E2, 0xA0E2 }, +{ 0xA0E3, 0xA0E3, 0xA0E3 }, +{ 0xA0E4, 0xA0E4, 0xA0E4 }, +{ 0xA0E5, 0xA0E5, 0xA0E5 }, +{ 0xA0E6, 0xA0E6, 0xA0E6 }, +{ 0xA0E7, 0xA0E7, 0xA0E7 }, +{ 0xA0E8, 0xA0E8, 0xA0E8 }, +{ 0xA0E9, 0xA0E9, 0xA0E9 }, +{ 0xA0EA, 0xA0EA, 0xA0EA }, +{ 0xA0EB, 0xA0EB, 0xA0EB }, +{ 0xA0EC, 0xA0EC, 0xA0EC }, +{ 0xA0ED, 0xA0ED, 0xA0ED }, +{ 0xA0EE, 0xA0EE, 0xA0EE }, +{ 0xA0EF, 0xA0EF, 0xA0EF }, +{ 0xA0F0, 0xA0F0, 0xA0F0 }, +{ 0xA0F1, 0xA0F1, 0xA0F1 }, +{ 0xA0F2, 0xA0F2, 0xA0F2 }, +{ 0xA0F3, 0xA0F3, 0xA0F3 }, +{ 0xA0F4, 0xA0F4, 0xA0F4 }, +{ 0xA0F5, 0xA0F5, 0xA0F5 }, +{ 0xA0F6, 0xA0F6, 0xA0F6 }, +{ 0xA0F7, 0xA0F7, 0xA0F7 }, +{ 0xA0F8, 0xA0F8, 0xA0F8 }, +{ 0xA0F9, 0xA0F9, 0xA0F9 }, +{ 0xA0FA, 0xA0FA, 0xA0FA }, +{ 0xA0FB, 0xA0FB, 0xA0FB }, +{ 0xA0FC, 0xA0FC, 0xA0FC }, +{ 0xA0FD, 0xA0FD, 0xA0FD }, +{ 0xA0FE, 0xA0FE, 0xA0FE }, +{ 0xA0FF, 0xA0FF, 0xA0FF }, +{ 0xA100, 0xA100, 0xA100 }, +{ 0xA101, 0xA101, 0xA101 }, +{ 0xA102, 0xA102, 0xA102 }, +{ 0xA103, 0xA103, 0xA103 }, +{ 0xA104, 0xA104, 0xA104 }, +{ 0xA105, 0xA105, 0xA105 }, +{ 0xA106, 0xA106, 0xA106 }, +{ 0xA107, 0xA107, 0xA107 }, +{ 0xA108, 0xA108, 0xA108 }, +{ 0xA109, 0xA109, 0xA109 }, +{ 0xA10A, 0xA10A, 0xA10A }, +{ 0xA10B, 0xA10B, 0xA10B }, +{ 0xA10C, 0xA10C, 0xA10C }, +{ 0xA10D, 0xA10D, 0xA10D }, +{ 0xA10E, 0xA10E, 0xA10E }, +{ 0xA10F, 0xA10F, 0xA10F }, +{ 0xA110, 0xA110, 0xA110 }, +{ 0xA111, 0xA111, 0xA111 }, +{ 0xA112, 0xA112, 0xA112 }, +{ 0xA113, 0xA113, 0xA113 }, +{ 0xA114, 0xA114, 0xA114 }, +{ 0xA115, 0xA115, 0xA115 }, +{ 0xA116, 0xA116, 0xA116 }, +{ 0xA117, 0xA117, 0xA117 }, +{ 0xA118, 0xA118, 0xA118 }, +{ 0xA119, 0xA119, 0xA119 }, +{ 0xA11A, 0xA11A, 0xA11A }, +{ 0xA11B, 0xA11B, 0xA11B }, +{ 0xA11C, 0xA11C, 0xA11C }, +{ 0xA11D, 0xA11D, 0xA11D }, +{ 0xA11E, 0xA11E, 0xA11E }, +{ 0xA11F, 0xA11F, 0xA11F }, +{ 0xA120, 0xA120, 0xA120 }, +{ 0xA121, 0xA121, 0xA121 }, +{ 0xA122, 0xA122, 0xA122 }, +{ 0xA123, 0xA123, 0xA123 }, +{ 0xA124, 0xA124, 0xA124 }, +{ 0xA125, 0xA125, 0xA125 }, +{ 0xA126, 0xA126, 0xA126 }, +{ 0xA127, 0xA127, 0xA127 }, +{ 0xA128, 0xA128, 0xA128 }, +{ 0xA129, 0xA129, 0xA129 }, +{ 0xA12A, 0xA12A, 0xA12A }, +{ 0xA12B, 0xA12B, 0xA12B }, +{ 0xA12C, 0xA12C, 0xA12C }, +{ 0xA12D, 0xA12D, 0xA12D }, +{ 0xA12E, 0xA12E, 0xA12E }, +{ 0xA12F, 0xA12F, 0xA12F }, +{ 0xA130, 0xA130, 0xA130 }, +{ 0xA131, 0xA131, 0xA131 }, +{ 0xA132, 0xA132, 0xA132 }, +{ 0xA133, 0xA133, 0xA133 }, +{ 0xA134, 0xA134, 0xA134 }, +{ 0xA135, 0xA135, 0xA135 }, +{ 0xA136, 0xA136, 0xA136 }, +{ 0xA137, 0xA137, 0xA137 }, +{ 0xA138, 0xA138, 0xA138 }, +{ 0xA139, 0xA139, 0xA139 }, +{ 0xA13A, 0xA13A, 0xA13A }, +{ 0xA13B, 0xA13B, 0xA13B }, +{ 0xA13C, 0xA13C, 0xA13C }, +{ 0xA13D, 0xA13D, 0xA13D }, +{ 0xA13E, 0xA13E, 0xA13E }, +{ 0xA13F, 0xA13F, 0xA13F }, +{ 0xA140, 0xA140, 0xA140 }, +{ 0xA141, 0xA141, 0xA141 }, +{ 0xA142, 0xA142, 0xA142 }, +{ 0xA143, 0xA143, 0xA143 }, +{ 0xA144, 0xA144, 0xA144 }, +{ 0xA145, 0xA145, 0xA145 }, +{ 0xA146, 0xA146, 0xA146 }, +{ 0xA147, 0xA147, 0xA147 }, +{ 0xA148, 0xA148, 0xA148 }, +{ 0xA149, 0xA149, 0xA149 }, +{ 0xA14A, 0xA14A, 0xA14A }, +{ 0xA14B, 0xA14B, 0xA14B }, +{ 0xA14C, 0xA14C, 0xA14C }, +{ 0xA14D, 0xA14D, 0xA14D }, +{ 0xA14E, 0xA14E, 0xA14E }, +{ 0xA14F, 0xA14F, 0xA14F }, +{ 0xA150, 0xA150, 0xA150 }, +{ 0xA151, 0xA151, 0xA151 }, +{ 0xA152, 0xA152, 0xA152 }, +{ 0xA153, 0xA153, 0xA153 }, +{ 0xA154, 0xA154, 0xA154 }, +{ 0xA155, 0xA155, 0xA155 }, +{ 0xA156, 0xA156, 0xA156 }, +{ 0xA157, 0xA157, 0xA157 }, +{ 0xA158, 0xA158, 0xA158 }, +{ 0xA159, 0xA159, 0xA159 }, +{ 0xA15A, 0xA15A, 0xA15A }, +{ 0xA15B, 0xA15B, 0xA15B }, +{ 0xA15C, 0xA15C, 0xA15C }, +{ 0xA15D, 0xA15D, 0xA15D }, +{ 0xA15E, 0xA15E, 0xA15E }, +{ 0xA15F, 0xA15F, 0xA15F }, +{ 0xA160, 0xA160, 0xA160 }, +{ 0xA161, 0xA161, 0xA161 }, +{ 0xA162, 0xA162, 0xA162 }, +{ 0xA163, 0xA163, 0xA163 }, +{ 0xA164, 0xA164, 0xA164 }, +{ 0xA165, 0xA165, 0xA165 }, +{ 0xA166, 0xA166, 0xA166 }, +{ 0xA167, 0xA167, 0xA167 }, +{ 0xA168, 0xA168, 0xA168 }, +{ 0xA169, 0xA169, 0xA169 }, +{ 0xA16A, 0xA16A, 0xA16A }, +{ 0xA16B, 0xA16B, 0xA16B }, +{ 0xA16C, 0xA16C, 0xA16C }, +{ 0xA16D, 0xA16D, 0xA16D }, +{ 0xA16E, 0xA16E, 0xA16E }, +{ 0xA16F, 0xA16F, 0xA16F }, +{ 0xA170, 0xA170, 0xA170 }, +{ 0xA171, 0xA171, 0xA171 }, +{ 0xA172, 0xA172, 0xA172 }, +{ 0xA173, 0xA173, 0xA173 }, +{ 0xA174, 0xA174, 0xA174 }, +{ 0xA175, 0xA175, 0xA175 }, +{ 0xA176, 0xA176, 0xA176 }, +{ 0xA177, 0xA177, 0xA177 }, +{ 0xA178, 0xA178, 0xA178 }, +{ 0xA179, 0xA179, 0xA179 }, +{ 0xA17A, 0xA17A, 0xA17A }, +{ 0xA17B, 0xA17B, 0xA17B }, +{ 0xA17C, 0xA17C, 0xA17C }, +{ 0xA17D, 0xA17D, 0xA17D }, +{ 0xA17E, 0xA17E, 0xA17E }, +{ 0xA17F, 0xA17F, 0xA17F }, +{ 0xA180, 0xA180, 0xA180 }, +{ 0xA181, 0xA181, 0xA181 }, +{ 0xA182, 0xA182, 0xA182 }, +{ 0xA183, 0xA183, 0xA183 }, +{ 0xA184, 0xA184, 0xA184 }, +{ 0xA185, 0xA185, 0xA185 }, +{ 0xA186, 0xA186, 0xA186 }, +{ 0xA187, 0xA187, 0xA187 }, +{ 0xA188, 0xA188, 0xA188 }, +{ 0xA189, 0xA189, 0xA189 }, +{ 0xA18A, 0xA18A, 0xA18A }, +{ 0xA18B, 0xA18B, 0xA18B }, +{ 0xA18C, 0xA18C, 0xA18C }, +{ 0xA18D, 0xA18D, 0xA18D }, +{ 0xA18E, 0xA18E, 0xA18E }, +{ 0xA18F, 0xA18F, 0xA18F }, +{ 0xA190, 0xA190, 0xA190 }, +{ 0xA191, 0xA191, 0xA191 }, +{ 0xA192, 0xA192, 0xA192 }, +{ 0xA193, 0xA193, 0xA193 }, +{ 0xA194, 0xA194, 0xA194 }, +{ 0xA195, 0xA195, 0xA195 }, +{ 0xA196, 0xA196, 0xA196 }, +{ 0xA197, 0xA197, 0xA197 }, +{ 0xA198, 0xA198, 0xA198 }, +{ 0xA199, 0xA199, 0xA199 }, +{ 0xA19A, 0xA19A, 0xA19A }, +{ 0xA19B, 0xA19B, 0xA19B }, +{ 0xA19C, 0xA19C, 0xA19C }, +{ 0xA19D, 0xA19D, 0xA19D }, +{ 0xA19E, 0xA19E, 0xA19E }, +{ 0xA19F, 0xA19F, 0xA19F }, +{ 0xA1A0, 0xA1A0, 0xA1A0 }, +{ 0xA1A1, 0xA1A1, 0xA1A1 }, +{ 0xA1A2, 0xA1A2, 0xA1A2 }, +{ 0xA1A3, 0xA1A3, 0xA1A3 }, +{ 0xA1A4, 0xA1A4, 0xA1A4 }, +{ 0xA1A5, 0xA1A5, 0xA1A5 }, +{ 0xA1A6, 0xA1A6, 0xA1A6 }, +{ 0xA1A7, 0xA1A7, 0xA1A7 }, +{ 0xA1A8, 0xA1A8, 0xA1A8 }, +{ 0xA1A9, 0xA1A9, 0xA1A9 }, +{ 0xA1AA, 0xA1AA, 0xA1AA }, +{ 0xA1AB, 0xA1AB, 0xA1AB }, +{ 0xA1AC, 0xA1AC, 0xA1AC }, +{ 0xA1AD, 0xA1AD, 0xA1AD }, +{ 0xA1AE, 0xA1AE, 0xA1AE }, +{ 0xA1AF, 0xA1AF, 0xA1AF }, +{ 0xA1B0, 0xA1B0, 0xA1B0 }, +{ 0xA1B1, 0xA1B1, 0xA1B1 }, +{ 0xA1B2, 0xA1B2, 0xA1B2 }, +{ 0xA1B3, 0xA1B3, 0xA1B3 }, +{ 0xA1B4, 0xA1B4, 0xA1B4 }, +{ 0xA1B5, 0xA1B5, 0xA1B5 }, +{ 0xA1B6, 0xA1B6, 0xA1B6 }, +{ 0xA1B7, 0xA1B7, 0xA1B7 }, +{ 0xA1B8, 0xA1B8, 0xA1B8 }, +{ 0xA1B9, 0xA1B9, 0xA1B9 }, +{ 0xA1BA, 0xA1BA, 0xA1BA }, +{ 0xA1BB, 0xA1BB, 0xA1BB }, +{ 0xA1BC, 0xA1BC, 0xA1BC }, +{ 0xA1BD, 0xA1BD, 0xA1BD }, +{ 0xA1BE, 0xA1BE, 0xA1BE }, +{ 0xA1BF, 0xA1BF, 0xA1BF }, +{ 0xA1C0, 0xA1C0, 0xA1C0 }, +{ 0xA1C1, 0xA1C1, 0xA1C1 }, +{ 0xA1C2, 0xA1C2, 0xA1C2 }, +{ 0xA1C3, 0xA1C3, 0xA1C3 }, +{ 0xA1C4, 0xA1C4, 0xA1C4 }, +{ 0xA1C5, 0xA1C5, 0xA1C5 }, +{ 0xA1C6, 0xA1C6, 0xA1C6 }, +{ 0xA1C7, 0xA1C7, 0xA1C7 }, +{ 0xA1C8, 0xA1C8, 0xA1C8 }, +{ 0xA1C9, 0xA1C9, 0xA1C9 }, +{ 0xA1CA, 0xA1CA, 0xA1CA }, +{ 0xA1CB, 0xA1CB, 0xA1CB }, +{ 0xA1CC, 0xA1CC, 0xA1CC }, +{ 0xA1CD, 0xA1CD, 0xA1CD }, +{ 0xA1CE, 0xA1CE, 0xA1CE }, +{ 0xA1CF, 0xA1CF, 0xA1CF }, +{ 0xA1D0, 0xA1D0, 0xA1D0 }, +{ 0xA1D1, 0xA1D1, 0xA1D1 }, +{ 0xA1D2, 0xA1D2, 0xA1D2 }, +{ 0xA1D3, 0xA1D3, 0xA1D3 }, +{ 0xA1D4, 0xA1D4, 0xA1D4 }, +{ 0xA1D5, 0xA1D5, 0xA1D5 }, +{ 0xA1D6, 0xA1D6, 0xA1D6 }, +{ 0xA1D7, 0xA1D7, 0xA1D7 }, +{ 0xA1D8, 0xA1D8, 0xA1D8 }, +{ 0xA1D9, 0xA1D9, 0xA1D9 }, +{ 0xA1DA, 0xA1DA, 0xA1DA }, +{ 0xA1DB, 0xA1DB, 0xA1DB }, +{ 0xA1DC, 0xA1DC, 0xA1DC }, +{ 0xA1DD, 0xA1DD, 0xA1DD }, +{ 0xA1DE, 0xA1DE, 0xA1DE }, +{ 0xA1DF, 0xA1DF, 0xA1DF }, +{ 0xA1E0, 0xA1E0, 0xA1E0 }, +{ 0xA1E1, 0xA1E1, 0xA1E1 }, +{ 0xA1E2, 0xA1E2, 0xA1E2 }, +{ 0xA1E3, 0xA1E3, 0xA1E3 }, +{ 0xA1E4, 0xA1E4, 0xA1E4 }, +{ 0xA1E5, 0xA1E5, 0xA1E5 }, +{ 0xA1E6, 0xA1E6, 0xA1E6 }, +{ 0xA1E7, 0xA1E7, 0xA1E7 }, +{ 0xA1E8, 0xA1E8, 0xA1E8 }, +{ 0xA1E9, 0xA1E9, 0xA1E9 }, +{ 0xA1EA, 0xA1EA, 0xA1EA }, +{ 0xA1EB, 0xA1EB, 0xA1EB }, +{ 0xA1EC, 0xA1EC, 0xA1EC }, +{ 0xA1ED, 0xA1ED, 0xA1ED }, +{ 0xA1EE, 0xA1EE, 0xA1EE }, +{ 0xA1EF, 0xA1EF, 0xA1EF }, +{ 0xA1F0, 0xA1F0, 0xA1F0 }, +{ 0xA1F1, 0xA1F1, 0xA1F1 }, +{ 0xA1F2, 0xA1F2, 0xA1F2 }, +{ 0xA1F3, 0xA1F3, 0xA1F3 }, +{ 0xA1F4, 0xA1F4, 0xA1F4 }, +{ 0xA1F5, 0xA1F5, 0xA1F5 }, +{ 0xA1F6, 0xA1F6, 0xA1F6 }, +{ 0xA1F7, 0xA1F7, 0xA1F7 }, +{ 0xA1F8, 0xA1F8, 0xA1F8 }, +{ 0xA1F9, 0xA1F9, 0xA1F9 }, +{ 0xA1FA, 0xA1FA, 0xA1FA }, +{ 0xA1FB, 0xA1FB, 0xA1FB }, +{ 0xA1FC, 0xA1FC, 0xA1FC }, +{ 0xA1FD, 0xA1FD, 0xA1FD }, +{ 0xA1FE, 0xA1FE, 0xA1FE }, +{ 0xA1FF, 0xA1FF, 0xA1FF }, +{ 0xA200, 0xA200, 0xA200 }, +{ 0xA201, 0xA201, 0xA201 }, +{ 0xA202, 0xA202, 0xA202 }, +{ 0xA203, 0xA203, 0xA203 }, +{ 0xA204, 0xA204, 0xA204 }, +{ 0xA205, 0xA205, 0xA205 }, +{ 0xA206, 0xA206, 0xA206 }, +{ 0xA207, 0xA207, 0xA207 }, +{ 0xA208, 0xA208, 0xA208 }, +{ 0xA209, 0xA209, 0xA209 }, +{ 0xA20A, 0xA20A, 0xA20A }, +{ 0xA20B, 0xA20B, 0xA20B }, +{ 0xA20C, 0xA20C, 0xA20C }, +{ 0xA20D, 0xA20D, 0xA20D }, +{ 0xA20E, 0xA20E, 0xA20E }, +{ 0xA20F, 0xA20F, 0xA20F }, +{ 0xA210, 0xA210, 0xA210 }, +{ 0xA211, 0xA211, 0xA211 }, +{ 0xA212, 0xA212, 0xA212 }, +{ 0xA213, 0xA213, 0xA213 }, +{ 0xA214, 0xA214, 0xA214 }, +{ 0xA215, 0xA215, 0xA215 }, +{ 0xA216, 0xA216, 0xA216 }, +{ 0xA217, 0xA217, 0xA217 }, +{ 0xA218, 0xA218, 0xA218 }, +{ 0xA219, 0xA219, 0xA219 }, +{ 0xA21A, 0xA21A, 0xA21A }, +{ 0xA21B, 0xA21B, 0xA21B }, +{ 0xA21C, 0xA21C, 0xA21C }, +{ 0xA21D, 0xA21D, 0xA21D }, +{ 0xA21E, 0xA21E, 0xA21E }, +{ 0xA21F, 0xA21F, 0xA21F }, +{ 0xA220, 0xA220, 0xA220 }, +{ 0xA221, 0xA221, 0xA221 }, +{ 0xA222, 0xA222, 0xA222 }, +{ 0xA223, 0xA223, 0xA223 }, +{ 0xA224, 0xA224, 0xA224 }, +{ 0xA225, 0xA225, 0xA225 }, +{ 0xA226, 0xA226, 0xA226 }, +{ 0xA227, 0xA227, 0xA227 }, +{ 0xA228, 0xA228, 0xA228 }, +{ 0xA229, 0xA229, 0xA229 }, +{ 0xA22A, 0xA22A, 0xA22A }, +{ 0xA22B, 0xA22B, 0xA22B }, +{ 0xA22C, 0xA22C, 0xA22C }, +{ 0xA22D, 0xA22D, 0xA22D }, +{ 0xA22E, 0xA22E, 0xA22E }, +{ 0xA22F, 0xA22F, 0xA22F }, +{ 0xA230, 0xA230, 0xA230 }, +{ 0xA231, 0xA231, 0xA231 }, +{ 0xA232, 0xA232, 0xA232 }, +{ 0xA233, 0xA233, 0xA233 }, +{ 0xA234, 0xA234, 0xA234 }, +{ 0xA235, 0xA235, 0xA235 }, +{ 0xA236, 0xA236, 0xA236 }, +{ 0xA237, 0xA237, 0xA237 }, +{ 0xA238, 0xA238, 0xA238 }, +{ 0xA239, 0xA239, 0xA239 }, +{ 0xA23A, 0xA23A, 0xA23A }, +{ 0xA23B, 0xA23B, 0xA23B }, +{ 0xA23C, 0xA23C, 0xA23C }, +{ 0xA23D, 0xA23D, 0xA23D }, +{ 0xA23E, 0xA23E, 0xA23E }, +{ 0xA23F, 0xA23F, 0xA23F }, +{ 0xA240, 0xA240, 0xA240 }, +{ 0xA241, 0xA241, 0xA241 }, +{ 0xA242, 0xA242, 0xA242 }, +{ 0xA243, 0xA243, 0xA243 }, +{ 0xA244, 0xA244, 0xA244 }, +{ 0xA245, 0xA245, 0xA245 }, +{ 0xA246, 0xA246, 0xA246 }, +{ 0xA247, 0xA247, 0xA247 }, +{ 0xA248, 0xA248, 0xA248 }, +{ 0xA249, 0xA249, 0xA249 }, +{ 0xA24A, 0xA24A, 0xA24A }, +{ 0xA24B, 0xA24B, 0xA24B }, +{ 0xA24C, 0xA24C, 0xA24C }, +{ 0xA24D, 0xA24D, 0xA24D }, +{ 0xA24E, 0xA24E, 0xA24E }, +{ 0xA24F, 0xA24F, 0xA24F }, +{ 0xA250, 0xA250, 0xA250 }, +{ 0xA251, 0xA251, 0xA251 }, +{ 0xA252, 0xA252, 0xA252 }, +{ 0xA253, 0xA253, 0xA253 }, +{ 0xA254, 0xA254, 0xA254 }, +{ 0xA255, 0xA255, 0xA255 }, +{ 0xA256, 0xA256, 0xA256 }, +{ 0xA257, 0xA257, 0xA257 }, +{ 0xA258, 0xA258, 0xA258 }, +{ 0xA259, 0xA259, 0xA259 }, +{ 0xA25A, 0xA25A, 0xA25A }, +{ 0xA25B, 0xA25B, 0xA25B }, +{ 0xA25C, 0xA25C, 0xA25C }, +{ 0xA25D, 0xA25D, 0xA25D }, +{ 0xA25E, 0xA25E, 0xA25E }, +{ 0xA25F, 0xA25F, 0xA25F }, +{ 0xA260, 0xA260, 0xA260 }, +{ 0xA261, 0xA261, 0xA261 }, +{ 0xA262, 0xA262, 0xA262 }, +{ 0xA263, 0xA263, 0xA263 }, +{ 0xA264, 0xA264, 0xA264 }, +{ 0xA265, 0xA265, 0xA265 }, +{ 0xA266, 0xA266, 0xA266 }, +{ 0xA267, 0xA267, 0xA267 }, +{ 0xA268, 0xA268, 0xA268 }, +{ 0xA269, 0xA269, 0xA269 }, +{ 0xA26A, 0xA26A, 0xA26A }, +{ 0xA26B, 0xA26B, 0xA26B }, +{ 0xA26C, 0xA26C, 0xA26C }, +{ 0xA26D, 0xA26D, 0xA26D }, +{ 0xA26E, 0xA26E, 0xA26E }, +{ 0xA26F, 0xA26F, 0xA26F }, +{ 0xA270, 0xA270, 0xA270 }, +{ 0xA271, 0xA271, 0xA271 }, +{ 0xA272, 0xA272, 0xA272 }, +{ 0xA273, 0xA273, 0xA273 }, +{ 0xA274, 0xA274, 0xA274 }, +{ 0xA275, 0xA275, 0xA275 }, +{ 0xA276, 0xA276, 0xA276 }, +{ 0xA277, 0xA277, 0xA277 }, +{ 0xA278, 0xA278, 0xA278 }, +{ 0xA279, 0xA279, 0xA279 }, +{ 0xA27A, 0xA27A, 0xA27A }, +{ 0xA27B, 0xA27B, 0xA27B }, +{ 0xA27C, 0xA27C, 0xA27C }, +{ 0xA27D, 0xA27D, 0xA27D }, +{ 0xA27E, 0xA27E, 0xA27E }, +{ 0xA27F, 0xA27F, 0xA27F }, +{ 0xA280, 0xA280, 0xA280 }, +{ 0xA281, 0xA281, 0xA281 }, +{ 0xA282, 0xA282, 0xA282 }, +{ 0xA283, 0xA283, 0xA283 }, +{ 0xA284, 0xA284, 0xA284 }, +{ 0xA285, 0xA285, 0xA285 }, +{ 0xA286, 0xA286, 0xA286 }, +{ 0xA287, 0xA287, 0xA287 }, +{ 0xA288, 0xA288, 0xA288 }, +{ 0xA289, 0xA289, 0xA289 }, +{ 0xA28A, 0xA28A, 0xA28A }, +{ 0xA28B, 0xA28B, 0xA28B }, +{ 0xA28C, 0xA28C, 0xA28C }, +{ 0xA28D, 0xA28D, 0xA28D }, +{ 0xA28E, 0xA28E, 0xA28E }, +{ 0xA28F, 0xA28F, 0xA28F }, +{ 0xA290, 0xA290, 0xA290 }, +{ 0xA291, 0xA291, 0xA291 }, +{ 0xA292, 0xA292, 0xA292 }, +{ 0xA293, 0xA293, 0xA293 }, +{ 0xA294, 0xA294, 0xA294 }, +{ 0xA295, 0xA295, 0xA295 }, +{ 0xA296, 0xA296, 0xA296 }, +{ 0xA297, 0xA297, 0xA297 }, +{ 0xA298, 0xA298, 0xA298 }, +{ 0xA299, 0xA299, 0xA299 }, +{ 0xA29A, 0xA29A, 0xA29A }, +{ 0xA29B, 0xA29B, 0xA29B }, +{ 0xA29C, 0xA29C, 0xA29C }, +{ 0xA29D, 0xA29D, 0xA29D }, +{ 0xA29E, 0xA29E, 0xA29E }, +{ 0xA29F, 0xA29F, 0xA29F }, +{ 0xA2A0, 0xA2A0, 0xA2A0 }, +{ 0xA2A1, 0xA2A1, 0xA2A1 }, +{ 0xA2A2, 0xA2A2, 0xA2A2 }, +{ 0xA2A3, 0xA2A3, 0xA2A3 }, +{ 0xA2A4, 0xA2A4, 0xA2A4 }, +{ 0xA2A5, 0xA2A5, 0xA2A5 }, +{ 0xA2A6, 0xA2A6, 0xA2A6 }, +{ 0xA2A7, 0xA2A7, 0xA2A7 }, +{ 0xA2A8, 0xA2A8, 0xA2A8 }, +{ 0xA2A9, 0xA2A9, 0xA2A9 }, +{ 0xA2AA, 0xA2AA, 0xA2AA }, +{ 0xA2AB, 0xA2AB, 0xA2AB }, +{ 0xA2AC, 0xA2AC, 0xA2AC }, +{ 0xA2AD, 0xA2AD, 0xA2AD }, +{ 0xA2AE, 0xA2AE, 0xA2AE }, +{ 0xA2AF, 0xA2AF, 0xA2AF }, +{ 0xA2B0, 0xA2B0, 0xA2B0 }, +{ 0xA2B1, 0xA2B1, 0xA2B1 }, +{ 0xA2B2, 0xA2B2, 0xA2B2 }, +{ 0xA2B3, 0xA2B3, 0xA2B3 }, +{ 0xA2B4, 0xA2B4, 0xA2B4 }, +{ 0xA2B5, 0xA2B5, 0xA2B5 }, +{ 0xA2B6, 0xA2B6, 0xA2B6 }, +{ 0xA2B7, 0xA2B7, 0xA2B7 }, +{ 0xA2B8, 0xA2B8, 0xA2B8 }, +{ 0xA2B9, 0xA2B9, 0xA2B9 }, +{ 0xA2BA, 0xA2BA, 0xA2BA }, +{ 0xA2BB, 0xA2BB, 0xA2BB }, +{ 0xA2BC, 0xA2BC, 0xA2BC }, +{ 0xA2BD, 0xA2BD, 0xA2BD }, +{ 0xA2BE, 0xA2BE, 0xA2BE }, +{ 0xA2BF, 0xA2BF, 0xA2BF }, +{ 0xA2C0, 0xA2C0, 0xA2C0 }, +{ 0xA2C1, 0xA2C1, 0xA2C1 }, +{ 0xA2C2, 0xA2C2, 0xA2C2 }, +{ 0xA2C3, 0xA2C3, 0xA2C3 }, +{ 0xA2C4, 0xA2C4, 0xA2C4 }, +{ 0xA2C5, 0xA2C5, 0xA2C5 }, +{ 0xA2C6, 0xA2C6, 0xA2C6 }, +{ 0xA2C7, 0xA2C7, 0xA2C7 }, +{ 0xA2C8, 0xA2C8, 0xA2C8 }, +{ 0xA2C9, 0xA2C9, 0xA2C9 }, +{ 0xA2CA, 0xA2CA, 0xA2CA }, +{ 0xA2CB, 0xA2CB, 0xA2CB }, +{ 0xA2CC, 0xA2CC, 0xA2CC }, +{ 0xA2CD, 0xA2CD, 0xA2CD }, +{ 0xA2CE, 0xA2CE, 0xA2CE }, +{ 0xA2CF, 0xA2CF, 0xA2CF }, +{ 0xA2D0, 0xA2D0, 0xA2D0 }, +{ 0xA2D1, 0xA2D1, 0xA2D1 }, +{ 0xA2D2, 0xA2D2, 0xA2D2 }, +{ 0xA2D3, 0xA2D3, 0xA2D3 }, +{ 0xA2D4, 0xA2D4, 0xA2D4 }, +{ 0xA2D5, 0xA2D5, 0xA2D5 }, +{ 0xA2D6, 0xA2D6, 0xA2D6 }, +{ 0xA2D7, 0xA2D7, 0xA2D7 }, +{ 0xA2D8, 0xA2D8, 0xA2D8 }, +{ 0xA2D9, 0xA2D9, 0xA2D9 }, +{ 0xA2DA, 0xA2DA, 0xA2DA }, +{ 0xA2DB, 0xA2DB, 0xA2DB }, +{ 0xA2DC, 0xA2DC, 0xA2DC }, +{ 0xA2DD, 0xA2DD, 0xA2DD }, +{ 0xA2DE, 0xA2DE, 0xA2DE }, +{ 0xA2DF, 0xA2DF, 0xA2DF }, +{ 0xA2E0, 0xA2E0, 0xA2E0 }, +{ 0xA2E1, 0xA2E1, 0xA2E1 }, +{ 0xA2E2, 0xA2E2, 0xA2E2 }, +{ 0xA2E3, 0xA2E3, 0xA2E3 }, +{ 0xA2E4, 0xA2E4, 0xA2E4 }, +{ 0xA2E5, 0xA2E5, 0xA2E5 }, +{ 0xA2E6, 0xA2E6, 0xA2E6 }, +{ 0xA2E7, 0xA2E7, 0xA2E7 }, +{ 0xA2E8, 0xA2E8, 0xA2E8 }, +{ 0xA2E9, 0xA2E9, 0xA2E9 }, +{ 0xA2EA, 0xA2EA, 0xA2EA }, +{ 0xA2EB, 0xA2EB, 0xA2EB }, +{ 0xA2EC, 0xA2EC, 0xA2EC }, +{ 0xA2ED, 0xA2ED, 0xA2ED }, +{ 0xA2EE, 0xA2EE, 0xA2EE }, +{ 0xA2EF, 0xA2EF, 0xA2EF }, +{ 0xA2F0, 0xA2F0, 0xA2F0 }, +{ 0xA2F1, 0xA2F1, 0xA2F1 }, +{ 0xA2F2, 0xA2F2, 0xA2F2 }, +{ 0xA2F3, 0xA2F3, 0xA2F3 }, +{ 0xA2F4, 0xA2F4, 0xA2F4 }, +{ 0xA2F5, 0xA2F5, 0xA2F5 }, +{ 0xA2F6, 0xA2F6, 0xA2F6 }, +{ 0xA2F7, 0xA2F7, 0xA2F7 }, +{ 0xA2F8, 0xA2F8, 0xA2F8 }, +{ 0xA2F9, 0xA2F9, 0xA2F9 }, +{ 0xA2FA, 0xA2FA, 0xA2FA }, +{ 0xA2FB, 0xA2FB, 0xA2FB }, +{ 0xA2FC, 0xA2FC, 0xA2FC }, +{ 0xA2FD, 0xA2FD, 0xA2FD }, +{ 0xA2FE, 0xA2FE, 0xA2FE }, +{ 0xA2FF, 0xA2FF, 0xA2FF }, +{ 0xA300, 0xA300, 0xA300 }, +{ 0xA301, 0xA301, 0xA301 }, +{ 0xA302, 0xA302, 0xA302 }, +{ 0xA303, 0xA303, 0xA303 }, +{ 0xA304, 0xA304, 0xA304 }, +{ 0xA305, 0xA305, 0xA305 }, +{ 0xA306, 0xA306, 0xA306 }, +{ 0xA307, 0xA307, 0xA307 }, +{ 0xA308, 0xA308, 0xA308 }, +{ 0xA309, 0xA309, 0xA309 }, +{ 0xA30A, 0xA30A, 0xA30A }, +{ 0xA30B, 0xA30B, 0xA30B }, +{ 0xA30C, 0xA30C, 0xA30C }, +{ 0xA30D, 0xA30D, 0xA30D }, +{ 0xA30E, 0xA30E, 0xA30E }, +{ 0xA30F, 0xA30F, 0xA30F }, +{ 0xA310, 0xA310, 0xA310 }, +{ 0xA311, 0xA311, 0xA311 }, +{ 0xA312, 0xA312, 0xA312 }, +{ 0xA313, 0xA313, 0xA313 }, +{ 0xA314, 0xA314, 0xA314 }, +{ 0xA315, 0xA315, 0xA315 }, +{ 0xA316, 0xA316, 0xA316 }, +{ 0xA317, 0xA317, 0xA317 }, +{ 0xA318, 0xA318, 0xA318 }, +{ 0xA319, 0xA319, 0xA319 }, +{ 0xA31A, 0xA31A, 0xA31A }, +{ 0xA31B, 0xA31B, 0xA31B }, +{ 0xA31C, 0xA31C, 0xA31C }, +{ 0xA31D, 0xA31D, 0xA31D }, +{ 0xA31E, 0xA31E, 0xA31E }, +{ 0xA31F, 0xA31F, 0xA31F }, +{ 0xA320, 0xA320, 0xA320 }, +{ 0xA321, 0xA321, 0xA321 }, +{ 0xA322, 0xA322, 0xA322 }, +{ 0xA323, 0xA323, 0xA323 }, +{ 0xA324, 0xA324, 0xA324 }, +{ 0xA325, 0xA325, 0xA325 }, +{ 0xA326, 0xA326, 0xA326 }, +{ 0xA327, 0xA327, 0xA327 }, +{ 0xA328, 0xA328, 0xA328 }, +{ 0xA329, 0xA329, 0xA329 }, +{ 0xA32A, 0xA32A, 0xA32A }, +{ 0xA32B, 0xA32B, 0xA32B }, +{ 0xA32C, 0xA32C, 0xA32C }, +{ 0xA32D, 0xA32D, 0xA32D }, +{ 0xA32E, 0xA32E, 0xA32E }, +{ 0xA32F, 0xA32F, 0xA32F }, +{ 0xA330, 0xA330, 0xA330 }, +{ 0xA331, 0xA331, 0xA331 }, +{ 0xA332, 0xA332, 0xA332 }, +{ 0xA333, 0xA333, 0xA333 }, +{ 0xA334, 0xA334, 0xA334 }, +{ 0xA335, 0xA335, 0xA335 }, +{ 0xA336, 0xA336, 0xA336 }, +{ 0xA337, 0xA337, 0xA337 }, +{ 0xA338, 0xA338, 0xA338 }, +{ 0xA339, 0xA339, 0xA339 }, +{ 0xA33A, 0xA33A, 0xA33A }, +{ 0xA33B, 0xA33B, 0xA33B }, +{ 0xA33C, 0xA33C, 0xA33C }, +{ 0xA33D, 0xA33D, 0xA33D }, +{ 0xA33E, 0xA33E, 0xA33E }, +{ 0xA33F, 0xA33F, 0xA33F }, +{ 0xA340, 0xA340, 0xA340 }, +{ 0xA341, 0xA341, 0xA341 }, +{ 0xA342, 0xA342, 0xA342 }, +{ 0xA343, 0xA343, 0xA343 }, +{ 0xA344, 0xA344, 0xA344 }, +{ 0xA345, 0xA345, 0xA345 }, +{ 0xA346, 0xA346, 0xA346 }, +{ 0xA347, 0xA347, 0xA347 }, +{ 0xA348, 0xA348, 0xA348 }, +{ 0xA349, 0xA349, 0xA349 }, +{ 0xA34A, 0xA34A, 0xA34A }, +{ 0xA34B, 0xA34B, 0xA34B }, +{ 0xA34C, 0xA34C, 0xA34C }, +{ 0xA34D, 0xA34D, 0xA34D }, +{ 0xA34E, 0xA34E, 0xA34E }, +{ 0xA34F, 0xA34F, 0xA34F }, +{ 0xA350, 0xA350, 0xA350 }, +{ 0xA351, 0xA351, 0xA351 }, +{ 0xA352, 0xA352, 0xA352 }, +{ 0xA353, 0xA353, 0xA353 }, +{ 0xA354, 0xA354, 0xA354 }, +{ 0xA355, 0xA355, 0xA355 }, +{ 0xA356, 0xA356, 0xA356 }, +{ 0xA357, 0xA357, 0xA357 }, +{ 0xA358, 0xA358, 0xA358 }, +{ 0xA359, 0xA359, 0xA359 }, +{ 0xA35A, 0xA35A, 0xA35A }, +{ 0xA35B, 0xA35B, 0xA35B }, +{ 0xA35C, 0xA35C, 0xA35C }, +{ 0xA35D, 0xA35D, 0xA35D }, +{ 0xA35E, 0xA35E, 0xA35E }, +{ 0xA35F, 0xA35F, 0xA35F }, +{ 0xA360, 0xA360, 0xA360 }, +{ 0xA361, 0xA361, 0xA361 }, +{ 0xA362, 0xA362, 0xA362 }, +{ 0xA363, 0xA363, 0xA363 }, +{ 0xA364, 0xA364, 0xA364 }, +{ 0xA365, 0xA365, 0xA365 }, +{ 0xA366, 0xA366, 0xA366 }, +{ 0xA367, 0xA367, 0xA367 }, +{ 0xA368, 0xA368, 0xA368 }, +{ 0xA369, 0xA369, 0xA369 }, +{ 0xA36A, 0xA36A, 0xA36A }, +{ 0xA36B, 0xA36B, 0xA36B }, +{ 0xA36C, 0xA36C, 0xA36C }, +{ 0xA36D, 0xA36D, 0xA36D }, +{ 0xA36E, 0xA36E, 0xA36E }, +{ 0xA36F, 0xA36F, 0xA36F }, +{ 0xA370, 0xA370, 0xA370 }, +{ 0xA371, 0xA371, 0xA371 }, +{ 0xA372, 0xA372, 0xA372 }, +{ 0xA373, 0xA373, 0xA373 }, +{ 0xA374, 0xA374, 0xA374 }, +{ 0xA375, 0xA375, 0xA375 }, +{ 0xA376, 0xA376, 0xA376 }, +{ 0xA377, 0xA377, 0xA377 }, +{ 0xA378, 0xA378, 0xA378 }, +{ 0xA379, 0xA379, 0xA379 }, +{ 0xA37A, 0xA37A, 0xA37A }, +{ 0xA37B, 0xA37B, 0xA37B }, +{ 0xA37C, 0xA37C, 0xA37C }, +{ 0xA37D, 0xA37D, 0xA37D }, +{ 0xA37E, 0xA37E, 0xA37E }, +{ 0xA37F, 0xA37F, 0xA37F }, +{ 0xA380, 0xA380, 0xA380 }, +{ 0xA381, 0xA381, 0xA381 }, +{ 0xA382, 0xA382, 0xA382 }, +{ 0xA383, 0xA383, 0xA383 }, +{ 0xA384, 0xA384, 0xA384 }, +{ 0xA385, 0xA385, 0xA385 }, +{ 0xA386, 0xA386, 0xA386 }, +{ 0xA387, 0xA387, 0xA387 }, +{ 0xA388, 0xA388, 0xA388 }, +{ 0xA389, 0xA389, 0xA389 }, +{ 0xA38A, 0xA38A, 0xA38A }, +{ 0xA38B, 0xA38B, 0xA38B }, +{ 0xA38C, 0xA38C, 0xA38C }, +{ 0xA38D, 0xA38D, 0xA38D }, +{ 0xA38E, 0xA38E, 0xA38E }, +{ 0xA38F, 0xA38F, 0xA38F }, +{ 0xA390, 0xA390, 0xA390 }, +{ 0xA391, 0xA391, 0xA391 }, +{ 0xA392, 0xA392, 0xA392 }, +{ 0xA393, 0xA393, 0xA393 }, +{ 0xA394, 0xA394, 0xA394 }, +{ 0xA395, 0xA395, 0xA395 }, +{ 0xA396, 0xA396, 0xA396 }, +{ 0xA397, 0xA397, 0xA397 }, +{ 0xA398, 0xA398, 0xA398 }, +{ 0xA399, 0xA399, 0xA399 }, +{ 0xA39A, 0xA39A, 0xA39A }, +{ 0xA39B, 0xA39B, 0xA39B }, +{ 0xA39C, 0xA39C, 0xA39C }, +{ 0xA39D, 0xA39D, 0xA39D }, +{ 0xA39E, 0xA39E, 0xA39E }, +{ 0xA39F, 0xA39F, 0xA39F }, +{ 0xA3A0, 0xA3A0, 0xA3A0 }, +{ 0xA3A1, 0xA3A1, 0xA3A1 }, +{ 0xA3A2, 0xA3A2, 0xA3A2 }, +{ 0xA3A3, 0xA3A3, 0xA3A3 }, +{ 0xA3A4, 0xA3A4, 0xA3A4 }, +{ 0xA3A5, 0xA3A5, 0xA3A5 }, +{ 0xA3A6, 0xA3A6, 0xA3A6 }, +{ 0xA3A7, 0xA3A7, 0xA3A7 }, +{ 0xA3A8, 0xA3A8, 0xA3A8 }, +{ 0xA3A9, 0xA3A9, 0xA3A9 }, +{ 0xA3AA, 0xA3AA, 0xA3AA }, +{ 0xA3AB, 0xA3AB, 0xA3AB }, +{ 0xA3AC, 0xA3AC, 0xA3AC }, +{ 0xA3AD, 0xA3AD, 0xA3AD }, +{ 0xA3AE, 0xA3AE, 0xA3AE }, +{ 0xA3AF, 0xA3AF, 0xA3AF }, +{ 0xA3B0, 0xA3B0, 0xA3B0 }, +{ 0xA3B1, 0xA3B1, 0xA3B1 }, +{ 0xA3B2, 0xA3B2, 0xA3B2 }, +{ 0xA3B3, 0xA3B3, 0xA3B3 }, +{ 0xA3B4, 0xA3B4, 0xA3B4 }, +{ 0xA3B5, 0xA3B5, 0xA3B5 }, +{ 0xA3B6, 0xA3B6, 0xA3B6 }, +{ 0xA3B7, 0xA3B7, 0xA3B7 }, +{ 0xA3B8, 0xA3B8, 0xA3B8 }, +{ 0xA3B9, 0xA3B9, 0xA3B9 }, +{ 0xA3BA, 0xA3BA, 0xA3BA }, +{ 0xA3BB, 0xA3BB, 0xA3BB }, +{ 0xA3BC, 0xA3BC, 0xA3BC }, +{ 0xA3BD, 0xA3BD, 0xA3BD }, +{ 0xA3BE, 0xA3BE, 0xA3BE }, +{ 0xA3BF, 0xA3BF, 0xA3BF }, +{ 0xA3C0, 0xA3C0, 0xA3C0 }, +{ 0xA3C1, 0xA3C1, 0xA3C1 }, +{ 0xA3C2, 0xA3C2, 0xA3C2 }, +{ 0xA3C3, 0xA3C3, 0xA3C3 }, +{ 0xA3C4, 0xA3C4, 0xA3C4 }, +{ 0xA3C5, 0xA3C5, 0xA3C5 }, +{ 0xA3C6, 0xA3C6, 0xA3C6 }, +{ 0xA3C7, 0xA3C7, 0xA3C7 }, +{ 0xA3C8, 0xA3C8, 0xA3C8 }, +{ 0xA3C9, 0xA3C9, 0xA3C9 }, +{ 0xA3CA, 0xA3CA, 0xA3CA }, +{ 0xA3CB, 0xA3CB, 0xA3CB }, +{ 0xA3CC, 0xA3CC, 0xA3CC }, +{ 0xA3CD, 0xA3CD, 0xA3CD }, +{ 0xA3CE, 0xA3CE, 0xA3CE }, +{ 0xA3CF, 0xA3CF, 0xA3CF }, +{ 0xA3D0, 0xA3D0, 0xA3D0 }, +{ 0xA3D1, 0xA3D1, 0xA3D1 }, +{ 0xA3D2, 0xA3D2, 0xA3D2 }, +{ 0xA3D3, 0xA3D3, 0xA3D3 }, +{ 0xA3D4, 0xA3D4, 0xA3D4 }, +{ 0xA3D5, 0xA3D5, 0xA3D5 }, +{ 0xA3D6, 0xA3D6, 0xA3D6 }, +{ 0xA3D7, 0xA3D7, 0xA3D7 }, +{ 0xA3D8, 0xA3D8, 0xA3D8 }, +{ 0xA3D9, 0xA3D9, 0xA3D9 }, +{ 0xA3DA, 0xA3DA, 0xA3DA }, +{ 0xA3DB, 0xA3DB, 0xA3DB }, +{ 0xA3DC, 0xA3DC, 0xA3DC }, +{ 0xA3DD, 0xA3DD, 0xA3DD }, +{ 0xA3DE, 0xA3DE, 0xA3DE }, +{ 0xA3DF, 0xA3DF, 0xA3DF }, +{ 0xA3E0, 0xA3E0, 0xA3E0 }, +{ 0xA3E1, 0xA3E1, 0xA3E1 }, +{ 0xA3E2, 0xA3E2, 0xA3E2 }, +{ 0xA3E3, 0xA3E3, 0xA3E3 }, +{ 0xA3E4, 0xA3E4, 0xA3E4 }, +{ 0xA3E5, 0xA3E5, 0xA3E5 }, +{ 0xA3E6, 0xA3E6, 0xA3E6 }, +{ 0xA3E7, 0xA3E7, 0xA3E7 }, +{ 0xA3E8, 0xA3E8, 0xA3E8 }, +{ 0xA3E9, 0xA3E9, 0xA3E9 }, +{ 0xA3EA, 0xA3EA, 0xA3EA }, +{ 0xA3EB, 0xA3EB, 0xA3EB }, +{ 0xA3EC, 0xA3EC, 0xA3EC }, +{ 0xA3ED, 0xA3ED, 0xA3ED }, +{ 0xA3EE, 0xA3EE, 0xA3EE }, +{ 0xA3EF, 0xA3EF, 0xA3EF }, +{ 0xA3F0, 0xA3F0, 0xA3F0 }, +{ 0xA3F1, 0xA3F1, 0xA3F1 }, +{ 0xA3F2, 0xA3F2, 0xA3F2 }, +{ 0xA3F3, 0xA3F3, 0xA3F3 }, +{ 0xA3F4, 0xA3F4, 0xA3F4 }, +{ 0xA3F5, 0xA3F5, 0xA3F5 }, +{ 0xA3F6, 0xA3F6, 0xA3F6 }, +{ 0xA3F7, 0xA3F7, 0xA3F7 }, +{ 0xA3F8, 0xA3F8, 0xA3F8 }, +{ 0xA3F9, 0xA3F9, 0xA3F9 }, +{ 0xA3FA, 0xA3FA, 0xA3FA }, +{ 0xA3FB, 0xA3FB, 0xA3FB }, +{ 0xA3FC, 0xA3FC, 0xA3FC }, +{ 0xA3FD, 0xA3FD, 0xA3FD }, +{ 0xA3FE, 0xA3FE, 0xA3FE }, +{ 0xA3FF, 0xA3FF, 0xA3FF }, +{ 0xA400, 0xA400, 0xA400 }, +{ 0xA401, 0xA401, 0xA401 }, +{ 0xA402, 0xA402, 0xA402 }, +{ 0xA403, 0xA403, 0xA403 }, +{ 0xA404, 0xA404, 0xA404 }, +{ 0xA405, 0xA405, 0xA405 }, +{ 0xA406, 0xA406, 0xA406 }, +{ 0xA407, 0xA407, 0xA407 }, +{ 0xA408, 0xA408, 0xA408 }, +{ 0xA409, 0xA409, 0xA409 }, +{ 0xA40A, 0xA40A, 0xA40A }, +{ 0xA40B, 0xA40B, 0xA40B }, +{ 0xA40C, 0xA40C, 0xA40C }, +{ 0xA40D, 0xA40D, 0xA40D }, +{ 0xA40E, 0xA40E, 0xA40E }, +{ 0xA40F, 0xA40F, 0xA40F }, +{ 0xA410, 0xA410, 0xA410 }, +{ 0xA411, 0xA411, 0xA411 }, +{ 0xA412, 0xA412, 0xA412 }, +{ 0xA413, 0xA413, 0xA413 }, +{ 0xA414, 0xA414, 0xA414 }, +{ 0xA415, 0xA415, 0xA415 }, +{ 0xA416, 0xA416, 0xA416 }, +{ 0xA417, 0xA417, 0xA417 }, +{ 0xA418, 0xA418, 0xA418 }, +{ 0xA419, 0xA419, 0xA419 }, +{ 0xA41A, 0xA41A, 0xA41A }, +{ 0xA41B, 0xA41B, 0xA41B }, +{ 0xA41C, 0xA41C, 0xA41C }, +{ 0xA41D, 0xA41D, 0xA41D }, +{ 0xA41E, 0xA41E, 0xA41E }, +{ 0xA41F, 0xA41F, 0xA41F }, +{ 0xA420, 0xA420, 0xA420 }, +{ 0xA421, 0xA421, 0xA421 }, +{ 0xA422, 0xA422, 0xA422 }, +{ 0xA423, 0xA423, 0xA423 }, +{ 0xA424, 0xA424, 0xA424 }, +{ 0xA425, 0xA425, 0xA425 }, +{ 0xA426, 0xA426, 0xA426 }, +{ 0xA427, 0xA427, 0xA427 }, +{ 0xA428, 0xA428, 0xA428 }, +{ 0xA429, 0xA429, 0xA429 }, +{ 0xA42A, 0xA42A, 0xA42A }, +{ 0xA42B, 0xA42B, 0xA42B }, +{ 0xA42C, 0xA42C, 0xA42C }, +{ 0xA42D, 0xA42D, 0xA42D }, +{ 0xA42E, 0xA42E, 0xA42E }, +{ 0xA42F, 0xA42F, 0xA42F }, +{ 0xA430, 0xA430, 0xA430 }, +{ 0xA431, 0xA431, 0xA431 }, +{ 0xA432, 0xA432, 0xA432 }, +{ 0xA433, 0xA433, 0xA433 }, +{ 0xA434, 0xA434, 0xA434 }, +{ 0xA435, 0xA435, 0xA435 }, +{ 0xA436, 0xA436, 0xA436 }, +{ 0xA437, 0xA437, 0xA437 }, +{ 0xA438, 0xA438, 0xA438 }, +{ 0xA439, 0xA439, 0xA439 }, +{ 0xA43A, 0xA43A, 0xA43A }, +{ 0xA43B, 0xA43B, 0xA43B }, +{ 0xA43C, 0xA43C, 0xA43C }, +{ 0xA43D, 0xA43D, 0xA43D }, +{ 0xA43E, 0xA43E, 0xA43E }, +{ 0xA43F, 0xA43F, 0xA43F }, +{ 0xA440, 0xA440, 0xA440 }, +{ 0xA441, 0xA441, 0xA441 }, +{ 0xA442, 0xA442, 0xA442 }, +{ 0xA443, 0xA443, 0xA443 }, +{ 0xA444, 0xA444, 0xA444 }, +{ 0xA445, 0xA445, 0xA445 }, +{ 0xA446, 0xA446, 0xA446 }, +{ 0xA447, 0xA447, 0xA447 }, +{ 0xA448, 0xA448, 0xA448 }, +{ 0xA449, 0xA449, 0xA449 }, +{ 0xA44A, 0xA44A, 0xA44A }, +{ 0xA44B, 0xA44B, 0xA44B }, +{ 0xA44C, 0xA44C, 0xA44C }, +{ 0xA44D, 0xA44D, 0xA44D }, +{ 0xA44E, 0xA44E, 0xA44E }, +{ 0xA44F, 0xA44F, 0xA44F }, +{ 0xA450, 0xA450, 0xA450 }, +{ 0xA451, 0xA451, 0xA451 }, +{ 0xA452, 0xA452, 0xA452 }, +{ 0xA453, 0xA453, 0xA453 }, +{ 0xA454, 0xA454, 0xA454 }, +{ 0xA455, 0xA455, 0xA455 }, +{ 0xA456, 0xA456, 0xA456 }, +{ 0xA457, 0xA457, 0xA457 }, +{ 0xA458, 0xA458, 0xA458 }, +{ 0xA459, 0xA459, 0xA459 }, +{ 0xA45A, 0xA45A, 0xA45A }, +{ 0xA45B, 0xA45B, 0xA45B }, +{ 0xA45C, 0xA45C, 0xA45C }, +{ 0xA45D, 0xA45D, 0xA45D }, +{ 0xA45E, 0xA45E, 0xA45E }, +{ 0xA45F, 0xA45F, 0xA45F }, +{ 0xA460, 0xA460, 0xA460 }, +{ 0xA461, 0xA461, 0xA461 }, +{ 0xA462, 0xA462, 0xA462 }, +{ 0xA463, 0xA463, 0xA463 }, +{ 0xA464, 0xA464, 0xA464 }, +{ 0xA465, 0xA465, 0xA465 }, +{ 0xA466, 0xA466, 0xA466 }, +{ 0xA467, 0xA467, 0xA467 }, +{ 0xA468, 0xA468, 0xA468 }, +{ 0xA469, 0xA469, 0xA469 }, +{ 0xA46A, 0xA46A, 0xA46A }, +{ 0xA46B, 0xA46B, 0xA46B }, +{ 0xA46C, 0xA46C, 0xA46C }, +{ 0xA46D, 0xA46D, 0xA46D }, +{ 0xA46E, 0xA46E, 0xA46E }, +{ 0xA46F, 0xA46F, 0xA46F }, +{ 0xA470, 0xA470, 0xA470 }, +{ 0xA471, 0xA471, 0xA471 }, +{ 0xA472, 0xA472, 0xA472 }, +{ 0xA473, 0xA473, 0xA473 }, +{ 0xA474, 0xA474, 0xA474 }, +{ 0xA475, 0xA475, 0xA475 }, +{ 0xA476, 0xA476, 0xA476 }, +{ 0xA477, 0xA477, 0xA477 }, +{ 0xA478, 0xA478, 0xA478 }, +{ 0xA479, 0xA479, 0xA479 }, +{ 0xA47A, 0xA47A, 0xA47A }, +{ 0xA47B, 0xA47B, 0xA47B }, +{ 0xA47C, 0xA47C, 0xA47C }, +{ 0xA47D, 0xA47D, 0xA47D }, +{ 0xA47E, 0xA47E, 0xA47E }, +{ 0xA47F, 0xA47F, 0xA47F }, +{ 0xA480, 0xA480, 0xA480 }, +{ 0xA481, 0xA481, 0xA481 }, +{ 0xA482, 0xA482, 0xA482 }, +{ 0xA483, 0xA483, 0xA483 }, +{ 0xA484, 0xA484, 0xA484 }, +{ 0xA485, 0xA485, 0xA485 }, +{ 0xA486, 0xA486, 0xA486 }, +{ 0xA487, 0xA487, 0xA487 }, +{ 0xA488, 0xA488, 0xA488 }, +{ 0xA489, 0xA489, 0xA489 }, +{ 0xA48A, 0xA48A, 0xA48A }, +{ 0xA48B, 0xA48B, 0xA48B }, +{ 0xA48C, 0xA48C, 0xA48C }, +{ 0xA800, 0xA800, 0xA800 }, +{ 0xA801, 0xA801, 0xA801 }, +{ 0xA803, 0xA803, 0xA803 }, +{ 0xA804, 0xA804, 0xA804 }, +{ 0xA805, 0xA805, 0xA805 }, +{ 0xA806, 0xA806, 0xA806 }, +{ 0xA807, 0xA807, 0xA807 }, +{ 0xA808, 0xA808, 0xA808 }, +{ 0xA809, 0xA809, 0xA809 }, +{ 0xA80A, 0xA80A, 0xA80A }, +{ 0xA80B, 0xA80B, 0xA80B }, +{ 0xA80C, 0xA80C, 0xA80C }, +{ 0xA80D, 0xA80D, 0xA80D }, +{ 0xA80E, 0xA80E, 0xA80E }, +{ 0xA80F, 0xA80F, 0xA80F }, +{ 0xA810, 0xA810, 0xA810 }, +{ 0xA811, 0xA811, 0xA811 }, +{ 0xA812, 0xA812, 0xA812 }, +{ 0xA813, 0xA813, 0xA813 }, +{ 0xA814, 0xA814, 0xA814 }, +{ 0xA815, 0xA815, 0xA815 }, +{ 0xA816, 0xA816, 0xA816 }, +{ 0xA817, 0xA817, 0xA817 }, +{ 0xA818, 0xA818, 0xA818 }, +{ 0xA819, 0xA819, 0xA819 }, +{ 0xA81A, 0xA81A, 0xA81A }, +{ 0xA81B, 0xA81B, 0xA81B }, +{ 0xA81C, 0xA81C, 0xA81C }, +{ 0xA81D, 0xA81D, 0xA81D }, +{ 0xA81E, 0xA81E, 0xA81E }, +{ 0xA81F, 0xA81F, 0xA81F }, +{ 0xA820, 0xA820, 0xA820 }, +{ 0xA821, 0xA821, 0xA821 }, +{ 0xA822, 0xA822, 0xA822 }, +{ 0xA825, 0xA825, 0xA825 }, +{ 0xA826, 0xA826, 0xA826 }, +{ 0xAC00, 0xAC00, 0xAC00 }, +{ 0xAC01, 0xAC01, 0xAC01 }, +{ 0xAC02, 0xAC02, 0xAC02 }, +{ 0xAC03, 0xAC03, 0xAC03 }, +{ 0xAC04, 0xAC04, 0xAC04 }, +{ 0xAC05, 0xAC05, 0xAC05 }, +{ 0xAC06, 0xAC06, 0xAC06 }, +{ 0xAC07, 0xAC07, 0xAC07 }, +{ 0xAC08, 0xAC08, 0xAC08 }, +{ 0xAC09, 0xAC09, 0xAC09 }, +{ 0xAC0A, 0xAC0A, 0xAC0A }, +{ 0xAC0B, 0xAC0B, 0xAC0B }, +{ 0xAC0C, 0xAC0C, 0xAC0C }, +{ 0xAC0D, 0xAC0D, 0xAC0D }, +{ 0xAC0E, 0xAC0E, 0xAC0E }, +{ 0xAC0F, 0xAC0F, 0xAC0F }, +{ 0xAC10, 0xAC10, 0xAC10 }, +{ 0xAC11, 0xAC11, 0xAC11 }, +{ 0xAC12, 0xAC12, 0xAC12 }, +{ 0xAC13, 0xAC13, 0xAC13 }, +{ 0xAC14, 0xAC14, 0xAC14 }, +{ 0xAC15, 0xAC15, 0xAC15 }, +{ 0xAC16, 0xAC16, 0xAC16 }, +{ 0xAC17, 0xAC17, 0xAC17 }, +{ 0xAC18, 0xAC18, 0xAC18 }, +{ 0xAC19, 0xAC19, 0xAC19 }, +{ 0xAC1A, 0xAC1A, 0xAC1A }, +{ 0xAC1B, 0xAC1B, 0xAC1B }, +{ 0xAC1C, 0xAC1C, 0xAC1C }, +{ 0xAC1D, 0xAC1D, 0xAC1D }, +{ 0xAC1E, 0xAC1E, 0xAC1E }, +{ 0xAC1F, 0xAC1F, 0xAC1F }, +{ 0xAC20, 0xAC20, 0xAC20 }, +{ 0xAC21, 0xAC21, 0xAC21 }, +{ 0xAC22, 0xAC22, 0xAC22 }, +{ 0xAC23, 0xAC23, 0xAC23 }, +{ 0xAC24, 0xAC24, 0xAC24 }, +{ 0xAC25, 0xAC25, 0xAC25 }, +{ 0xAC26, 0xAC26, 0xAC26 }, +{ 0xAC27, 0xAC27, 0xAC27 }, +{ 0xAC28, 0xAC28, 0xAC28 }, +{ 0xAC29, 0xAC29, 0xAC29 }, +{ 0xAC2A, 0xAC2A, 0xAC2A }, +{ 0xAC2B, 0xAC2B, 0xAC2B }, +{ 0xAC2C, 0xAC2C, 0xAC2C }, +{ 0xAC2D, 0xAC2D, 0xAC2D }, +{ 0xAC2E, 0xAC2E, 0xAC2E }, +{ 0xAC2F, 0xAC2F, 0xAC2F }, +{ 0xAC30, 0xAC30, 0xAC30 }, +{ 0xAC31, 0xAC31, 0xAC31 }, +{ 0xAC32, 0xAC32, 0xAC32 }, +{ 0xAC33, 0xAC33, 0xAC33 }, +{ 0xAC34, 0xAC34, 0xAC34 }, +{ 0xAC35, 0xAC35, 0xAC35 }, +{ 0xAC36, 0xAC36, 0xAC36 }, +{ 0xAC37, 0xAC37, 0xAC37 }, +{ 0xAC38, 0xAC38, 0xAC38 }, +{ 0xAC39, 0xAC39, 0xAC39 }, +{ 0xAC3A, 0xAC3A, 0xAC3A }, +{ 0xAC3B, 0xAC3B, 0xAC3B }, +{ 0xAC3C, 0xAC3C, 0xAC3C }, +{ 0xAC3D, 0xAC3D, 0xAC3D }, +{ 0xAC3E, 0xAC3E, 0xAC3E }, +{ 0xAC3F, 0xAC3F, 0xAC3F }, +{ 0xAC40, 0xAC40, 0xAC40 }, +{ 0xAC41, 0xAC41, 0xAC41 }, +{ 0xAC42, 0xAC42, 0xAC42 }, +{ 0xAC43, 0xAC43, 0xAC43 }, +{ 0xAC44, 0xAC44, 0xAC44 }, +{ 0xAC45, 0xAC45, 0xAC45 }, +{ 0xAC46, 0xAC46, 0xAC46 }, +{ 0xAC47, 0xAC47, 0xAC47 }, +{ 0xAC48, 0xAC48, 0xAC48 }, +{ 0xAC49, 0xAC49, 0xAC49 }, +{ 0xAC4A, 0xAC4A, 0xAC4A }, +{ 0xAC4B, 0xAC4B, 0xAC4B }, +{ 0xAC4C, 0xAC4C, 0xAC4C }, +{ 0xAC4D, 0xAC4D, 0xAC4D }, +{ 0xAC4E, 0xAC4E, 0xAC4E }, +{ 0xAC4F, 0xAC4F, 0xAC4F }, +{ 0xAC50, 0xAC50, 0xAC50 }, +{ 0xAC51, 0xAC51, 0xAC51 }, +{ 0xAC52, 0xAC52, 0xAC52 }, +{ 0xAC53, 0xAC53, 0xAC53 }, +{ 0xAC54, 0xAC54, 0xAC54 }, +{ 0xAC55, 0xAC55, 0xAC55 }, +{ 0xAC56, 0xAC56, 0xAC56 }, +{ 0xAC57, 0xAC57, 0xAC57 }, +{ 0xAC58, 0xAC58, 0xAC58 }, +{ 0xAC59, 0xAC59, 0xAC59 }, +{ 0xAC5A, 0xAC5A, 0xAC5A }, +{ 0xAC5B, 0xAC5B, 0xAC5B }, +{ 0xAC5C, 0xAC5C, 0xAC5C }, +{ 0xAC5D, 0xAC5D, 0xAC5D }, +{ 0xAC5E, 0xAC5E, 0xAC5E }, +{ 0xAC5F, 0xAC5F, 0xAC5F }, +{ 0xAC60, 0xAC60, 0xAC60 }, +{ 0xAC61, 0xAC61, 0xAC61 }, +{ 0xAC62, 0xAC62, 0xAC62 }, +{ 0xAC63, 0xAC63, 0xAC63 }, +{ 0xAC64, 0xAC64, 0xAC64 }, +{ 0xAC65, 0xAC65, 0xAC65 }, +{ 0xAC66, 0xAC66, 0xAC66 }, +{ 0xAC67, 0xAC67, 0xAC67 }, +{ 0xAC68, 0xAC68, 0xAC68 }, +{ 0xAC69, 0xAC69, 0xAC69 }, +{ 0xAC6A, 0xAC6A, 0xAC6A }, +{ 0xAC6B, 0xAC6B, 0xAC6B }, +{ 0xAC6C, 0xAC6C, 0xAC6C }, +{ 0xAC6D, 0xAC6D, 0xAC6D }, +{ 0xAC6E, 0xAC6E, 0xAC6E }, +{ 0xAC6F, 0xAC6F, 0xAC6F }, +{ 0xAC70, 0xAC70, 0xAC70 }, +{ 0xAC71, 0xAC71, 0xAC71 }, +{ 0xAC72, 0xAC72, 0xAC72 }, +{ 0xAC73, 0xAC73, 0xAC73 }, +{ 0xAC74, 0xAC74, 0xAC74 }, +{ 0xAC75, 0xAC75, 0xAC75 }, +{ 0xAC76, 0xAC76, 0xAC76 }, +{ 0xAC77, 0xAC77, 0xAC77 }, +{ 0xAC78, 0xAC78, 0xAC78 }, +{ 0xAC79, 0xAC79, 0xAC79 }, +{ 0xAC7A, 0xAC7A, 0xAC7A }, +{ 0xAC7B, 0xAC7B, 0xAC7B }, +{ 0xAC7C, 0xAC7C, 0xAC7C }, +{ 0xAC7D, 0xAC7D, 0xAC7D }, +{ 0xAC7E, 0xAC7E, 0xAC7E }, +{ 0xAC7F, 0xAC7F, 0xAC7F }, +{ 0xAC80, 0xAC80, 0xAC80 }, +{ 0xAC81, 0xAC81, 0xAC81 }, +{ 0xAC82, 0xAC82, 0xAC82 }, +{ 0xAC83, 0xAC83, 0xAC83 }, +{ 0xAC84, 0xAC84, 0xAC84 }, +{ 0xAC85, 0xAC85, 0xAC85 }, +{ 0xAC86, 0xAC86, 0xAC86 }, +{ 0xAC87, 0xAC87, 0xAC87 }, +{ 0xAC88, 0xAC88, 0xAC88 }, +{ 0xAC89, 0xAC89, 0xAC89 }, +{ 0xAC8A, 0xAC8A, 0xAC8A }, +{ 0xAC8B, 0xAC8B, 0xAC8B }, +{ 0xAC8C, 0xAC8C, 0xAC8C }, +{ 0xAC8D, 0xAC8D, 0xAC8D }, +{ 0xAC8E, 0xAC8E, 0xAC8E }, +{ 0xAC8F, 0xAC8F, 0xAC8F }, +{ 0xAC90, 0xAC90, 0xAC90 }, +{ 0xAC91, 0xAC91, 0xAC91 }, +{ 0xAC92, 0xAC92, 0xAC92 }, +{ 0xAC93, 0xAC93, 0xAC93 }, +{ 0xAC94, 0xAC94, 0xAC94 }, +{ 0xAC95, 0xAC95, 0xAC95 }, +{ 0xAC96, 0xAC96, 0xAC96 }, +{ 0xAC97, 0xAC97, 0xAC97 }, +{ 0xAC98, 0xAC98, 0xAC98 }, +{ 0xAC99, 0xAC99, 0xAC99 }, +{ 0xAC9A, 0xAC9A, 0xAC9A }, +{ 0xAC9B, 0xAC9B, 0xAC9B }, +{ 0xAC9C, 0xAC9C, 0xAC9C }, +{ 0xAC9D, 0xAC9D, 0xAC9D }, +{ 0xAC9E, 0xAC9E, 0xAC9E }, +{ 0xAC9F, 0xAC9F, 0xAC9F }, +{ 0xACA0, 0xACA0, 0xACA0 }, +{ 0xACA1, 0xACA1, 0xACA1 }, +{ 0xACA2, 0xACA2, 0xACA2 }, +{ 0xACA3, 0xACA3, 0xACA3 }, +{ 0xACA4, 0xACA4, 0xACA4 }, +{ 0xACA5, 0xACA5, 0xACA5 }, +{ 0xACA6, 0xACA6, 0xACA6 }, +{ 0xACA7, 0xACA7, 0xACA7 }, +{ 0xACA8, 0xACA8, 0xACA8 }, +{ 0xACA9, 0xACA9, 0xACA9 }, +{ 0xACAA, 0xACAA, 0xACAA }, +{ 0xACAB, 0xACAB, 0xACAB }, +{ 0xACAC, 0xACAC, 0xACAC }, +{ 0xACAD, 0xACAD, 0xACAD }, +{ 0xACAE, 0xACAE, 0xACAE }, +{ 0xACAF, 0xACAF, 0xACAF }, +{ 0xACB0, 0xACB0, 0xACB0 }, +{ 0xACB1, 0xACB1, 0xACB1 }, +{ 0xACB2, 0xACB2, 0xACB2 }, +{ 0xACB3, 0xACB3, 0xACB3 }, +{ 0xACB4, 0xACB4, 0xACB4 }, +{ 0xACB5, 0xACB5, 0xACB5 }, +{ 0xACB6, 0xACB6, 0xACB6 }, +{ 0xACB7, 0xACB7, 0xACB7 }, +{ 0xACB8, 0xACB8, 0xACB8 }, +{ 0xACB9, 0xACB9, 0xACB9 }, +{ 0xACBA, 0xACBA, 0xACBA }, +{ 0xACBB, 0xACBB, 0xACBB }, +{ 0xACBC, 0xACBC, 0xACBC }, +{ 0xACBD, 0xACBD, 0xACBD }, +{ 0xACBE, 0xACBE, 0xACBE }, +{ 0xACBF, 0xACBF, 0xACBF }, +{ 0xACC0, 0xACC0, 0xACC0 }, +{ 0xACC1, 0xACC1, 0xACC1 }, +{ 0xACC2, 0xACC2, 0xACC2 }, +{ 0xACC3, 0xACC3, 0xACC3 }, +{ 0xACC4, 0xACC4, 0xACC4 }, +{ 0xACC5, 0xACC5, 0xACC5 }, +{ 0xACC6, 0xACC6, 0xACC6 }, +{ 0xACC7, 0xACC7, 0xACC7 }, +{ 0xACC8, 0xACC8, 0xACC8 }, +{ 0xACC9, 0xACC9, 0xACC9 }, +{ 0xACCA, 0xACCA, 0xACCA }, +{ 0xACCB, 0xACCB, 0xACCB }, +{ 0xACCC, 0xACCC, 0xACCC }, +{ 0xACCD, 0xACCD, 0xACCD }, +{ 0xACCE, 0xACCE, 0xACCE }, +{ 0xACCF, 0xACCF, 0xACCF }, +{ 0xACD0, 0xACD0, 0xACD0 }, +{ 0xACD1, 0xACD1, 0xACD1 }, +{ 0xACD2, 0xACD2, 0xACD2 }, +{ 0xACD3, 0xACD3, 0xACD3 }, +{ 0xACD4, 0xACD4, 0xACD4 }, +{ 0xACD5, 0xACD5, 0xACD5 }, +{ 0xACD6, 0xACD6, 0xACD6 }, +{ 0xACD7, 0xACD7, 0xACD7 }, +{ 0xACD8, 0xACD8, 0xACD8 }, +{ 0xACD9, 0xACD9, 0xACD9 }, +{ 0xACDA, 0xACDA, 0xACDA }, +{ 0xACDB, 0xACDB, 0xACDB }, +{ 0xACDC, 0xACDC, 0xACDC }, +{ 0xACDD, 0xACDD, 0xACDD }, +{ 0xACDE, 0xACDE, 0xACDE }, +{ 0xACDF, 0xACDF, 0xACDF }, +{ 0xACE0, 0xACE0, 0xACE0 }, +{ 0xACE1, 0xACE1, 0xACE1 }, +{ 0xACE2, 0xACE2, 0xACE2 }, +{ 0xACE3, 0xACE3, 0xACE3 }, +{ 0xACE4, 0xACE4, 0xACE4 }, +{ 0xACE5, 0xACE5, 0xACE5 }, +{ 0xACE6, 0xACE6, 0xACE6 }, +{ 0xACE7, 0xACE7, 0xACE7 }, +{ 0xACE8, 0xACE8, 0xACE8 }, +{ 0xACE9, 0xACE9, 0xACE9 }, +{ 0xACEA, 0xACEA, 0xACEA }, +{ 0xACEB, 0xACEB, 0xACEB }, +{ 0xACEC, 0xACEC, 0xACEC }, +{ 0xACED, 0xACED, 0xACED }, +{ 0xACEE, 0xACEE, 0xACEE }, +{ 0xACEF, 0xACEF, 0xACEF }, +{ 0xACF0, 0xACF0, 0xACF0 }, +{ 0xACF1, 0xACF1, 0xACF1 }, +{ 0xACF2, 0xACF2, 0xACF2 }, +{ 0xACF3, 0xACF3, 0xACF3 }, +{ 0xACF4, 0xACF4, 0xACF4 }, +{ 0xACF5, 0xACF5, 0xACF5 }, +{ 0xACF6, 0xACF6, 0xACF6 }, +{ 0xACF7, 0xACF7, 0xACF7 }, +{ 0xACF8, 0xACF8, 0xACF8 }, +{ 0xACF9, 0xACF9, 0xACF9 }, +{ 0xACFA, 0xACFA, 0xACFA }, +{ 0xACFB, 0xACFB, 0xACFB }, +{ 0xACFC, 0xACFC, 0xACFC }, +{ 0xACFD, 0xACFD, 0xACFD }, +{ 0xACFE, 0xACFE, 0xACFE }, +{ 0xACFF, 0xACFF, 0xACFF }, +{ 0xAD00, 0xAD00, 0xAD00 }, +{ 0xAD01, 0xAD01, 0xAD01 }, +{ 0xAD02, 0xAD02, 0xAD02 }, +{ 0xAD03, 0xAD03, 0xAD03 }, +{ 0xAD04, 0xAD04, 0xAD04 }, +{ 0xAD05, 0xAD05, 0xAD05 }, +{ 0xAD06, 0xAD06, 0xAD06 }, +{ 0xAD07, 0xAD07, 0xAD07 }, +{ 0xAD08, 0xAD08, 0xAD08 }, +{ 0xAD09, 0xAD09, 0xAD09 }, +{ 0xAD0A, 0xAD0A, 0xAD0A }, +{ 0xAD0B, 0xAD0B, 0xAD0B }, +{ 0xAD0C, 0xAD0C, 0xAD0C }, +{ 0xAD0D, 0xAD0D, 0xAD0D }, +{ 0xAD0E, 0xAD0E, 0xAD0E }, +{ 0xAD0F, 0xAD0F, 0xAD0F }, +{ 0xAD10, 0xAD10, 0xAD10 }, +{ 0xAD11, 0xAD11, 0xAD11 }, +{ 0xAD12, 0xAD12, 0xAD12 }, +{ 0xAD13, 0xAD13, 0xAD13 }, +{ 0xAD14, 0xAD14, 0xAD14 }, +{ 0xAD15, 0xAD15, 0xAD15 }, +{ 0xAD16, 0xAD16, 0xAD16 }, +{ 0xAD17, 0xAD17, 0xAD17 }, +{ 0xAD18, 0xAD18, 0xAD18 }, +{ 0xAD19, 0xAD19, 0xAD19 }, +{ 0xAD1A, 0xAD1A, 0xAD1A }, +{ 0xAD1B, 0xAD1B, 0xAD1B }, +{ 0xAD1C, 0xAD1C, 0xAD1C }, +{ 0xAD1D, 0xAD1D, 0xAD1D }, +{ 0xAD1E, 0xAD1E, 0xAD1E }, +{ 0xAD1F, 0xAD1F, 0xAD1F }, +{ 0xAD20, 0xAD20, 0xAD20 }, +{ 0xAD21, 0xAD21, 0xAD21 }, +{ 0xAD22, 0xAD22, 0xAD22 }, +{ 0xAD23, 0xAD23, 0xAD23 }, +{ 0xAD24, 0xAD24, 0xAD24 }, +{ 0xAD25, 0xAD25, 0xAD25 }, +{ 0xAD26, 0xAD26, 0xAD26 }, +{ 0xAD27, 0xAD27, 0xAD27 }, +{ 0xAD28, 0xAD28, 0xAD28 }, +{ 0xAD29, 0xAD29, 0xAD29 }, +{ 0xAD2A, 0xAD2A, 0xAD2A }, +{ 0xAD2B, 0xAD2B, 0xAD2B }, +{ 0xAD2C, 0xAD2C, 0xAD2C }, +{ 0xAD2D, 0xAD2D, 0xAD2D }, +{ 0xAD2E, 0xAD2E, 0xAD2E }, +{ 0xAD2F, 0xAD2F, 0xAD2F }, +{ 0xAD30, 0xAD30, 0xAD30 }, +{ 0xAD31, 0xAD31, 0xAD31 }, +{ 0xAD32, 0xAD32, 0xAD32 }, +{ 0xAD33, 0xAD33, 0xAD33 }, +{ 0xAD34, 0xAD34, 0xAD34 }, +{ 0xAD35, 0xAD35, 0xAD35 }, +{ 0xAD36, 0xAD36, 0xAD36 }, +{ 0xAD37, 0xAD37, 0xAD37 }, +{ 0xAD38, 0xAD38, 0xAD38 }, +{ 0xAD39, 0xAD39, 0xAD39 }, +{ 0xAD3A, 0xAD3A, 0xAD3A }, +{ 0xAD3B, 0xAD3B, 0xAD3B }, +{ 0xAD3C, 0xAD3C, 0xAD3C }, +{ 0xAD3D, 0xAD3D, 0xAD3D }, +{ 0xAD3E, 0xAD3E, 0xAD3E }, +{ 0xAD3F, 0xAD3F, 0xAD3F }, +{ 0xAD40, 0xAD40, 0xAD40 }, +{ 0xAD41, 0xAD41, 0xAD41 }, +{ 0xAD42, 0xAD42, 0xAD42 }, +{ 0xAD43, 0xAD43, 0xAD43 }, +{ 0xAD44, 0xAD44, 0xAD44 }, +{ 0xAD45, 0xAD45, 0xAD45 }, +{ 0xAD46, 0xAD46, 0xAD46 }, +{ 0xAD47, 0xAD47, 0xAD47 }, +{ 0xAD48, 0xAD48, 0xAD48 }, +{ 0xAD49, 0xAD49, 0xAD49 }, +{ 0xAD4A, 0xAD4A, 0xAD4A }, +{ 0xAD4B, 0xAD4B, 0xAD4B }, +{ 0xAD4C, 0xAD4C, 0xAD4C }, +{ 0xAD4D, 0xAD4D, 0xAD4D }, +{ 0xAD4E, 0xAD4E, 0xAD4E }, +{ 0xAD4F, 0xAD4F, 0xAD4F }, +{ 0xAD50, 0xAD50, 0xAD50 }, +{ 0xAD51, 0xAD51, 0xAD51 }, +{ 0xAD52, 0xAD52, 0xAD52 }, +{ 0xAD53, 0xAD53, 0xAD53 }, +{ 0xAD54, 0xAD54, 0xAD54 }, +{ 0xAD55, 0xAD55, 0xAD55 }, +{ 0xAD56, 0xAD56, 0xAD56 }, +{ 0xAD57, 0xAD57, 0xAD57 }, +{ 0xAD58, 0xAD58, 0xAD58 }, +{ 0xAD59, 0xAD59, 0xAD59 }, +{ 0xAD5A, 0xAD5A, 0xAD5A }, +{ 0xAD5B, 0xAD5B, 0xAD5B }, +{ 0xAD5C, 0xAD5C, 0xAD5C }, +{ 0xAD5D, 0xAD5D, 0xAD5D }, +{ 0xAD5E, 0xAD5E, 0xAD5E }, +{ 0xAD5F, 0xAD5F, 0xAD5F }, +{ 0xAD60, 0xAD60, 0xAD60 }, +{ 0xAD61, 0xAD61, 0xAD61 }, +{ 0xAD62, 0xAD62, 0xAD62 }, +{ 0xAD63, 0xAD63, 0xAD63 }, +{ 0xAD64, 0xAD64, 0xAD64 }, +{ 0xAD65, 0xAD65, 0xAD65 }, +{ 0xAD66, 0xAD66, 0xAD66 }, +{ 0xAD67, 0xAD67, 0xAD67 }, +{ 0xAD68, 0xAD68, 0xAD68 }, +{ 0xAD69, 0xAD69, 0xAD69 }, +{ 0xAD6A, 0xAD6A, 0xAD6A }, +{ 0xAD6B, 0xAD6B, 0xAD6B }, +{ 0xAD6C, 0xAD6C, 0xAD6C }, +{ 0xAD6D, 0xAD6D, 0xAD6D }, +{ 0xAD6E, 0xAD6E, 0xAD6E }, +{ 0xAD6F, 0xAD6F, 0xAD6F }, +{ 0xAD70, 0xAD70, 0xAD70 }, +{ 0xAD71, 0xAD71, 0xAD71 }, +{ 0xAD72, 0xAD72, 0xAD72 }, +{ 0xAD73, 0xAD73, 0xAD73 }, +{ 0xAD74, 0xAD74, 0xAD74 }, +{ 0xAD75, 0xAD75, 0xAD75 }, +{ 0xAD76, 0xAD76, 0xAD76 }, +{ 0xAD77, 0xAD77, 0xAD77 }, +{ 0xAD78, 0xAD78, 0xAD78 }, +{ 0xAD79, 0xAD79, 0xAD79 }, +{ 0xAD7A, 0xAD7A, 0xAD7A }, +{ 0xAD7B, 0xAD7B, 0xAD7B }, +{ 0xAD7C, 0xAD7C, 0xAD7C }, +{ 0xAD7D, 0xAD7D, 0xAD7D }, +{ 0xAD7E, 0xAD7E, 0xAD7E }, +{ 0xAD7F, 0xAD7F, 0xAD7F }, +{ 0xAD80, 0xAD80, 0xAD80 }, +{ 0xAD81, 0xAD81, 0xAD81 }, +{ 0xAD82, 0xAD82, 0xAD82 }, +{ 0xAD83, 0xAD83, 0xAD83 }, +{ 0xAD84, 0xAD84, 0xAD84 }, +{ 0xAD85, 0xAD85, 0xAD85 }, +{ 0xAD86, 0xAD86, 0xAD86 }, +{ 0xAD87, 0xAD87, 0xAD87 }, +{ 0xAD88, 0xAD88, 0xAD88 }, +{ 0xAD89, 0xAD89, 0xAD89 }, +{ 0xAD8A, 0xAD8A, 0xAD8A }, +{ 0xAD8B, 0xAD8B, 0xAD8B }, +{ 0xAD8C, 0xAD8C, 0xAD8C }, +{ 0xAD8D, 0xAD8D, 0xAD8D }, +{ 0xAD8E, 0xAD8E, 0xAD8E }, +{ 0xAD8F, 0xAD8F, 0xAD8F }, +{ 0xAD90, 0xAD90, 0xAD90 }, +{ 0xAD91, 0xAD91, 0xAD91 }, +{ 0xAD92, 0xAD92, 0xAD92 }, +{ 0xAD93, 0xAD93, 0xAD93 }, +{ 0xAD94, 0xAD94, 0xAD94 }, +{ 0xAD95, 0xAD95, 0xAD95 }, +{ 0xAD96, 0xAD96, 0xAD96 }, +{ 0xAD97, 0xAD97, 0xAD97 }, +{ 0xAD98, 0xAD98, 0xAD98 }, +{ 0xAD99, 0xAD99, 0xAD99 }, +{ 0xAD9A, 0xAD9A, 0xAD9A }, +{ 0xAD9B, 0xAD9B, 0xAD9B }, +{ 0xAD9C, 0xAD9C, 0xAD9C }, +{ 0xAD9D, 0xAD9D, 0xAD9D }, +{ 0xAD9E, 0xAD9E, 0xAD9E }, +{ 0xAD9F, 0xAD9F, 0xAD9F }, +{ 0xADA0, 0xADA0, 0xADA0 }, +{ 0xADA1, 0xADA1, 0xADA1 }, +{ 0xADA2, 0xADA2, 0xADA2 }, +{ 0xADA3, 0xADA3, 0xADA3 }, +{ 0xADA4, 0xADA4, 0xADA4 }, +{ 0xADA5, 0xADA5, 0xADA5 }, +{ 0xADA6, 0xADA6, 0xADA6 }, +{ 0xADA7, 0xADA7, 0xADA7 }, +{ 0xADA8, 0xADA8, 0xADA8 }, +{ 0xADA9, 0xADA9, 0xADA9 }, +{ 0xADAA, 0xADAA, 0xADAA }, +{ 0xADAB, 0xADAB, 0xADAB }, +{ 0xADAC, 0xADAC, 0xADAC }, +{ 0xADAD, 0xADAD, 0xADAD }, +{ 0xADAE, 0xADAE, 0xADAE }, +{ 0xADAF, 0xADAF, 0xADAF }, +{ 0xADB0, 0xADB0, 0xADB0 }, +{ 0xADB1, 0xADB1, 0xADB1 }, +{ 0xADB2, 0xADB2, 0xADB2 }, +{ 0xADB3, 0xADB3, 0xADB3 }, +{ 0xADB4, 0xADB4, 0xADB4 }, +{ 0xADB5, 0xADB5, 0xADB5 }, +{ 0xADB6, 0xADB6, 0xADB6 }, +{ 0xADB7, 0xADB7, 0xADB7 }, +{ 0xADB8, 0xADB8, 0xADB8 }, +{ 0xADB9, 0xADB9, 0xADB9 }, +{ 0xADBA, 0xADBA, 0xADBA }, +{ 0xADBB, 0xADBB, 0xADBB }, +{ 0xADBC, 0xADBC, 0xADBC }, +{ 0xADBD, 0xADBD, 0xADBD }, +{ 0xADBE, 0xADBE, 0xADBE }, +{ 0xADBF, 0xADBF, 0xADBF }, +{ 0xADC0, 0xADC0, 0xADC0 }, +{ 0xADC1, 0xADC1, 0xADC1 }, +{ 0xADC2, 0xADC2, 0xADC2 }, +{ 0xADC3, 0xADC3, 0xADC3 }, +{ 0xADC4, 0xADC4, 0xADC4 }, +{ 0xADC5, 0xADC5, 0xADC5 }, +{ 0xADC6, 0xADC6, 0xADC6 }, +{ 0xADC7, 0xADC7, 0xADC7 }, +{ 0xADC8, 0xADC8, 0xADC8 }, +{ 0xADC9, 0xADC9, 0xADC9 }, +{ 0xADCA, 0xADCA, 0xADCA }, +{ 0xADCB, 0xADCB, 0xADCB }, +{ 0xADCC, 0xADCC, 0xADCC }, +{ 0xADCD, 0xADCD, 0xADCD }, +{ 0xADCE, 0xADCE, 0xADCE }, +{ 0xADCF, 0xADCF, 0xADCF }, +{ 0xADD0, 0xADD0, 0xADD0 }, +{ 0xADD1, 0xADD1, 0xADD1 }, +{ 0xADD2, 0xADD2, 0xADD2 }, +{ 0xADD3, 0xADD3, 0xADD3 }, +{ 0xADD4, 0xADD4, 0xADD4 }, +{ 0xADD5, 0xADD5, 0xADD5 }, +{ 0xADD6, 0xADD6, 0xADD6 }, +{ 0xADD7, 0xADD7, 0xADD7 }, +{ 0xADD8, 0xADD8, 0xADD8 }, +{ 0xADD9, 0xADD9, 0xADD9 }, +{ 0xADDA, 0xADDA, 0xADDA }, +{ 0xADDB, 0xADDB, 0xADDB }, +{ 0xADDC, 0xADDC, 0xADDC }, +{ 0xADDD, 0xADDD, 0xADDD }, +{ 0xADDE, 0xADDE, 0xADDE }, +{ 0xADDF, 0xADDF, 0xADDF }, +{ 0xADE0, 0xADE0, 0xADE0 }, +{ 0xADE1, 0xADE1, 0xADE1 }, +{ 0xADE2, 0xADE2, 0xADE2 }, +{ 0xADE3, 0xADE3, 0xADE3 }, +{ 0xADE4, 0xADE4, 0xADE4 }, +{ 0xADE5, 0xADE5, 0xADE5 }, +{ 0xADE6, 0xADE6, 0xADE6 }, +{ 0xADE7, 0xADE7, 0xADE7 }, +{ 0xADE8, 0xADE8, 0xADE8 }, +{ 0xADE9, 0xADE9, 0xADE9 }, +{ 0xADEA, 0xADEA, 0xADEA }, +{ 0xADEB, 0xADEB, 0xADEB }, +{ 0xADEC, 0xADEC, 0xADEC }, +{ 0xADED, 0xADED, 0xADED }, +{ 0xADEE, 0xADEE, 0xADEE }, +{ 0xADEF, 0xADEF, 0xADEF }, +{ 0xADF0, 0xADF0, 0xADF0 }, +{ 0xADF1, 0xADF1, 0xADF1 }, +{ 0xADF2, 0xADF2, 0xADF2 }, +{ 0xADF3, 0xADF3, 0xADF3 }, +{ 0xADF4, 0xADF4, 0xADF4 }, +{ 0xADF5, 0xADF5, 0xADF5 }, +{ 0xADF6, 0xADF6, 0xADF6 }, +{ 0xADF7, 0xADF7, 0xADF7 }, +{ 0xADF8, 0xADF8, 0xADF8 }, +{ 0xADF9, 0xADF9, 0xADF9 }, +{ 0xADFA, 0xADFA, 0xADFA }, +{ 0xADFB, 0xADFB, 0xADFB }, +{ 0xADFC, 0xADFC, 0xADFC }, +{ 0xADFD, 0xADFD, 0xADFD }, +{ 0xADFE, 0xADFE, 0xADFE }, +{ 0xADFF, 0xADFF, 0xADFF }, +{ 0xAE00, 0xAE00, 0xAE00 }, +{ 0xAE01, 0xAE01, 0xAE01 }, +{ 0xAE02, 0xAE02, 0xAE02 }, +{ 0xAE03, 0xAE03, 0xAE03 }, +{ 0xAE04, 0xAE04, 0xAE04 }, +{ 0xAE05, 0xAE05, 0xAE05 }, +{ 0xAE06, 0xAE06, 0xAE06 }, +{ 0xAE07, 0xAE07, 0xAE07 }, +{ 0xAE08, 0xAE08, 0xAE08 }, +{ 0xAE09, 0xAE09, 0xAE09 }, +{ 0xAE0A, 0xAE0A, 0xAE0A }, +{ 0xAE0B, 0xAE0B, 0xAE0B }, +{ 0xAE0C, 0xAE0C, 0xAE0C }, +{ 0xAE0D, 0xAE0D, 0xAE0D }, +{ 0xAE0E, 0xAE0E, 0xAE0E }, +{ 0xAE0F, 0xAE0F, 0xAE0F }, +{ 0xAE10, 0xAE10, 0xAE10 }, +{ 0xAE11, 0xAE11, 0xAE11 }, +{ 0xAE12, 0xAE12, 0xAE12 }, +{ 0xAE13, 0xAE13, 0xAE13 }, +{ 0xAE14, 0xAE14, 0xAE14 }, +{ 0xAE15, 0xAE15, 0xAE15 }, +{ 0xAE16, 0xAE16, 0xAE16 }, +{ 0xAE17, 0xAE17, 0xAE17 }, +{ 0xAE18, 0xAE18, 0xAE18 }, +{ 0xAE19, 0xAE19, 0xAE19 }, +{ 0xAE1A, 0xAE1A, 0xAE1A }, +{ 0xAE1B, 0xAE1B, 0xAE1B }, +{ 0xAE1C, 0xAE1C, 0xAE1C }, +{ 0xAE1D, 0xAE1D, 0xAE1D }, +{ 0xAE1E, 0xAE1E, 0xAE1E }, +{ 0xAE1F, 0xAE1F, 0xAE1F }, +{ 0xAE20, 0xAE20, 0xAE20 }, +{ 0xAE21, 0xAE21, 0xAE21 }, +{ 0xAE22, 0xAE22, 0xAE22 }, +{ 0xAE23, 0xAE23, 0xAE23 }, +{ 0xAE24, 0xAE24, 0xAE24 }, +{ 0xAE25, 0xAE25, 0xAE25 }, +{ 0xAE26, 0xAE26, 0xAE26 }, +{ 0xAE27, 0xAE27, 0xAE27 }, +{ 0xAE28, 0xAE28, 0xAE28 }, +{ 0xAE29, 0xAE29, 0xAE29 }, +{ 0xAE2A, 0xAE2A, 0xAE2A }, +{ 0xAE2B, 0xAE2B, 0xAE2B }, +{ 0xAE2C, 0xAE2C, 0xAE2C }, +{ 0xAE2D, 0xAE2D, 0xAE2D }, +{ 0xAE2E, 0xAE2E, 0xAE2E }, +{ 0xAE2F, 0xAE2F, 0xAE2F }, +{ 0xAE30, 0xAE30, 0xAE30 }, +{ 0xAE31, 0xAE31, 0xAE31 }, +{ 0xAE32, 0xAE32, 0xAE32 }, +{ 0xAE33, 0xAE33, 0xAE33 }, +{ 0xAE34, 0xAE34, 0xAE34 }, +{ 0xAE35, 0xAE35, 0xAE35 }, +{ 0xAE36, 0xAE36, 0xAE36 }, +{ 0xAE37, 0xAE37, 0xAE37 }, +{ 0xAE38, 0xAE38, 0xAE38 }, +{ 0xAE39, 0xAE39, 0xAE39 }, +{ 0xAE3A, 0xAE3A, 0xAE3A }, +{ 0xAE3B, 0xAE3B, 0xAE3B }, +{ 0xAE3C, 0xAE3C, 0xAE3C }, +{ 0xAE3D, 0xAE3D, 0xAE3D }, +{ 0xAE3E, 0xAE3E, 0xAE3E }, +{ 0xAE3F, 0xAE3F, 0xAE3F }, +{ 0xAE40, 0xAE40, 0xAE40 }, +{ 0xAE41, 0xAE41, 0xAE41 }, +{ 0xAE42, 0xAE42, 0xAE42 }, +{ 0xAE43, 0xAE43, 0xAE43 }, +{ 0xAE44, 0xAE44, 0xAE44 }, +{ 0xAE45, 0xAE45, 0xAE45 }, +{ 0xAE46, 0xAE46, 0xAE46 }, +{ 0xAE47, 0xAE47, 0xAE47 }, +{ 0xAE48, 0xAE48, 0xAE48 }, +{ 0xAE49, 0xAE49, 0xAE49 }, +{ 0xAE4A, 0xAE4A, 0xAE4A }, +{ 0xAE4B, 0xAE4B, 0xAE4B }, +{ 0xAE4C, 0xAE4C, 0xAE4C }, +{ 0xAE4D, 0xAE4D, 0xAE4D }, +{ 0xAE4E, 0xAE4E, 0xAE4E }, +{ 0xAE4F, 0xAE4F, 0xAE4F }, +{ 0xAE50, 0xAE50, 0xAE50 }, +{ 0xAE51, 0xAE51, 0xAE51 }, +{ 0xAE52, 0xAE52, 0xAE52 }, +{ 0xAE53, 0xAE53, 0xAE53 }, +{ 0xAE54, 0xAE54, 0xAE54 }, +{ 0xAE55, 0xAE55, 0xAE55 }, +{ 0xAE56, 0xAE56, 0xAE56 }, +{ 0xAE57, 0xAE57, 0xAE57 }, +{ 0xAE58, 0xAE58, 0xAE58 }, +{ 0xAE59, 0xAE59, 0xAE59 }, +{ 0xAE5A, 0xAE5A, 0xAE5A }, +{ 0xAE5B, 0xAE5B, 0xAE5B }, +{ 0xAE5C, 0xAE5C, 0xAE5C }, +{ 0xAE5D, 0xAE5D, 0xAE5D }, +{ 0xAE5E, 0xAE5E, 0xAE5E }, +{ 0xAE5F, 0xAE5F, 0xAE5F }, +{ 0xAE60, 0xAE60, 0xAE60 }, +{ 0xAE61, 0xAE61, 0xAE61 }, +{ 0xAE62, 0xAE62, 0xAE62 }, +{ 0xAE63, 0xAE63, 0xAE63 }, +{ 0xAE64, 0xAE64, 0xAE64 }, +{ 0xAE65, 0xAE65, 0xAE65 }, +{ 0xAE66, 0xAE66, 0xAE66 }, +{ 0xAE67, 0xAE67, 0xAE67 }, +{ 0xAE68, 0xAE68, 0xAE68 }, +{ 0xAE69, 0xAE69, 0xAE69 }, +{ 0xAE6A, 0xAE6A, 0xAE6A }, +{ 0xAE6B, 0xAE6B, 0xAE6B }, +{ 0xAE6C, 0xAE6C, 0xAE6C }, +{ 0xAE6D, 0xAE6D, 0xAE6D }, +{ 0xAE6E, 0xAE6E, 0xAE6E }, +{ 0xAE6F, 0xAE6F, 0xAE6F }, +{ 0xAE70, 0xAE70, 0xAE70 }, +{ 0xAE71, 0xAE71, 0xAE71 }, +{ 0xAE72, 0xAE72, 0xAE72 }, +{ 0xAE73, 0xAE73, 0xAE73 }, +{ 0xAE74, 0xAE74, 0xAE74 }, +{ 0xAE75, 0xAE75, 0xAE75 }, +{ 0xAE76, 0xAE76, 0xAE76 }, +{ 0xAE77, 0xAE77, 0xAE77 }, +{ 0xAE78, 0xAE78, 0xAE78 }, +{ 0xAE79, 0xAE79, 0xAE79 }, +{ 0xAE7A, 0xAE7A, 0xAE7A }, +{ 0xAE7B, 0xAE7B, 0xAE7B }, +{ 0xAE7C, 0xAE7C, 0xAE7C }, +{ 0xAE7D, 0xAE7D, 0xAE7D }, +{ 0xAE7E, 0xAE7E, 0xAE7E }, +{ 0xAE7F, 0xAE7F, 0xAE7F }, +{ 0xAE80, 0xAE80, 0xAE80 }, +{ 0xAE81, 0xAE81, 0xAE81 }, +{ 0xAE82, 0xAE82, 0xAE82 }, +{ 0xAE83, 0xAE83, 0xAE83 }, +{ 0xAE84, 0xAE84, 0xAE84 }, +{ 0xAE85, 0xAE85, 0xAE85 }, +{ 0xAE86, 0xAE86, 0xAE86 }, +{ 0xAE87, 0xAE87, 0xAE87 }, +{ 0xAE88, 0xAE88, 0xAE88 }, +{ 0xAE89, 0xAE89, 0xAE89 }, +{ 0xAE8A, 0xAE8A, 0xAE8A }, +{ 0xAE8B, 0xAE8B, 0xAE8B }, +{ 0xAE8C, 0xAE8C, 0xAE8C }, +{ 0xAE8D, 0xAE8D, 0xAE8D }, +{ 0xAE8E, 0xAE8E, 0xAE8E }, +{ 0xAE8F, 0xAE8F, 0xAE8F }, +{ 0xAE90, 0xAE90, 0xAE90 }, +{ 0xAE91, 0xAE91, 0xAE91 }, +{ 0xAE92, 0xAE92, 0xAE92 }, +{ 0xAE93, 0xAE93, 0xAE93 }, +{ 0xAE94, 0xAE94, 0xAE94 }, +{ 0xAE95, 0xAE95, 0xAE95 }, +{ 0xAE96, 0xAE96, 0xAE96 }, +{ 0xAE97, 0xAE97, 0xAE97 }, +{ 0xAE98, 0xAE98, 0xAE98 }, +{ 0xAE99, 0xAE99, 0xAE99 }, +{ 0xAE9A, 0xAE9A, 0xAE9A }, +{ 0xAE9B, 0xAE9B, 0xAE9B }, +{ 0xAE9C, 0xAE9C, 0xAE9C }, +{ 0xAE9D, 0xAE9D, 0xAE9D }, +{ 0xAE9E, 0xAE9E, 0xAE9E }, +{ 0xAE9F, 0xAE9F, 0xAE9F }, +{ 0xAEA0, 0xAEA0, 0xAEA0 }, +{ 0xAEA1, 0xAEA1, 0xAEA1 }, +{ 0xAEA2, 0xAEA2, 0xAEA2 }, +{ 0xAEA3, 0xAEA3, 0xAEA3 }, +{ 0xAEA4, 0xAEA4, 0xAEA4 }, +{ 0xAEA5, 0xAEA5, 0xAEA5 }, +{ 0xAEA6, 0xAEA6, 0xAEA6 }, +{ 0xAEA7, 0xAEA7, 0xAEA7 }, +{ 0xAEA8, 0xAEA8, 0xAEA8 }, +{ 0xAEA9, 0xAEA9, 0xAEA9 }, +{ 0xAEAA, 0xAEAA, 0xAEAA }, +{ 0xAEAB, 0xAEAB, 0xAEAB }, +{ 0xAEAC, 0xAEAC, 0xAEAC }, +{ 0xAEAD, 0xAEAD, 0xAEAD }, +{ 0xAEAE, 0xAEAE, 0xAEAE }, +{ 0xAEAF, 0xAEAF, 0xAEAF }, +{ 0xAEB0, 0xAEB0, 0xAEB0 }, +{ 0xAEB1, 0xAEB1, 0xAEB1 }, +{ 0xAEB2, 0xAEB2, 0xAEB2 }, +{ 0xAEB3, 0xAEB3, 0xAEB3 }, +{ 0xAEB4, 0xAEB4, 0xAEB4 }, +{ 0xAEB5, 0xAEB5, 0xAEB5 }, +{ 0xAEB6, 0xAEB6, 0xAEB6 }, +{ 0xAEB7, 0xAEB7, 0xAEB7 }, +{ 0xAEB8, 0xAEB8, 0xAEB8 }, +{ 0xAEB9, 0xAEB9, 0xAEB9 }, +{ 0xAEBA, 0xAEBA, 0xAEBA }, +{ 0xAEBB, 0xAEBB, 0xAEBB }, +{ 0xAEBC, 0xAEBC, 0xAEBC }, +{ 0xAEBD, 0xAEBD, 0xAEBD }, +{ 0xAEBE, 0xAEBE, 0xAEBE }, +{ 0xAEBF, 0xAEBF, 0xAEBF }, +{ 0xAEC0, 0xAEC0, 0xAEC0 }, +{ 0xAEC1, 0xAEC1, 0xAEC1 }, +{ 0xAEC2, 0xAEC2, 0xAEC2 }, +{ 0xAEC3, 0xAEC3, 0xAEC3 }, +{ 0xAEC4, 0xAEC4, 0xAEC4 }, +{ 0xAEC5, 0xAEC5, 0xAEC5 }, +{ 0xAEC6, 0xAEC6, 0xAEC6 }, +{ 0xAEC7, 0xAEC7, 0xAEC7 }, +{ 0xAEC8, 0xAEC8, 0xAEC8 }, +{ 0xAEC9, 0xAEC9, 0xAEC9 }, +{ 0xAECA, 0xAECA, 0xAECA }, +{ 0xAECB, 0xAECB, 0xAECB }, +{ 0xAECC, 0xAECC, 0xAECC }, +{ 0xAECD, 0xAECD, 0xAECD }, +{ 0xAECE, 0xAECE, 0xAECE }, +{ 0xAECF, 0xAECF, 0xAECF }, +{ 0xAED0, 0xAED0, 0xAED0 }, +{ 0xAED1, 0xAED1, 0xAED1 }, +{ 0xAED2, 0xAED2, 0xAED2 }, +{ 0xAED3, 0xAED3, 0xAED3 }, +{ 0xAED4, 0xAED4, 0xAED4 }, +{ 0xAED5, 0xAED5, 0xAED5 }, +{ 0xAED6, 0xAED6, 0xAED6 }, +{ 0xAED7, 0xAED7, 0xAED7 }, +{ 0xAED8, 0xAED8, 0xAED8 }, +{ 0xAED9, 0xAED9, 0xAED9 }, +{ 0xAEDA, 0xAEDA, 0xAEDA }, +{ 0xAEDB, 0xAEDB, 0xAEDB }, +{ 0xAEDC, 0xAEDC, 0xAEDC }, +{ 0xAEDD, 0xAEDD, 0xAEDD }, +{ 0xAEDE, 0xAEDE, 0xAEDE }, +{ 0xAEDF, 0xAEDF, 0xAEDF }, +{ 0xAEE0, 0xAEE0, 0xAEE0 }, +{ 0xAEE1, 0xAEE1, 0xAEE1 }, +{ 0xAEE2, 0xAEE2, 0xAEE2 }, +{ 0xAEE3, 0xAEE3, 0xAEE3 }, +{ 0xAEE4, 0xAEE4, 0xAEE4 }, +{ 0xAEE5, 0xAEE5, 0xAEE5 }, +{ 0xAEE6, 0xAEE6, 0xAEE6 }, +{ 0xAEE7, 0xAEE7, 0xAEE7 }, +{ 0xAEE8, 0xAEE8, 0xAEE8 }, +{ 0xAEE9, 0xAEE9, 0xAEE9 }, +{ 0xAEEA, 0xAEEA, 0xAEEA }, +{ 0xAEEB, 0xAEEB, 0xAEEB }, +{ 0xAEEC, 0xAEEC, 0xAEEC }, +{ 0xAEED, 0xAEED, 0xAEED }, +{ 0xAEEE, 0xAEEE, 0xAEEE }, +{ 0xAEEF, 0xAEEF, 0xAEEF }, +{ 0xAEF0, 0xAEF0, 0xAEF0 }, +{ 0xAEF1, 0xAEF1, 0xAEF1 }, +{ 0xAEF2, 0xAEF2, 0xAEF2 }, +{ 0xAEF3, 0xAEF3, 0xAEF3 }, +{ 0xAEF4, 0xAEF4, 0xAEF4 }, +{ 0xAEF5, 0xAEF5, 0xAEF5 }, +{ 0xAEF6, 0xAEF6, 0xAEF6 }, +{ 0xAEF7, 0xAEF7, 0xAEF7 }, +{ 0xAEF8, 0xAEF8, 0xAEF8 }, +{ 0xAEF9, 0xAEF9, 0xAEF9 }, +{ 0xAEFA, 0xAEFA, 0xAEFA }, +{ 0xAEFB, 0xAEFB, 0xAEFB }, +{ 0xAEFC, 0xAEFC, 0xAEFC }, +{ 0xAEFD, 0xAEFD, 0xAEFD }, +{ 0xAEFE, 0xAEFE, 0xAEFE }, +{ 0xAEFF, 0xAEFF, 0xAEFF }, +{ 0xAF00, 0xAF00, 0xAF00 }, +{ 0xAF01, 0xAF01, 0xAF01 }, +{ 0xAF02, 0xAF02, 0xAF02 }, +{ 0xAF03, 0xAF03, 0xAF03 }, +{ 0xAF04, 0xAF04, 0xAF04 }, +{ 0xAF05, 0xAF05, 0xAF05 }, +{ 0xAF06, 0xAF06, 0xAF06 }, +{ 0xAF07, 0xAF07, 0xAF07 }, +{ 0xAF08, 0xAF08, 0xAF08 }, +{ 0xAF09, 0xAF09, 0xAF09 }, +{ 0xAF0A, 0xAF0A, 0xAF0A }, +{ 0xAF0B, 0xAF0B, 0xAF0B }, +{ 0xAF0C, 0xAF0C, 0xAF0C }, +{ 0xAF0D, 0xAF0D, 0xAF0D }, +{ 0xAF0E, 0xAF0E, 0xAF0E }, +{ 0xAF0F, 0xAF0F, 0xAF0F }, +{ 0xAF10, 0xAF10, 0xAF10 }, +{ 0xAF11, 0xAF11, 0xAF11 }, +{ 0xAF12, 0xAF12, 0xAF12 }, +{ 0xAF13, 0xAF13, 0xAF13 }, +{ 0xAF14, 0xAF14, 0xAF14 }, +{ 0xAF15, 0xAF15, 0xAF15 }, +{ 0xAF16, 0xAF16, 0xAF16 }, +{ 0xAF17, 0xAF17, 0xAF17 }, +{ 0xAF18, 0xAF18, 0xAF18 }, +{ 0xAF19, 0xAF19, 0xAF19 }, +{ 0xAF1A, 0xAF1A, 0xAF1A }, +{ 0xAF1B, 0xAF1B, 0xAF1B }, +{ 0xAF1C, 0xAF1C, 0xAF1C }, +{ 0xAF1D, 0xAF1D, 0xAF1D }, +{ 0xAF1E, 0xAF1E, 0xAF1E }, +{ 0xAF1F, 0xAF1F, 0xAF1F }, +{ 0xAF20, 0xAF20, 0xAF20 }, +{ 0xAF21, 0xAF21, 0xAF21 }, +{ 0xAF22, 0xAF22, 0xAF22 }, +{ 0xAF23, 0xAF23, 0xAF23 }, +{ 0xAF24, 0xAF24, 0xAF24 }, +{ 0xAF25, 0xAF25, 0xAF25 }, +{ 0xAF26, 0xAF26, 0xAF26 }, +{ 0xAF27, 0xAF27, 0xAF27 }, +{ 0xAF28, 0xAF28, 0xAF28 }, +{ 0xAF29, 0xAF29, 0xAF29 }, +{ 0xAF2A, 0xAF2A, 0xAF2A }, +{ 0xAF2B, 0xAF2B, 0xAF2B }, +{ 0xAF2C, 0xAF2C, 0xAF2C }, +{ 0xAF2D, 0xAF2D, 0xAF2D }, +{ 0xAF2E, 0xAF2E, 0xAF2E }, +{ 0xAF2F, 0xAF2F, 0xAF2F }, +{ 0xAF30, 0xAF30, 0xAF30 }, +{ 0xAF31, 0xAF31, 0xAF31 }, +{ 0xAF32, 0xAF32, 0xAF32 }, +{ 0xAF33, 0xAF33, 0xAF33 }, +{ 0xAF34, 0xAF34, 0xAF34 }, +{ 0xAF35, 0xAF35, 0xAF35 }, +{ 0xAF36, 0xAF36, 0xAF36 }, +{ 0xAF37, 0xAF37, 0xAF37 }, +{ 0xAF38, 0xAF38, 0xAF38 }, +{ 0xAF39, 0xAF39, 0xAF39 }, +{ 0xAF3A, 0xAF3A, 0xAF3A }, +{ 0xAF3B, 0xAF3B, 0xAF3B }, +{ 0xAF3C, 0xAF3C, 0xAF3C }, +{ 0xAF3D, 0xAF3D, 0xAF3D }, +{ 0xAF3E, 0xAF3E, 0xAF3E }, +{ 0xAF3F, 0xAF3F, 0xAF3F }, +{ 0xAF40, 0xAF40, 0xAF40 }, +{ 0xAF41, 0xAF41, 0xAF41 }, +{ 0xAF42, 0xAF42, 0xAF42 }, +{ 0xAF43, 0xAF43, 0xAF43 }, +{ 0xAF44, 0xAF44, 0xAF44 }, +{ 0xAF45, 0xAF45, 0xAF45 }, +{ 0xAF46, 0xAF46, 0xAF46 }, +{ 0xAF47, 0xAF47, 0xAF47 }, +{ 0xAF48, 0xAF48, 0xAF48 }, +{ 0xAF49, 0xAF49, 0xAF49 }, +{ 0xAF4A, 0xAF4A, 0xAF4A }, +{ 0xAF4B, 0xAF4B, 0xAF4B }, +{ 0xAF4C, 0xAF4C, 0xAF4C }, +{ 0xAF4D, 0xAF4D, 0xAF4D }, +{ 0xAF4E, 0xAF4E, 0xAF4E }, +{ 0xAF4F, 0xAF4F, 0xAF4F }, +{ 0xAF50, 0xAF50, 0xAF50 }, +{ 0xAF51, 0xAF51, 0xAF51 }, +{ 0xAF52, 0xAF52, 0xAF52 }, +{ 0xAF53, 0xAF53, 0xAF53 }, +{ 0xAF54, 0xAF54, 0xAF54 }, +{ 0xAF55, 0xAF55, 0xAF55 }, +{ 0xAF56, 0xAF56, 0xAF56 }, +{ 0xAF57, 0xAF57, 0xAF57 }, +{ 0xAF58, 0xAF58, 0xAF58 }, +{ 0xAF59, 0xAF59, 0xAF59 }, +{ 0xAF5A, 0xAF5A, 0xAF5A }, +{ 0xAF5B, 0xAF5B, 0xAF5B }, +{ 0xAF5C, 0xAF5C, 0xAF5C }, +{ 0xAF5D, 0xAF5D, 0xAF5D }, +{ 0xAF5E, 0xAF5E, 0xAF5E }, +{ 0xAF5F, 0xAF5F, 0xAF5F }, +{ 0xAF60, 0xAF60, 0xAF60 }, +{ 0xAF61, 0xAF61, 0xAF61 }, +{ 0xAF62, 0xAF62, 0xAF62 }, +{ 0xAF63, 0xAF63, 0xAF63 }, +{ 0xAF64, 0xAF64, 0xAF64 }, +{ 0xAF65, 0xAF65, 0xAF65 }, +{ 0xAF66, 0xAF66, 0xAF66 }, +{ 0xAF67, 0xAF67, 0xAF67 }, +{ 0xAF68, 0xAF68, 0xAF68 }, +{ 0xAF69, 0xAF69, 0xAF69 }, +{ 0xAF6A, 0xAF6A, 0xAF6A }, +{ 0xAF6B, 0xAF6B, 0xAF6B }, +{ 0xAF6C, 0xAF6C, 0xAF6C }, +{ 0xAF6D, 0xAF6D, 0xAF6D }, +{ 0xAF6E, 0xAF6E, 0xAF6E }, +{ 0xAF6F, 0xAF6F, 0xAF6F }, +{ 0xAF70, 0xAF70, 0xAF70 }, +{ 0xAF71, 0xAF71, 0xAF71 }, +{ 0xAF72, 0xAF72, 0xAF72 }, +{ 0xAF73, 0xAF73, 0xAF73 }, +{ 0xAF74, 0xAF74, 0xAF74 }, +{ 0xAF75, 0xAF75, 0xAF75 }, +{ 0xAF76, 0xAF76, 0xAF76 }, +{ 0xAF77, 0xAF77, 0xAF77 }, +{ 0xAF78, 0xAF78, 0xAF78 }, +{ 0xAF79, 0xAF79, 0xAF79 }, +{ 0xAF7A, 0xAF7A, 0xAF7A }, +{ 0xAF7B, 0xAF7B, 0xAF7B }, +{ 0xAF7C, 0xAF7C, 0xAF7C }, +{ 0xAF7D, 0xAF7D, 0xAF7D }, +{ 0xAF7E, 0xAF7E, 0xAF7E }, +{ 0xAF7F, 0xAF7F, 0xAF7F }, +{ 0xAF80, 0xAF80, 0xAF80 }, +{ 0xAF81, 0xAF81, 0xAF81 }, +{ 0xAF82, 0xAF82, 0xAF82 }, +{ 0xAF83, 0xAF83, 0xAF83 }, +{ 0xAF84, 0xAF84, 0xAF84 }, +{ 0xAF85, 0xAF85, 0xAF85 }, +{ 0xAF86, 0xAF86, 0xAF86 }, +{ 0xAF87, 0xAF87, 0xAF87 }, +{ 0xAF88, 0xAF88, 0xAF88 }, +{ 0xAF89, 0xAF89, 0xAF89 }, +{ 0xAF8A, 0xAF8A, 0xAF8A }, +{ 0xAF8B, 0xAF8B, 0xAF8B }, +{ 0xAF8C, 0xAF8C, 0xAF8C }, +{ 0xAF8D, 0xAF8D, 0xAF8D }, +{ 0xAF8E, 0xAF8E, 0xAF8E }, +{ 0xAF8F, 0xAF8F, 0xAF8F }, +{ 0xAF90, 0xAF90, 0xAF90 }, +{ 0xAF91, 0xAF91, 0xAF91 }, +{ 0xAF92, 0xAF92, 0xAF92 }, +{ 0xAF93, 0xAF93, 0xAF93 }, +{ 0xAF94, 0xAF94, 0xAF94 }, +{ 0xAF95, 0xAF95, 0xAF95 }, +{ 0xAF96, 0xAF96, 0xAF96 }, +{ 0xAF97, 0xAF97, 0xAF97 }, +{ 0xAF98, 0xAF98, 0xAF98 }, +{ 0xAF99, 0xAF99, 0xAF99 }, +{ 0xAF9A, 0xAF9A, 0xAF9A }, +{ 0xAF9B, 0xAF9B, 0xAF9B }, +{ 0xAF9C, 0xAF9C, 0xAF9C }, +{ 0xAF9D, 0xAF9D, 0xAF9D }, +{ 0xAF9E, 0xAF9E, 0xAF9E }, +{ 0xAF9F, 0xAF9F, 0xAF9F }, +{ 0xAFA0, 0xAFA0, 0xAFA0 }, +{ 0xAFA1, 0xAFA1, 0xAFA1 }, +{ 0xAFA2, 0xAFA2, 0xAFA2 }, +{ 0xAFA3, 0xAFA3, 0xAFA3 }, +{ 0xAFA4, 0xAFA4, 0xAFA4 }, +{ 0xAFA5, 0xAFA5, 0xAFA5 }, +{ 0xAFA6, 0xAFA6, 0xAFA6 }, +{ 0xAFA7, 0xAFA7, 0xAFA7 }, +{ 0xAFA8, 0xAFA8, 0xAFA8 }, +{ 0xAFA9, 0xAFA9, 0xAFA9 }, +{ 0xAFAA, 0xAFAA, 0xAFAA }, +{ 0xAFAB, 0xAFAB, 0xAFAB }, +{ 0xAFAC, 0xAFAC, 0xAFAC }, +{ 0xAFAD, 0xAFAD, 0xAFAD }, +{ 0xAFAE, 0xAFAE, 0xAFAE }, +{ 0xAFAF, 0xAFAF, 0xAFAF }, +{ 0xAFB0, 0xAFB0, 0xAFB0 }, +{ 0xAFB1, 0xAFB1, 0xAFB1 }, +{ 0xAFB2, 0xAFB2, 0xAFB2 }, +{ 0xAFB3, 0xAFB3, 0xAFB3 }, +{ 0xAFB4, 0xAFB4, 0xAFB4 }, +{ 0xAFB5, 0xAFB5, 0xAFB5 }, +{ 0xAFB6, 0xAFB6, 0xAFB6 }, +{ 0xAFB7, 0xAFB7, 0xAFB7 }, +{ 0xAFB8, 0xAFB8, 0xAFB8 }, +{ 0xAFB9, 0xAFB9, 0xAFB9 }, +{ 0xAFBA, 0xAFBA, 0xAFBA }, +{ 0xAFBB, 0xAFBB, 0xAFBB }, +{ 0xAFBC, 0xAFBC, 0xAFBC }, +{ 0xAFBD, 0xAFBD, 0xAFBD }, +{ 0xAFBE, 0xAFBE, 0xAFBE }, +{ 0xAFBF, 0xAFBF, 0xAFBF }, +{ 0xAFC0, 0xAFC0, 0xAFC0 }, +{ 0xAFC1, 0xAFC1, 0xAFC1 }, +{ 0xAFC2, 0xAFC2, 0xAFC2 }, +{ 0xAFC3, 0xAFC3, 0xAFC3 }, +{ 0xAFC4, 0xAFC4, 0xAFC4 }, +{ 0xAFC5, 0xAFC5, 0xAFC5 }, +{ 0xAFC6, 0xAFC6, 0xAFC6 }, +{ 0xAFC7, 0xAFC7, 0xAFC7 }, +{ 0xAFC8, 0xAFC8, 0xAFC8 }, +{ 0xAFC9, 0xAFC9, 0xAFC9 }, +{ 0xAFCA, 0xAFCA, 0xAFCA }, +{ 0xAFCB, 0xAFCB, 0xAFCB }, +{ 0xAFCC, 0xAFCC, 0xAFCC }, +{ 0xAFCD, 0xAFCD, 0xAFCD }, +{ 0xAFCE, 0xAFCE, 0xAFCE }, +{ 0xAFCF, 0xAFCF, 0xAFCF }, +{ 0xAFD0, 0xAFD0, 0xAFD0 }, +{ 0xAFD1, 0xAFD1, 0xAFD1 }, +{ 0xAFD2, 0xAFD2, 0xAFD2 }, +{ 0xAFD3, 0xAFD3, 0xAFD3 }, +{ 0xAFD4, 0xAFD4, 0xAFD4 }, +{ 0xAFD5, 0xAFD5, 0xAFD5 }, +{ 0xAFD6, 0xAFD6, 0xAFD6 }, +{ 0xAFD7, 0xAFD7, 0xAFD7 }, +{ 0xAFD8, 0xAFD8, 0xAFD8 }, +{ 0xAFD9, 0xAFD9, 0xAFD9 }, +{ 0xAFDA, 0xAFDA, 0xAFDA }, +{ 0xAFDB, 0xAFDB, 0xAFDB }, +{ 0xAFDC, 0xAFDC, 0xAFDC }, +{ 0xAFDD, 0xAFDD, 0xAFDD }, +{ 0xAFDE, 0xAFDE, 0xAFDE }, +{ 0xAFDF, 0xAFDF, 0xAFDF }, +{ 0xAFE0, 0xAFE0, 0xAFE0 }, +{ 0xAFE1, 0xAFE1, 0xAFE1 }, +{ 0xAFE2, 0xAFE2, 0xAFE2 }, +{ 0xAFE3, 0xAFE3, 0xAFE3 }, +{ 0xAFE4, 0xAFE4, 0xAFE4 }, +{ 0xAFE5, 0xAFE5, 0xAFE5 }, +{ 0xAFE6, 0xAFE6, 0xAFE6 }, +{ 0xAFE7, 0xAFE7, 0xAFE7 }, +{ 0xAFE8, 0xAFE8, 0xAFE8 }, +{ 0xAFE9, 0xAFE9, 0xAFE9 }, +{ 0xAFEA, 0xAFEA, 0xAFEA }, +{ 0xAFEB, 0xAFEB, 0xAFEB }, +{ 0xAFEC, 0xAFEC, 0xAFEC }, +{ 0xAFED, 0xAFED, 0xAFED }, +{ 0xAFEE, 0xAFEE, 0xAFEE }, +{ 0xAFEF, 0xAFEF, 0xAFEF }, +{ 0xAFF0, 0xAFF0, 0xAFF0 }, +{ 0xAFF1, 0xAFF1, 0xAFF1 }, +{ 0xAFF2, 0xAFF2, 0xAFF2 }, +{ 0xAFF3, 0xAFF3, 0xAFF3 }, +{ 0xAFF4, 0xAFF4, 0xAFF4 }, +{ 0xAFF5, 0xAFF5, 0xAFF5 }, +{ 0xAFF6, 0xAFF6, 0xAFF6 }, +{ 0xAFF7, 0xAFF7, 0xAFF7 }, +{ 0xAFF8, 0xAFF8, 0xAFF8 }, +{ 0xAFF9, 0xAFF9, 0xAFF9 }, +{ 0xAFFA, 0xAFFA, 0xAFFA }, +{ 0xAFFB, 0xAFFB, 0xAFFB }, +{ 0xAFFC, 0xAFFC, 0xAFFC }, +{ 0xAFFD, 0xAFFD, 0xAFFD }, +{ 0xAFFE, 0xAFFE, 0xAFFE }, +{ 0xAFFF, 0xAFFF, 0xAFFF }, +{ 0xB000, 0xB000, 0xB000 }, +{ 0xB001, 0xB001, 0xB001 }, +{ 0xB002, 0xB002, 0xB002 }, +{ 0xB003, 0xB003, 0xB003 }, +{ 0xB004, 0xB004, 0xB004 }, +{ 0xB005, 0xB005, 0xB005 }, +{ 0xB006, 0xB006, 0xB006 }, +{ 0xB007, 0xB007, 0xB007 }, +{ 0xB008, 0xB008, 0xB008 }, +{ 0xB009, 0xB009, 0xB009 }, +{ 0xB00A, 0xB00A, 0xB00A }, +{ 0xB00B, 0xB00B, 0xB00B }, +{ 0xB00C, 0xB00C, 0xB00C }, +{ 0xB00D, 0xB00D, 0xB00D }, +{ 0xB00E, 0xB00E, 0xB00E }, +{ 0xB00F, 0xB00F, 0xB00F }, +{ 0xB010, 0xB010, 0xB010 }, +{ 0xB011, 0xB011, 0xB011 }, +{ 0xB012, 0xB012, 0xB012 }, +{ 0xB013, 0xB013, 0xB013 }, +{ 0xB014, 0xB014, 0xB014 }, +{ 0xB015, 0xB015, 0xB015 }, +{ 0xB016, 0xB016, 0xB016 }, +{ 0xB017, 0xB017, 0xB017 }, +{ 0xB018, 0xB018, 0xB018 }, +{ 0xB019, 0xB019, 0xB019 }, +{ 0xB01A, 0xB01A, 0xB01A }, +{ 0xB01B, 0xB01B, 0xB01B }, +{ 0xB01C, 0xB01C, 0xB01C }, +{ 0xB01D, 0xB01D, 0xB01D }, +{ 0xB01E, 0xB01E, 0xB01E }, +{ 0xB01F, 0xB01F, 0xB01F }, +{ 0xB020, 0xB020, 0xB020 }, +{ 0xB021, 0xB021, 0xB021 }, +{ 0xB022, 0xB022, 0xB022 }, +{ 0xB023, 0xB023, 0xB023 }, +{ 0xB024, 0xB024, 0xB024 }, +{ 0xB025, 0xB025, 0xB025 }, +{ 0xB026, 0xB026, 0xB026 }, +{ 0xB027, 0xB027, 0xB027 }, +{ 0xB028, 0xB028, 0xB028 }, +{ 0xB029, 0xB029, 0xB029 }, +{ 0xB02A, 0xB02A, 0xB02A }, +{ 0xB02B, 0xB02B, 0xB02B }, +{ 0xB02C, 0xB02C, 0xB02C }, +{ 0xB02D, 0xB02D, 0xB02D }, +{ 0xB02E, 0xB02E, 0xB02E }, +{ 0xB02F, 0xB02F, 0xB02F }, +{ 0xB030, 0xB030, 0xB030 }, +{ 0xB031, 0xB031, 0xB031 }, +{ 0xB032, 0xB032, 0xB032 }, +{ 0xB033, 0xB033, 0xB033 }, +{ 0xB034, 0xB034, 0xB034 }, +{ 0xB035, 0xB035, 0xB035 }, +{ 0xB036, 0xB036, 0xB036 }, +{ 0xB037, 0xB037, 0xB037 }, +{ 0xB038, 0xB038, 0xB038 }, +{ 0xB039, 0xB039, 0xB039 }, +{ 0xB03A, 0xB03A, 0xB03A }, +{ 0xB03B, 0xB03B, 0xB03B }, +{ 0xB03C, 0xB03C, 0xB03C }, +{ 0xB03D, 0xB03D, 0xB03D }, +{ 0xB03E, 0xB03E, 0xB03E }, +{ 0xB03F, 0xB03F, 0xB03F }, +{ 0xB040, 0xB040, 0xB040 }, +{ 0xB041, 0xB041, 0xB041 }, +{ 0xB042, 0xB042, 0xB042 }, +{ 0xB043, 0xB043, 0xB043 }, +{ 0xB044, 0xB044, 0xB044 }, +{ 0xB045, 0xB045, 0xB045 }, +{ 0xB046, 0xB046, 0xB046 }, +{ 0xB047, 0xB047, 0xB047 }, +{ 0xB048, 0xB048, 0xB048 }, +{ 0xB049, 0xB049, 0xB049 }, +{ 0xB04A, 0xB04A, 0xB04A }, +{ 0xB04B, 0xB04B, 0xB04B }, +{ 0xB04C, 0xB04C, 0xB04C }, +{ 0xB04D, 0xB04D, 0xB04D }, +{ 0xB04E, 0xB04E, 0xB04E }, +{ 0xB04F, 0xB04F, 0xB04F }, +{ 0xB050, 0xB050, 0xB050 }, +{ 0xB051, 0xB051, 0xB051 }, +{ 0xB052, 0xB052, 0xB052 }, +{ 0xB053, 0xB053, 0xB053 }, +{ 0xB054, 0xB054, 0xB054 }, +{ 0xB055, 0xB055, 0xB055 }, +{ 0xB056, 0xB056, 0xB056 }, +{ 0xB057, 0xB057, 0xB057 }, +{ 0xB058, 0xB058, 0xB058 }, +{ 0xB059, 0xB059, 0xB059 }, +{ 0xB05A, 0xB05A, 0xB05A }, +{ 0xB05B, 0xB05B, 0xB05B }, +{ 0xB05C, 0xB05C, 0xB05C }, +{ 0xB05D, 0xB05D, 0xB05D }, +{ 0xB05E, 0xB05E, 0xB05E }, +{ 0xB05F, 0xB05F, 0xB05F }, +{ 0xB060, 0xB060, 0xB060 }, +{ 0xB061, 0xB061, 0xB061 }, +{ 0xB062, 0xB062, 0xB062 }, +{ 0xB063, 0xB063, 0xB063 }, +{ 0xB064, 0xB064, 0xB064 }, +{ 0xB065, 0xB065, 0xB065 }, +{ 0xB066, 0xB066, 0xB066 }, +{ 0xB067, 0xB067, 0xB067 }, +{ 0xB068, 0xB068, 0xB068 }, +{ 0xB069, 0xB069, 0xB069 }, +{ 0xB06A, 0xB06A, 0xB06A }, +{ 0xB06B, 0xB06B, 0xB06B }, +{ 0xB06C, 0xB06C, 0xB06C }, +{ 0xB06D, 0xB06D, 0xB06D }, +{ 0xB06E, 0xB06E, 0xB06E }, +{ 0xB06F, 0xB06F, 0xB06F }, +{ 0xB070, 0xB070, 0xB070 }, +{ 0xB071, 0xB071, 0xB071 }, +{ 0xB072, 0xB072, 0xB072 }, +{ 0xB073, 0xB073, 0xB073 }, +{ 0xB074, 0xB074, 0xB074 }, +{ 0xB075, 0xB075, 0xB075 }, +{ 0xB076, 0xB076, 0xB076 }, +{ 0xB077, 0xB077, 0xB077 }, +{ 0xB078, 0xB078, 0xB078 }, +{ 0xB079, 0xB079, 0xB079 }, +{ 0xB07A, 0xB07A, 0xB07A }, +{ 0xB07B, 0xB07B, 0xB07B }, +{ 0xB07C, 0xB07C, 0xB07C }, +{ 0xB07D, 0xB07D, 0xB07D }, +{ 0xB07E, 0xB07E, 0xB07E }, +{ 0xB07F, 0xB07F, 0xB07F }, +{ 0xB080, 0xB080, 0xB080 }, +{ 0xB081, 0xB081, 0xB081 }, +{ 0xB082, 0xB082, 0xB082 }, +{ 0xB083, 0xB083, 0xB083 }, +{ 0xB084, 0xB084, 0xB084 }, +{ 0xB085, 0xB085, 0xB085 }, +{ 0xB086, 0xB086, 0xB086 }, +{ 0xB087, 0xB087, 0xB087 }, +{ 0xB088, 0xB088, 0xB088 }, +{ 0xB089, 0xB089, 0xB089 }, +{ 0xB08A, 0xB08A, 0xB08A }, +{ 0xB08B, 0xB08B, 0xB08B }, +{ 0xB08C, 0xB08C, 0xB08C }, +{ 0xB08D, 0xB08D, 0xB08D }, +{ 0xB08E, 0xB08E, 0xB08E }, +{ 0xB08F, 0xB08F, 0xB08F }, +{ 0xB090, 0xB090, 0xB090 }, +{ 0xB091, 0xB091, 0xB091 }, +{ 0xB092, 0xB092, 0xB092 }, +{ 0xB093, 0xB093, 0xB093 }, +{ 0xB094, 0xB094, 0xB094 }, +{ 0xB095, 0xB095, 0xB095 }, +{ 0xB096, 0xB096, 0xB096 }, +{ 0xB097, 0xB097, 0xB097 }, +{ 0xB098, 0xB098, 0xB098 }, +{ 0xB099, 0xB099, 0xB099 }, +{ 0xB09A, 0xB09A, 0xB09A }, +{ 0xB09B, 0xB09B, 0xB09B }, +{ 0xB09C, 0xB09C, 0xB09C }, +{ 0xB09D, 0xB09D, 0xB09D }, +{ 0xB09E, 0xB09E, 0xB09E }, +{ 0xB09F, 0xB09F, 0xB09F }, +{ 0xB0A0, 0xB0A0, 0xB0A0 }, +{ 0xB0A1, 0xB0A1, 0xB0A1 }, +{ 0xB0A2, 0xB0A2, 0xB0A2 }, +{ 0xB0A3, 0xB0A3, 0xB0A3 }, +{ 0xB0A4, 0xB0A4, 0xB0A4 }, +{ 0xB0A5, 0xB0A5, 0xB0A5 }, +{ 0xB0A6, 0xB0A6, 0xB0A6 }, +{ 0xB0A7, 0xB0A7, 0xB0A7 }, +{ 0xB0A8, 0xB0A8, 0xB0A8 }, +{ 0xB0A9, 0xB0A9, 0xB0A9 }, +{ 0xB0AA, 0xB0AA, 0xB0AA }, +{ 0xB0AB, 0xB0AB, 0xB0AB }, +{ 0xB0AC, 0xB0AC, 0xB0AC }, +{ 0xB0AD, 0xB0AD, 0xB0AD }, +{ 0xB0AE, 0xB0AE, 0xB0AE }, +{ 0xB0AF, 0xB0AF, 0xB0AF }, +{ 0xB0B0, 0xB0B0, 0xB0B0 }, +{ 0xB0B1, 0xB0B1, 0xB0B1 }, +{ 0xB0B2, 0xB0B2, 0xB0B2 }, +{ 0xB0B3, 0xB0B3, 0xB0B3 }, +{ 0xB0B4, 0xB0B4, 0xB0B4 }, +{ 0xB0B5, 0xB0B5, 0xB0B5 }, +{ 0xB0B6, 0xB0B6, 0xB0B6 }, +{ 0xB0B7, 0xB0B7, 0xB0B7 }, +{ 0xB0B8, 0xB0B8, 0xB0B8 }, +{ 0xB0B9, 0xB0B9, 0xB0B9 }, +{ 0xB0BA, 0xB0BA, 0xB0BA }, +{ 0xB0BB, 0xB0BB, 0xB0BB }, +{ 0xB0BC, 0xB0BC, 0xB0BC }, +{ 0xB0BD, 0xB0BD, 0xB0BD }, +{ 0xB0BE, 0xB0BE, 0xB0BE }, +{ 0xB0BF, 0xB0BF, 0xB0BF }, +{ 0xB0C0, 0xB0C0, 0xB0C0 }, +{ 0xB0C1, 0xB0C1, 0xB0C1 }, +{ 0xB0C2, 0xB0C2, 0xB0C2 }, +{ 0xB0C3, 0xB0C3, 0xB0C3 }, +{ 0xB0C4, 0xB0C4, 0xB0C4 }, +{ 0xB0C5, 0xB0C5, 0xB0C5 }, +{ 0xB0C6, 0xB0C6, 0xB0C6 }, +{ 0xB0C7, 0xB0C7, 0xB0C7 }, +{ 0xB0C8, 0xB0C8, 0xB0C8 }, +{ 0xB0C9, 0xB0C9, 0xB0C9 }, +{ 0xB0CA, 0xB0CA, 0xB0CA }, +{ 0xB0CB, 0xB0CB, 0xB0CB }, +{ 0xB0CC, 0xB0CC, 0xB0CC }, +{ 0xB0CD, 0xB0CD, 0xB0CD }, +{ 0xB0CE, 0xB0CE, 0xB0CE }, +{ 0xB0CF, 0xB0CF, 0xB0CF }, +{ 0xB0D0, 0xB0D0, 0xB0D0 }, +{ 0xB0D1, 0xB0D1, 0xB0D1 }, +{ 0xB0D2, 0xB0D2, 0xB0D2 }, +{ 0xB0D3, 0xB0D3, 0xB0D3 }, +{ 0xB0D4, 0xB0D4, 0xB0D4 }, +{ 0xB0D5, 0xB0D5, 0xB0D5 }, +{ 0xB0D6, 0xB0D6, 0xB0D6 }, +{ 0xB0D7, 0xB0D7, 0xB0D7 }, +{ 0xB0D8, 0xB0D8, 0xB0D8 }, +{ 0xB0D9, 0xB0D9, 0xB0D9 }, +{ 0xB0DA, 0xB0DA, 0xB0DA }, +{ 0xB0DB, 0xB0DB, 0xB0DB }, +{ 0xB0DC, 0xB0DC, 0xB0DC }, +{ 0xB0DD, 0xB0DD, 0xB0DD }, +{ 0xB0DE, 0xB0DE, 0xB0DE }, +{ 0xB0DF, 0xB0DF, 0xB0DF }, +{ 0xB0E0, 0xB0E0, 0xB0E0 }, +{ 0xB0E1, 0xB0E1, 0xB0E1 }, +{ 0xB0E2, 0xB0E2, 0xB0E2 }, +{ 0xB0E3, 0xB0E3, 0xB0E3 }, +{ 0xB0E4, 0xB0E4, 0xB0E4 }, +{ 0xB0E5, 0xB0E5, 0xB0E5 }, +{ 0xB0E6, 0xB0E6, 0xB0E6 }, +{ 0xB0E7, 0xB0E7, 0xB0E7 }, +{ 0xB0E8, 0xB0E8, 0xB0E8 }, +{ 0xB0E9, 0xB0E9, 0xB0E9 }, +{ 0xB0EA, 0xB0EA, 0xB0EA }, +{ 0xB0EB, 0xB0EB, 0xB0EB }, +{ 0xB0EC, 0xB0EC, 0xB0EC }, +{ 0xB0ED, 0xB0ED, 0xB0ED }, +{ 0xB0EE, 0xB0EE, 0xB0EE }, +{ 0xB0EF, 0xB0EF, 0xB0EF }, +{ 0xB0F0, 0xB0F0, 0xB0F0 }, +{ 0xB0F1, 0xB0F1, 0xB0F1 }, +{ 0xB0F2, 0xB0F2, 0xB0F2 }, +{ 0xB0F3, 0xB0F3, 0xB0F3 }, +{ 0xB0F4, 0xB0F4, 0xB0F4 }, +{ 0xB0F5, 0xB0F5, 0xB0F5 }, +{ 0xB0F6, 0xB0F6, 0xB0F6 }, +{ 0xB0F7, 0xB0F7, 0xB0F7 }, +{ 0xB0F8, 0xB0F8, 0xB0F8 }, +{ 0xB0F9, 0xB0F9, 0xB0F9 }, +{ 0xB0FA, 0xB0FA, 0xB0FA }, +{ 0xB0FB, 0xB0FB, 0xB0FB }, +{ 0xB0FC, 0xB0FC, 0xB0FC }, +{ 0xB0FD, 0xB0FD, 0xB0FD }, +{ 0xB0FE, 0xB0FE, 0xB0FE }, +{ 0xB0FF, 0xB0FF, 0xB0FF }, +{ 0xB100, 0xB100, 0xB100 }, +{ 0xB101, 0xB101, 0xB101 }, +{ 0xB102, 0xB102, 0xB102 }, +{ 0xB103, 0xB103, 0xB103 }, +{ 0xB104, 0xB104, 0xB104 }, +{ 0xB105, 0xB105, 0xB105 }, +{ 0xB106, 0xB106, 0xB106 }, +{ 0xB107, 0xB107, 0xB107 }, +{ 0xB108, 0xB108, 0xB108 }, +{ 0xB109, 0xB109, 0xB109 }, +{ 0xB10A, 0xB10A, 0xB10A }, +{ 0xB10B, 0xB10B, 0xB10B }, +{ 0xB10C, 0xB10C, 0xB10C }, +{ 0xB10D, 0xB10D, 0xB10D }, +{ 0xB10E, 0xB10E, 0xB10E }, +{ 0xB10F, 0xB10F, 0xB10F }, +{ 0xB110, 0xB110, 0xB110 }, +{ 0xB111, 0xB111, 0xB111 }, +{ 0xB112, 0xB112, 0xB112 }, +{ 0xB113, 0xB113, 0xB113 }, +{ 0xB114, 0xB114, 0xB114 }, +{ 0xB115, 0xB115, 0xB115 }, +{ 0xB116, 0xB116, 0xB116 }, +{ 0xB117, 0xB117, 0xB117 }, +{ 0xB118, 0xB118, 0xB118 }, +{ 0xB119, 0xB119, 0xB119 }, +{ 0xB11A, 0xB11A, 0xB11A }, +{ 0xB11B, 0xB11B, 0xB11B }, +{ 0xB11C, 0xB11C, 0xB11C }, +{ 0xB11D, 0xB11D, 0xB11D }, +{ 0xB11E, 0xB11E, 0xB11E }, +{ 0xB11F, 0xB11F, 0xB11F }, +{ 0xB120, 0xB120, 0xB120 }, +{ 0xB121, 0xB121, 0xB121 }, +{ 0xB122, 0xB122, 0xB122 }, +{ 0xB123, 0xB123, 0xB123 }, +{ 0xB124, 0xB124, 0xB124 }, +{ 0xB125, 0xB125, 0xB125 }, +{ 0xB126, 0xB126, 0xB126 }, +{ 0xB127, 0xB127, 0xB127 }, +{ 0xB128, 0xB128, 0xB128 }, +{ 0xB129, 0xB129, 0xB129 }, +{ 0xB12A, 0xB12A, 0xB12A }, +{ 0xB12B, 0xB12B, 0xB12B }, +{ 0xB12C, 0xB12C, 0xB12C }, +{ 0xB12D, 0xB12D, 0xB12D }, +{ 0xB12E, 0xB12E, 0xB12E }, +{ 0xB12F, 0xB12F, 0xB12F }, +{ 0xB130, 0xB130, 0xB130 }, +{ 0xB131, 0xB131, 0xB131 }, +{ 0xB132, 0xB132, 0xB132 }, +{ 0xB133, 0xB133, 0xB133 }, +{ 0xB134, 0xB134, 0xB134 }, +{ 0xB135, 0xB135, 0xB135 }, +{ 0xB136, 0xB136, 0xB136 }, +{ 0xB137, 0xB137, 0xB137 }, +{ 0xB138, 0xB138, 0xB138 }, +{ 0xB139, 0xB139, 0xB139 }, +{ 0xB13A, 0xB13A, 0xB13A }, +{ 0xB13B, 0xB13B, 0xB13B }, +{ 0xB13C, 0xB13C, 0xB13C }, +{ 0xB13D, 0xB13D, 0xB13D }, +{ 0xB13E, 0xB13E, 0xB13E }, +{ 0xB13F, 0xB13F, 0xB13F }, +{ 0xB140, 0xB140, 0xB140 }, +{ 0xB141, 0xB141, 0xB141 }, +{ 0xB142, 0xB142, 0xB142 }, +{ 0xB143, 0xB143, 0xB143 }, +{ 0xB144, 0xB144, 0xB144 }, +{ 0xB145, 0xB145, 0xB145 }, +{ 0xB146, 0xB146, 0xB146 }, +{ 0xB147, 0xB147, 0xB147 }, +{ 0xB148, 0xB148, 0xB148 }, +{ 0xB149, 0xB149, 0xB149 }, +{ 0xB14A, 0xB14A, 0xB14A }, +{ 0xB14B, 0xB14B, 0xB14B }, +{ 0xB14C, 0xB14C, 0xB14C }, +{ 0xB14D, 0xB14D, 0xB14D }, +{ 0xB14E, 0xB14E, 0xB14E }, +{ 0xB14F, 0xB14F, 0xB14F }, +{ 0xB150, 0xB150, 0xB150 }, +{ 0xB151, 0xB151, 0xB151 }, +{ 0xB152, 0xB152, 0xB152 }, +{ 0xB153, 0xB153, 0xB153 }, +{ 0xB154, 0xB154, 0xB154 }, +{ 0xB155, 0xB155, 0xB155 }, +{ 0xB156, 0xB156, 0xB156 }, +{ 0xB157, 0xB157, 0xB157 }, +{ 0xB158, 0xB158, 0xB158 }, +{ 0xB159, 0xB159, 0xB159 }, +{ 0xB15A, 0xB15A, 0xB15A }, +{ 0xB15B, 0xB15B, 0xB15B }, +{ 0xB15C, 0xB15C, 0xB15C }, +{ 0xB15D, 0xB15D, 0xB15D }, +{ 0xB15E, 0xB15E, 0xB15E }, +{ 0xB15F, 0xB15F, 0xB15F }, +{ 0xB160, 0xB160, 0xB160 }, +{ 0xB161, 0xB161, 0xB161 }, +{ 0xB162, 0xB162, 0xB162 }, +{ 0xB163, 0xB163, 0xB163 }, +{ 0xB164, 0xB164, 0xB164 }, +{ 0xB165, 0xB165, 0xB165 }, +{ 0xB166, 0xB166, 0xB166 }, +{ 0xB167, 0xB167, 0xB167 }, +{ 0xB168, 0xB168, 0xB168 }, +{ 0xB169, 0xB169, 0xB169 }, +{ 0xB16A, 0xB16A, 0xB16A }, +{ 0xB16B, 0xB16B, 0xB16B }, +{ 0xB16C, 0xB16C, 0xB16C }, +{ 0xB16D, 0xB16D, 0xB16D }, +{ 0xB16E, 0xB16E, 0xB16E }, +{ 0xB16F, 0xB16F, 0xB16F }, +{ 0xB170, 0xB170, 0xB170 }, +{ 0xB171, 0xB171, 0xB171 }, +{ 0xB172, 0xB172, 0xB172 }, +{ 0xB173, 0xB173, 0xB173 }, +{ 0xB174, 0xB174, 0xB174 }, +{ 0xB175, 0xB175, 0xB175 }, +{ 0xB176, 0xB176, 0xB176 }, +{ 0xB177, 0xB177, 0xB177 }, +{ 0xB178, 0xB178, 0xB178 }, +{ 0xB179, 0xB179, 0xB179 }, +{ 0xB17A, 0xB17A, 0xB17A }, +{ 0xB17B, 0xB17B, 0xB17B }, +{ 0xB17C, 0xB17C, 0xB17C }, +{ 0xB17D, 0xB17D, 0xB17D }, +{ 0xB17E, 0xB17E, 0xB17E }, +{ 0xB17F, 0xB17F, 0xB17F }, +{ 0xB180, 0xB180, 0xB180 }, +{ 0xB181, 0xB181, 0xB181 }, +{ 0xB182, 0xB182, 0xB182 }, +{ 0xB183, 0xB183, 0xB183 }, +{ 0xB184, 0xB184, 0xB184 }, +{ 0xB185, 0xB185, 0xB185 }, +{ 0xB186, 0xB186, 0xB186 }, +{ 0xB187, 0xB187, 0xB187 }, +{ 0xB188, 0xB188, 0xB188 }, +{ 0xB189, 0xB189, 0xB189 }, +{ 0xB18A, 0xB18A, 0xB18A }, +{ 0xB18B, 0xB18B, 0xB18B }, +{ 0xB18C, 0xB18C, 0xB18C }, +{ 0xB18D, 0xB18D, 0xB18D }, +{ 0xB18E, 0xB18E, 0xB18E }, +{ 0xB18F, 0xB18F, 0xB18F }, +{ 0xB190, 0xB190, 0xB190 }, +{ 0xB191, 0xB191, 0xB191 }, +{ 0xB192, 0xB192, 0xB192 }, +{ 0xB193, 0xB193, 0xB193 }, +{ 0xB194, 0xB194, 0xB194 }, +{ 0xB195, 0xB195, 0xB195 }, +{ 0xB196, 0xB196, 0xB196 }, +{ 0xB197, 0xB197, 0xB197 }, +{ 0xB198, 0xB198, 0xB198 }, +{ 0xB199, 0xB199, 0xB199 }, +{ 0xB19A, 0xB19A, 0xB19A }, +{ 0xB19B, 0xB19B, 0xB19B }, +{ 0xB19C, 0xB19C, 0xB19C }, +{ 0xB19D, 0xB19D, 0xB19D }, +{ 0xB19E, 0xB19E, 0xB19E }, +{ 0xB19F, 0xB19F, 0xB19F }, +{ 0xB1A0, 0xB1A0, 0xB1A0 }, +{ 0xB1A1, 0xB1A1, 0xB1A1 }, +{ 0xB1A2, 0xB1A2, 0xB1A2 }, +{ 0xB1A3, 0xB1A3, 0xB1A3 }, +{ 0xB1A4, 0xB1A4, 0xB1A4 }, +{ 0xB1A5, 0xB1A5, 0xB1A5 }, +{ 0xB1A6, 0xB1A6, 0xB1A6 }, +{ 0xB1A7, 0xB1A7, 0xB1A7 }, +{ 0xB1A8, 0xB1A8, 0xB1A8 }, +{ 0xB1A9, 0xB1A9, 0xB1A9 }, +{ 0xB1AA, 0xB1AA, 0xB1AA }, +{ 0xB1AB, 0xB1AB, 0xB1AB }, +{ 0xB1AC, 0xB1AC, 0xB1AC }, +{ 0xB1AD, 0xB1AD, 0xB1AD }, +{ 0xB1AE, 0xB1AE, 0xB1AE }, +{ 0xB1AF, 0xB1AF, 0xB1AF }, +{ 0xB1B0, 0xB1B0, 0xB1B0 }, +{ 0xB1B1, 0xB1B1, 0xB1B1 }, +{ 0xB1B2, 0xB1B2, 0xB1B2 }, +{ 0xB1B3, 0xB1B3, 0xB1B3 }, +{ 0xB1B4, 0xB1B4, 0xB1B4 }, +{ 0xB1B5, 0xB1B5, 0xB1B5 }, +{ 0xB1B6, 0xB1B6, 0xB1B6 }, +{ 0xB1B7, 0xB1B7, 0xB1B7 }, +{ 0xB1B8, 0xB1B8, 0xB1B8 }, +{ 0xB1B9, 0xB1B9, 0xB1B9 }, +{ 0xB1BA, 0xB1BA, 0xB1BA }, +{ 0xB1BB, 0xB1BB, 0xB1BB }, +{ 0xB1BC, 0xB1BC, 0xB1BC }, +{ 0xB1BD, 0xB1BD, 0xB1BD }, +{ 0xB1BE, 0xB1BE, 0xB1BE }, +{ 0xB1BF, 0xB1BF, 0xB1BF }, +{ 0xB1C0, 0xB1C0, 0xB1C0 }, +{ 0xB1C1, 0xB1C1, 0xB1C1 }, +{ 0xB1C2, 0xB1C2, 0xB1C2 }, +{ 0xB1C3, 0xB1C3, 0xB1C3 }, +{ 0xB1C4, 0xB1C4, 0xB1C4 }, +{ 0xB1C5, 0xB1C5, 0xB1C5 }, +{ 0xB1C6, 0xB1C6, 0xB1C6 }, +{ 0xB1C7, 0xB1C7, 0xB1C7 }, +{ 0xB1C8, 0xB1C8, 0xB1C8 }, +{ 0xB1C9, 0xB1C9, 0xB1C9 }, +{ 0xB1CA, 0xB1CA, 0xB1CA }, +{ 0xB1CB, 0xB1CB, 0xB1CB }, +{ 0xB1CC, 0xB1CC, 0xB1CC }, +{ 0xB1CD, 0xB1CD, 0xB1CD }, +{ 0xB1CE, 0xB1CE, 0xB1CE }, +{ 0xB1CF, 0xB1CF, 0xB1CF }, +{ 0xB1D0, 0xB1D0, 0xB1D0 }, +{ 0xB1D1, 0xB1D1, 0xB1D1 }, +{ 0xB1D2, 0xB1D2, 0xB1D2 }, +{ 0xB1D3, 0xB1D3, 0xB1D3 }, +{ 0xB1D4, 0xB1D4, 0xB1D4 }, +{ 0xB1D5, 0xB1D5, 0xB1D5 }, +{ 0xB1D6, 0xB1D6, 0xB1D6 }, +{ 0xB1D7, 0xB1D7, 0xB1D7 }, +{ 0xB1D8, 0xB1D8, 0xB1D8 }, +{ 0xB1D9, 0xB1D9, 0xB1D9 }, +{ 0xB1DA, 0xB1DA, 0xB1DA }, +{ 0xB1DB, 0xB1DB, 0xB1DB }, +{ 0xB1DC, 0xB1DC, 0xB1DC }, +{ 0xB1DD, 0xB1DD, 0xB1DD }, +{ 0xB1DE, 0xB1DE, 0xB1DE }, +{ 0xB1DF, 0xB1DF, 0xB1DF }, +{ 0xB1E0, 0xB1E0, 0xB1E0 }, +{ 0xB1E1, 0xB1E1, 0xB1E1 }, +{ 0xB1E2, 0xB1E2, 0xB1E2 }, +{ 0xB1E3, 0xB1E3, 0xB1E3 }, +{ 0xB1E4, 0xB1E4, 0xB1E4 }, +{ 0xB1E5, 0xB1E5, 0xB1E5 }, +{ 0xB1E6, 0xB1E6, 0xB1E6 }, +{ 0xB1E7, 0xB1E7, 0xB1E7 }, +{ 0xB1E8, 0xB1E8, 0xB1E8 }, +{ 0xB1E9, 0xB1E9, 0xB1E9 }, +{ 0xB1EA, 0xB1EA, 0xB1EA }, +{ 0xB1EB, 0xB1EB, 0xB1EB }, +{ 0xB1EC, 0xB1EC, 0xB1EC }, +{ 0xB1ED, 0xB1ED, 0xB1ED }, +{ 0xB1EE, 0xB1EE, 0xB1EE }, +{ 0xB1EF, 0xB1EF, 0xB1EF }, +{ 0xB1F0, 0xB1F0, 0xB1F0 }, +{ 0xB1F1, 0xB1F1, 0xB1F1 }, +{ 0xB1F2, 0xB1F2, 0xB1F2 }, +{ 0xB1F3, 0xB1F3, 0xB1F3 }, +{ 0xB1F4, 0xB1F4, 0xB1F4 }, +{ 0xB1F5, 0xB1F5, 0xB1F5 }, +{ 0xB1F6, 0xB1F6, 0xB1F6 }, +{ 0xB1F7, 0xB1F7, 0xB1F7 }, +{ 0xB1F8, 0xB1F8, 0xB1F8 }, +{ 0xB1F9, 0xB1F9, 0xB1F9 }, +{ 0xB1FA, 0xB1FA, 0xB1FA }, +{ 0xB1FB, 0xB1FB, 0xB1FB }, +{ 0xB1FC, 0xB1FC, 0xB1FC }, +{ 0xB1FD, 0xB1FD, 0xB1FD }, +{ 0xB1FE, 0xB1FE, 0xB1FE }, +{ 0xB1FF, 0xB1FF, 0xB1FF }, +{ 0xB200, 0xB200, 0xB200 }, +{ 0xB201, 0xB201, 0xB201 }, +{ 0xB202, 0xB202, 0xB202 }, +{ 0xB203, 0xB203, 0xB203 }, +{ 0xB204, 0xB204, 0xB204 }, +{ 0xB205, 0xB205, 0xB205 }, +{ 0xB206, 0xB206, 0xB206 }, +{ 0xB207, 0xB207, 0xB207 }, +{ 0xB208, 0xB208, 0xB208 }, +{ 0xB209, 0xB209, 0xB209 }, +{ 0xB20A, 0xB20A, 0xB20A }, +{ 0xB20B, 0xB20B, 0xB20B }, +{ 0xB20C, 0xB20C, 0xB20C }, +{ 0xB20D, 0xB20D, 0xB20D }, +{ 0xB20E, 0xB20E, 0xB20E }, +{ 0xB20F, 0xB20F, 0xB20F }, +{ 0xB210, 0xB210, 0xB210 }, +{ 0xB211, 0xB211, 0xB211 }, +{ 0xB212, 0xB212, 0xB212 }, +{ 0xB213, 0xB213, 0xB213 }, +{ 0xB214, 0xB214, 0xB214 }, +{ 0xB215, 0xB215, 0xB215 }, +{ 0xB216, 0xB216, 0xB216 }, +{ 0xB217, 0xB217, 0xB217 }, +{ 0xB218, 0xB218, 0xB218 }, +{ 0xB219, 0xB219, 0xB219 }, +{ 0xB21A, 0xB21A, 0xB21A }, +{ 0xB21B, 0xB21B, 0xB21B }, +{ 0xB21C, 0xB21C, 0xB21C }, +{ 0xB21D, 0xB21D, 0xB21D }, +{ 0xB21E, 0xB21E, 0xB21E }, +{ 0xB21F, 0xB21F, 0xB21F }, +{ 0xB220, 0xB220, 0xB220 }, +{ 0xB221, 0xB221, 0xB221 }, +{ 0xB222, 0xB222, 0xB222 }, +{ 0xB223, 0xB223, 0xB223 }, +{ 0xB224, 0xB224, 0xB224 }, +{ 0xB225, 0xB225, 0xB225 }, +{ 0xB226, 0xB226, 0xB226 }, +{ 0xB227, 0xB227, 0xB227 }, +{ 0xB228, 0xB228, 0xB228 }, +{ 0xB229, 0xB229, 0xB229 }, +{ 0xB22A, 0xB22A, 0xB22A }, +{ 0xB22B, 0xB22B, 0xB22B }, +{ 0xB22C, 0xB22C, 0xB22C }, +{ 0xB22D, 0xB22D, 0xB22D }, +{ 0xB22E, 0xB22E, 0xB22E }, +{ 0xB22F, 0xB22F, 0xB22F }, +{ 0xB230, 0xB230, 0xB230 }, +{ 0xB231, 0xB231, 0xB231 }, +{ 0xB232, 0xB232, 0xB232 }, +{ 0xB233, 0xB233, 0xB233 }, +{ 0xB234, 0xB234, 0xB234 }, +{ 0xB235, 0xB235, 0xB235 }, +{ 0xB236, 0xB236, 0xB236 }, +{ 0xB237, 0xB237, 0xB237 }, +{ 0xB238, 0xB238, 0xB238 }, +{ 0xB239, 0xB239, 0xB239 }, +{ 0xB23A, 0xB23A, 0xB23A }, +{ 0xB23B, 0xB23B, 0xB23B }, +{ 0xB23C, 0xB23C, 0xB23C }, +{ 0xB23D, 0xB23D, 0xB23D }, +{ 0xB23E, 0xB23E, 0xB23E }, +{ 0xB23F, 0xB23F, 0xB23F }, +{ 0xB240, 0xB240, 0xB240 }, +{ 0xB241, 0xB241, 0xB241 }, +{ 0xB242, 0xB242, 0xB242 }, +{ 0xB243, 0xB243, 0xB243 }, +{ 0xB244, 0xB244, 0xB244 }, +{ 0xB245, 0xB245, 0xB245 }, +{ 0xB246, 0xB246, 0xB246 }, +{ 0xB247, 0xB247, 0xB247 }, +{ 0xB248, 0xB248, 0xB248 }, +{ 0xB249, 0xB249, 0xB249 }, +{ 0xB24A, 0xB24A, 0xB24A }, +{ 0xB24B, 0xB24B, 0xB24B }, +{ 0xB24C, 0xB24C, 0xB24C }, +{ 0xB24D, 0xB24D, 0xB24D }, +{ 0xB24E, 0xB24E, 0xB24E }, +{ 0xB24F, 0xB24F, 0xB24F }, +{ 0xB250, 0xB250, 0xB250 }, +{ 0xB251, 0xB251, 0xB251 }, +{ 0xB252, 0xB252, 0xB252 }, +{ 0xB253, 0xB253, 0xB253 }, +{ 0xB254, 0xB254, 0xB254 }, +{ 0xB255, 0xB255, 0xB255 }, +{ 0xB256, 0xB256, 0xB256 }, +{ 0xB257, 0xB257, 0xB257 }, +{ 0xB258, 0xB258, 0xB258 }, +{ 0xB259, 0xB259, 0xB259 }, +{ 0xB25A, 0xB25A, 0xB25A }, +{ 0xB25B, 0xB25B, 0xB25B }, +{ 0xB25C, 0xB25C, 0xB25C }, +{ 0xB25D, 0xB25D, 0xB25D }, +{ 0xB25E, 0xB25E, 0xB25E }, +{ 0xB25F, 0xB25F, 0xB25F }, +{ 0xB260, 0xB260, 0xB260 }, +{ 0xB261, 0xB261, 0xB261 }, +{ 0xB262, 0xB262, 0xB262 }, +{ 0xB263, 0xB263, 0xB263 }, +{ 0xB264, 0xB264, 0xB264 }, +{ 0xB265, 0xB265, 0xB265 }, +{ 0xB266, 0xB266, 0xB266 }, +{ 0xB267, 0xB267, 0xB267 }, +{ 0xB268, 0xB268, 0xB268 }, +{ 0xB269, 0xB269, 0xB269 }, +{ 0xB26A, 0xB26A, 0xB26A }, +{ 0xB26B, 0xB26B, 0xB26B }, +{ 0xB26C, 0xB26C, 0xB26C }, +{ 0xB26D, 0xB26D, 0xB26D }, +{ 0xB26E, 0xB26E, 0xB26E }, +{ 0xB26F, 0xB26F, 0xB26F }, +{ 0xB270, 0xB270, 0xB270 }, +{ 0xB271, 0xB271, 0xB271 }, +{ 0xB272, 0xB272, 0xB272 }, +{ 0xB273, 0xB273, 0xB273 }, +{ 0xB274, 0xB274, 0xB274 }, +{ 0xB275, 0xB275, 0xB275 }, +{ 0xB276, 0xB276, 0xB276 }, +{ 0xB277, 0xB277, 0xB277 }, +{ 0xB278, 0xB278, 0xB278 }, +{ 0xB279, 0xB279, 0xB279 }, +{ 0xB27A, 0xB27A, 0xB27A }, +{ 0xB27B, 0xB27B, 0xB27B }, +{ 0xB27C, 0xB27C, 0xB27C }, +{ 0xB27D, 0xB27D, 0xB27D }, +{ 0xB27E, 0xB27E, 0xB27E }, +{ 0xB27F, 0xB27F, 0xB27F }, +{ 0xB280, 0xB280, 0xB280 }, +{ 0xB281, 0xB281, 0xB281 }, +{ 0xB282, 0xB282, 0xB282 }, +{ 0xB283, 0xB283, 0xB283 }, +{ 0xB284, 0xB284, 0xB284 }, +{ 0xB285, 0xB285, 0xB285 }, +{ 0xB286, 0xB286, 0xB286 }, +{ 0xB287, 0xB287, 0xB287 }, +{ 0xB288, 0xB288, 0xB288 }, +{ 0xB289, 0xB289, 0xB289 }, +{ 0xB28A, 0xB28A, 0xB28A }, +{ 0xB28B, 0xB28B, 0xB28B }, +{ 0xB28C, 0xB28C, 0xB28C }, +{ 0xB28D, 0xB28D, 0xB28D }, +{ 0xB28E, 0xB28E, 0xB28E }, +{ 0xB28F, 0xB28F, 0xB28F }, +{ 0xB290, 0xB290, 0xB290 }, +{ 0xB291, 0xB291, 0xB291 }, +{ 0xB292, 0xB292, 0xB292 }, +{ 0xB293, 0xB293, 0xB293 }, +{ 0xB294, 0xB294, 0xB294 }, +{ 0xB295, 0xB295, 0xB295 }, +{ 0xB296, 0xB296, 0xB296 }, +{ 0xB297, 0xB297, 0xB297 }, +{ 0xB298, 0xB298, 0xB298 }, +{ 0xB299, 0xB299, 0xB299 }, +{ 0xB29A, 0xB29A, 0xB29A }, +{ 0xB29B, 0xB29B, 0xB29B }, +{ 0xB29C, 0xB29C, 0xB29C }, +{ 0xB29D, 0xB29D, 0xB29D }, +{ 0xB29E, 0xB29E, 0xB29E }, +{ 0xB29F, 0xB29F, 0xB29F }, +{ 0xB2A0, 0xB2A0, 0xB2A0 }, +{ 0xB2A1, 0xB2A1, 0xB2A1 }, +{ 0xB2A2, 0xB2A2, 0xB2A2 }, +{ 0xB2A3, 0xB2A3, 0xB2A3 }, +{ 0xB2A4, 0xB2A4, 0xB2A4 }, +{ 0xB2A5, 0xB2A5, 0xB2A5 }, +{ 0xB2A6, 0xB2A6, 0xB2A6 }, +{ 0xB2A7, 0xB2A7, 0xB2A7 }, +{ 0xB2A8, 0xB2A8, 0xB2A8 }, +{ 0xB2A9, 0xB2A9, 0xB2A9 }, +{ 0xB2AA, 0xB2AA, 0xB2AA }, +{ 0xB2AB, 0xB2AB, 0xB2AB }, +{ 0xB2AC, 0xB2AC, 0xB2AC }, +{ 0xB2AD, 0xB2AD, 0xB2AD }, +{ 0xB2AE, 0xB2AE, 0xB2AE }, +{ 0xB2AF, 0xB2AF, 0xB2AF }, +{ 0xB2B0, 0xB2B0, 0xB2B0 }, +{ 0xB2B1, 0xB2B1, 0xB2B1 }, +{ 0xB2B2, 0xB2B2, 0xB2B2 }, +{ 0xB2B3, 0xB2B3, 0xB2B3 }, +{ 0xB2B4, 0xB2B4, 0xB2B4 }, +{ 0xB2B5, 0xB2B5, 0xB2B5 }, +{ 0xB2B6, 0xB2B6, 0xB2B6 }, +{ 0xB2B7, 0xB2B7, 0xB2B7 }, +{ 0xB2B8, 0xB2B8, 0xB2B8 }, +{ 0xB2B9, 0xB2B9, 0xB2B9 }, +{ 0xB2BA, 0xB2BA, 0xB2BA }, +{ 0xB2BB, 0xB2BB, 0xB2BB }, +{ 0xB2BC, 0xB2BC, 0xB2BC }, +{ 0xB2BD, 0xB2BD, 0xB2BD }, +{ 0xB2BE, 0xB2BE, 0xB2BE }, +{ 0xB2BF, 0xB2BF, 0xB2BF }, +{ 0xB2C0, 0xB2C0, 0xB2C0 }, +{ 0xB2C1, 0xB2C1, 0xB2C1 }, +{ 0xB2C2, 0xB2C2, 0xB2C2 }, +{ 0xB2C3, 0xB2C3, 0xB2C3 }, +{ 0xB2C4, 0xB2C4, 0xB2C4 }, +{ 0xB2C5, 0xB2C5, 0xB2C5 }, +{ 0xB2C6, 0xB2C6, 0xB2C6 }, +{ 0xB2C7, 0xB2C7, 0xB2C7 }, +{ 0xB2C8, 0xB2C8, 0xB2C8 }, +{ 0xB2C9, 0xB2C9, 0xB2C9 }, +{ 0xB2CA, 0xB2CA, 0xB2CA }, +{ 0xB2CB, 0xB2CB, 0xB2CB }, +{ 0xB2CC, 0xB2CC, 0xB2CC }, +{ 0xB2CD, 0xB2CD, 0xB2CD }, +{ 0xB2CE, 0xB2CE, 0xB2CE }, +{ 0xB2CF, 0xB2CF, 0xB2CF }, +{ 0xB2D0, 0xB2D0, 0xB2D0 }, +{ 0xB2D1, 0xB2D1, 0xB2D1 }, +{ 0xB2D2, 0xB2D2, 0xB2D2 }, +{ 0xB2D3, 0xB2D3, 0xB2D3 }, +{ 0xB2D4, 0xB2D4, 0xB2D4 }, +{ 0xB2D5, 0xB2D5, 0xB2D5 }, +{ 0xB2D6, 0xB2D6, 0xB2D6 }, +{ 0xB2D7, 0xB2D7, 0xB2D7 }, +{ 0xB2D8, 0xB2D8, 0xB2D8 }, +{ 0xB2D9, 0xB2D9, 0xB2D9 }, +{ 0xB2DA, 0xB2DA, 0xB2DA }, +{ 0xB2DB, 0xB2DB, 0xB2DB }, +{ 0xB2DC, 0xB2DC, 0xB2DC }, +{ 0xB2DD, 0xB2DD, 0xB2DD }, +{ 0xB2DE, 0xB2DE, 0xB2DE }, +{ 0xB2DF, 0xB2DF, 0xB2DF }, +{ 0xB2E0, 0xB2E0, 0xB2E0 }, +{ 0xB2E1, 0xB2E1, 0xB2E1 }, +{ 0xB2E2, 0xB2E2, 0xB2E2 }, +{ 0xB2E3, 0xB2E3, 0xB2E3 }, +{ 0xB2E4, 0xB2E4, 0xB2E4 }, +{ 0xB2E5, 0xB2E5, 0xB2E5 }, +{ 0xB2E6, 0xB2E6, 0xB2E6 }, +{ 0xB2E7, 0xB2E7, 0xB2E7 }, +{ 0xB2E8, 0xB2E8, 0xB2E8 }, +{ 0xB2E9, 0xB2E9, 0xB2E9 }, +{ 0xB2EA, 0xB2EA, 0xB2EA }, +{ 0xB2EB, 0xB2EB, 0xB2EB }, +{ 0xB2EC, 0xB2EC, 0xB2EC }, +{ 0xB2ED, 0xB2ED, 0xB2ED }, +{ 0xB2EE, 0xB2EE, 0xB2EE }, +{ 0xB2EF, 0xB2EF, 0xB2EF }, +{ 0xB2F0, 0xB2F0, 0xB2F0 }, +{ 0xB2F1, 0xB2F1, 0xB2F1 }, +{ 0xB2F2, 0xB2F2, 0xB2F2 }, +{ 0xB2F3, 0xB2F3, 0xB2F3 }, +{ 0xB2F4, 0xB2F4, 0xB2F4 }, +{ 0xB2F5, 0xB2F5, 0xB2F5 }, +{ 0xB2F6, 0xB2F6, 0xB2F6 }, +{ 0xB2F7, 0xB2F7, 0xB2F7 }, +{ 0xB2F8, 0xB2F8, 0xB2F8 }, +{ 0xB2F9, 0xB2F9, 0xB2F9 }, +{ 0xB2FA, 0xB2FA, 0xB2FA }, +{ 0xB2FB, 0xB2FB, 0xB2FB }, +{ 0xB2FC, 0xB2FC, 0xB2FC }, +{ 0xB2FD, 0xB2FD, 0xB2FD }, +{ 0xB2FE, 0xB2FE, 0xB2FE }, +{ 0xB2FF, 0xB2FF, 0xB2FF }, +{ 0xB300, 0xB300, 0xB300 }, +{ 0xB301, 0xB301, 0xB301 }, +{ 0xB302, 0xB302, 0xB302 }, +{ 0xB303, 0xB303, 0xB303 }, +{ 0xB304, 0xB304, 0xB304 }, +{ 0xB305, 0xB305, 0xB305 }, +{ 0xB306, 0xB306, 0xB306 }, +{ 0xB307, 0xB307, 0xB307 }, +{ 0xB308, 0xB308, 0xB308 }, +{ 0xB309, 0xB309, 0xB309 }, +{ 0xB30A, 0xB30A, 0xB30A }, +{ 0xB30B, 0xB30B, 0xB30B }, +{ 0xB30C, 0xB30C, 0xB30C }, +{ 0xB30D, 0xB30D, 0xB30D }, +{ 0xB30E, 0xB30E, 0xB30E }, +{ 0xB30F, 0xB30F, 0xB30F }, +{ 0xB310, 0xB310, 0xB310 }, +{ 0xB311, 0xB311, 0xB311 }, +{ 0xB312, 0xB312, 0xB312 }, +{ 0xB313, 0xB313, 0xB313 }, +{ 0xB314, 0xB314, 0xB314 }, +{ 0xB315, 0xB315, 0xB315 }, +{ 0xB316, 0xB316, 0xB316 }, +{ 0xB317, 0xB317, 0xB317 }, +{ 0xB318, 0xB318, 0xB318 }, +{ 0xB319, 0xB319, 0xB319 }, +{ 0xB31A, 0xB31A, 0xB31A }, +{ 0xB31B, 0xB31B, 0xB31B }, +{ 0xB31C, 0xB31C, 0xB31C }, +{ 0xB31D, 0xB31D, 0xB31D }, +{ 0xB31E, 0xB31E, 0xB31E }, +{ 0xB31F, 0xB31F, 0xB31F }, +{ 0xB320, 0xB320, 0xB320 }, +{ 0xB321, 0xB321, 0xB321 }, +{ 0xB322, 0xB322, 0xB322 }, +{ 0xB323, 0xB323, 0xB323 }, +{ 0xB324, 0xB324, 0xB324 }, +{ 0xB325, 0xB325, 0xB325 }, +{ 0xB326, 0xB326, 0xB326 }, +{ 0xB327, 0xB327, 0xB327 }, +{ 0xB328, 0xB328, 0xB328 }, +{ 0xB329, 0xB329, 0xB329 }, +{ 0xB32A, 0xB32A, 0xB32A }, +{ 0xB32B, 0xB32B, 0xB32B }, +{ 0xB32C, 0xB32C, 0xB32C }, +{ 0xB32D, 0xB32D, 0xB32D }, +{ 0xB32E, 0xB32E, 0xB32E }, +{ 0xB32F, 0xB32F, 0xB32F }, +{ 0xB330, 0xB330, 0xB330 }, +{ 0xB331, 0xB331, 0xB331 }, +{ 0xB332, 0xB332, 0xB332 }, +{ 0xB333, 0xB333, 0xB333 }, +{ 0xB334, 0xB334, 0xB334 }, +{ 0xB335, 0xB335, 0xB335 }, +{ 0xB336, 0xB336, 0xB336 }, +{ 0xB337, 0xB337, 0xB337 }, +{ 0xB338, 0xB338, 0xB338 }, +{ 0xB339, 0xB339, 0xB339 }, +{ 0xB33A, 0xB33A, 0xB33A }, +{ 0xB33B, 0xB33B, 0xB33B }, +{ 0xB33C, 0xB33C, 0xB33C }, +{ 0xB33D, 0xB33D, 0xB33D }, +{ 0xB33E, 0xB33E, 0xB33E }, +{ 0xB33F, 0xB33F, 0xB33F }, +{ 0xB340, 0xB340, 0xB340 }, +{ 0xB341, 0xB341, 0xB341 }, +{ 0xB342, 0xB342, 0xB342 }, +{ 0xB343, 0xB343, 0xB343 }, +{ 0xB344, 0xB344, 0xB344 }, +{ 0xB345, 0xB345, 0xB345 }, +{ 0xB346, 0xB346, 0xB346 }, +{ 0xB347, 0xB347, 0xB347 }, +{ 0xB348, 0xB348, 0xB348 }, +{ 0xB349, 0xB349, 0xB349 }, +{ 0xB34A, 0xB34A, 0xB34A }, +{ 0xB34B, 0xB34B, 0xB34B }, +{ 0xB34C, 0xB34C, 0xB34C }, +{ 0xB34D, 0xB34D, 0xB34D }, +{ 0xB34E, 0xB34E, 0xB34E }, +{ 0xB34F, 0xB34F, 0xB34F }, +{ 0xB350, 0xB350, 0xB350 }, +{ 0xB351, 0xB351, 0xB351 }, +{ 0xB352, 0xB352, 0xB352 }, +{ 0xB353, 0xB353, 0xB353 }, +{ 0xB354, 0xB354, 0xB354 }, +{ 0xB355, 0xB355, 0xB355 }, +{ 0xB356, 0xB356, 0xB356 }, +{ 0xB357, 0xB357, 0xB357 }, +{ 0xB358, 0xB358, 0xB358 }, +{ 0xB359, 0xB359, 0xB359 }, +{ 0xB35A, 0xB35A, 0xB35A }, +{ 0xB35B, 0xB35B, 0xB35B }, +{ 0xB35C, 0xB35C, 0xB35C }, +{ 0xB35D, 0xB35D, 0xB35D }, +{ 0xB35E, 0xB35E, 0xB35E }, +{ 0xB35F, 0xB35F, 0xB35F }, +{ 0xB360, 0xB360, 0xB360 }, +{ 0xB361, 0xB361, 0xB361 }, +{ 0xB362, 0xB362, 0xB362 }, +{ 0xB363, 0xB363, 0xB363 }, +{ 0xB364, 0xB364, 0xB364 }, +{ 0xB365, 0xB365, 0xB365 }, +{ 0xB366, 0xB366, 0xB366 }, +{ 0xB367, 0xB367, 0xB367 }, +{ 0xB368, 0xB368, 0xB368 }, +{ 0xB369, 0xB369, 0xB369 }, +{ 0xB36A, 0xB36A, 0xB36A }, +{ 0xB36B, 0xB36B, 0xB36B }, +{ 0xB36C, 0xB36C, 0xB36C }, +{ 0xB36D, 0xB36D, 0xB36D }, +{ 0xB36E, 0xB36E, 0xB36E }, +{ 0xB36F, 0xB36F, 0xB36F }, +{ 0xB370, 0xB370, 0xB370 }, +{ 0xB371, 0xB371, 0xB371 }, +{ 0xB372, 0xB372, 0xB372 }, +{ 0xB373, 0xB373, 0xB373 }, +{ 0xB374, 0xB374, 0xB374 }, +{ 0xB375, 0xB375, 0xB375 }, +{ 0xB376, 0xB376, 0xB376 }, +{ 0xB377, 0xB377, 0xB377 }, +{ 0xB378, 0xB378, 0xB378 }, +{ 0xB379, 0xB379, 0xB379 }, +{ 0xB37A, 0xB37A, 0xB37A }, +{ 0xB37B, 0xB37B, 0xB37B }, +{ 0xB37C, 0xB37C, 0xB37C }, +{ 0xB37D, 0xB37D, 0xB37D }, +{ 0xB37E, 0xB37E, 0xB37E }, +{ 0xB37F, 0xB37F, 0xB37F }, +{ 0xB380, 0xB380, 0xB380 }, +{ 0xB381, 0xB381, 0xB381 }, +{ 0xB382, 0xB382, 0xB382 }, +{ 0xB383, 0xB383, 0xB383 }, +{ 0xB384, 0xB384, 0xB384 }, +{ 0xB385, 0xB385, 0xB385 }, +{ 0xB386, 0xB386, 0xB386 }, +{ 0xB387, 0xB387, 0xB387 }, +{ 0xB388, 0xB388, 0xB388 }, +{ 0xB389, 0xB389, 0xB389 }, +{ 0xB38A, 0xB38A, 0xB38A }, +{ 0xB38B, 0xB38B, 0xB38B }, +{ 0xB38C, 0xB38C, 0xB38C }, +{ 0xB38D, 0xB38D, 0xB38D }, +{ 0xB38E, 0xB38E, 0xB38E }, +{ 0xB38F, 0xB38F, 0xB38F }, +{ 0xB390, 0xB390, 0xB390 }, +{ 0xB391, 0xB391, 0xB391 }, +{ 0xB392, 0xB392, 0xB392 }, +{ 0xB393, 0xB393, 0xB393 }, +{ 0xB394, 0xB394, 0xB394 }, +{ 0xB395, 0xB395, 0xB395 }, +{ 0xB396, 0xB396, 0xB396 }, +{ 0xB397, 0xB397, 0xB397 }, +{ 0xB398, 0xB398, 0xB398 }, +{ 0xB399, 0xB399, 0xB399 }, +{ 0xB39A, 0xB39A, 0xB39A }, +{ 0xB39B, 0xB39B, 0xB39B }, +{ 0xB39C, 0xB39C, 0xB39C }, +{ 0xB39D, 0xB39D, 0xB39D }, +{ 0xB39E, 0xB39E, 0xB39E }, +{ 0xB39F, 0xB39F, 0xB39F }, +{ 0xB3A0, 0xB3A0, 0xB3A0 }, +{ 0xB3A1, 0xB3A1, 0xB3A1 }, +{ 0xB3A2, 0xB3A2, 0xB3A2 }, +{ 0xB3A3, 0xB3A3, 0xB3A3 }, +{ 0xB3A4, 0xB3A4, 0xB3A4 }, +{ 0xB3A5, 0xB3A5, 0xB3A5 }, +{ 0xB3A6, 0xB3A6, 0xB3A6 }, +{ 0xB3A7, 0xB3A7, 0xB3A7 }, +{ 0xB3A8, 0xB3A8, 0xB3A8 }, +{ 0xB3A9, 0xB3A9, 0xB3A9 }, +{ 0xB3AA, 0xB3AA, 0xB3AA }, +{ 0xB3AB, 0xB3AB, 0xB3AB }, +{ 0xB3AC, 0xB3AC, 0xB3AC }, +{ 0xB3AD, 0xB3AD, 0xB3AD }, +{ 0xB3AE, 0xB3AE, 0xB3AE }, +{ 0xB3AF, 0xB3AF, 0xB3AF }, +{ 0xB3B0, 0xB3B0, 0xB3B0 }, +{ 0xB3B1, 0xB3B1, 0xB3B1 }, +{ 0xB3B2, 0xB3B2, 0xB3B2 }, +{ 0xB3B3, 0xB3B3, 0xB3B3 }, +{ 0xB3B4, 0xB3B4, 0xB3B4 }, +{ 0xB3B5, 0xB3B5, 0xB3B5 }, +{ 0xB3B6, 0xB3B6, 0xB3B6 }, +{ 0xB3B7, 0xB3B7, 0xB3B7 }, +{ 0xB3B8, 0xB3B8, 0xB3B8 }, +{ 0xB3B9, 0xB3B9, 0xB3B9 }, +{ 0xB3BA, 0xB3BA, 0xB3BA }, +{ 0xB3BB, 0xB3BB, 0xB3BB }, +{ 0xB3BC, 0xB3BC, 0xB3BC }, +{ 0xB3BD, 0xB3BD, 0xB3BD }, +{ 0xB3BE, 0xB3BE, 0xB3BE }, +{ 0xB3BF, 0xB3BF, 0xB3BF }, +{ 0xB3C0, 0xB3C0, 0xB3C0 }, +{ 0xB3C1, 0xB3C1, 0xB3C1 }, +{ 0xB3C2, 0xB3C2, 0xB3C2 }, +{ 0xB3C3, 0xB3C3, 0xB3C3 }, +{ 0xB3C4, 0xB3C4, 0xB3C4 }, +{ 0xB3C5, 0xB3C5, 0xB3C5 }, +{ 0xB3C6, 0xB3C6, 0xB3C6 }, +{ 0xB3C7, 0xB3C7, 0xB3C7 }, +{ 0xB3C8, 0xB3C8, 0xB3C8 }, +{ 0xB3C9, 0xB3C9, 0xB3C9 }, +{ 0xB3CA, 0xB3CA, 0xB3CA }, +{ 0xB3CB, 0xB3CB, 0xB3CB }, +{ 0xB3CC, 0xB3CC, 0xB3CC }, +{ 0xB3CD, 0xB3CD, 0xB3CD }, +{ 0xB3CE, 0xB3CE, 0xB3CE }, +{ 0xB3CF, 0xB3CF, 0xB3CF }, +{ 0xB3D0, 0xB3D0, 0xB3D0 }, +{ 0xB3D1, 0xB3D1, 0xB3D1 }, +{ 0xB3D2, 0xB3D2, 0xB3D2 }, +{ 0xB3D3, 0xB3D3, 0xB3D3 }, +{ 0xB3D4, 0xB3D4, 0xB3D4 }, +{ 0xB3D5, 0xB3D5, 0xB3D5 }, +{ 0xB3D6, 0xB3D6, 0xB3D6 }, +{ 0xB3D7, 0xB3D7, 0xB3D7 }, +{ 0xB3D8, 0xB3D8, 0xB3D8 }, +{ 0xB3D9, 0xB3D9, 0xB3D9 }, +{ 0xB3DA, 0xB3DA, 0xB3DA }, +{ 0xB3DB, 0xB3DB, 0xB3DB }, +{ 0xB3DC, 0xB3DC, 0xB3DC }, +{ 0xB3DD, 0xB3DD, 0xB3DD }, +{ 0xB3DE, 0xB3DE, 0xB3DE }, +{ 0xB3DF, 0xB3DF, 0xB3DF }, +{ 0xB3E0, 0xB3E0, 0xB3E0 }, +{ 0xB3E1, 0xB3E1, 0xB3E1 }, +{ 0xB3E2, 0xB3E2, 0xB3E2 }, +{ 0xB3E3, 0xB3E3, 0xB3E3 }, +{ 0xB3E4, 0xB3E4, 0xB3E4 }, +{ 0xB3E5, 0xB3E5, 0xB3E5 }, +{ 0xB3E6, 0xB3E6, 0xB3E6 }, +{ 0xB3E7, 0xB3E7, 0xB3E7 }, +{ 0xB3E8, 0xB3E8, 0xB3E8 }, +{ 0xB3E9, 0xB3E9, 0xB3E9 }, +{ 0xB3EA, 0xB3EA, 0xB3EA }, +{ 0xB3EB, 0xB3EB, 0xB3EB }, +{ 0xB3EC, 0xB3EC, 0xB3EC }, +{ 0xB3ED, 0xB3ED, 0xB3ED }, +{ 0xB3EE, 0xB3EE, 0xB3EE }, +{ 0xB3EF, 0xB3EF, 0xB3EF }, +{ 0xB3F0, 0xB3F0, 0xB3F0 }, +{ 0xB3F1, 0xB3F1, 0xB3F1 }, +{ 0xB3F2, 0xB3F2, 0xB3F2 }, +{ 0xB3F3, 0xB3F3, 0xB3F3 }, +{ 0xB3F4, 0xB3F4, 0xB3F4 }, +{ 0xB3F5, 0xB3F5, 0xB3F5 }, +{ 0xB3F6, 0xB3F6, 0xB3F6 }, +{ 0xB3F7, 0xB3F7, 0xB3F7 }, +{ 0xB3F8, 0xB3F8, 0xB3F8 }, +{ 0xB3F9, 0xB3F9, 0xB3F9 }, +{ 0xB3FA, 0xB3FA, 0xB3FA }, +{ 0xB3FB, 0xB3FB, 0xB3FB }, +{ 0xB3FC, 0xB3FC, 0xB3FC }, +{ 0xB3FD, 0xB3FD, 0xB3FD }, +{ 0xB3FE, 0xB3FE, 0xB3FE }, +{ 0xB3FF, 0xB3FF, 0xB3FF }, +{ 0xB400, 0xB400, 0xB400 }, +{ 0xB401, 0xB401, 0xB401 }, +{ 0xB402, 0xB402, 0xB402 }, +{ 0xB403, 0xB403, 0xB403 }, +{ 0xB404, 0xB404, 0xB404 }, +{ 0xB405, 0xB405, 0xB405 }, +{ 0xB406, 0xB406, 0xB406 }, +{ 0xB407, 0xB407, 0xB407 }, +{ 0xB408, 0xB408, 0xB408 }, +{ 0xB409, 0xB409, 0xB409 }, +{ 0xB40A, 0xB40A, 0xB40A }, +{ 0xB40B, 0xB40B, 0xB40B }, +{ 0xB40C, 0xB40C, 0xB40C }, +{ 0xB40D, 0xB40D, 0xB40D }, +{ 0xB40E, 0xB40E, 0xB40E }, +{ 0xB40F, 0xB40F, 0xB40F }, +{ 0xB410, 0xB410, 0xB410 }, +{ 0xB411, 0xB411, 0xB411 }, +{ 0xB412, 0xB412, 0xB412 }, +{ 0xB413, 0xB413, 0xB413 }, +{ 0xB414, 0xB414, 0xB414 }, +{ 0xB415, 0xB415, 0xB415 }, +{ 0xB416, 0xB416, 0xB416 }, +{ 0xB417, 0xB417, 0xB417 }, +{ 0xB418, 0xB418, 0xB418 }, +{ 0xB419, 0xB419, 0xB419 }, +{ 0xB41A, 0xB41A, 0xB41A }, +{ 0xB41B, 0xB41B, 0xB41B }, +{ 0xB41C, 0xB41C, 0xB41C }, +{ 0xB41D, 0xB41D, 0xB41D }, +{ 0xB41E, 0xB41E, 0xB41E }, +{ 0xB41F, 0xB41F, 0xB41F }, +{ 0xB420, 0xB420, 0xB420 }, +{ 0xB421, 0xB421, 0xB421 }, +{ 0xB422, 0xB422, 0xB422 }, +{ 0xB423, 0xB423, 0xB423 }, +{ 0xB424, 0xB424, 0xB424 }, +{ 0xB425, 0xB425, 0xB425 }, +{ 0xB426, 0xB426, 0xB426 }, +{ 0xB427, 0xB427, 0xB427 }, +{ 0xB428, 0xB428, 0xB428 }, +{ 0xB429, 0xB429, 0xB429 }, +{ 0xB42A, 0xB42A, 0xB42A }, +{ 0xB42B, 0xB42B, 0xB42B }, +{ 0xB42C, 0xB42C, 0xB42C }, +{ 0xB42D, 0xB42D, 0xB42D }, +{ 0xB42E, 0xB42E, 0xB42E }, +{ 0xB42F, 0xB42F, 0xB42F }, +{ 0xB430, 0xB430, 0xB430 }, +{ 0xB431, 0xB431, 0xB431 }, +{ 0xB432, 0xB432, 0xB432 }, +{ 0xB433, 0xB433, 0xB433 }, +{ 0xB434, 0xB434, 0xB434 }, +{ 0xB435, 0xB435, 0xB435 }, +{ 0xB436, 0xB436, 0xB436 }, +{ 0xB437, 0xB437, 0xB437 }, +{ 0xB438, 0xB438, 0xB438 }, +{ 0xB439, 0xB439, 0xB439 }, +{ 0xB43A, 0xB43A, 0xB43A }, +{ 0xB43B, 0xB43B, 0xB43B }, +{ 0xB43C, 0xB43C, 0xB43C }, +{ 0xB43D, 0xB43D, 0xB43D }, +{ 0xB43E, 0xB43E, 0xB43E }, +{ 0xB43F, 0xB43F, 0xB43F }, +{ 0xB440, 0xB440, 0xB440 }, +{ 0xB441, 0xB441, 0xB441 }, +{ 0xB442, 0xB442, 0xB442 }, +{ 0xB443, 0xB443, 0xB443 }, +{ 0xB444, 0xB444, 0xB444 }, +{ 0xB445, 0xB445, 0xB445 }, +{ 0xB446, 0xB446, 0xB446 }, +{ 0xB447, 0xB447, 0xB447 }, +{ 0xB448, 0xB448, 0xB448 }, +{ 0xB449, 0xB449, 0xB449 }, +{ 0xB44A, 0xB44A, 0xB44A }, +{ 0xB44B, 0xB44B, 0xB44B }, +{ 0xB44C, 0xB44C, 0xB44C }, +{ 0xB44D, 0xB44D, 0xB44D }, +{ 0xB44E, 0xB44E, 0xB44E }, +{ 0xB44F, 0xB44F, 0xB44F }, +{ 0xB450, 0xB450, 0xB450 }, +{ 0xB451, 0xB451, 0xB451 }, +{ 0xB452, 0xB452, 0xB452 }, +{ 0xB453, 0xB453, 0xB453 }, +{ 0xB454, 0xB454, 0xB454 }, +{ 0xB455, 0xB455, 0xB455 }, +{ 0xB456, 0xB456, 0xB456 }, +{ 0xB457, 0xB457, 0xB457 }, +{ 0xB458, 0xB458, 0xB458 }, +{ 0xB459, 0xB459, 0xB459 }, +{ 0xB45A, 0xB45A, 0xB45A }, +{ 0xB45B, 0xB45B, 0xB45B }, +{ 0xB45C, 0xB45C, 0xB45C }, +{ 0xB45D, 0xB45D, 0xB45D }, +{ 0xB45E, 0xB45E, 0xB45E }, +{ 0xB45F, 0xB45F, 0xB45F }, +{ 0xB460, 0xB460, 0xB460 }, +{ 0xB461, 0xB461, 0xB461 }, +{ 0xB462, 0xB462, 0xB462 }, +{ 0xB463, 0xB463, 0xB463 }, +{ 0xB464, 0xB464, 0xB464 }, +{ 0xB465, 0xB465, 0xB465 }, +{ 0xB466, 0xB466, 0xB466 }, +{ 0xB467, 0xB467, 0xB467 }, +{ 0xB468, 0xB468, 0xB468 }, +{ 0xB469, 0xB469, 0xB469 }, +{ 0xB46A, 0xB46A, 0xB46A }, +{ 0xB46B, 0xB46B, 0xB46B }, +{ 0xB46C, 0xB46C, 0xB46C }, +{ 0xB46D, 0xB46D, 0xB46D }, +{ 0xB46E, 0xB46E, 0xB46E }, +{ 0xB46F, 0xB46F, 0xB46F }, +{ 0xB470, 0xB470, 0xB470 }, +{ 0xB471, 0xB471, 0xB471 }, +{ 0xB472, 0xB472, 0xB472 }, +{ 0xB473, 0xB473, 0xB473 }, +{ 0xB474, 0xB474, 0xB474 }, +{ 0xB475, 0xB475, 0xB475 }, +{ 0xB476, 0xB476, 0xB476 }, +{ 0xB477, 0xB477, 0xB477 }, +{ 0xB478, 0xB478, 0xB478 }, +{ 0xB479, 0xB479, 0xB479 }, +{ 0xB47A, 0xB47A, 0xB47A }, +{ 0xB47B, 0xB47B, 0xB47B }, +{ 0xB47C, 0xB47C, 0xB47C }, +{ 0xB47D, 0xB47D, 0xB47D }, +{ 0xB47E, 0xB47E, 0xB47E }, +{ 0xB47F, 0xB47F, 0xB47F }, +{ 0xB480, 0xB480, 0xB480 }, +{ 0xB481, 0xB481, 0xB481 }, +{ 0xB482, 0xB482, 0xB482 }, +{ 0xB483, 0xB483, 0xB483 }, +{ 0xB484, 0xB484, 0xB484 }, +{ 0xB485, 0xB485, 0xB485 }, +{ 0xB486, 0xB486, 0xB486 }, +{ 0xB487, 0xB487, 0xB487 }, +{ 0xB488, 0xB488, 0xB488 }, +{ 0xB489, 0xB489, 0xB489 }, +{ 0xB48A, 0xB48A, 0xB48A }, +{ 0xB48B, 0xB48B, 0xB48B }, +{ 0xB48C, 0xB48C, 0xB48C }, +{ 0xB48D, 0xB48D, 0xB48D }, +{ 0xB48E, 0xB48E, 0xB48E }, +{ 0xB48F, 0xB48F, 0xB48F }, +{ 0xB490, 0xB490, 0xB490 }, +{ 0xB491, 0xB491, 0xB491 }, +{ 0xB492, 0xB492, 0xB492 }, +{ 0xB493, 0xB493, 0xB493 }, +{ 0xB494, 0xB494, 0xB494 }, +{ 0xB495, 0xB495, 0xB495 }, +{ 0xB496, 0xB496, 0xB496 }, +{ 0xB497, 0xB497, 0xB497 }, +{ 0xB498, 0xB498, 0xB498 }, +{ 0xB499, 0xB499, 0xB499 }, +{ 0xB49A, 0xB49A, 0xB49A }, +{ 0xB49B, 0xB49B, 0xB49B }, +{ 0xB49C, 0xB49C, 0xB49C }, +{ 0xB49D, 0xB49D, 0xB49D }, +{ 0xB49E, 0xB49E, 0xB49E }, +{ 0xB49F, 0xB49F, 0xB49F }, +{ 0xB4A0, 0xB4A0, 0xB4A0 }, +{ 0xB4A1, 0xB4A1, 0xB4A1 }, +{ 0xB4A2, 0xB4A2, 0xB4A2 }, +{ 0xB4A3, 0xB4A3, 0xB4A3 }, +{ 0xB4A4, 0xB4A4, 0xB4A4 }, +{ 0xB4A5, 0xB4A5, 0xB4A5 }, +{ 0xB4A6, 0xB4A6, 0xB4A6 }, +{ 0xB4A7, 0xB4A7, 0xB4A7 }, +{ 0xB4A8, 0xB4A8, 0xB4A8 }, +{ 0xB4A9, 0xB4A9, 0xB4A9 }, +{ 0xB4AA, 0xB4AA, 0xB4AA }, +{ 0xB4AB, 0xB4AB, 0xB4AB }, +{ 0xB4AC, 0xB4AC, 0xB4AC }, +{ 0xB4AD, 0xB4AD, 0xB4AD }, +{ 0xB4AE, 0xB4AE, 0xB4AE }, +{ 0xB4AF, 0xB4AF, 0xB4AF }, +{ 0xB4B0, 0xB4B0, 0xB4B0 }, +{ 0xB4B1, 0xB4B1, 0xB4B1 }, +{ 0xB4B2, 0xB4B2, 0xB4B2 }, +{ 0xB4B3, 0xB4B3, 0xB4B3 }, +{ 0xB4B4, 0xB4B4, 0xB4B4 }, +{ 0xB4B5, 0xB4B5, 0xB4B5 }, +{ 0xB4B6, 0xB4B6, 0xB4B6 }, +{ 0xB4B7, 0xB4B7, 0xB4B7 }, +{ 0xB4B8, 0xB4B8, 0xB4B8 }, +{ 0xB4B9, 0xB4B9, 0xB4B9 }, +{ 0xB4BA, 0xB4BA, 0xB4BA }, +{ 0xB4BB, 0xB4BB, 0xB4BB }, +{ 0xB4BC, 0xB4BC, 0xB4BC }, +{ 0xB4BD, 0xB4BD, 0xB4BD }, +{ 0xB4BE, 0xB4BE, 0xB4BE }, +{ 0xB4BF, 0xB4BF, 0xB4BF }, +{ 0xB4C0, 0xB4C0, 0xB4C0 }, +{ 0xB4C1, 0xB4C1, 0xB4C1 }, +{ 0xB4C2, 0xB4C2, 0xB4C2 }, +{ 0xB4C3, 0xB4C3, 0xB4C3 }, +{ 0xB4C4, 0xB4C4, 0xB4C4 }, +{ 0xB4C5, 0xB4C5, 0xB4C5 }, +{ 0xB4C6, 0xB4C6, 0xB4C6 }, +{ 0xB4C7, 0xB4C7, 0xB4C7 }, +{ 0xB4C8, 0xB4C8, 0xB4C8 }, +{ 0xB4C9, 0xB4C9, 0xB4C9 }, +{ 0xB4CA, 0xB4CA, 0xB4CA }, +{ 0xB4CB, 0xB4CB, 0xB4CB }, +{ 0xB4CC, 0xB4CC, 0xB4CC }, +{ 0xB4CD, 0xB4CD, 0xB4CD }, +{ 0xB4CE, 0xB4CE, 0xB4CE }, +{ 0xB4CF, 0xB4CF, 0xB4CF }, +{ 0xB4D0, 0xB4D0, 0xB4D0 }, +{ 0xB4D1, 0xB4D1, 0xB4D1 }, +{ 0xB4D2, 0xB4D2, 0xB4D2 }, +{ 0xB4D3, 0xB4D3, 0xB4D3 }, +{ 0xB4D4, 0xB4D4, 0xB4D4 }, +{ 0xB4D5, 0xB4D5, 0xB4D5 }, +{ 0xB4D6, 0xB4D6, 0xB4D6 }, +{ 0xB4D7, 0xB4D7, 0xB4D7 }, +{ 0xB4D8, 0xB4D8, 0xB4D8 }, +{ 0xB4D9, 0xB4D9, 0xB4D9 }, +{ 0xB4DA, 0xB4DA, 0xB4DA }, +{ 0xB4DB, 0xB4DB, 0xB4DB }, +{ 0xB4DC, 0xB4DC, 0xB4DC }, +{ 0xB4DD, 0xB4DD, 0xB4DD }, +{ 0xB4DE, 0xB4DE, 0xB4DE }, +{ 0xB4DF, 0xB4DF, 0xB4DF }, +{ 0xB4E0, 0xB4E0, 0xB4E0 }, +{ 0xB4E1, 0xB4E1, 0xB4E1 }, +{ 0xB4E2, 0xB4E2, 0xB4E2 }, +{ 0xB4E3, 0xB4E3, 0xB4E3 }, +{ 0xB4E4, 0xB4E4, 0xB4E4 }, +{ 0xB4E5, 0xB4E5, 0xB4E5 }, +{ 0xB4E6, 0xB4E6, 0xB4E6 }, +{ 0xB4E7, 0xB4E7, 0xB4E7 }, +{ 0xB4E8, 0xB4E8, 0xB4E8 }, +{ 0xB4E9, 0xB4E9, 0xB4E9 }, +{ 0xB4EA, 0xB4EA, 0xB4EA }, +{ 0xB4EB, 0xB4EB, 0xB4EB }, +{ 0xB4EC, 0xB4EC, 0xB4EC }, +{ 0xB4ED, 0xB4ED, 0xB4ED }, +{ 0xB4EE, 0xB4EE, 0xB4EE }, +{ 0xB4EF, 0xB4EF, 0xB4EF }, +{ 0xB4F0, 0xB4F0, 0xB4F0 }, +{ 0xB4F1, 0xB4F1, 0xB4F1 }, +{ 0xB4F2, 0xB4F2, 0xB4F2 }, +{ 0xB4F3, 0xB4F3, 0xB4F3 }, +{ 0xB4F4, 0xB4F4, 0xB4F4 }, +{ 0xB4F5, 0xB4F5, 0xB4F5 }, +{ 0xB4F6, 0xB4F6, 0xB4F6 }, +{ 0xB4F7, 0xB4F7, 0xB4F7 }, +{ 0xB4F8, 0xB4F8, 0xB4F8 }, +{ 0xB4F9, 0xB4F9, 0xB4F9 }, +{ 0xB4FA, 0xB4FA, 0xB4FA }, +{ 0xB4FB, 0xB4FB, 0xB4FB }, +{ 0xB4FC, 0xB4FC, 0xB4FC }, +{ 0xB4FD, 0xB4FD, 0xB4FD }, +{ 0xB4FE, 0xB4FE, 0xB4FE }, +{ 0xB4FF, 0xB4FF, 0xB4FF }, +{ 0xB500, 0xB500, 0xB500 }, +{ 0xB501, 0xB501, 0xB501 }, +{ 0xB502, 0xB502, 0xB502 }, +{ 0xB503, 0xB503, 0xB503 }, +{ 0xB504, 0xB504, 0xB504 }, +{ 0xB505, 0xB505, 0xB505 }, +{ 0xB506, 0xB506, 0xB506 }, +{ 0xB507, 0xB507, 0xB507 }, +{ 0xB508, 0xB508, 0xB508 }, +{ 0xB509, 0xB509, 0xB509 }, +{ 0xB50A, 0xB50A, 0xB50A }, +{ 0xB50B, 0xB50B, 0xB50B }, +{ 0xB50C, 0xB50C, 0xB50C }, +{ 0xB50D, 0xB50D, 0xB50D }, +{ 0xB50E, 0xB50E, 0xB50E }, +{ 0xB50F, 0xB50F, 0xB50F }, +{ 0xB510, 0xB510, 0xB510 }, +{ 0xB511, 0xB511, 0xB511 }, +{ 0xB512, 0xB512, 0xB512 }, +{ 0xB513, 0xB513, 0xB513 }, +{ 0xB514, 0xB514, 0xB514 }, +{ 0xB515, 0xB515, 0xB515 }, +{ 0xB516, 0xB516, 0xB516 }, +{ 0xB517, 0xB517, 0xB517 }, +{ 0xB518, 0xB518, 0xB518 }, +{ 0xB519, 0xB519, 0xB519 }, +{ 0xB51A, 0xB51A, 0xB51A }, +{ 0xB51B, 0xB51B, 0xB51B }, +{ 0xB51C, 0xB51C, 0xB51C }, +{ 0xB51D, 0xB51D, 0xB51D }, +{ 0xB51E, 0xB51E, 0xB51E }, +{ 0xB51F, 0xB51F, 0xB51F }, +{ 0xB520, 0xB520, 0xB520 }, +{ 0xB521, 0xB521, 0xB521 }, +{ 0xB522, 0xB522, 0xB522 }, +{ 0xB523, 0xB523, 0xB523 }, +{ 0xB524, 0xB524, 0xB524 }, +{ 0xB525, 0xB525, 0xB525 }, +{ 0xB526, 0xB526, 0xB526 }, +{ 0xB527, 0xB527, 0xB527 }, +{ 0xB528, 0xB528, 0xB528 }, +{ 0xB529, 0xB529, 0xB529 }, +{ 0xB52A, 0xB52A, 0xB52A }, +{ 0xB52B, 0xB52B, 0xB52B }, +{ 0xB52C, 0xB52C, 0xB52C }, +{ 0xB52D, 0xB52D, 0xB52D }, +{ 0xB52E, 0xB52E, 0xB52E }, +{ 0xB52F, 0xB52F, 0xB52F }, +{ 0xB530, 0xB530, 0xB530 }, +{ 0xB531, 0xB531, 0xB531 }, +{ 0xB532, 0xB532, 0xB532 }, +{ 0xB533, 0xB533, 0xB533 }, +{ 0xB534, 0xB534, 0xB534 }, +{ 0xB535, 0xB535, 0xB535 }, +{ 0xB536, 0xB536, 0xB536 }, +{ 0xB537, 0xB537, 0xB537 }, +{ 0xB538, 0xB538, 0xB538 }, +{ 0xB539, 0xB539, 0xB539 }, +{ 0xB53A, 0xB53A, 0xB53A }, +{ 0xB53B, 0xB53B, 0xB53B }, +{ 0xB53C, 0xB53C, 0xB53C }, +{ 0xB53D, 0xB53D, 0xB53D }, +{ 0xB53E, 0xB53E, 0xB53E }, +{ 0xB53F, 0xB53F, 0xB53F }, +{ 0xB540, 0xB540, 0xB540 }, +{ 0xB541, 0xB541, 0xB541 }, +{ 0xB542, 0xB542, 0xB542 }, +{ 0xB543, 0xB543, 0xB543 }, +{ 0xB544, 0xB544, 0xB544 }, +{ 0xB545, 0xB545, 0xB545 }, +{ 0xB546, 0xB546, 0xB546 }, +{ 0xB547, 0xB547, 0xB547 }, +{ 0xB548, 0xB548, 0xB548 }, +{ 0xB549, 0xB549, 0xB549 }, +{ 0xB54A, 0xB54A, 0xB54A }, +{ 0xB54B, 0xB54B, 0xB54B }, +{ 0xB54C, 0xB54C, 0xB54C }, +{ 0xB54D, 0xB54D, 0xB54D }, +{ 0xB54E, 0xB54E, 0xB54E }, +{ 0xB54F, 0xB54F, 0xB54F }, +{ 0xB550, 0xB550, 0xB550 }, +{ 0xB551, 0xB551, 0xB551 }, +{ 0xB552, 0xB552, 0xB552 }, +{ 0xB553, 0xB553, 0xB553 }, +{ 0xB554, 0xB554, 0xB554 }, +{ 0xB555, 0xB555, 0xB555 }, +{ 0xB556, 0xB556, 0xB556 }, +{ 0xB557, 0xB557, 0xB557 }, +{ 0xB558, 0xB558, 0xB558 }, +{ 0xB559, 0xB559, 0xB559 }, +{ 0xB55A, 0xB55A, 0xB55A }, +{ 0xB55B, 0xB55B, 0xB55B }, +{ 0xB55C, 0xB55C, 0xB55C }, +{ 0xB55D, 0xB55D, 0xB55D }, +{ 0xB55E, 0xB55E, 0xB55E }, +{ 0xB55F, 0xB55F, 0xB55F }, +{ 0xB560, 0xB560, 0xB560 }, +{ 0xB561, 0xB561, 0xB561 }, +{ 0xB562, 0xB562, 0xB562 }, +{ 0xB563, 0xB563, 0xB563 }, +{ 0xB564, 0xB564, 0xB564 }, +{ 0xB565, 0xB565, 0xB565 }, +{ 0xB566, 0xB566, 0xB566 }, +{ 0xB567, 0xB567, 0xB567 }, +{ 0xB568, 0xB568, 0xB568 }, +{ 0xB569, 0xB569, 0xB569 }, +{ 0xB56A, 0xB56A, 0xB56A }, +{ 0xB56B, 0xB56B, 0xB56B }, +{ 0xB56C, 0xB56C, 0xB56C }, +{ 0xB56D, 0xB56D, 0xB56D }, +{ 0xB56E, 0xB56E, 0xB56E }, +{ 0xB56F, 0xB56F, 0xB56F }, +{ 0xB570, 0xB570, 0xB570 }, +{ 0xB571, 0xB571, 0xB571 }, +{ 0xB572, 0xB572, 0xB572 }, +{ 0xB573, 0xB573, 0xB573 }, +{ 0xB574, 0xB574, 0xB574 }, +{ 0xB575, 0xB575, 0xB575 }, +{ 0xB576, 0xB576, 0xB576 }, +{ 0xB577, 0xB577, 0xB577 }, +{ 0xB578, 0xB578, 0xB578 }, +{ 0xB579, 0xB579, 0xB579 }, +{ 0xB57A, 0xB57A, 0xB57A }, +{ 0xB57B, 0xB57B, 0xB57B }, +{ 0xB57C, 0xB57C, 0xB57C }, +{ 0xB57D, 0xB57D, 0xB57D }, +{ 0xB57E, 0xB57E, 0xB57E }, +{ 0xB57F, 0xB57F, 0xB57F }, +{ 0xB580, 0xB580, 0xB580 }, +{ 0xB581, 0xB581, 0xB581 }, +{ 0xB582, 0xB582, 0xB582 }, +{ 0xB583, 0xB583, 0xB583 }, +{ 0xB584, 0xB584, 0xB584 }, +{ 0xB585, 0xB585, 0xB585 }, +{ 0xB586, 0xB586, 0xB586 }, +{ 0xB587, 0xB587, 0xB587 }, +{ 0xB588, 0xB588, 0xB588 }, +{ 0xB589, 0xB589, 0xB589 }, +{ 0xB58A, 0xB58A, 0xB58A }, +{ 0xB58B, 0xB58B, 0xB58B }, +{ 0xB58C, 0xB58C, 0xB58C }, +{ 0xB58D, 0xB58D, 0xB58D }, +{ 0xB58E, 0xB58E, 0xB58E }, +{ 0xB58F, 0xB58F, 0xB58F }, +{ 0xB590, 0xB590, 0xB590 }, +{ 0xB591, 0xB591, 0xB591 }, +{ 0xB592, 0xB592, 0xB592 }, +{ 0xB593, 0xB593, 0xB593 }, +{ 0xB594, 0xB594, 0xB594 }, +{ 0xB595, 0xB595, 0xB595 }, +{ 0xB596, 0xB596, 0xB596 }, +{ 0xB597, 0xB597, 0xB597 }, +{ 0xB598, 0xB598, 0xB598 }, +{ 0xB599, 0xB599, 0xB599 }, +{ 0xB59A, 0xB59A, 0xB59A }, +{ 0xB59B, 0xB59B, 0xB59B }, +{ 0xB59C, 0xB59C, 0xB59C }, +{ 0xB59D, 0xB59D, 0xB59D }, +{ 0xB59E, 0xB59E, 0xB59E }, +{ 0xB59F, 0xB59F, 0xB59F }, +{ 0xB5A0, 0xB5A0, 0xB5A0 }, +{ 0xB5A1, 0xB5A1, 0xB5A1 }, +{ 0xB5A2, 0xB5A2, 0xB5A2 }, +{ 0xB5A3, 0xB5A3, 0xB5A3 }, +{ 0xB5A4, 0xB5A4, 0xB5A4 }, +{ 0xB5A5, 0xB5A5, 0xB5A5 }, +{ 0xB5A6, 0xB5A6, 0xB5A6 }, +{ 0xB5A7, 0xB5A7, 0xB5A7 }, +{ 0xB5A8, 0xB5A8, 0xB5A8 }, +{ 0xB5A9, 0xB5A9, 0xB5A9 }, +{ 0xB5AA, 0xB5AA, 0xB5AA }, +{ 0xB5AB, 0xB5AB, 0xB5AB }, +{ 0xB5AC, 0xB5AC, 0xB5AC }, +{ 0xB5AD, 0xB5AD, 0xB5AD }, +{ 0xB5AE, 0xB5AE, 0xB5AE }, +{ 0xB5AF, 0xB5AF, 0xB5AF }, +{ 0xB5B0, 0xB5B0, 0xB5B0 }, +{ 0xB5B1, 0xB5B1, 0xB5B1 }, +{ 0xB5B2, 0xB5B2, 0xB5B2 }, +{ 0xB5B3, 0xB5B3, 0xB5B3 }, +{ 0xB5B4, 0xB5B4, 0xB5B4 }, +{ 0xB5B5, 0xB5B5, 0xB5B5 }, +{ 0xB5B6, 0xB5B6, 0xB5B6 }, +{ 0xB5B7, 0xB5B7, 0xB5B7 }, +{ 0xB5B8, 0xB5B8, 0xB5B8 }, +{ 0xB5B9, 0xB5B9, 0xB5B9 }, +{ 0xB5BA, 0xB5BA, 0xB5BA }, +{ 0xB5BB, 0xB5BB, 0xB5BB }, +{ 0xB5BC, 0xB5BC, 0xB5BC }, +{ 0xB5BD, 0xB5BD, 0xB5BD }, +{ 0xB5BE, 0xB5BE, 0xB5BE }, +{ 0xB5BF, 0xB5BF, 0xB5BF }, +{ 0xB5C0, 0xB5C0, 0xB5C0 }, +{ 0xB5C1, 0xB5C1, 0xB5C1 }, +{ 0xB5C2, 0xB5C2, 0xB5C2 }, +{ 0xB5C3, 0xB5C3, 0xB5C3 }, +{ 0xB5C4, 0xB5C4, 0xB5C4 }, +{ 0xB5C5, 0xB5C5, 0xB5C5 }, +{ 0xB5C6, 0xB5C6, 0xB5C6 }, +{ 0xB5C7, 0xB5C7, 0xB5C7 }, +{ 0xB5C8, 0xB5C8, 0xB5C8 }, +{ 0xB5C9, 0xB5C9, 0xB5C9 }, +{ 0xB5CA, 0xB5CA, 0xB5CA }, +{ 0xB5CB, 0xB5CB, 0xB5CB }, +{ 0xB5CC, 0xB5CC, 0xB5CC }, +{ 0xB5CD, 0xB5CD, 0xB5CD }, +{ 0xB5CE, 0xB5CE, 0xB5CE }, +{ 0xB5CF, 0xB5CF, 0xB5CF }, +{ 0xB5D0, 0xB5D0, 0xB5D0 }, +{ 0xB5D1, 0xB5D1, 0xB5D1 }, +{ 0xB5D2, 0xB5D2, 0xB5D2 }, +{ 0xB5D3, 0xB5D3, 0xB5D3 }, +{ 0xB5D4, 0xB5D4, 0xB5D4 }, +{ 0xB5D5, 0xB5D5, 0xB5D5 }, +{ 0xB5D6, 0xB5D6, 0xB5D6 }, +{ 0xB5D7, 0xB5D7, 0xB5D7 }, +{ 0xB5D8, 0xB5D8, 0xB5D8 }, +{ 0xB5D9, 0xB5D9, 0xB5D9 }, +{ 0xB5DA, 0xB5DA, 0xB5DA }, +{ 0xB5DB, 0xB5DB, 0xB5DB }, +{ 0xB5DC, 0xB5DC, 0xB5DC }, +{ 0xB5DD, 0xB5DD, 0xB5DD }, +{ 0xB5DE, 0xB5DE, 0xB5DE }, +{ 0xB5DF, 0xB5DF, 0xB5DF }, +{ 0xB5E0, 0xB5E0, 0xB5E0 }, +{ 0xB5E1, 0xB5E1, 0xB5E1 }, +{ 0xB5E2, 0xB5E2, 0xB5E2 }, +{ 0xB5E3, 0xB5E3, 0xB5E3 }, +{ 0xB5E4, 0xB5E4, 0xB5E4 }, +{ 0xB5E5, 0xB5E5, 0xB5E5 }, +{ 0xB5E6, 0xB5E6, 0xB5E6 }, +{ 0xB5E7, 0xB5E7, 0xB5E7 }, +{ 0xB5E8, 0xB5E8, 0xB5E8 }, +{ 0xB5E9, 0xB5E9, 0xB5E9 }, +{ 0xB5EA, 0xB5EA, 0xB5EA }, +{ 0xB5EB, 0xB5EB, 0xB5EB }, +{ 0xB5EC, 0xB5EC, 0xB5EC }, +{ 0xB5ED, 0xB5ED, 0xB5ED }, +{ 0xB5EE, 0xB5EE, 0xB5EE }, +{ 0xB5EF, 0xB5EF, 0xB5EF }, +{ 0xB5F0, 0xB5F0, 0xB5F0 }, +{ 0xB5F1, 0xB5F1, 0xB5F1 }, +{ 0xB5F2, 0xB5F2, 0xB5F2 }, +{ 0xB5F3, 0xB5F3, 0xB5F3 }, +{ 0xB5F4, 0xB5F4, 0xB5F4 }, +{ 0xB5F5, 0xB5F5, 0xB5F5 }, +{ 0xB5F6, 0xB5F6, 0xB5F6 }, +{ 0xB5F7, 0xB5F7, 0xB5F7 }, +{ 0xB5F8, 0xB5F8, 0xB5F8 }, +{ 0xB5F9, 0xB5F9, 0xB5F9 }, +{ 0xB5FA, 0xB5FA, 0xB5FA }, +{ 0xB5FB, 0xB5FB, 0xB5FB }, +{ 0xB5FC, 0xB5FC, 0xB5FC }, +{ 0xB5FD, 0xB5FD, 0xB5FD }, +{ 0xB5FE, 0xB5FE, 0xB5FE }, +{ 0xB5FF, 0xB5FF, 0xB5FF }, +{ 0xB600, 0xB600, 0xB600 }, +{ 0xB601, 0xB601, 0xB601 }, +{ 0xB602, 0xB602, 0xB602 }, +{ 0xB603, 0xB603, 0xB603 }, +{ 0xB604, 0xB604, 0xB604 }, +{ 0xB605, 0xB605, 0xB605 }, +{ 0xB606, 0xB606, 0xB606 }, +{ 0xB607, 0xB607, 0xB607 }, +{ 0xB608, 0xB608, 0xB608 }, +{ 0xB609, 0xB609, 0xB609 }, +{ 0xB60A, 0xB60A, 0xB60A }, +{ 0xB60B, 0xB60B, 0xB60B }, +{ 0xB60C, 0xB60C, 0xB60C }, +{ 0xB60D, 0xB60D, 0xB60D }, +{ 0xB60E, 0xB60E, 0xB60E }, +{ 0xB60F, 0xB60F, 0xB60F }, +{ 0xB610, 0xB610, 0xB610 }, +{ 0xB611, 0xB611, 0xB611 }, +{ 0xB612, 0xB612, 0xB612 }, +{ 0xB613, 0xB613, 0xB613 }, +{ 0xB614, 0xB614, 0xB614 }, +{ 0xB615, 0xB615, 0xB615 }, +{ 0xB616, 0xB616, 0xB616 }, +{ 0xB617, 0xB617, 0xB617 }, +{ 0xB618, 0xB618, 0xB618 }, +{ 0xB619, 0xB619, 0xB619 }, +{ 0xB61A, 0xB61A, 0xB61A }, +{ 0xB61B, 0xB61B, 0xB61B }, +{ 0xB61C, 0xB61C, 0xB61C }, +{ 0xB61D, 0xB61D, 0xB61D }, +{ 0xB61E, 0xB61E, 0xB61E }, +{ 0xB61F, 0xB61F, 0xB61F }, +{ 0xB620, 0xB620, 0xB620 }, +{ 0xB621, 0xB621, 0xB621 }, +{ 0xB622, 0xB622, 0xB622 }, +{ 0xB623, 0xB623, 0xB623 }, +{ 0xB624, 0xB624, 0xB624 }, +{ 0xB625, 0xB625, 0xB625 }, +{ 0xB626, 0xB626, 0xB626 }, +{ 0xB627, 0xB627, 0xB627 }, +{ 0xB628, 0xB628, 0xB628 }, +{ 0xB629, 0xB629, 0xB629 }, +{ 0xB62A, 0xB62A, 0xB62A }, +{ 0xB62B, 0xB62B, 0xB62B }, +{ 0xB62C, 0xB62C, 0xB62C }, +{ 0xB62D, 0xB62D, 0xB62D }, +{ 0xB62E, 0xB62E, 0xB62E }, +{ 0xB62F, 0xB62F, 0xB62F }, +{ 0xB630, 0xB630, 0xB630 }, +{ 0xB631, 0xB631, 0xB631 }, +{ 0xB632, 0xB632, 0xB632 }, +{ 0xB633, 0xB633, 0xB633 }, +{ 0xB634, 0xB634, 0xB634 }, +{ 0xB635, 0xB635, 0xB635 }, +{ 0xB636, 0xB636, 0xB636 }, +{ 0xB637, 0xB637, 0xB637 }, +{ 0xB638, 0xB638, 0xB638 }, +{ 0xB639, 0xB639, 0xB639 }, +{ 0xB63A, 0xB63A, 0xB63A }, +{ 0xB63B, 0xB63B, 0xB63B }, +{ 0xB63C, 0xB63C, 0xB63C }, +{ 0xB63D, 0xB63D, 0xB63D }, +{ 0xB63E, 0xB63E, 0xB63E }, +{ 0xB63F, 0xB63F, 0xB63F }, +{ 0xB640, 0xB640, 0xB640 }, +{ 0xB641, 0xB641, 0xB641 }, +{ 0xB642, 0xB642, 0xB642 }, +{ 0xB643, 0xB643, 0xB643 }, +{ 0xB644, 0xB644, 0xB644 }, +{ 0xB645, 0xB645, 0xB645 }, +{ 0xB646, 0xB646, 0xB646 }, +{ 0xB647, 0xB647, 0xB647 }, +{ 0xB648, 0xB648, 0xB648 }, +{ 0xB649, 0xB649, 0xB649 }, +{ 0xB64A, 0xB64A, 0xB64A }, +{ 0xB64B, 0xB64B, 0xB64B }, +{ 0xB64C, 0xB64C, 0xB64C }, +{ 0xB64D, 0xB64D, 0xB64D }, +{ 0xB64E, 0xB64E, 0xB64E }, +{ 0xB64F, 0xB64F, 0xB64F }, +{ 0xB650, 0xB650, 0xB650 }, +{ 0xB651, 0xB651, 0xB651 }, +{ 0xB652, 0xB652, 0xB652 }, +{ 0xB653, 0xB653, 0xB653 }, +{ 0xB654, 0xB654, 0xB654 }, +{ 0xB655, 0xB655, 0xB655 }, +{ 0xB656, 0xB656, 0xB656 }, +{ 0xB657, 0xB657, 0xB657 }, +{ 0xB658, 0xB658, 0xB658 }, +{ 0xB659, 0xB659, 0xB659 }, +{ 0xB65A, 0xB65A, 0xB65A }, +{ 0xB65B, 0xB65B, 0xB65B }, +{ 0xB65C, 0xB65C, 0xB65C }, +{ 0xB65D, 0xB65D, 0xB65D }, +{ 0xB65E, 0xB65E, 0xB65E }, +{ 0xB65F, 0xB65F, 0xB65F }, +{ 0xB660, 0xB660, 0xB660 }, +{ 0xB661, 0xB661, 0xB661 }, +{ 0xB662, 0xB662, 0xB662 }, +{ 0xB663, 0xB663, 0xB663 }, +{ 0xB664, 0xB664, 0xB664 }, +{ 0xB665, 0xB665, 0xB665 }, +{ 0xB666, 0xB666, 0xB666 }, +{ 0xB667, 0xB667, 0xB667 }, +{ 0xB668, 0xB668, 0xB668 }, +{ 0xB669, 0xB669, 0xB669 }, +{ 0xB66A, 0xB66A, 0xB66A }, +{ 0xB66B, 0xB66B, 0xB66B }, +{ 0xB66C, 0xB66C, 0xB66C }, +{ 0xB66D, 0xB66D, 0xB66D }, +{ 0xB66E, 0xB66E, 0xB66E }, +{ 0xB66F, 0xB66F, 0xB66F }, +{ 0xB670, 0xB670, 0xB670 }, +{ 0xB671, 0xB671, 0xB671 }, +{ 0xB672, 0xB672, 0xB672 }, +{ 0xB673, 0xB673, 0xB673 }, +{ 0xB674, 0xB674, 0xB674 }, +{ 0xB675, 0xB675, 0xB675 }, +{ 0xB676, 0xB676, 0xB676 }, +{ 0xB677, 0xB677, 0xB677 }, +{ 0xB678, 0xB678, 0xB678 }, +{ 0xB679, 0xB679, 0xB679 }, +{ 0xB67A, 0xB67A, 0xB67A }, +{ 0xB67B, 0xB67B, 0xB67B }, +{ 0xB67C, 0xB67C, 0xB67C }, +{ 0xB67D, 0xB67D, 0xB67D }, +{ 0xB67E, 0xB67E, 0xB67E }, +{ 0xB67F, 0xB67F, 0xB67F }, +{ 0xB680, 0xB680, 0xB680 }, +{ 0xB681, 0xB681, 0xB681 }, +{ 0xB682, 0xB682, 0xB682 }, +{ 0xB683, 0xB683, 0xB683 }, +{ 0xB684, 0xB684, 0xB684 }, +{ 0xB685, 0xB685, 0xB685 }, +{ 0xB686, 0xB686, 0xB686 }, +{ 0xB687, 0xB687, 0xB687 }, +{ 0xB688, 0xB688, 0xB688 }, +{ 0xB689, 0xB689, 0xB689 }, +{ 0xB68A, 0xB68A, 0xB68A }, +{ 0xB68B, 0xB68B, 0xB68B }, +{ 0xB68C, 0xB68C, 0xB68C }, +{ 0xB68D, 0xB68D, 0xB68D }, +{ 0xB68E, 0xB68E, 0xB68E }, +{ 0xB68F, 0xB68F, 0xB68F }, +{ 0xB690, 0xB690, 0xB690 }, +{ 0xB691, 0xB691, 0xB691 }, +{ 0xB692, 0xB692, 0xB692 }, +{ 0xB693, 0xB693, 0xB693 }, +{ 0xB694, 0xB694, 0xB694 }, +{ 0xB695, 0xB695, 0xB695 }, +{ 0xB696, 0xB696, 0xB696 }, +{ 0xB697, 0xB697, 0xB697 }, +{ 0xB698, 0xB698, 0xB698 }, +{ 0xB699, 0xB699, 0xB699 }, +{ 0xB69A, 0xB69A, 0xB69A }, +{ 0xB69B, 0xB69B, 0xB69B }, +{ 0xB69C, 0xB69C, 0xB69C }, +{ 0xB69D, 0xB69D, 0xB69D }, +{ 0xB69E, 0xB69E, 0xB69E }, +{ 0xB69F, 0xB69F, 0xB69F }, +{ 0xB6A0, 0xB6A0, 0xB6A0 }, +{ 0xB6A1, 0xB6A1, 0xB6A1 }, +{ 0xB6A2, 0xB6A2, 0xB6A2 }, +{ 0xB6A3, 0xB6A3, 0xB6A3 }, +{ 0xB6A4, 0xB6A4, 0xB6A4 }, +{ 0xB6A5, 0xB6A5, 0xB6A5 }, +{ 0xB6A6, 0xB6A6, 0xB6A6 }, +{ 0xB6A7, 0xB6A7, 0xB6A7 }, +{ 0xB6A8, 0xB6A8, 0xB6A8 }, +{ 0xB6A9, 0xB6A9, 0xB6A9 }, +{ 0xB6AA, 0xB6AA, 0xB6AA }, +{ 0xB6AB, 0xB6AB, 0xB6AB }, +{ 0xB6AC, 0xB6AC, 0xB6AC }, +{ 0xB6AD, 0xB6AD, 0xB6AD }, +{ 0xB6AE, 0xB6AE, 0xB6AE }, +{ 0xB6AF, 0xB6AF, 0xB6AF }, +{ 0xB6B0, 0xB6B0, 0xB6B0 }, +{ 0xB6B1, 0xB6B1, 0xB6B1 }, +{ 0xB6B2, 0xB6B2, 0xB6B2 }, +{ 0xB6B3, 0xB6B3, 0xB6B3 }, +{ 0xB6B4, 0xB6B4, 0xB6B4 }, +{ 0xB6B5, 0xB6B5, 0xB6B5 }, +{ 0xB6B6, 0xB6B6, 0xB6B6 }, +{ 0xB6B7, 0xB6B7, 0xB6B7 }, +{ 0xB6B8, 0xB6B8, 0xB6B8 }, +{ 0xB6B9, 0xB6B9, 0xB6B9 }, +{ 0xB6BA, 0xB6BA, 0xB6BA }, +{ 0xB6BB, 0xB6BB, 0xB6BB }, +{ 0xB6BC, 0xB6BC, 0xB6BC }, +{ 0xB6BD, 0xB6BD, 0xB6BD }, +{ 0xB6BE, 0xB6BE, 0xB6BE }, +{ 0xB6BF, 0xB6BF, 0xB6BF }, +{ 0xB6C0, 0xB6C0, 0xB6C0 }, +{ 0xB6C1, 0xB6C1, 0xB6C1 }, +{ 0xB6C2, 0xB6C2, 0xB6C2 }, +{ 0xB6C3, 0xB6C3, 0xB6C3 }, +{ 0xB6C4, 0xB6C4, 0xB6C4 }, +{ 0xB6C5, 0xB6C5, 0xB6C5 }, +{ 0xB6C6, 0xB6C6, 0xB6C6 }, +{ 0xB6C7, 0xB6C7, 0xB6C7 }, +{ 0xB6C8, 0xB6C8, 0xB6C8 }, +{ 0xB6C9, 0xB6C9, 0xB6C9 }, +{ 0xB6CA, 0xB6CA, 0xB6CA }, +{ 0xB6CB, 0xB6CB, 0xB6CB }, +{ 0xB6CC, 0xB6CC, 0xB6CC }, +{ 0xB6CD, 0xB6CD, 0xB6CD }, +{ 0xB6CE, 0xB6CE, 0xB6CE }, +{ 0xB6CF, 0xB6CF, 0xB6CF }, +{ 0xB6D0, 0xB6D0, 0xB6D0 }, +{ 0xB6D1, 0xB6D1, 0xB6D1 }, +{ 0xB6D2, 0xB6D2, 0xB6D2 }, +{ 0xB6D3, 0xB6D3, 0xB6D3 }, +{ 0xB6D4, 0xB6D4, 0xB6D4 }, +{ 0xB6D5, 0xB6D5, 0xB6D5 }, +{ 0xB6D6, 0xB6D6, 0xB6D6 }, +{ 0xB6D7, 0xB6D7, 0xB6D7 }, +{ 0xB6D8, 0xB6D8, 0xB6D8 }, +{ 0xB6D9, 0xB6D9, 0xB6D9 }, +{ 0xB6DA, 0xB6DA, 0xB6DA }, +{ 0xB6DB, 0xB6DB, 0xB6DB }, +{ 0xB6DC, 0xB6DC, 0xB6DC }, +{ 0xB6DD, 0xB6DD, 0xB6DD }, +{ 0xB6DE, 0xB6DE, 0xB6DE }, +{ 0xB6DF, 0xB6DF, 0xB6DF }, +{ 0xB6E0, 0xB6E0, 0xB6E0 }, +{ 0xB6E1, 0xB6E1, 0xB6E1 }, +{ 0xB6E2, 0xB6E2, 0xB6E2 }, +{ 0xB6E3, 0xB6E3, 0xB6E3 }, +{ 0xB6E4, 0xB6E4, 0xB6E4 }, +{ 0xB6E5, 0xB6E5, 0xB6E5 }, +{ 0xB6E6, 0xB6E6, 0xB6E6 }, +{ 0xB6E7, 0xB6E7, 0xB6E7 }, +{ 0xB6E8, 0xB6E8, 0xB6E8 }, +{ 0xB6E9, 0xB6E9, 0xB6E9 }, +{ 0xB6EA, 0xB6EA, 0xB6EA }, +{ 0xB6EB, 0xB6EB, 0xB6EB }, +{ 0xB6EC, 0xB6EC, 0xB6EC }, +{ 0xB6ED, 0xB6ED, 0xB6ED }, +{ 0xB6EE, 0xB6EE, 0xB6EE }, +{ 0xB6EF, 0xB6EF, 0xB6EF }, +{ 0xB6F0, 0xB6F0, 0xB6F0 }, +{ 0xB6F1, 0xB6F1, 0xB6F1 }, +{ 0xB6F2, 0xB6F2, 0xB6F2 }, +{ 0xB6F3, 0xB6F3, 0xB6F3 }, +{ 0xB6F4, 0xB6F4, 0xB6F4 }, +{ 0xB6F5, 0xB6F5, 0xB6F5 }, +{ 0xB6F6, 0xB6F6, 0xB6F6 }, +{ 0xB6F7, 0xB6F7, 0xB6F7 }, +{ 0xB6F8, 0xB6F8, 0xB6F8 }, +{ 0xB6F9, 0xB6F9, 0xB6F9 }, +{ 0xB6FA, 0xB6FA, 0xB6FA }, +{ 0xB6FB, 0xB6FB, 0xB6FB }, +{ 0xB6FC, 0xB6FC, 0xB6FC }, +{ 0xB6FD, 0xB6FD, 0xB6FD }, +{ 0xB6FE, 0xB6FE, 0xB6FE }, +{ 0xB6FF, 0xB6FF, 0xB6FF }, +{ 0xB700, 0xB700, 0xB700 }, +{ 0xB701, 0xB701, 0xB701 }, +{ 0xB702, 0xB702, 0xB702 }, +{ 0xB703, 0xB703, 0xB703 }, +{ 0xB704, 0xB704, 0xB704 }, +{ 0xB705, 0xB705, 0xB705 }, +{ 0xB706, 0xB706, 0xB706 }, +{ 0xB707, 0xB707, 0xB707 }, +{ 0xB708, 0xB708, 0xB708 }, +{ 0xB709, 0xB709, 0xB709 }, +{ 0xB70A, 0xB70A, 0xB70A }, +{ 0xB70B, 0xB70B, 0xB70B }, +{ 0xB70C, 0xB70C, 0xB70C }, +{ 0xB70D, 0xB70D, 0xB70D }, +{ 0xB70E, 0xB70E, 0xB70E }, +{ 0xB70F, 0xB70F, 0xB70F }, +{ 0xB710, 0xB710, 0xB710 }, +{ 0xB711, 0xB711, 0xB711 }, +{ 0xB712, 0xB712, 0xB712 }, +{ 0xB713, 0xB713, 0xB713 }, +{ 0xB714, 0xB714, 0xB714 }, +{ 0xB715, 0xB715, 0xB715 }, +{ 0xB716, 0xB716, 0xB716 }, +{ 0xB717, 0xB717, 0xB717 }, +{ 0xB718, 0xB718, 0xB718 }, +{ 0xB719, 0xB719, 0xB719 }, +{ 0xB71A, 0xB71A, 0xB71A }, +{ 0xB71B, 0xB71B, 0xB71B }, +{ 0xB71C, 0xB71C, 0xB71C }, +{ 0xB71D, 0xB71D, 0xB71D }, +{ 0xB71E, 0xB71E, 0xB71E }, +{ 0xB71F, 0xB71F, 0xB71F }, +{ 0xB720, 0xB720, 0xB720 }, +{ 0xB721, 0xB721, 0xB721 }, +{ 0xB722, 0xB722, 0xB722 }, +{ 0xB723, 0xB723, 0xB723 }, +{ 0xB724, 0xB724, 0xB724 }, +{ 0xB725, 0xB725, 0xB725 }, +{ 0xB726, 0xB726, 0xB726 }, +{ 0xB727, 0xB727, 0xB727 }, +{ 0xB728, 0xB728, 0xB728 }, +{ 0xB729, 0xB729, 0xB729 }, +{ 0xB72A, 0xB72A, 0xB72A }, +{ 0xB72B, 0xB72B, 0xB72B }, +{ 0xB72C, 0xB72C, 0xB72C }, +{ 0xB72D, 0xB72D, 0xB72D }, +{ 0xB72E, 0xB72E, 0xB72E }, +{ 0xB72F, 0xB72F, 0xB72F }, +{ 0xB730, 0xB730, 0xB730 }, +{ 0xB731, 0xB731, 0xB731 }, +{ 0xB732, 0xB732, 0xB732 }, +{ 0xB733, 0xB733, 0xB733 }, +{ 0xB734, 0xB734, 0xB734 }, +{ 0xB735, 0xB735, 0xB735 }, +{ 0xB736, 0xB736, 0xB736 }, +{ 0xB737, 0xB737, 0xB737 }, +{ 0xB738, 0xB738, 0xB738 }, +{ 0xB739, 0xB739, 0xB739 }, +{ 0xB73A, 0xB73A, 0xB73A }, +{ 0xB73B, 0xB73B, 0xB73B }, +{ 0xB73C, 0xB73C, 0xB73C }, +{ 0xB73D, 0xB73D, 0xB73D }, +{ 0xB73E, 0xB73E, 0xB73E }, +{ 0xB73F, 0xB73F, 0xB73F }, +{ 0xB740, 0xB740, 0xB740 }, +{ 0xB741, 0xB741, 0xB741 }, +{ 0xB742, 0xB742, 0xB742 }, +{ 0xB743, 0xB743, 0xB743 }, +{ 0xB744, 0xB744, 0xB744 }, +{ 0xB745, 0xB745, 0xB745 }, +{ 0xB746, 0xB746, 0xB746 }, +{ 0xB747, 0xB747, 0xB747 }, +{ 0xB748, 0xB748, 0xB748 }, +{ 0xB749, 0xB749, 0xB749 }, +{ 0xB74A, 0xB74A, 0xB74A }, +{ 0xB74B, 0xB74B, 0xB74B }, +{ 0xB74C, 0xB74C, 0xB74C }, +{ 0xB74D, 0xB74D, 0xB74D }, +{ 0xB74E, 0xB74E, 0xB74E }, +{ 0xB74F, 0xB74F, 0xB74F }, +{ 0xB750, 0xB750, 0xB750 }, +{ 0xB751, 0xB751, 0xB751 }, +{ 0xB752, 0xB752, 0xB752 }, +{ 0xB753, 0xB753, 0xB753 }, +{ 0xB754, 0xB754, 0xB754 }, +{ 0xB755, 0xB755, 0xB755 }, +{ 0xB756, 0xB756, 0xB756 }, +{ 0xB757, 0xB757, 0xB757 }, +{ 0xB758, 0xB758, 0xB758 }, +{ 0xB759, 0xB759, 0xB759 }, +{ 0xB75A, 0xB75A, 0xB75A }, +{ 0xB75B, 0xB75B, 0xB75B }, +{ 0xB75C, 0xB75C, 0xB75C }, +{ 0xB75D, 0xB75D, 0xB75D }, +{ 0xB75E, 0xB75E, 0xB75E }, +{ 0xB75F, 0xB75F, 0xB75F }, +{ 0xB760, 0xB760, 0xB760 }, +{ 0xB761, 0xB761, 0xB761 }, +{ 0xB762, 0xB762, 0xB762 }, +{ 0xB763, 0xB763, 0xB763 }, +{ 0xB764, 0xB764, 0xB764 }, +{ 0xB765, 0xB765, 0xB765 }, +{ 0xB766, 0xB766, 0xB766 }, +{ 0xB767, 0xB767, 0xB767 }, +{ 0xB768, 0xB768, 0xB768 }, +{ 0xB769, 0xB769, 0xB769 }, +{ 0xB76A, 0xB76A, 0xB76A }, +{ 0xB76B, 0xB76B, 0xB76B }, +{ 0xB76C, 0xB76C, 0xB76C }, +{ 0xB76D, 0xB76D, 0xB76D }, +{ 0xB76E, 0xB76E, 0xB76E }, +{ 0xB76F, 0xB76F, 0xB76F }, +{ 0xB770, 0xB770, 0xB770 }, +{ 0xB771, 0xB771, 0xB771 }, +{ 0xB772, 0xB772, 0xB772 }, +{ 0xB773, 0xB773, 0xB773 }, +{ 0xB774, 0xB774, 0xB774 }, +{ 0xB775, 0xB775, 0xB775 }, +{ 0xB776, 0xB776, 0xB776 }, +{ 0xB777, 0xB777, 0xB777 }, +{ 0xB778, 0xB778, 0xB778 }, +{ 0xB779, 0xB779, 0xB779 }, +{ 0xB77A, 0xB77A, 0xB77A }, +{ 0xB77B, 0xB77B, 0xB77B }, +{ 0xB77C, 0xB77C, 0xB77C }, +{ 0xB77D, 0xB77D, 0xB77D }, +{ 0xB77E, 0xB77E, 0xB77E }, +{ 0xB77F, 0xB77F, 0xB77F }, +{ 0xB780, 0xB780, 0xB780 }, +{ 0xB781, 0xB781, 0xB781 }, +{ 0xB782, 0xB782, 0xB782 }, +{ 0xB783, 0xB783, 0xB783 }, +{ 0xB784, 0xB784, 0xB784 }, +{ 0xB785, 0xB785, 0xB785 }, +{ 0xB786, 0xB786, 0xB786 }, +{ 0xB787, 0xB787, 0xB787 }, +{ 0xB788, 0xB788, 0xB788 }, +{ 0xB789, 0xB789, 0xB789 }, +{ 0xB78A, 0xB78A, 0xB78A }, +{ 0xB78B, 0xB78B, 0xB78B }, +{ 0xB78C, 0xB78C, 0xB78C }, +{ 0xB78D, 0xB78D, 0xB78D }, +{ 0xB78E, 0xB78E, 0xB78E }, +{ 0xB78F, 0xB78F, 0xB78F }, +{ 0xB790, 0xB790, 0xB790 }, +{ 0xB791, 0xB791, 0xB791 }, +{ 0xB792, 0xB792, 0xB792 }, +{ 0xB793, 0xB793, 0xB793 }, +{ 0xB794, 0xB794, 0xB794 }, +{ 0xB795, 0xB795, 0xB795 }, +{ 0xB796, 0xB796, 0xB796 }, +{ 0xB797, 0xB797, 0xB797 }, +{ 0xB798, 0xB798, 0xB798 }, +{ 0xB799, 0xB799, 0xB799 }, +{ 0xB79A, 0xB79A, 0xB79A }, +{ 0xB79B, 0xB79B, 0xB79B }, +{ 0xB79C, 0xB79C, 0xB79C }, +{ 0xB79D, 0xB79D, 0xB79D }, +{ 0xB79E, 0xB79E, 0xB79E }, +{ 0xB79F, 0xB79F, 0xB79F }, +{ 0xB7A0, 0xB7A0, 0xB7A0 }, +{ 0xB7A1, 0xB7A1, 0xB7A1 }, +{ 0xB7A2, 0xB7A2, 0xB7A2 }, +{ 0xB7A3, 0xB7A3, 0xB7A3 }, +{ 0xB7A4, 0xB7A4, 0xB7A4 }, +{ 0xB7A5, 0xB7A5, 0xB7A5 }, +{ 0xB7A6, 0xB7A6, 0xB7A6 }, +{ 0xB7A7, 0xB7A7, 0xB7A7 }, +{ 0xB7A8, 0xB7A8, 0xB7A8 }, +{ 0xB7A9, 0xB7A9, 0xB7A9 }, +{ 0xB7AA, 0xB7AA, 0xB7AA }, +{ 0xB7AB, 0xB7AB, 0xB7AB }, +{ 0xB7AC, 0xB7AC, 0xB7AC }, +{ 0xB7AD, 0xB7AD, 0xB7AD }, +{ 0xB7AE, 0xB7AE, 0xB7AE }, +{ 0xB7AF, 0xB7AF, 0xB7AF }, +{ 0xB7B0, 0xB7B0, 0xB7B0 }, +{ 0xB7B1, 0xB7B1, 0xB7B1 }, +{ 0xB7B2, 0xB7B2, 0xB7B2 }, +{ 0xB7B3, 0xB7B3, 0xB7B3 }, +{ 0xB7B4, 0xB7B4, 0xB7B4 }, +{ 0xB7B5, 0xB7B5, 0xB7B5 }, +{ 0xB7B6, 0xB7B6, 0xB7B6 }, +{ 0xB7B7, 0xB7B7, 0xB7B7 }, +{ 0xB7B8, 0xB7B8, 0xB7B8 }, +{ 0xB7B9, 0xB7B9, 0xB7B9 }, +{ 0xB7BA, 0xB7BA, 0xB7BA }, +{ 0xB7BB, 0xB7BB, 0xB7BB }, +{ 0xB7BC, 0xB7BC, 0xB7BC }, +{ 0xB7BD, 0xB7BD, 0xB7BD }, +{ 0xB7BE, 0xB7BE, 0xB7BE }, +{ 0xB7BF, 0xB7BF, 0xB7BF }, +{ 0xB7C0, 0xB7C0, 0xB7C0 }, +{ 0xB7C1, 0xB7C1, 0xB7C1 }, +{ 0xB7C2, 0xB7C2, 0xB7C2 }, +{ 0xB7C3, 0xB7C3, 0xB7C3 }, +{ 0xB7C4, 0xB7C4, 0xB7C4 }, +{ 0xB7C5, 0xB7C5, 0xB7C5 }, +{ 0xB7C6, 0xB7C6, 0xB7C6 }, +{ 0xB7C7, 0xB7C7, 0xB7C7 }, +{ 0xB7C8, 0xB7C8, 0xB7C8 }, +{ 0xB7C9, 0xB7C9, 0xB7C9 }, +{ 0xB7CA, 0xB7CA, 0xB7CA }, +{ 0xB7CB, 0xB7CB, 0xB7CB }, +{ 0xB7CC, 0xB7CC, 0xB7CC }, +{ 0xB7CD, 0xB7CD, 0xB7CD }, +{ 0xB7CE, 0xB7CE, 0xB7CE }, +{ 0xB7CF, 0xB7CF, 0xB7CF }, +{ 0xB7D0, 0xB7D0, 0xB7D0 }, +{ 0xB7D1, 0xB7D1, 0xB7D1 }, +{ 0xB7D2, 0xB7D2, 0xB7D2 }, +{ 0xB7D3, 0xB7D3, 0xB7D3 }, +{ 0xB7D4, 0xB7D4, 0xB7D4 }, +{ 0xB7D5, 0xB7D5, 0xB7D5 }, +{ 0xB7D6, 0xB7D6, 0xB7D6 }, +{ 0xB7D7, 0xB7D7, 0xB7D7 }, +{ 0xB7D8, 0xB7D8, 0xB7D8 }, +{ 0xB7D9, 0xB7D9, 0xB7D9 }, +{ 0xB7DA, 0xB7DA, 0xB7DA }, +{ 0xB7DB, 0xB7DB, 0xB7DB }, +{ 0xB7DC, 0xB7DC, 0xB7DC }, +{ 0xB7DD, 0xB7DD, 0xB7DD }, +{ 0xB7DE, 0xB7DE, 0xB7DE }, +{ 0xB7DF, 0xB7DF, 0xB7DF }, +{ 0xB7E0, 0xB7E0, 0xB7E0 }, +{ 0xB7E1, 0xB7E1, 0xB7E1 }, +{ 0xB7E2, 0xB7E2, 0xB7E2 }, +{ 0xB7E3, 0xB7E3, 0xB7E3 }, +{ 0xB7E4, 0xB7E4, 0xB7E4 }, +{ 0xB7E5, 0xB7E5, 0xB7E5 }, +{ 0xB7E6, 0xB7E6, 0xB7E6 }, +{ 0xB7E7, 0xB7E7, 0xB7E7 }, +{ 0xB7E8, 0xB7E8, 0xB7E8 }, +{ 0xB7E9, 0xB7E9, 0xB7E9 }, +{ 0xB7EA, 0xB7EA, 0xB7EA }, +{ 0xB7EB, 0xB7EB, 0xB7EB }, +{ 0xB7EC, 0xB7EC, 0xB7EC }, +{ 0xB7ED, 0xB7ED, 0xB7ED }, +{ 0xB7EE, 0xB7EE, 0xB7EE }, +{ 0xB7EF, 0xB7EF, 0xB7EF }, +{ 0xB7F0, 0xB7F0, 0xB7F0 }, +{ 0xB7F1, 0xB7F1, 0xB7F1 }, +{ 0xB7F2, 0xB7F2, 0xB7F2 }, +{ 0xB7F3, 0xB7F3, 0xB7F3 }, +{ 0xB7F4, 0xB7F4, 0xB7F4 }, +{ 0xB7F5, 0xB7F5, 0xB7F5 }, +{ 0xB7F6, 0xB7F6, 0xB7F6 }, +{ 0xB7F7, 0xB7F7, 0xB7F7 }, +{ 0xB7F8, 0xB7F8, 0xB7F8 }, +{ 0xB7F9, 0xB7F9, 0xB7F9 }, +{ 0xB7FA, 0xB7FA, 0xB7FA }, +{ 0xB7FB, 0xB7FB, 0xB7FB }, +{ 0xB7FC, 0xB7FC, 0xB7FC }, +{ 0xB7FD, 0xB7FD, 0xB7FD }, +{ 0xB7FE, 0xB7FE, 0xB7FE }, +{ 0xB7FF, 0xB7FF, 0xB7FF }, +{ 0xB800, 0xB800, 0xB800 }, +{ 0xB801, 0xB801, 0xB801 }, +{ 0xB802, 0xB802, 0xB802 }, +{ 0xB803, 0xB803, 0xB803 }, +{ 0xB804, 0xB804, 0xB804 }, +{ 0xB805, 0xB805, 0xB805 }, +{ 0xB806, 0xB806, 0xB806 }, +{ 0xB807, 0xB807, 0xB807 }, +{ 0xB808, 0xB808, 0xB808 }, +{ 0xB809, 0xB809, 0xB809 }, +{ 0xB80A, 0xB80A, 0xB80A }, +{ 0xB80B, 0xB80B, 0xB80B }, +{ 0xB80C, 0xB80C, 0xB80C }, +{ 0xB80D, 0xB80D, 0xB80D }, +{ 0xB80E, 0xB80E, 0xB80E }, +{ 0xB80F, 0xB80F, 0xB80F }, +{ 0xB810, 0xB810, 0xB810 }, +{ 0xB811, 0xB811, 0xB811 }, +{ 0xB812, 0xB812, 0xB812 }, +{ 0xB813, 0xB813, 0xB813 }, +{ 0xB814, 0xB814, 0xB814 }, +{ 0xB815, 0xB815, 0xB815 }, +{ 0xB816, 0xB816, 0xB816 }, +{ 0xB817, 0xB817, 0xB817 }, +{ 0xB818, 0xB818, 0xB818 }, +{ 0xB819, 0xB819, 0xB819 }, +{ 0xB81A, 0xB81A, 0xB81A }, +{ 0xB81B, 0xB81B, 0xB81B }, +{ 0xB81C, 0xB81C, 0xB81C }, +{ 0xB81D, 0xB81D, 0xB81D }, +{ 0xB81E, 0xB81E, 0xB81E }, +{ 0xB81F, 0xB81F, 0xB81F }, +{ 0xB820, 0xB820, 0xB820 }, +{ 0xB821, 0xB821, 0xB821 }, +{ 0xB822, 0xB822, 0xB822 }, +{ 0xB823, 0xB823, 0xB823 }, +{ 0xB824, 0xB824, 0xB824 }, +{ 0xB825, 0xB825, 0xB825 }, +{ 0xB826, 0xB826, 0xB826 }, +{ 0xB827, 0xB827, 0xB827 }, +{ 0xB828, 0xB828, 0xB828 }, +{ 0xB829, 0xB829, 0xB829 }, +{ 0xB82A, 0xB82A, 0xB82A }, +{ 0xB82B, 0xB82B, 0xB82B }, +{ 0xB82C, 0xB82C, 0xB82C }, +{ 0xB82D, 0xB82D, 0xB82D }, +{ 0xB82E, 0xB82E, 0xB82E }, +{ 0xB82F, 0xB82F, 0xB82F }, +{ 0xB830, 0xB830, 0xB830 }, +{ 0xB831, 0xB831, 0xB831 }, +{ 0xB832, 0xB832, 0xB832 }, +{ 0xB833, 0xB833, 0xB833 }, +{ 0xB834, 0xB834, 0xB834 }, +{ 0xB835, 0xB835, 0xB835 }, +{ 0xB836, 0xB836, 0xB836 }, +{ 0xB837, 0xB837, 0xB837 }, +{ 0xB838, 0xB838, 0xB838 }, +{ 0xB839, 0xB839, 0xB839 }, +{ 0xB83A, 0xB83A, 0xB83A }, +{ 0xB83B, 0xB83B, 0xB83B }, +{ 0xB83C, 0xB83C, 0xB83C }, +{ 0xB83D, 0xB83D, 0xB83D }, +{ 0xB83E, 0xB83E, 0xB83E }, +{ 0xB83F, 0xB83F, 0xB83F }, +{ 0xB840, 0xB840, 0xB840 }, +{ 0xB841, 0xB841, 0xB841 }, +{ 0xB842, 0xB842, 0xB842 }, +{ 0xB843, 0xB843, 0xB843 }, +{ 0xB844, 0xB844, 0xB844 }, +{ 0xB845, 0xB845, 0xB845 }, +{ 0xB846, 0xB846, 0xB846 }, +{ 0xB847, 0xB847, 0xB847 }, +{ 0xB848, 0xB848, 0xB848 }, +{ 0xB849, 0xB849, 0xB849 }, +{ 0xB84A, 0xB84A, 0xB84A }, +{ 0xB84B, 0xB84B, 0xB84B }, +{ 0xB84C, 0xB84C, 0xB84C }, +{ 0xB84D, 0xB84D, 0xB84D }, +{ 0xB84E, 0xB84E, 0xB84E }, +{ 0xB84F, 0xB84F, 0xB84F }, +{ 0xB850, 0xB850, 0xB850 }, +{ 0xB851, 0xB851, 0xB851 }, +{ 0xB852, 0xB852, 0xB852 }, +{ 0xB853, 0xB853, 0xB853 }, +{ 0xB854, 0xB854, 0xB854 }, +{ 0xB855, 0xB855, 0xB855 }, +{ 0xB856, 0xB856, 0xB856 }, +{ 0xB857, 0xB857, 0xB857 }, +{ 0xB858, 0xB858, 0xB858 }, +{ 0xB859, 0xB859, 0xB859 }, +{ 0xB85A, 0xB85A, 0xB85A }, +{ 0xB85B, 0xB85B, 0xB85B }, +{ 0xB85C, 0xB85C, 0xB85C }, +{ 0xB85D, 0xB85D, 0xB85D }, +{ 0xB85E, 0xB85E, 0xB85E }, +{ 0xB85F, 0xB85F, 0xB85F }, +{ 0xB860, 0xB860, 0xB860 }, +{ 0xB861, 0xB861, 0xB861 }, +{ 0xB862, 0xB862, 0xB862 }, +{ 0xB863, 0xB863, 0xB863 }, +{ 0xB864, 0xB864, 0xB864 }, +{ 0xB865, 0xB865, 0xB865 }, +{ 0xB866, 0xB866, 0xB866 }, +{ 0xB867, 0xB867, 0xB867 }, +{ 0xB868, 0xB868, 0xB868 }, +{ 0xB869, 0xB869, 0xB869 }, +{ 0xB86A, 0xB86A, 0xB86A }, +{ 0xB86B, 0xB86B, 0xB86B }, +{ 0xB86C, 0xB86C, 0xB86C }, +{ 0xB86D, 0xB86D, 0xB86D }, +{ 0xB86E, 0xB86E, 0xB86E }, +{ 0xB86F, 0xB86F, 0xB86F }, +{ 0xB870, 0xB870, 0xB870 }, +{ 0xB871, 0xB871, 0xB871 }, +{ 0xB872, 0xB872, 0xB872 }, +{ 0xB873, 0xB873, 0xB873 }, +{ 0xB874, 0xB874, 0xB874 }, +{ 0xB875, 0xB875, 0xB875 }, +{ 0xB876, 0xB876, 0xB876 }, +{ 0xB877, 0xB877, 0xB877 }, +{ 0xB878, 0xB878, 0xB878 }, +{ 0xB879, 0xB879, 0xB879 }, +{ 0xB87A, 0xB87A, 0xB87A }, +{ 0xB87B, 0xB87B, 0xB87B }, +{ 0xB87C, 0xB87C, 0xB87C }, +{ 0xB87D, 0xB87D, 0xB87D }, +{ 0xB87E, 0xB87E, 0xB87E }, +{ 0xB87F, 0xB87F, 0xB87F }, +{ 0xB880, 0xB880, 0xB880 }, +{ 0xB881, 0xB881, 0xB881 }, +{ 0xB882, 0xB882, 0xB882 }, +{ 0xB883, 0xB883, 0xB883 }, +{ 0xB884, 0xB884, 0xB884 }, +{ 0xB885, 0xB885, 0xB885 }, +{ 0xB886, 0xB886, 0xB886 }, +{ 0xB887, 0xB887, 0xB887 }, +{ 0xB888, 0xB888, 0xB888 }, +{ 0xB889, 0xB889, 0xB889 }, +{ 0xB88A, 0xB88A, 0xB88A }, +{ 0xB88B, 0xB88B, 0xB88B }, +{ 0xB88C, 0xB88C, 0xB88C }, +{ 0xB88D, 0xB88D, 0xB88D }, +{ 0xB88E, 0xB88E, 0xB88E }, +{ 0xB88F, 0xB88F, 0xB88F }, +{ 0xB890, 0xB890, 0xB890 }, +{ 0xB891, 0xB891, 0xB891 }, +{ 0xB892, 0xB892, 0xB892 }, +{ 0xB893, 0xB893, 0xB893 }, +{ 0xB894, 0xB894, 0xB894 }, +{ 0xB895, 0xB895, 0xB895 }, +{ 0xB896, 0xB896, 0xB896 }, +{ 0xB897, 0xB897, 0xB897 }, +{ 0xB898, 0xB898, 0xB898 }, +{ 0xB899, 0xB899, 0xB899 }, +{ 0xB89A, 0xB89A, 0xB89A }, +{ 0xB89B, 0xB89B, 0xB89B }, +{ 0xB89C, 0xB89C, 0xB89C }, +{ 0xB89D, 0xB89D, 0xB89D }, +{ 0xB89E, 0xB89E, 0xB89E }, +{ 0xB89F, 0xB89F, 0xB89F }, +{ 0xB8A0, 0xB8A0, 0xB8A0 }, +{ 0xB8A1, 0xB8A1, 0xB8A1 }, +{ 0xB8A2, 0xB8A2, 0xB8A2 }, +{ 0xB8A3, 0xB8A3, 0xB8A3 }, +{ 0xB8A4, 0xB8A4, 0xB8A4 }, +{ 0xB8A5, 0xB8A5, 0xB8A5 }, +{ 0xB8A6, 0xB8A6, 0xB8A6 }, +{ 0xB8A7, 0xB8A7, 0xB8A7 }, +{ 0xB8A8, 0xB8A8, 0xB8A8 }, +{ 0xB8A9, 0xB8A9, 0xB8A9 }, +{ 0xB8AA, 0xB8AA, 0xB8AA }, +{ 0xB8AB, 0xB8AB, 0xB8AB }, +{ 0xB8AC, 0xB8AC, 0xB8AC }, +{ 0xB8AD, 0xB8AD, 0xB8AD }, +{ 0xB8AE, 0xB8AE, 0xB8AE }, +{ 0xB8AF, 0xB8AF, 0xB8AF }, +{ 0xB8B0, 0xB8B0, 0xB8B0 }, +{ 0xB8B1, 0xB8B1, 0xB8B1 }, +{ 0xB8B2, 0xB8B2, 0xB8B2 }, +{ 0xB8B3, 0xB8B3, 0xB8B3 }, +{ 0xB8B4, 0xB8B4, 0xB8B4 }, +{ 0xB8B5, 0xB8B5, 0xB8B5 }, +{ 0xB8B6, 0xB8B6, 0xB8B6 }, +{ 0xB8B7, 0xB8B7, 0xB8B7 }, +{ 0xB8B8, 0xB8B8, 0xB8B8 }, +{ 0xB8B9, 0xB8B9, 0xB8B9 }, +{ 0xB8BA, 0xB8BA, 0xB8BA }, +{ 0xB8BB, 0xB8BB, 0xB8BB }, +{ 0xB8BC, 0xB8BC, 0xB8BC }, +{ 0xB8BD, 0xB8BD, 0xB8BD }, +{ 0xB8BE, 0xB8BE, 0xB8BE }, +{ 0xB8BF, 0xB8BF, 0xB8BF }, +{ 0xB8C0, 0xB8C0, 0xB8C0 }, +{ 0xB8C1, 0xB8C1, 0xB8C1 }, +{ 0xB8C2, 0xB8C2, 0xB8C2 }, +{ 0xB8C3, 0xB8C3, 0xB8C3 }, +{ 0xB8C4, 0xB8C4, 0xB8C4 }, +{ 0xB8C5, 0xB8C5, 0xB8C5 }, +{ 0xB8C6, 0xB8C6, 0xB8C6 }, +{ 0xB8C7, 0xB8C7, 0xB8C7 }, +{ 0xB8C8, 0xB8C8, 0xB8C8 }, +{ 0xB8C9, 0xB8C9, 0xB8C9 }, +{ 0xB8CA, 0xB8CA, 0xB8CA }, +{ 0xB8CB, 0xB8CB, 0xB8CB }, +{ 0xB8CC, 0xB8CC, 0xB8CC }, +{ 0xB8CD, 0xB8CD, 0xB8CD }, +{ 0xB8CE, 0xB8CE, 0xB8CE }, +{ 0xB8CF, 0xB8CF, 0xB8CF }, +{ 0xB8D0, 0xB8D0, 0xB8D0 }, +{ 0xB8D1, 0xB8D1, 0xB8D1 }, +{ 0xB8D2, 0xB8D2, 0xB8D2 }, +{ 0xB8D3, 0xB8D3, 0xB8D3 }, +{ 0xB8D4, 0xB8D4, 0xB8D4 }, +{ 0xB8D5, 0xB8D5, 0xB8D5 }, +{ 0xB8D6, 0xB8D6, 0xB8D6 }, +{ 0xB8D7, 0xB8D7, 0xB8D7 }, +{ 0xB8D8, 0xB8D8, 0xB8D8 }, +{ 0xB8D9, 0xB8D9, 0xB8D9 }, +{ 0xB8DA, 0xB8DA, 0xB8DA }, +{ 0xB8DB, 0xB8DB, 0xB8DB }, +{ 0xB8DC, 0xB8DC, 0xB8DC }, +{ 0xB8DD, 0xB8DD, 0xB8DD }, +{ 0xB8DE, 0xB8DE, 0xB8DE }, +{ 0xB8DF, 0xB8DF, 0xB8DF }, +{ 0xB8E0, 0xB8E0, 0xB8E0 }, +{ 0xB8E1, 0xB8E1, 0xB8E1 }, +{ 0xB8E2, 0xB8E2, 0xB8E2 }, +{ 0xB8E3, 0xB8E3, 0xB8E3 }, +{ 0xB8E4, 0xB8E4, 0xB8E4 }, +{ 0xB8E5, 0xB8E5, 0xB8E5 }, +{ 0xB8E6, 0xB8E6, 0xB8E6 }, +{ 0xB8E7, 0xB8E7, 0xB8E7 }, +{ 0xB8E8, 0xB8E8, 0xB8E8 }, +{ 0xB8E9, 0xB8E9, 0xB8E9 }, +{ 0xB8EA, 0xB8EA, 0xB8EA }, +{ 0xB8EB, 0xB8EB, 0xB8EB }, +{ 0xB8EC, 0xB8EC, 0xB8EC }, +{ 0xB8ED, 0xB8ED, 0xB8ED }, +{ 0xB8EE, 0xB8EE, 0xB8EE }, +{ 0xB8EF, 0xB8EF, 0xB8EF }, +{ 0xB8F0, 0xB8F0, 0xB8F0 }, +{ 0xB8F1, 0xB8F1, 0xB8F1 }, +{ 0xB8F2, 0xB8F2, 0xB8F2 }, +{ 0xB8F3, 0xB8F3, 0xB8F3 }, +{ 0xB8F4, 0xB8F4, 0xB8F4 }, +{ 0xB8F5, 0xB8F5, 0xB8F5 }, +{ 0xB8F6, 0xB8F6, 0xB8F6 }, +{ 0xB8F7, 0xB8F7, 0xB8F7 }, +{ 0xB8F8, 0xB8F8, 0xB8F8 }, +{ 0xB8F9, 0xB8F9, 0xB8F9 }, +{ 0xB8FA, 0xB8FA, 0xB8FA }, +{ 0xB8FB, 0xB8FB, 0xB8FB }, +{ 0xB8FC, 0xB8FC, 0xB8FC }, +{ 0xB8FD, 0xB8FD, 0xB8FD }, +{ 0xB8FE, 0xB8FE, 0xB8FE }, +{ 0xB8FF, 0xB8FF, 0xB8FF }, +{ 0xB900, 0xB900, 0xB900 }, +{ 0xB901, 0xB901, 0xB901 }, +{ 0xB902, 0xB902, 0xB902 }, +{ 0xB903, 0xB903, 0xB903 }, +{ 0xB904, 0xB904, 0xB904 }, +{ 0xB905, 0xB905, 0xB905 }, +{ 0xB906, 0xB906, 0xB906 }, +{ 0xB907, 0xB907, 0xB907 }, +{ 0xB908, 0xB908, 0xB908 }, +{ 0xB909, 0xB909, 0xB909 }, +{ 0xB90A, 0xB90A, 0xB90A }, +{ 0xB90B, 0xB90B, 0xB90B }, +{ 0xB90C, 0xB90C, 0xB90C }, +{ 0xB90D, 0xB90D, 0xB90D }, +{ 0xB90E, 0xB90E, 0xB90E }, +{ 0xB90F, 0xB90F, 0xB90F }, +{ 0xB910, 0xB910, 0xB910 }, +{ 0xB911, 0xB911, 0xB911 }, +{ 0xB912, 0xB912, 0xB912 }, +{ 0xB913, 0xB913, 0xB913 }, +{ 0xB914, 0xB914, 0xB914 }, +{ 0xB915, 0xB915, 0xB915 }, +{ 0xB916, 0xB916, 0xB916 }, +{ 0xB917, 0xB917, 0xB917 }, +{ 0xB918, 0xB918, 0xB918 }, +{ 0xB919, 0xB919, 0xB919 }, +{ 0xB91A, 0xB91A, 0xB91A }, +{ 0xB91B, 0xB91B, 0xB91B }, +{ 0xB91C, 0xB91C, 0xB91C }, +{ 0xB91D, 0xB91D, 0xB91D }, +{ 0xB91E, 0xB91E, 0xB91E }, +{ 0xB91F, 0xB91F, 0xB91F }, +{ 0xB920, 0xB920, 0xB920 }, +{ 0xB921, 0xB921, 0xB921 }, +{ 0xB922, 0xB922, 0xB922 }, +{ 0xB923, 0xB923, 0xB923 }, +{ 0xB924, 0xB924, 0xB924 }, +{ 0xB925, 0xB925, 0xB925 }, +{ 0xB926, 0xB926, 0xB926 }, +{ 0xB927, 0xB927, 0xB927 }, +{ 0xB928, 0xB928, 0xB928 }, +{ 0xB929, 0xB929, 0xB929 }, +{ 0xB92A, 0xB92A, 0xB92A }, +{ 0xB92B, 0xB92B, 0xB92B }, +{ 0xB92C, 0xB92C, 0xB92C }, +{ 0xB92D, 0xB92D, 0xB92D }, +{ 0xB92E, 0xB92E, 0xB92E }, +{ 0xB92F, 0xB92F, 0xB92F }, +{ 0xB930, 0xB930, 0xB930 }, +{ 0xB931, 0xB931, 0xB931 }, +{ 0xB932, 0xB932, 0xB932 }, +{ 0xB933, 0xB933, 0xB933 }, +{ 0xB934, 0xB934, 0xB934 }, +{ 0xB935, 0xB935, 0xB935 }, +{ 0xB936, 0xB936, 0xB936 }, +{ 0xB937, 0xB937, 0xB937 }, +{ 0xB938, 0xB938, 0xB938 }, +{ 0xB939, 0xB939, 0xB939 }, +{ 0xB93A, 0xB93A, 0xB93A }, +{ 0xB93B, 0xB93B, 0xB93B }, +{ 0xB93C, 0xB93C, 0xB93C }, +{ 0xB93D, 0xB93D, 0xB93D }, +{ 0xB93E, 0xB93E, 0xB93E }, +{ 0xB93F, 0xB93F, 0xB93F }, +{ 0xB940, 0xB940, 0xB940 }, +{ 0xB941, 0xB941, 0xB941 }, +{ 0xB942, 0xB942, 0xB942 }, +{ 0xB943, 0xB943, 0xB943 }, +{ 0xB944, 0xB944, 0xB944 }, +{ 0xB945, 0xB945, 0xB945 }, +{ 0xB946, 0xB946, 0xB946 }, +{ 0xB947, 0xB947, 0xB947 }, +{ 0xB948, 0xB948, 0xB948 }, +{ 0xB949, 0xB949, 0xB949 }, +{ 0xB94A, 0xB94A, 0xB94A }, +{ 0xB94B, 0xB94B, 0xB94B }, +{ 0xB94C, 0xB94C, 0xB94C }, +{ 0xB94D, 0xB94D, 0xB94D }, +{ 0xB94E, 0xB94E, 0xB94E }, +{ 0xB94F, 0xB94F, 0xB94F }, +{ 0xB950, 0xB950, 0xB950 }, +{ 0xB951, 0xB951, 0xB951 }, +{ 0xB952, 0xB952, 0xB952 }, +{ 0xB953, 0xB953, 0xB953 }, +{ 0xB954, 0xB954, 0xB954 }, +{ 0xB955, 0xB955, 0xB955 }, +{ 0xB956, 0xB956, 0xB956 }, +{ 0xB957, 0xB957, 0xB957 }, +{ 0xB958, 0xB958, 0xB958 }, +{ 0xB959, 0xB959, 0xB959 }, +{ 0xB95A, 0xB95A, 0xB95A }, +{ 0xB95B, 0xB95B, 0xB95B }, +{ 0xB95C, 0xB95C, 0xB95C }, +{ 0xB95D, 0xB95D, 0xB95D }, +{ 0xB95E, 0xB95E, 0xB95E }, +{ 0xB95F, 0xB95F, 0xB95F }, +{ 0xB960, 0xB960, 0xB960 }, +{ 0xB961, 0xB961, 0xB961 }, +{ 0xB962, 0xB962, 0xB962 }, +{ 0xB963, 0xB963, 0xB963 }, +{ 0xB964, 0xB964, 0xB964 }, +{ 0xB965, 0xB965, 0xB965 }, +{ 0xB966, 0xB966, 0xB966 }, +{ 0xB967, 0xB967, 0xB967 }, +{ 0xB968, 0xB968, 0xB968 }, +{ 0xB969, 0xB969, 0xB969 }, +{ 0xB96A, 0xB96A, 0xB96A }, +{ 0xB96B, 0xB96B, 0xB96B }, +{ 0xB96C, 0xB96C, 0xB96C }, +{ 0xB96D, 0xB96D, 0xB96D }, +{ 0xB96E, 0xB96E, 0xB96E }, +{ 0xB96F, 0xB96F, 0xB96F }, +{ 0xB970, 0xB970, 0xB970 }, +{ 0xB971, 0xB971, 0xB971 }, +{ 0xB972, 0xB972, 0xB972 }, +{ 0xB973, 0xB973, 0xB973 }, +{ 0xB974, 0xB974, 0xB974 }, +{ 0xB975, 0xB975, 0xB975 }, +{ 0xB976, 0xB976, 0xB976 }, +{ 0xB977, 0xB977, 0xB977 }, +{ 0xB978, 0xB978, 0xB978 }, +{ 0xB979, 0xB979, 0xB979 }, +{ 0xB97A, 0xB97A, 0xB97A }, +{ 0xB97B, 0xB97B, 0xB97B }, +{ 0xB97C, 0xB97C, 0xB97C }, +{ 0xB97D, 0xB97D, 0xB97D }, +{ 0xB97E, 0xB97E, 0xB97E }, +{ 0xB97F, 0xB97F, 0xB97F }, +{ 0xB980, 0xB980, 0xB980 }, +{ 0xB981, 0xB981, 0xB981 }, +{ 0xB982, 0xB982, 0xB982 }, +{ 0xB983, 0xB983, 0xB983 }, +{ 0xB984, 0xB984, 0xB984 }, +{ 0xB985, 0xB985, 0xB985 }, +{ 0xB986, 0xB986, 0xB986 }, +{ 0xB987, 0xB987, 0xB987 }, +{ 0xB988, 0xB988, 0xB988 }, +{ 0xB989, 0xB989, 0xB989 }, +{ 0xB98A, 0xB98A, 0xB98A }, +{ 0xB98B, 0xB98B, 0xB98B }, +{ 0xB98C, 0xB98C, 0xB98C }, +{ 0xB98D, 0xB98D, 0xB98D }, +{ 0xB98E, 0xB98E, 0xB98E }, +{ 0xB98F, 0xB98F, 0xB98F }, +{ 0xB990, 0xB990, 0xB990 }, +{ 0xB991, 0xB991, 0xB991 }, +{ 0xB992, 0xB992, 0xB992 }, +{ 0xB993, 0xB993, 0xB993 }, +{ 0xB994, 0xB994, 0xB994 }, +{ 0xB995, 0xB995, 0xB995 }, +{ 0xB996, 0xB996, 0xB996 }, +{ 0xB997, 0xB997, 0xB997 }, +{ 0xB998, 0xB998, 0xB998 }, +{ 0xB999, 0xB999, 0xB999 }, +{ 0xB99A, 0xB99A, 0xB99A }, +{ 0xB99B, 0xB99B, 0xB99B }, +{ 0xB99C, 0xB99C, 0xB99C }, +{ 0xB99D, 0xB99D, 0xB99D }, +{ 0xB99E, 0xB99E, 0xB99E }, +{ 0xB99F, 0xB99F, 0xB99F }, +{ 0xB9A0, 0xB9A0, 0xB9A0 }, +{ 0xB9A1, 0xB9A1, 0xB9A1 }, +{ 0xB9A2, 0xB9A2, 0xB9A2 }, +{ 0xB9A3, 0xB9A3, 0xB9A3 }, +{ 0xB9A4, 0xB9A4, 0xB9A4 }, +{ 0xB9A5, 0xB9A5, 0xB9A5 }, +{ 0xB9A6, 0xB9A6, 0xB9A6 }, +{ 0xB9A7, 0xB9A7, 0xB9A7 }, +{ 0xB9A8, 0xB9A8, 0xB9A8 }, +{ 0xB9A9, 0xB9A9, 0xB9A9 }, +{ 0xB9AA, 0xB9AA, 0xB9AA }, +{ 0xB9AB, 0xB9AB, 0xB9AB }, +{ 0xB9AC, 0xB9AC, 0xB9AC }, +{ 0xB9AD, 0xB9AD, 0xB9AD }, +{ 0xB9AE, 0xB9AE, 0xB9AE }, +{ 0xB9AF, 0xB9AF, 0xB9AF }, +{ 0xB9B0, 0xB9B0, 0xB9B0 }, +{ 0xB9B1, 0xB9B1, 0xB9B1 }, +{ 0xB9B2, 0xB9B2, 0xB9B2 }, +{ 0xB9B3, 0xB9B3, 0xB9B3 }, +{ 0xB9B4, 0xB9B4, 0xB9B4 }, +{ 0xB9B5, 0xB9B5, 0xB9B5 }, +{ 0xB9B6, 0xB9B6, 0xB9B6 }, +{ 0xB9B7, 0xB9B7, 0xB9B7 }, +{ 0xB9B8, 0xB9B8, 0xB9B8 }, +{ 0xB9B9, 0xB9B9, 0xB9B9 }, +{ 0xB9BA, 0xB9BA, 0xB9BA }, +{ 0xB9BB, 0xB9BB, 0xB9BB }, +{ 0xB9BC, 0xB9BC, 0xB9BC }, +{ 0xB9BD, 0xB9BD, 0xB9BD }, +{ 0xB9BE, 0xB9BE, 0xB9BE }, +{ 0xB9BF, 0xB9BF, 0xB9BF }, +{ 0xB9C0, 0xB9C0, 0xB9C0 }, +{ 0xB9C1, 0xB9C1, 0xB9C1 }, +{ 0xB9C2, 0xB9C2, 0xB9C2 }, +{ 0xB9C3, 0xB9C3, 0xB9C3 }, +{ 0xB9C4, 0xB9C4, 0xB9C4 }, +{ 0xB9C5, 0xB9C5, 0xB9C5 }, +{ 0xB9C6, 0xB9C6, 0xB9C6 }, +{ 0xB9C7, 0xB9C7, 0xB9C7 }, +{ 0xB9C8, 0xB9C8, 0xB9C8 }, +{ 0xB9C9, 0xB9C9, 0xB9C9 }, +{ 0xB9CA, 0xB9CA, 0xB9CA }, +{ 0xB9CB, 0xB9CB, 0xB9CB }, +{ 0xB9CC, 0xB9CC, 0xB9CC }, +{ 0xB9CD, 0xB9CD, 0xB9CD }, +{ 0xB9CE, 0xB9CE, 0xB9CE }, +{ 0xB9CF, 0xB9CF, 0xB9CF }, +{ 0xB9D0, 0xB9D0, 0xB9D0 }, +{ 0xB9D1, 0xB9D1, 0xB9D1 }, +{ 0xB9D2, 0xB9D2, 0xB9D2 }, +{ 0xB9D3, 0xB9D3, 0xB9D3 }, +{ 0xB9D4, 0xB9D4, 0xB9D4 }, +{ 0xB9D5, 0xB9D5, 0xB9D5 }, +{ 0xB9D6, 0xB9D6, 0xB9D6 }, +{ 0xB9D7, 0xB9D7, 0xB9D7 }, +{ 0xB9D8, 0xB9D8, 0xB9D8 }, +{ 0xB9D9, 0xB9D9, 0xB9D9 }, +{ 0xB9DA, 0xB9DA, 0xB9DA }, +{ 0xB9DB, 0xB9DB, 0xB9DB }, +{ 0xB9DC, 0xB9DC, 0xB9DC }, +{ 0xB9DD, 0xB9DD, 0xB9DD }, +{ 0xB9DE, 0xB9DE, 0xB9DE }, +{ 0xB9DF, 0xB9DF, 0xB9DF }, +{ 0xB9E0, 0xB9E0, 0xB9E0 }, +{ 0xB9E1, 0xB9E1, 0xB9E1 }, +{ 0xB9E2, 0xB9E2, 0xB9E2 }, +{ 0xB9E3, 0xB9E3, 0xB9E3 }, +{ 0xB9E4, 0xB9E4, 0xB9E4 }, +{ 0xB9E5, 0xB9E5, 0xB9E5 }, +{ 0xB9E6, 0xB9E6, 0xB9E6 }, +{ 0xB9E7, 0xB9E7, 0xB9E7 }, +{ 0xB9E8, 0xB9E8, 0xB9E8 }, +{ 0xB9E9, 0xB9E9, 0xB9E9 }, +{ 0xB9EA, 0xB9EA, 0xB9EA }, +{ 0xB9EB, 0xB9EB, 0xB9EB }, +{ 0xB9EC, 0xB9EC, 0xB9EC }, +{ 0xB9ED, 0xB9ED, 0xB9ED }, +{ 0xB9EE, 0xB9EE, 0xB9EE }, +{ 0xB9EF, 0xB9EF, 0xB9EF }, +{ 0xB9F0, 0xB9F0, 0xB9F0 }, +{ 0xB9F1, 0xB9F1, 0xB9F1 }, +{ 0xB9F2, 0xB9F2, 0xB9F2 }, +{ 0xB9F3, 0xB9F3, 0xB9F3 }, +{ 0xB9F4, 0xB9F4, 0xB9F4 }, +{ 0xB9F5, 0xB9F5, 0xB9F5 }, +{ 0xB9F6, 0xB9F6, 0xB9F6 }, +{ 0xB9F7, 0xB9F7, 0xB9F7 }, +{ 0xB9F8, 0xB9F8, 0xB9F8 }, +{ 0xB9F9, 0xB9F9, 0xB9F9 }, +{ 0xB9FA, 0xB9FA, 0xB9FA }, +{ 0xB9FB, 0xB9FB, 0xB9FB }, +{ 0xB9FC, 0xB9FC, 0xB9FC }, +{ 0xB9FD, 0xB9FD, 0xB9FD }, +{ 0xB9FE, 0xB9FE, 0xB9FE }, +{ 0xB9FF, 0xB9FF, 0xB9FF }, +{ 0xBA00, 0xBA00, 0xBA00 }, +{ 0xBA01, 0xBA01, 0xBA01 }, +{ 0xBA02, 0xBA02, 0xBA02 }, +{ 0xBA03, 0xBA03, 0xBA03 }, +{ 0xBA04, 0xBA04, 0xBA04 }, +{ 0xBA05, 0xBA05, 0xBA05 }, +{ 0xBA06, 0xBA06, 0xBA06 }, +{ 0xBA07, 0xBA07, 0xBA07 }, +{ 0xBA08, 0xBA08, 0xBA08 }, +{ 0xBA09, 0xBA09, 0xBA09 }, +{ 0xBA0A, 0xBA0A, 0xBA0A }, +{ 0xBA0B, 0xBA0B, 0xBA0B }, +{ 0xBA0C, 0xBA0C, 0xBA0C }, +{ 0xBA0D, 0xBA0D, 0xBA0D }, +{ 0xBA0E, 0xBA0E, 0xBA0E }, +{ 0xBA0F, 0xBA0F, 0xBA0F }, +{ 0xBA10, 0xBA10, 0xBA10 }, +{ 0xBA11, 0xBA11, 0xBA11 }, +{ 0xBA12, 0xBA12, 0xBA12 }, +{ 0xBA13, 0xBA13, 0xBA13 }, +{ 0xBA14, 0xBA14, 0xBA14 }, +{ 0xBA15, 0xBA15, 0xBA15 }, +{ 0xBA16, 0xBA16, 0xBA16 }, +{ 0xBA17, 0xBA17, 0xBA17 }, +{ 0xBA18, 0xBA18, 0xBA18 }, +{ 0xBA19, 0xBA19, 0xBA19 }, +{ 0xBA1A, 0xBA1A, 0xBA1A }, +{ 0xBA1B, 0xBA1B, 0xBA1B }, +{ 0xBA1C, 0xBA1C, 0xBA1C }, +{ 0xBA1D, 0xBA1D, 0xBA1D }, +{ 0xBA1E, 0xBA1E, 0xBA1E }, +{ 0xBA1F, 0xBA1F, 0xBA1F }, +{ 0xBA20, 0xBA20, 0xBA20 }, +{ 0xBA21, 0xBA21, 0xBA21 }, +{ 0xBA22, 0xBA22, 0xBA22 }, +{ 0xBA23, 0xBA23, 0xBA23 }, +{ 0xBA24, 0xBA24, 0xBA24 }, +{ 0xBA25, 0xBA25, 0xBA25 }, +{ 0xBA26, 0xBA26, 0xBA26 }, +{ 0xBA27, 0xBA27, 0xBA27 }, +{ 0xBA28, 0xBA28, 0xBA28 }, +{ 0xBA29, 0xBA29, 0xBA29 }, +{ 0xBA2A, 0xBA2A, 0xBA2A }, +{ 0xBA2B, 0xBA2B, 0xBA2B }, +{ 0xBA2C, 0xBA2C, 0xBA2C }, +{ 0xBA2D, 0xBA2D, 0xBA2D }, +{ 0xBA2E, 0xBA2E, 0xBA2E }, +{ 0xBA2F, 0xBA2F, 0xBA2F }, +{ 0xBA30, 0xBA30, 0xBA30 }, +{ 0xBA31, 0xBA31, 0xBA31 }, +{ 0xBA32, 0xBA32, 0xBA32 }, +{ 0xBA33, 0xBA33, 0xBA33 }, +{ 0xBA34, 0xBA34, 0xBA34 }, +{ 0xBA35, 0xBA35, 0xBA35 }, +{ 0xBA36, 0xBA36, 0xBA36 }, +{ 0xBA37, 0xBA37, 0xBA37 }, +{ 0xBA38, 0xBA38, 0xBA38 }, +{ 0xBA39, 0xBA39, 0xBA39 }, +{ 0xBA3A, 0xBA3A, 0xBA3A }, +{ 0xBA3B, 0xBA3B, 0xBA3B }, +{ 0xBA3C, 0xBA3C, 0xBA3C }, +{ 0xBA3D, 0xBA3D, 0xBA3D }, +{ 0xBA3E, 0xBA3E, 0xBA3E }, +{ 0xBA3F, 0xBA3F, 0xBA3F }, +{ 0xBA40, 0xBA40, 0xBA40 }, +{ 0xBA41, 0xBA41, 0xBA41 }, +{ 0xBA42, 0xBA42, 0xBA42 }, +{ 0xBA43, 0xBA43, 0xBA43 }, +{ 0xBA44, 0xBA44, 0xBA44 }, +{ 0xBA45, 0xBA45, 0xBA45 }, +{ 0xBA46, 0xBA46, 0xBA46 }, +{ 0xBA47, 0xBA47, 0xBA47 }, +{ 0xBA48, 0xBA48, 0xBA48 }, +{ 0xBA49, 0xBA49, 0xBA49 }, +{ 0xBA4A, 0xBA4A, 0xBA4A }, +{ 0xBA4B, 0xBA4B, 0xBA4B }, +{ 0xBA4C, 0xBA4C, 0xBA4C }, +{ 0xBA4D, 0xBA4D, 0xBA4D }, +{ 0xBA4E, 0xBA4E, 0xBA4E }, +{ 0xBA4F, 0xBA4F, 0xBA4F }, +{ 0xBA50, 0xBA50, 0xBA50 }, +{ 0xBA51, 0xBA51, 0xBA51 }, +{ 0xBA52, 0xBA52, 0xBA52 }, +{ 0xBA53, 0xBA53, 0xBA53 }, +{ 0xBA54, 0xBA54, 0xBA54 }, +{ 0xBA55, 0xBA55, 0xBA55 }, +{ 0xBA56, 0xBA56, 0xBA56 }, +{ 0xBA57, 0xBA57, 0xBA57 }, +{ 0xBA58, 0xBA58, 0xBA58 }, +{ 0xBA59, 0xBA59, 0xBA59 }, +{ 0xBA5A, 0xBA5A, 0xBA5A }, +{ 0xBA5B, 0xBA5B, 0xBA5B }, +{ 0xBA5C, 0xBA5C, 0xBA5C }, +{ 0xBA5D, 0xBA5D, 0xBA5D }, +{ 0xBA5E, 0xBA5E, 0xBA5E }, +{ 0xBA5F, 0xBA5F, 0xBA5F }, +{ 0xBA60, 0xBA60, 0xBA60 }, +{ 0xBA61, 0xBA61, 0xBA61 }, +{ 0xBA62, 0xBA62, 0xBA62 }, +{ 0xBA63, 0xBA63, 0xBA63 }, +{ 0xBA64, 0xBA64, 0xBA64 }, +{ 0xBA65, 0xBA65, 0xBA65 }, +{ 0xBA66, 0xBA66, 0xBA66 }, +{ 0xBA67, 0xBA67, 0xBA67 }, +{ 0xBA68, 0xBA68, 0xBA68 }, +{ 0xBA69, 0xBA69, 0xBA69 }, +{ 0xBA6A, 0xBA6A, 0xBA6A }, +{ 0xBA6B, 0xBA6B, 0xBA6B }, +{ 0xBA6C, 0xBA6C, 0xBA6C }, +{ 0xBA6D, 0xBA6D, 0xBA6D }, +{ 0xBA6E, 0xBA6E, 0xBA6E }, +{ 0xBA6F, 0xBA6F, 0xBA6F }, +{ 0xBA70, 0xBA70, 0xBA70 }, +{ 0xBA71, 0xBA71, 0xBA71 }, +{ 0xBA72, 0xBA72, 0xBA72 }, +{ 0xBA73, 0xBA73, 0xBA73 }, +{ 0xBA74, 0xBA74, 0xBA74 }, +{ 0xBA75, 0xBA75, 0xBA75 }, +{ 0xBA76, 0xBA76, 0xBA76 }, +{ 0xBA77, 0xBA77, 0xBA77 }, +{ 0xBA78, 0xBA78, 0xBA78 }, +{ 0xBA79, 0xBA79, 0xBA79 }, +{ 0xBA7A, 0xBA7A, 0xBA7A }, +{ 0xBA7B, 0xBA7B, 0xBA7B }, +{ 0xBA7C, 0xBA7C, 0xBA7C }, +{ 0xBA7D, 0xBA7D, 0xBA7D }, +{ 0xBA7E, 0xBA7E, 0xBA7E }, +{ 0xBA7F, 0xBA7F, 0xBA7F }, +{ 0xBA80, 0xBA80, 0xBA80 }, +{ 0xBA81, 0xBA81, 0xBA81 }, +{ 0xBA82, 0xBA82, 0xBA82 }, +{ 0xBA83, 0xBA83, 0xBA83 }, +{ 0xBA84, 0xBA84, 0xBA84 }, +{ 0xBA85, 0xBA85, 0xBA85 }, +{ 0xBA86, 0xBA86, 0xBA86 }, +{ 0xBA87, 0xBA87, 0xBA87 }, +{ 0xBA88, 0xBA88, 0xBA88 }, +{ 0xBA89, 0xBA89, 0xBA89 }, +{ 0xBA8A, 0xBA8A, 0xBA8A }, +{ 0xBA8B, 0xBA8B, 0xBA8B }, +{ 0xBA8C, 0xBA8C, 0xBA8C }, +{ 0xBA8D, 0xBA8D, 0xBA8D }, +{ 0xBA8E, 0xBA8E, 0xBA8E }, +{ 0xBA8F, 0xBA8F, 0xBA8F }, +{ 0xBA90, 0xBA90, 0xBA90 }, +{ 0xBA91, 0xBA91, 0xBA91 }, +{ 0xBA92, 0xBA92, 0xBA92 }, +{ 0xBA93, 0xBA93, 0xBA93 }, +{ 0xBA94, 0xBA94, 0xBA94 }, +{ 0xBA95, 0xBA95, 0xBA95 }, +{ 0xBA96, 0xBA96, 0xBA96 }, +{ 0xBA97, 0xBA97, 0xBA97 }, +{ 0xBA98, 0xBA98, 0xBA98 }, +{ 0xBA99, 0xBA99, 0xBA99 }, +{ 0xBA9A, 0xBA9A, 0xBA9A }, +{ 0xBA9B, 0xBA9B, 0xBA9B }, +{ 0xBA9C, 0xBA9C, 0xBA9C }, +{ 0xBA9D, 0xBA9D, 0xBA9D }, +{ 0xBA9E, 0xBA9E, 0xBA9E }, +{ 0xBA9F, 0xBA9F, 0xBA9F }, +{ 0xBAA0, 0xBAA0, 0xBAA0 }, +{ 0xBAA1, 0xBAA1, 0xBAA1 }, +{ 0xBAA2, 0xBAA2, 0xBAA2 }, +{ 0xBAA3, 0xBAA3, 0xBAA3 }, +{ 0xBAA4, 0xBAA4, 0xBAA4 }, +{ 0xBAA5, 0xBAA5, 0xBAA5 }, +{ 0xBAA6, 0xBAA6, 0xBAA6 }, +{ 0xBAA7, 0xBAA7, 0xBAA7 }, +{ 0xBAA8, 0xBAA8, 0xBAA8 }, +{ 0xBAA9, 0xBAA9, 0xBAA9 }, +{ 0xBAAA, 0xBAAA, 0xBAAA }, +{ 0xBAAB, 0xBAAB, 0xBAAB }, +{ 0xBAAC, 0xBAAC, 0xBAAC }, +{ 0xBAAD, 0xBAAD, 0xBAAD }, +{ 0xBAAE, 0xBAAE, 0xBAAE }, +{ 0xBAAF, 0xBAAF, 0xBAAF }, +{ 0xBAB0, 0xBAB0, 0xBAB0 }, +{ 0xBAB1, 0xBAB1, 0xBAB1 }, +{ 0xBAB2, 0xBAB2, 0xBAB2 }, +{ 0xBAB3, 0xBAB3, 0xBAB3 }, +{ 0xBAB4, 0xBAB4, 0xBAB4 }, +{ 0xBAB5, 0xBAB5, 0xBAB5 }, +{ 0xBAB6, 0xBAB6, 0xBAB6 }, +{ 0xBAB7, 0xBAB7, 0xBAB7 }, +{ 0xBAB8, 0xBAB8, 0xBAB8 }, +{ 0xBAB9, 0xBAB9, 0xBAB9 }, +{ 0xBABA, 0xBABA, 0xBABA }, +{ 0xBABB, 0xBABB, 0xBABB }, +{ 0xBABC, 0xBABC, 0xBABC }, +{ 0xBABD, 0xBABD, 0xBABD }, +{ 0xBABE, 0xBABE, 0xBABE }, +{ 0xBABF, 0xBABF, 0xBABF }, +{ 0xBAC0, 0xBAC0, 0xBAC0 }, +{ 0xBAC1, 0xBAC1, 0xBAC1 }, +{ 0xBAC2, 0xBAC2, 0xBAC2 }, +{ 0xBAC3, 0xBAC3, 0xBAC3 }, +{ 0xBAC4, 0xBAC4, 0xBAC4 }, +{ 0xBAC5, 0xBAC5, 0xBAC5 }, +{ 0xBAC6, 0xBAC6, 0xBAC6 }, +{ 0xBAC7, 0xBAC7, 0xBAC7 }, +{ 0xBAC8, 0xBAC8, 0xBAC8 }, +{ 0xBAC9, 0xBAC9, 0xBAC9 }, +{ 0xBACA, 0xBACA, 0xBACA }, +{ 0xBACB, 0xBACB, 0xBACB }, +{ 0xBACC, 0xBACC, 0xBACC }, +{ 0xBACD, 0xBACD, 0xBACD }, +{ 0xBACE, 0xBACE, 0xBACE }, +{ 0xBACF, 0xBACF, 0xBACF }, +{ 0xBAD0, 0xBAD0, 0xBAD0 }, +{ 0xBAD1, 0xBAD1, 0xBAD1 }, +{ 0xBAD2, 0xBAD2, 0xBAD2 }, +{ 0xBAD3, 0xBAD3, 0xBAD3 }, +{ 0xBAD4, 0xBAD4, 0xBAD4 }, +{ 0xBAD5, 0xBAD5, 0xBAD5 }, +{ 0xBAD6, 0xBAD6, 0xBAD6 }, +{ 0xBAD7, 0xBAD7, 0xBAD7 }, +{ 0xBAD8, 0xBAD8, 0xBAD8 }, +{ 0xBAD9, 0xBAD9, 0xBAD9 }, +{ 0xBADA, 0xBADA, 0xBADA }, +{ 0xBADB, 0xBADB, 0xBADB }, +{ 0xBADC, 0xBADC, 0xBADC }, +{ 0xBADD, 0xBADD, 0xBADD }, +{ 0xBADE, 0xBADE, 0xBADE }, +{ 0xBADF, 0xBADF, 0xBADF }, +{ 0xBAE0, 0xBAE0, 0xBAE0 }, +{ 0xBAE1, 0xBAE1, 0xBAE1 }, +{ 0xBAE2, 0xBAE2, 0xBAE2 }, +{ 0xBAE3, 0xBAE3, 0xBAE3 }, +{ 0xBAE4, 0xBAE4, 0xBAE4 }, +{ 0xBAE5, 0xBAE5, 0xBAE5 }, +{ 0xBAE6, 0xBAE6, 0xBAE6 }, +{ 0xBAE7, 0xBAE7, 0xBAE7 }, +{ 0xBAE8, 0xBAE8, 0xBAE8 }, +{ 0xBAE9, 0xBAE9, 0xBAE9 }, +{ 0xBAEA, 0xBAEA, 0xBAEA }, +{ 0xBAEB, 0xBAEB, 0xBAEB }, +{ 0xBAEC, 0xBAEC, 0xBAEC }, +{ 0xBAED, 0xBAED, 0xBAED }, +{ 0xBAEE, 0xBAEE, 0xBAEE }, +{ 0xBAEF, 0xBAEF, 0xBAEF }, +{ 0xBAF0, 0xBAF0, 0xBAF0 }, +{ 0xBAF1, 0xBAF1, 0xBAF1 }, +{ 0xBAF2, 0xBAF2, 0xBAF2 }, +{ 0xBAF3, 0xBAF3, 0xBAF3 }, +{ 0xBAF4, 0xBAF4, 0xBAF4 }, +{ 0xBAF5, 0xBAF5, 0xBAF5 }, +{ 0xBAF6, 0xBAF6, 0xBAF6 }, +{ 0xBAF7, 0xBAF7, 0xBAF7 }, +{ 0xBAF8, 0xBAF8, 0xBAF8 }, +{ 0xBAF9, 0xBAF9, 0xBAF9 }, +{ 0xBAFA, 0xBAFA, 0xBAFA }, +{ 0xBAFB, 0xBAFB, 0xBAFB }, +{ 0xBAFC, 0xBAFC, 0xBAFC }, +{ 0xBAFD, 0xBAFD, 0xBAFD }, +{ 0xBAFE, 0xBAFE, 0xBAFE }, +{ 0xBAFF, 0xBAFF, 0xBAFF }, +{ 0xBB00, 0xBB00, 0xBB00 }, +{ 0xBB01, 0xBB01, 0xBB01 }, +{ 0xBB02, 0xBB02, 0xBB02 }, +{ 0xBB03, 0xBB03, 0xBB03 }, +{ 0xBB04, 0xBB04, 0xBB04 }, +{ 0xBB05, 0xBB05, 0xBB05 }, +{ 0xBB06, 0xBB06, 0xBB06 }, +{ 0xBB07, 0xBB07, 0xBB07 }, +{ 0xBB08, 0xBB08, 0xBB08 }, +{ 0xBB09, 0xBB09, 0xBB09 }, +{ 0xBB0A, 0xBB0A, 0xBB0A }, +{ 0xBB0B, 0xBB0B, 0xBB0B }, +{ 0xBB0C, 0xBB0C, 0xBB0C }, +{ 0xBB0D, 0xBB0D, 0xBB0D }, +{ 0xBB0E, 0xBB0E, 0xBB0E }, +{ 0xBB0F, 0xBB0F, 0xBB0F }, +{ 0xBB10, 0xBB10, 0xBB10 }, +{ 0xBB11, 0xBB11, 0xBB11 }, +{ 0xBB12, 0xBB12, 0xBB12 }, +{ 0xBB13, 0xBB13, 0xBB13 }, +{ 0xBB14, 0xBB14, 0xBB14 }, +{ 0xBB15, 0xBB15, 0xBB15 }, +{ 0xBB16, 0xBB16, 0xBB16 }, +{ 0xBB17, 0xBB17, 0xBB17 }, +{ 0xBB18, 0xBB18, 0xBB18 }, +{ 0xBB19, 0xBB19, 0xBB19 }, +{ 0xBB1A, 0xBB1A, 0xBB1A }, +{ 0xBB1B, 0xBB1B, 0xBB1B }, +{ 0xBB1C, 0xBB1C, 0xBB1C }, +{ 0xBB1D, 0xBB1D, 0xBB1D }, +{ 0xBB1E, 0xBB1E, 0xBB1E }, +{ 0xBB1F, 0xBB1F, 0xBB1F }, +{ 0xBB20, 0xBB20, 0xBB20 }, +{ 0xBB21, 0xBB21, 0xBB21 }, +{ 0xBB22, 0xBB22, 0xBB22 }, +{ 0xBB23, 0xBB23, 0xBB23 }, +{ 0xBB24, 0xBB24, 0xBB24 }, +{ 0xBB25, 0xBB25, 0xBB25 }, +{ 0xBB26, 0xBB26, 0xBB26 }, +{ 0xBB27, 0xBB27, 0xBB27 }, +{ 0xBB28, 0xBB28, 0xBB28 }, +{ 0xBB29, 0xBB29, 0xBB29 }, +{ 0xBB2A, 0xBB2A, 0xBB2A }, +{ 0xBB2B, 0xBB2B, 0xBB2B }, +{ 0xBB2C, 0xBB2C, 0xBB2C }, +{ 0xBB2D, 0xBB2D, 0xBB2D }, +{ 0xBB2E, 0xBB2E, 0xBB2E }, +{ 0xBB2F, 0xBB2F, 0xBB2F }, +{ 0xBB30, 0xBB30, 0xBB30 }, +{ 0xBB31, 0xBB31, 0xBB31 }, +{ 0xBB32, 0xBB32, 0xBB32 }, +{ 0xBB33, 0xBB33, 0xBB33 }, +{ 0xBB34, 0xBB34, 0xBB34 }, +{ 0xBB35, 0xBB35, 0xBB35 }, +{ 0xBB36, 0xBB36, 0xBB36 }, +{ 0xBB37, 0xBB37, 0xBB37 }, +{ 0xBB38, 0xBB38, 0xBB38 }, +{ 0xBB39, 0xBB39, 0xBB39 }, +{ 0xBB3A, 0xBB3A, 0xBB3A }, +{ 0xBB3B, 0xBB3B, 0xBB3B }, +{ 0xBB3C, 0xBB3C, 0xBB3C }, +{ 0xBB3D, 0xBB3D, 0xBB3D }, +{ 0xBB3E, 0xBB3E, 0xBB3E }, +{ 0xBB3F, 0xBB3F, 0xBB3F }, +{ 0xBB40, 0xBB40, 0xBB40 }, +{ 0xBB41, 0xBB41, 0xBB41 }, +{ 0xBB42, 0xBB42, 0xBB42 }, +{ 0xBB43, 0xBB43, 0xBB43 }, +{ 0xBB44, 0xBB44, 0xBB44 }, +{ 0xBB45, 0xBB45, 0xBB45 }, +{ 0xBB46, 0xBB46, 0xBB46 }, +{ 0xBB47, 0xBB47, 0xBB47 }, +{ 0xBB48, 0xBB48, 0xBB48 }, +{ 0xBB49, 0xBB49, 0xBB49 }, +{ 0xBB4A, 0xBB4A, 0xBB4A }, +{ 0xBB4B, 0xBB4B, 0xBB4B }, +{ 0xBB4C, 0xBB4C, 0xBB4C }, +{ 0xBB4D, 0xBB4D, 0xBB4D }, +{ 0xBB4E, 0xBB4E, 0xBB4E }, +{ 0xBB4F, 0xBB4F, 0xBB4F }, +{ 0xBB50, 0xBB50, 0xBB50 }, +{ 0xBB51, 0xBB51, 0xBB51 }, +{ 0xBB52, 0xBB52, 0xBB52 }, +{ 0xBB53, 0xBB53, 0xBB53 }, +{ 0xBB54, 0xBB54, 0xBB54 }, +{ 0xBB55, 0xBB55, 0xBB55 }, +{ 0xBB56, 0xBB56, 0xBB56 }, +{ 0xBB57, 0xBB57, 0xBB57 }, +{ 0xBB58, 0xBB58, 0xBB58 }, +{ 0xBB59, 0xBB59, 0xBB59 }, +{ 0xBB5A, 0xBB5A, 0xBB5A }, +{ 0xBB5B, 0xBB5B, 0xBB5B }, +{ 0xBB5C, 0xBB5C, 0xBB5C }, +{ 0xBB5D, 0xBB5D, 0xBB5D }, +{ 0xBB5E, 0xBB5E, 0xBB5E }, +{ 0xBB5F, 0xBB5F, 0xBB5F }, +{ 0xBB60, 0xBB60, 0xBB60 }, +{ 0xBB61, 0xBB61, 0xBB61 }, +{ 0xBB62, 0xBB62, 0xBB62 }, +{ 0xBB63, 0xBB63, 0xBB63 }, +{ 0xBB64, 0xBB64, 0xBB64 }, +{ 0xBB65, 0xBB65, 0xBB65 }, +{ 0xBB66, 0xBB66, 0xBB66 }, +{ 0xBB67, 0xBB67, 0xBB67 }, +{ 0xBB68, 0xBB68, 0xBB68 }, +{ 0xBB69, 0xBB69, 0xBB69 }, +{ 0xBB6A, 0xBB6A, 0xBB6A }, +{ 0xBB6B, 0xBB6B, 0xBB6B }, +{ 0xBB6C, 0xBB6C, 0xBB6C }, +{ 0xBB6D, 0xBB6D, 0xBB6D }, +{ 0xBB6E, 0xBB6E, 0xBB6E }, +{ 0xBB6F, 0xBB6F, 0xBB6F }, +{ 0xBB70, 0xBB70, 0xBB70 }, +{ 0xBB71, 0xBB71, 0xBB71 }, +{ 0xBB72, 0xBB72, 0xBB72 }, +{ 0xBB73, 0xBB73, 0xBB73 }, +{ 0xBB74, 0xBB74, 0xBB74 }, +{ 0xBB75, 0xBB75, 0xBB75 }, +{ 0xBB76, 0xBB76, 0xBB76 }, +{ 0xBB77, 0xBB77, 0xBB77 }, +{ 0xBB78, 0xBB78, 0xBB78 }, +{ 0xBB79, 0xBB79, 0xBB79 }, +{ 0xBB7A, 0xBB7A, 0xBB7A }, +{ 0xBB7B, 0xBB7B, 0xBB7B }, +{ 0xBB7C, 0xBB7C, 0xBB7C }, +{ 0xBB7D, 0xBB7D, 0xBB7D }, +{ 0xBB7E, 0xBB7E, 0xBB7E }, +{ 0xBB7F, 0xBB7F, 0xBB7F }, +{ 0xBB80, 0xBB80, 0xBB80 }, +{ 0xBB81, 0xBB81, 0xBB81 }, +{ 0xBB82, 0xBB82, 0xBB82 }, +{ 0xBB83, 0xBB83, 0xBB83 }, +{ 0xBB84, 0xBB84, 0xBB84 }, +{ 0xBB85, 0xBB85, 0xBB85 }, +{ 0xBB86, 0xBB86, 0xBB86 }, +{ 0xBB87, 0xBB87, 0xBB87 }, +{ 0xBB88, 0xBB88, 0xBB88 }, +{ 0xBB89, 0xBB89, 0xBB89 }, +{ 0xBB8A, 0xBB8A, 0xBB8A }, +{ 0xBB8B, 0xBB8B, 0xBB8B }, +{ 0xBB8C, 0xBB8C, 0xBB8C }, +{ 0xBB8D, 0xBB8D, 0xBB8D }, +{ 0xBB8E, 0xBB8E, 0xBB8E }, +{ 0xBB8F, 0xBB8F, 0xBB8F }, +{ 0xBB90, 0xBB90, 0xBB90 }, +{ 0xBB91, 0xBB91, 0xBB91 }, +{ 0xBB92, 0xBB92, 0xBB92 }, +{ 0xBB93, 0xBB93, 0xBB93 }, +{ 0xBB94, 0xBB94, 0xBB94 }, +{ 0xBB95, 0xBB95, 0xBB95 }, +{ 0xBB96, 0xBB96, 0xBB96 }, +{ 0xBB97, 0xBB97, 0xBB97 }, +{ 0xBB98, 0xBB98, 0xBB98 }, +{ 0xBB99, 0xBB99, 0xBB99 }, +{ 0xBB9A, 0xBB9A, 0xBB9A }, +{ 0xBB9B, 0xBB9B, 0xBB9B }, +{ 0xBB9C, 0xBB9C, 0xBB9C }, +{ 0xBB9D, 0xBB9D, 0xBB9D }, +{ 0xBB9E, 0xBB9E, 0xBB9E }, +{ 0xBB9F, 0xBB9F, 0xBB9F }, +{ 0xBBA0, 0xBBA0, 0xBBA0 }, +{ 0xBBA1, 0xBBA1, 0xBBA1 }, +{ 0xBBA2, 0xBBA2, 0xBBA2 }, +{ 0xBBA3, 0xBBA3, 0xBBA3 }, +{ 0xBBA4, 0xBBA4, 0xBBA4 }, +{ 0xBBA5, 0xBBA5, 0xBBA5 }, +{ 0xBBA6, 0xBBA6, 0xBBA6 }, +{ 0xBBA7, 0xBBA7, 0xBBA7 }, +{ 0xBBA8, 0xBBA8, 0xBBA8 }, +{ 0xBBA9, 0xBBA9, 0xBBA9 }, +{ 0xBBAA, 0xBBAA, 0xBBAA }, +{ 0xBBAB, 0xBBAB, 0xBBAB }, +{ 0xBBAC, 0xBBAC, 0xBBAC }, +{ 0xBBAD, 0xBBAD, 0xBBAD }, +{ 0xBBAE, 0xBBAE, 0xBBAE }, +{ 0xBBAF, 0xBBAF, 0xBBAF }, +{ 0xBBB0, 0xBBB0, 0xBBB0 }, +{ 0xBBB1, 0xBBB1, 0xBBB1 }, +{ 0xBBB2, 0xBBB2, 0xBBB2 }, +{ 0xBBB3, 0xBBB3, 0xBBB3 }, +{ 0xBBB4, 0xBBB4, 0xBBB4 }, +{ 0xBBB5, 0xBBB5, 0xBBB5 }, +{ 0xBBB6, 0xBBB6, 0xBBB6 }, +{ 0xBBB7, 0xBBB7, 0xBBB7 }, +{ 0xBBB8, 0xBBB8, 0xBBB8 }, +{ 0xBBB9, 0xBBB9, 0xBBB9 }, +{ 0xBBBA, 0xBBBA, 0xBBBA }, +{ 0xBBBB, 0xBBBB, 0xBBBB }, +{ 0xBBBC, 0xBBBC, 0xBBBC }, +{ 0xBBBD, 0xBBBD, 0xBBBD }, +{ 0xBBBE, 0xBBBE, 0xBBBE }, +{ 0xBBBF, 0xBBBF, 0xBBBF }, +{ 0xBBC0, 0xBBC0, 0xBBC0 }, +{ 0xBBC1, 0xBBC1, 0xBBC1 }, +{ 0xBBC2, 0xBBC2, 0xBBC2 }, +{ 0xBBC3, 0xBBC3, 0xBBC3 }, +{ 0xBBC4, 0xBBC4, 0xBBC4 }, +{ 0xBBC5, 0xBBC5, 0xBBC5 }, +{ 0xBBC6, 0xBBC6, 0xBBC6 }, +{ 0xBBC7, 0xBBC7, 0xBBC7 }, +{ 0xBBC8, 0xBBC8, 0xBBC8 }, +{ 0xBBC9, 0xBBC9, 0xBBC9 }, +{ 0xBBCA, 0xBBCA, 0xBBCA }, +{ 0xBBCB, 0xBBCB, 0xBBCB }, +{ 0xBBCC, 0xBBCC, 0xBBCC }, +{ 0xBBCD, 0xBBCD, 0xBBCD }, +{ 0xBBCE, 0xBBCE, 0xBBCE }, +{ 0xBBCF, 0xBBCF, 0xBBCF }, +{ 0xBBD0, 0xBBD0, 0xBBD0 }, +{ 0xBBD1, 0xBBD1, 0xBBD1 }, +{ 0xBBD2, 0xBBD2, 0xBBD2 }, +{ 0xBBD3, 0xBBD3, 0xBBD3 }, +{ 0xBBD4, 0xBBD4, 0xBBD4 }, +{ 0xBBD5, 0xBBD5, 0xBBD5 }, +{ 0xBBD6, 0xBBD6, 0xBBD6 }, +{ 0xBBD7, 0xBBD7, 0xBBD7 }, +{ 0xBBD8, 0xBBD8, 0xBBD8 }, +{ 0xBBD9, 0xBBD9, 0xBBD9 }, +{ 0xBBDA, 0xBBDA, 0xBBDA }, +{ 0xBBDB, 0xBBDB, 0xBBDB }, +{ 0xBBDC, 0xBBDC, 0xBBDC }, +{ 0xBBDD, 0xBBDD, 0xBBDD }, +{ 0xBBDE, 0xBBDE, 0xBBDE }, +{ 0xBBDF, 0xBBDF, 0xBBDF }, +{ 0xBBE0, 0xBBE0, 0xBBE0 }, +{ 0xBBE1, 0xBBE1, 0xBBE1 }, +{ 0xBBE2, 0xBBE2, 0xBBE2 }, +{ 0xBBE3, 0xBBE3, 0xBBE3 }, +{ 0xBBE4, 0xBBE4, 0xBBE4 }, +{ 0xBBE5, 0xBBE5, 0xBBE5 }, +{ 0xBBE6, 0xBBE6, 0xBBE6 }, +{ 0xBBE7, 0xBBE7, 0xBBE7 }, +{ 0xBBE8, 0xBBE8, 0xBBE8 }, +{ 0xBBE9, 0xBBE9, 0xBBE9 }, +{ 0xBBEA, 0xBBEA, 0xBBEA }, +{ 0xBBEB, 0xBBEB, 0xBBEB }, +{ 0xBBEC, 0xBBEC, 0xBBEC }, +{ 0xBBED, 0xBBED, 0xBBED }, +{ 0xBBEE, 0xBBEE, 0xBBEE }, +{ 0xBBEF, 0xBBEF, 0xBBEF }, +{ 0xBBF0, 0xBBF0, 0xBBF0 }, +{ 0xBBF1, 0xBBF1, 0xBBF1 }, +{ 0xBBF2, 0xBBF2, 0xBBF2 }, +{ 0xBBF3, 0xBBF3, 0xBBF3 }, +{ 0xBBF4, 0xBBF4, 0xBBF4 }, +{ 0xBBF5, 0xBBF5, 0xBBF5 }, +{ 0xBBF6, 0xBBF6, 0xBBF6 }, +{ 0xBBF7, 0xBBF7, 0xBBF7 }, +{ 0xBBF8, 0xBBF8, 0xBBF8 }, +{ 0xBBF9, 0xBBF9, 0xBBF9 }, +{ 0xBBFA, 0xBBFA, 0xBBFA }, +{ 0xBBFB, 0xBBFB, 0xBBFB }, +{ 0xBBFC, 0xBBFC, 0xBBFC }, +{ 0xBBFD, 0xBBFD, 0xBBFD }, +{ 0xBBFE, 0xBBFE, 0xBBFE }, +{ 0xBBFF, 0xBBFF, 0xBBFF }, +{ 0xBC00, 0xBC00, 0xBC00 }, +{ 0xBC01, 0xBC01, 0xBC01 }, +{ 0xBC02, 0xBC02, 0xBC02 }, +{ 0xBC03, 0xBC03, 0xBC03 }, +{ 0xBC04, 0xBC04, 0xBC04 }, +{ 0xBC05, 0xBC05, 0xBC05 }, +{ 0xBC06, 0xBC06, 0xBC06 }, +{ 0xBC07, 0xBC07, 0xBC07 }, +{ 0xBC08, 0xBC08, 0xBC08 }, +{ 0xBC09, 0xBC09, 0xBC09 }, +{ 0xBC0A, 0xBC0A, 0xBC0A }, +{ 0xBC0B, 0xBC0B, 0xBC0B }, +{ 0xBC0C, 0xBC0C, 0xBC0C }, +{ 0xBC0D, 0xBC0D, 0xBC0D }, +{ 0xBC0E, 0xBC0E, 0xBC0E }, +{ 0xBC0F, 0xBC0F, 0xBC0F }, +{ 0xBC10, 0xBC10, 0xBC10 }, +{ 0xBC11, 0xBC11, 0xBC11 }, +{ 0xBC12, 0xBC12, 0xBC12 }, +{ 0xBC13, 0xBC13, 0xBC13 }, +{ 0xBC14, 0xBC14, 0xBC14 }, +{ 0xBC15, 0xBC15, 0xBC15 }, +{ 0xBC16, 0xBC16, 0xBC16 }, +{ 0xBC17, 0xBC17, 0xBC17 }, +{ 0xBC18, 0xBC18, 0xBC18 }, +{ 0xBC19, 0xBC19, 0xBC19 }, +{ 0xBC1A, 0xBC1A, 0xBC1A }, +{ 0xBC1B, 0xBC1B, 0xBC1B }, +{ 0xBC1C, 0xBC1C, 0xBC1C }, +{ 0xBC1D, 0xBC1D, 0xBC1D }, +{ 0xBC1E, 0xBC1E, 0xBC1E }, +{ 0xBC1F, 0xBC1F, 0xBC1F }, +{ 0xBC20, 0xBC20, 0xBC20 }, +{ 0xBC21, 0xBC21, 0xBC21 }, +{ 0xBC22, 0xBC22, 0xBC22 }, +{ 0xBC23, 0xBC23, 0xBC23 }, +{ 0xBC24, 0xBC24, 0xBC24 }, +{ 0xBC25, 0xBC25, 0xBC25 }, +{ 0xBC26, 0xBC26, 0xBC26 }, +{ 0xBC27, 0xBC27, 0xBC27 }, +{ 0xBC28, 0xBC28, 0xBC28 }, +{ 0xBC29, 0xBC29, 0xBC29 }, +{ 0xBC2A, 0xBC2A, 0xBC2A }, +{ 0xBC2B, 0xBC2B, 0xBC2B }, +{ 0xBC2C, 0xBC2C, 0xBC2C }, +{ 0xBC2D, 0xBC2D, 0xBC2D }, +{ 0xBC2E, 0xBC2E, 0xBC2E }, +{ 0xBC2F, 0xBC2F, 0xBC2F }, +{ 0xBC30, 0xBC30, 0xBC30 }, +{ 0xBC31, 0xBC31, 0xBC31 }, +{ 0xBC32, 0xBC32, 0xBC32 }, +{ 0xBC33, 0xBC33, 0xBC33 }, +{ 0xBC34, 0xBC34, 0xBC34 }, +{ 0xBC35, 0xBC35, 0xBC35 }, +{ 0xBC36, 0xBC36, 0xBC36 }, +{ 0xBC37, 0xBC37, 0xBC37 }, +{ 0xBC38, 0xBC38, 0xBC38 }, +{ 0xBC39, 0xBC39, 0xBC39 }, +{ 0xBC3A, 0xBC3A, 0xBC3A }, +{ 0xBC3B, 0xBC3B, 0xBC3B }, +{ 0xBC3C, 0xBC3C, 0xBC3C }, +{ 0xBC3D, 0xBC3D, 0xBC3D }, +{ 0xBC3E, 0xBC3E, 0xBC3E }, +{ 0xBC3F, 0xBC3F, 0xBC3F }, +{ 0xBC40, 0xBC40, 0xBC40 }, +{ 0xBC41, 0xBC41, 0xBC41 }, +{ 0xBC42, 0xBC42, 0xBC42 }, +{ 0xBC43, 0xBC43, 0xBC43 }, +{ 0xBC44, 0xBC44, 0xBC44 }, +{ 0xBC45, 0xBC45, 0xBC45 }, +{ 0xBC46, 0xBC46, 0xBC46 }, +{ 0xBC47, 0xBC47, 0xBC47 }, +{ 0xBC48, 0xBC48, 0xBC48 }, +{ 0xBC49, 0xBC49, 0xBC49 }, +{ 0xBC4A, 0xBC4A, 0xBC4A }, +{ 0xBC4B, 0xBC4B, 0xBC4B }, +{ 0xBC4C, 0xBC4C, 0xBC4C }, +{ 0xBC4D, 0xBC4D, 0xBC4D }, +{ 0xBC4E, 0xBC4E, 0xBC4E }, +{ 0xBC4F, 0xBC4F, 0xBC4F }, +{ 0xBC50, 0xBC50, 0xBC50 }, +{ 0xBC51, 0xBC51, 0xBC51 }, +{ 0xBC52, 0xBC52, 0xBC52 }, +{ 0xBC53, 0xBC53, 0xBC53 }, +{ 0xBC54, 0xBC54, 0xBC54 }, +{ 0xBC55, 0xBC55, 0xBC55 }, +{ 0xBC56, 0xBC56, 0xBC56 }, +{ 0xBC57, 0xBC57, 0xBC57 }, +{ 0xBC58, 0xBC58, 0xBC58 }, +{ 0xBC59, 0xBC59, 0xBC59 }, +{ 0xBC5A, 0xBC5A, 0xBC5A }, +{ 0xBC5B, 0xBC5B, 0xBC5B }, +{ 0xBC5C, 0xBC5C, 0xBC5C }, +{ 0xBC5D, 0xBC5D, 0xBC5D }, +{ 0xBC5E, 0xBC5E, 0xBC5E }, +{ 0xBC5F, 0xBC5F, 0xBC5F }, +{ 0xBC60, 0xBC60, 0xBC60 }, +{ 0xBC61, 0xBC61, 0xBC61 }, +{ 0xBC62, 0xBC62, 0xBC62 }, +{ 0xBC63, 0xBC63, 0xBC63 }, +{ 0xBC64, 0xBC64, 0xBC64 }, +{ 0xBC65, 0xBC65, 0xBC65 }, +{ 0xBC66, 0xBC66, 0xBC66 }, +{ 0xBC67, 0xBC67, 0xBC67 }, +{ 0xBC68, 0xBC68, 0xBC68 }, +{ 0xBC69, 0xBC69, 0xBC69 }, +{ 0xBC6A, 0xBC6A, 0xBC6A }, +{ 0xBC6B, 0xBC6B, 0xBC6B }, +{ 0xBC6C, 0xBC6C, 0xBC6C }, +{ 0xBC6D, 0xBC6D, 0xBC6D }, +{ 0xBC6E, 0xBC6E, 0xBC6E }, +{ 0xBC6F, 0xBC6F, 0xBC6F }, +{ 0xBC70, 0xBC70, 0xBC70 }, +{ 0xBC71, 0xBC71, 0xBC71 }, +{ 0xBC72, 0xBC72, 0xBC72 }, +{ 0xBC73, 0xBC73, 0xBC73 }, +{ 0xBC74, 0xBC74, 0xBC74 }, +{ 0xBC75, 0xBC75, 0xBC75 }, +{ 0xBC76, 0xBC76, 0xBC76 }, +{ 0xBC77, 0xBC77, 0xBC77 }, +{ 0xBC78, 0xBC78, 0xBC78 }, +{ 0xBC79, 0xBC79, 0xBC79 }, +{ 0xBC7A, 0xBC7A, 0xBC7A }, +{ 0xBC7B, 0xBC7B, 0xBC7B }, +{ 0xBC7C, 0xBC7C, 0xBC7C }, +{ 0xBC7D, 0xBC7D, 0xBC7D }, +{ 0xBC7E, 0xBC7E, 0xBC7E }, +{ 0xBC7F, 0xBC7F, 0xBC7F }, +{ 0xBC80, 0xBC80, 0xBC80 }, +{ 0xBC81, 0xBC81, 0xBC81 }, +{ 0xBC82, 0xBC82, 0xBC82 }, +{ 0xBC83, 0xBC83, 0xBC83 }, +{ 0xBC84, 0xBC84, 0xBC84 }, +{ 0xBC85, 0xBC85, 0xBC85 }, +{ 0xBC86, 0xBC86, 0xBC86 }, +{ 0xBC87, 0xBC87, 0xBC87 }, +{ 0xBC88, 0xBC88, 0xBC88 }, +{ 0xBC89, 0xBC89, 0xBC89 }, +{ 0xBC8A, 0xBC8A, 0xBC8A }, +{ 0xBC8B, 0xBC8B, 0xBC8B }, +{ 0xBC8C, 0xBC8C, 0xBC8C }, +{ 0xBC8D, 0xBC8D, 0xBC8D }, +{ 0xBC8E, 0xBC8E, 0xBC8E }, +{ 0xBC8F, 0xBC8F, 0xBC8F }, +{ 0xBC90, 0xBC90, 0xBC90 }, +{ 0xBC91, 0xBC91, 0xBC91 }, +{ 0xBC92, 0xBC92, 0xBC92 }, +{ 0xBC93, 0xBC93, 0xBC93 }, +{ 0xBC94, 0xBC94, 0xBC94 }, +{ 0xBC95, 0xBC95, 0xBC95 }, +{ 0xBC96, 0xBC96, 0xBC96 }, +{ 0xBC97, 0xBC97, 0xBC97 }, +{ 0xBC98, 0xBC98, 0xBC98 }, +{ 0xBC99, 0xBC99, 0xBC99 }, +{ 0xBC9A, 0xBC9A, 0xBC9A }, +{ 0xBC9B, 0xBC9B, 0xBC9B }, +{ 0xBC9C, 0xBC9C, 0xBC9C }, +{ 0xBC9D, 0xBC9D, 0xBC9D }, +{ 0xBC9E, 0xBC9E, 0xBC9E }, +{ 0xBC9F, 0xBC9F, 0xBC9F }, +{ 0xBCA0, 0xBCA0, 0xBCA0 }, +{ 0xBCA1, 0xBCA1, 0xBCA1 }, +{ 0xBCA2, 0xBCA2, 0xBCA2 }, +{ 0xBCA3, 0xBCA3, 0xBCA3 }, +{ 0xBCA4, 0xBCA4, 0xBCA4 }, +{ 0xBCA5, 0xBCA5, 0xBCA5 }, +{ 0xBCA6, 0xBCA6, 0xBCA6 }, +{ 0xBCA7, 0xBCA7, 0xBCA7 }, +{ 0xBCA8, 0xBCA8, 0xBCA8 }, +{ 0xBCA9, 0xBCA9, 0xBCA9 }, +{ 0xBCAA, 0xBCAA, 0xBCAA }, +{ 0xBCAB, 0xBCAB, 0xBCAB }, +{ 0xBCAC, 0xBCAC, 0xBCAC }, +{ 0xBCAD, 0xBCAD, 0xBCAD }, +{ 0xBCAE, 0xBCAE, 0xBCAE }, +{ 0xBCAF, 0xBCAF, 0xBCAF }, +{ 0xBCB0, 0xBCB0, 0xBCB0 }, +{ 0xBCB1, 0xBCB1, 0xBCB1 }, +{ 0xBCB2, 0xBCB2, 0xBCB2 }, +{ 0xBCB3, 0xBCB3, 0xBCB3 }, +{ 0xBCB4, 0xBCB4, 0xBCB4 }, +{ 0xBCB5, 0xBCB5, 0xBCB5 }, +{ 0xBCB6, 0xBCB6, 0xBCB6 }, +{ 0xBCB7, 0xBCB7, 0xBCB7 }, +{ 0xBCB8, 0xBCB8, 0xBCB8 }, +{ 0xBCB9, 0xBCB9, 0xBCB9 }, +{ 0xBCBA, 0xBCBA, 0xBCBA }, +{ 0xBCBB, 0xBCBB, 0xBCBB }, +{ 0xBCBC, 0xBCBC, 0xBCBC }, +{ 0xBCBD, 0xBCBD, 0xBCBD }, +{ 0xBCBE, 0xBCBE, 0xBCBE }, +{ 0xBCBF, 0xBCBF, 0xBCBF }, +{ 0xBCC0, 0xBCC0, 0xBCC0 }, +{ 0xBCC1, 0xBCC1, 0xBCC1 }, +{ 0xBCC2, 0xBCC2, 0xBCC2 }, +{ 0xBCC3, 0xBCC3, 0xBCC3 }, +{ 0xBCC4, 0xBCC4, 0xBCC4 }, +{ 0xBCC5, 0xBCC5, 0xBCC5 }, +{ 0xBCC6, 0xBCC6, 0xBCC6 }, +{ 0xBCC7, 0xBCC7, 0xBCC7 }, +{ 0xBCC8, 0xBCC8, 0xBCC8 }, +{ 0xBCC9, 0xBCC9, 0xBCC9 }, +{ 0xBCCA, 0xBCCA, 0xBCCA }, +{ 0xBCCB, 0xBCCB, 0xBCCB }, +{ 0xBCCC, 0xBCCC, 0xBCCC }, +{ 0xBCCD, 0xBCCD, 0xBCCD }, +{ 0xBCCE, 0xBCCE, 0xBCCE }, +{ 0xBCCF, 0xBCCF, 0xBCCF }, +{ 0xBCD0, 0xBCD0, 0xBCD0 }, +{ 0xBCD1, 0xBCD1, 0xBCD1 }, +{ 0xBCD2, 0xBCD2, 0xBCD2 }, +{ 0xBCD3, 0xBCD3, 0xBCD3 }, +{ 0xBCD4, 0xBCD4, 0xBCD4 }, +{ 0xBCD5, 0xBCD5, 0xBCD5 }, +{ 0xBCD6, 0xBCD6, 0xBCD6 }, +{ 0xBCD7, 0xBCD7, 0xBCD7 }, +{ 0xBCD8, 0xBCD8, 0xBCD8 }, +{ 0xBCD9, 0xBCD9, 0xBCD9 }, +{ 0xBCDA, 0xBCDA, 0xBCDA }, +{ 0xBCDB, 0xBCDB, 0xBCDB }, +{ 0xBCDC, 0xBCDC, 0xBCDC }, +{ 0xBCDD, 0xBCDD, 0xBCDD }, +{ 0xBCDE, 0xBCDE, 0xBCDE }, +{ 0xBCDF, 0xBCDF, 0xBCDF }, +{ 0xBCE0, 0xBCE0, 0xBCE0 }, +{ 0xBCE1, 0xBCE1, 0xBCE1 }, +{ 0xBCE2, 0xBCE2, 0xBCE2 }, +{ 0xBCE3, 0xBCE3, 0xBCE3 }, +{ 0xBCE4, 0xBCE4, 0xBCE4 }, +{ 0xBCE5, 0xBCE5, 0xBCE5 }, +{ 0xBCE6, 0xBCE6, 0xBCE6 }, +{ 0xBCE7, 0xBCE7, 0xBCE7 }, +{ 0xBCE8, 0xBCE8, 0xBCE8 }, +{ 0xBCE9, 0xBCE9, 0xBCE9 }, +{ 0xBCEA, 0xBCEA, 0xBCEA }, +{ 0xBCEB, 0xBCEB, 0xBCEB }, +{ 0xBCEC, 0xBCEC, 0xBCEC }, +{ 0xBCED, 0xBCED, 0xBCED }, +{ 0xBCEE, 0xBCEE, 0xBCEE }, +{ 0xBCEF, 0xBCEF, 0xBCEF }, +{ 0xBCF0, 0xBCF0, 0xBCF0 }, +{ 0xBCF1, 0xBCF1, 0xBCF1 }, +{ 0xBCF2, 0xBCF2, 0xBCF2 }, +{ 0xBCF3, 0xBCF3, 0xBCF3 }, +{ 0xBCF4, 0xBCF4, 0xBCF4 }, +{ 0xBCF5, 0xBCF5, 0xBCF5 }, +{ 0xBCF6, 0xBCF6, 0xBCF6 }, +{ 0xBCF7, 0xBCF7, 0xBCF7 }, +{ 0xBCF8, 0xBCF8, 0xBCF8 }, +{ 0xBCF9, 0xBCF9, 0xBCF9 }, +{ 0xBCFA, 0xBCFA, 0xBCFA }, +{ 0xBCFB, 0xBCFB, 0xBCFB }, +{ 0xBCFC, 0xBCFC, 0xBCFC }, +{ 0xBCFD, 0xBCFD, 0xBCFD }, +{ 0xBCFE, 0xBCFE, 0xBCFE }, +{ 0xBCFF, 0xBCFF, 0xBCFF }, +{ 0xBD00, 0xBD00, 0xBD00 }, +{ 0xBD01, 0xBD01, 0xBD01 }, +{ 0xBD02, 0xBD02, 0xBD02 }, +{ 0xBD03, 0xBD03, 0xBD03 }, +{ 0xBD04, 0xBD04, 0xBD04 }, +{ 0xBD05, 0xBD05, 0xBD05 }, +{ 0xBD06, 0xBD06, 0xBD06 }, +{ 0xBD07, 0xBD07, 0xBD07 }, +{ 0xBD08, 0xBD08, 0xBD08 }, +{ 0xBD09, 0xBD09, 0xBD09 }, +{ 0xBD0A, 0xBD0A, 0xBD0A }, +{ 0xBD0B, 0xBD0B, 0xBD0B }, +{ 0xBD0C, 0xBD0C, 0xBD0C }, +{ 0xBD0D, 0xBD0D, 0xBD0D }, +{ 0xBD0E, 0xBD0E, 0xBD0E }, +{ 0xBD0F, 0xBD0F, 0xBD0F }, +{ 0xBD10, 0xBD10, 0xBD10 }, +{ 0xBD11, 0xBD11, 0xBD11 }, +{ 0xBD12, 0xBD12, 0xBD12 }, +{ 0xBD13, 0xBD13, 0xBD13 }, +{ 0xBD14, 0xBD14, 0xBD14 }, +{ 0xBD15, 0xBD15, 0xBD15 }, +{ 0xBD16, 0xBD16, 0xBD16 }, +{ 0xBD17, 0xBD17, 0xBD17 }, +{ 0xBD18, 0xBD18, 0xBD18 }, +{ 0xBD19, 0xBD19, 0xBD19 }, +{ 0xBD1A, 0xBD1A, 0xBD1A }, +{ 0xBD1B, 0xBD1B, 0xBD1B }, +{ 0xBD1C, 0xBD1C, 0xBD1C }, +{ 0xBD1D, 0xBD1D, 0xBD1D }, +{ 0xBD1E, 0xBD1E, 0xBD1E }, +{ 0xBD1F, 0xBD1F, 0xBD1F }, +{ 0xBD20, 0xBD20, 0xBD20 }, +{ 0xBD21, 0xBD21, 0xBD21 }, +{ 0xBD22, 0xBD22, 0xBD22 }, +{ 0xBD23, 0xBD23, 0xBD23 }, +{ 0xBD24, 0xBD24, 0xBD24 }, +{ 0xBD25, 0xBD25, 0xBD25 }, +{ 0xBD26, 0xBD26, 0xBD26 }, +{ 0xBD27, 0xBD27, 0xBD27 }, +{ 0xBD28, 0xBD28, 0xBD28 }, +{ 0xBD29, 0xBD29, 0xBD29 }, +{ 0xBD2A, 0xBD2A, 0xBD2A }, +{ 0xBD2B, 0xBD2B, 0xBD2B }, +{ 0xBD2C, 0xBD2C, 0xBD2C }, +{ 0xBD2D, 0xBD2D, 0xBD2D }, +{ 0xBD2E, 0xBD2E, 0xBD2E }, +{ 0xBD2F, 0xBD2F, 0xBD2F }, +{ 0xBD30, 0xBD30, 0xBD30 }, +{ 0xBD31, 0xBD31, 0xBD31 }, +{ 0xBD32, 0xBD32, 0xBD32 }, +{ 0xBD33, 0xBD33, 0xBD33 }, +{ 0xBD34, 0xBD34, 0xBD34 }, +{ 0xBD35, 0xBD35, 0xBD35 }, +{ 0xBD36, 0xBD36, 0xBD36 }, +{ 0xBD37, 0xBD37, 0xBD37 }, +{ 0xBD38, 0xBD38, 0xBD38 }, +{ 0xBD39, 0xBD39, 0xBD39 }, +{ 0xBD3A, 0xBD3A, 0xBD3A }, +{ 0xBD3B, 0xBD3B, 0xBD3B }, +{ 0xBD3C, 0xBD3C, 0xBD3C }, +{ 0xBD3D, 0xBD3D, 0xBD3D }, +{ 0xBD3E, 0xBD3E, 0xBD3E }, +{ 0xBD3F, 0xBD3F, 0xBD3F }, +{ 0xBD40, 0xBD40, 0xBD40 }, +{ 0xBD41, 0xBD41, 0xBD41 }, +{ 0xBD42, 0xBD42, 0xBD42 }, +{ 0xBD43, 0xBD43, 0xBD43 }, +{ 0xBD44, 0xBD44, 0xBD44 }, +{ 0xBD45, 0xBD45, 0xBD45 }, +{ 0xBD46, 0xBD46, 0xBD46 }, +{ 0xBD47, 0xBD47, 0xBD47 }, +{ 0xBD48, 0xBD48, 0xBD48 }, +{ 0xBD49, 0xBD49, 0xBD49 }, +{ 0xBD4A, 0xBD4A, 0xBD4A }, +{ 0xBD4B, 0xBD4B, 0xBD4B }, +{ 0xBD4C, 0xBD4C, 0xBD4C }, +{ 0xBD4D, 0xBD4D, 0xBD4D }, +{ 0xBD4E, 0xBD4E, 0xBD4E }, +{ 0xBD4F, 0xBD4F, 0xBD4F }, +{ 0xBD50, 0xBD50, 0xBD50 }, +{ 0xBD51, 0xBD51, 0xBD51 }, +{ 0xBD52, 0xBD52, 0xBD52 }, +{ 0xBD53, 0xBD53, 0xBD53 }, +{ 0xBD54, 0xBD54, 0xBD54 }, +{ 0xBD55, 0xBD55, 0xBD55 }, +{ 0xBD56, 0xBD56, 0xBD56 }, +{ 0xBD57, 0xBD57, 0xBD57 }, +{ 0xBD58, 0xBD58, 0xBD58 }, +{ 0xBD59, 0xBD59, 0xBD59 }, +{ 0xBD5A, 0xBD5A, 0xBD5A }, +{ 0xBD5B, 0xBD5B, 0xBD5B }, +{ 0xBD5C, 0xBD5C, 0xBD5C }, +{ 0xBD5D, 0xBD5D, 0xBD5D }, +{ 0xBD5E, 0xBD5E, 0xBD5E }, +{ 0xBD5F, 0xBD5F, 0xBD5F }, +{ 0xBD60, 0xBD60, 0xBD60 }, +{ 0xBD61, 0xBD61, 0xBD61 }, +{ 0xBD62, 0xBD62, 0xBD62 }, +{ 0xBD63, 0xBD63, 0xBD63 }, +{ 0xBD64, 0xBD64, 0xBD64 }, +{ 0xBD65, 0xBD65, 0xBD65 }, +{ 0xBD66, 0xBD66, 0xBD66 }, +{ 0xBD67, 0xBD67, 0xBD67 }, +{ 0xBD68, 0xBD68, 0xBD68 }, +{ 0xBD69, 0xBD69, 0xBD69 }, +{ 0xBD6A, 0xBD6A, 0xBD6A }, +{ 0xBD6B, 0xBD6B, 0xBD6B }, +{ 0xBD6C, 0xBD6C, 0xBD6C }, +{ 0xBD6D, 0xBD6D, 0xBD6D }, +{ 0xBD6E, 0xBD6E, 0xBD6E }, +{ 0xBD6F, 0xBD6F, 0xBD6F }, +{ 0xBD70, 0xBD70, 0xBD70 }, +{ 0xBD71, 0xBD71, 0xBD71 }, +{ 0xBD72, 0xBD72, 0xBD72 }, +{ 0xBD73, 0xBD73, 0xBD73 }, +{ 0xBD74, 0xBD74, 0xBD74 }, +{ 0xBD75, 0xBD75, 0xBD75 }, +{ 0xBD76, 0xBD76, 0xBD76 }, +{ 0xBD77, 0xBD77, 0xBD77 }, +{ 0xBD78, 0xBD78, 0xBD78 }, +{ 0xBD79, 0xBD79, 0xBD79 }, +{ 0xBD7A, 0xBD7A, 0xBD7A }, +{ 0xBD7B, 0xBD7B, 0xBD7B }, +{ 0xBD7C, 0xBD7C, 0xBD7C }, +{ 0xBD7D, 0xBD7D, 0xBD7D }, +{ 0xBD7E, 0xBD7E, 0xBD7E }, +{ 0xBD7F, 0xBD7F, 0xBD7F }, +{ 0xBD80, 0xBD80, 0xBD80 }, +{ 0xBD81, 0xBD81, 0xBD81 }, +{ 0xBD82, 0xBD82, 0xBD82 }, +{ 0xBD83, 0xBD83, 0xBD83 }, +{ 0xBD84, 0xBD84, 0xBD84 }, +{ 0xBD85, 0xBD85, 0xBD85 }, +{ 0xBD86, 0xBD86, 0xBD86 }, +{ 0xBD87, 0xBD87, 0xBD87 }, +{ 0xBD88, 0xBD88, 0xBD88 }, +{ 0xBD89, 0xBD89, 0xBD89 }, +{ 0xBD8A, 0xBD8A, 0xBD8A }, +{ 0xBD8B, 0xBD8B, 0xBD8B }, +{ 0xBD8C, 0xBD8C, 0xBD8C }, +{ 0xBD8D, 0xBD8D, 0xBD8D }, +{ 0xBD8E, 0xBD8E, 0xBD8E }, +{ 0xBD8F, 0xBD8F, 0xBD8F }, +{ 0xBD90, 0xBD90, 0xBD90 }, +{ 0xBD91, 0xBD91, 0xBD91 }, +{ 0xBD92, 0xBD92, 0xBD92 }, +{ 0xBD93, 0xBD93, 0xBD93 }, +{ 0xBD94, 0xBD94, 0xBD94 }, +{ 0xBD95, 0xBD95, 0xBD95 }, +{ 0xBD96, 0xBD96, 0xBD96 }, +{ 0xBD97, 0xBD97, 0xBD97 }, +{ 0xBD98, 0xBD98, 0xBD98 }, +{ 0xBD99, 0xBD99, 0xBD99 }, +{ 0xBD9A, 0xBD9A, 0xBD9A }, +{ 0xBD9B, 0xBD9B, 0xBD9B }, +{ 0xBD9C, 0xBD9C, 0xBD9C }, +{ 0xBD9D, 0xBD9D, 0xBD9D }, +{ 0xBD9E, 0xBD9E, 0xBD9E }, +{ 0xBD9F, 0xBD9F, 0xBD9F }, +{ 0xBDA0, 0xBDA0, 0xBDA0 }, +{ 0xBDA1, 0xBDA1, 0xBDA1 }, +{ 0xBDA2, 0xBDA2, 0xBDA2 }, +{ 0xBDA3, 0xBDA3, 0xBDA3 }, +{ 0xBDA4, 0xBDA4, 0xBDA4 }, +{ 0xBDA5, 0xBDA5, 0xBDA5 }, +{ 0xBDA6, 0xBDA6, 0xBDA6 }, +{ 0xBDA7, 0xBDA7, 0xBDA7 }, +{ 0xBDA8, 0xBDA8, 0xBDA8 }, +{ 0xBDA9, 0xBDA9, 0xBDA9 }, +{ 0xBDAA, 0xBDAA, 0xBDAA }, +{ 0xBDAB, 0xBDAB, 0xBDAB }, +{ 0xBDAC, 0xBDAC, 0xBDAC }, +{ 0xBDAD, 0xBDAD, 0xBDAD }, +{ 0xBDAE, 0xBDAE, 0xBDAE }, +{ 0xBDAF, 0xBDAF, 0xBDAF }, +{ 0xBDB0, 0xBDB0, 0xBDB0 }, +{ 0xBDB1, 0xBDB1, 0xBDB1 }, +{ 0xBDB2, 0xBDB2, 0xBDB2 }, +{ 0xBDB3, 0xBDB3, 0xBDB3 }, +{ 0xBDB4, 0xBDB4, 0xBDB4 }, +{ 0xBDB5, 0xBDB5, 0xBDB5 }, +{ 0xBDB6, 0xBDB6, 0xBDB6 }, +{ 0xBDB7, 0xBDB7, 0xBDB7 }, +{ 0xBDB8, 0xBDB8, 0xBDB8 }, +{ 0xBDB9, 0xBDB9, 0xBDB9 }, +{ 0xBDBA, 0xBDBA, 0xBDBA }, +{ 0xBDBB, 0xBDBB, 0xBDBB }, +{ 0xBDBC, 0xBDBC, 0xBDBC }, +{ 0xBDBD, 0xBDBD, 0xBDBD }, +{ 0xBDBE, 0xBDBE, 0xBDBE }, +{ 0xBDBF, 0xBDBF, 0xBDBF }, +{ 0xBDC0, 0xBDC0, 0xBDC0 }, +{ 0xBDC1, 0xBDC1, 0xBDC1 }, +{ 0xBDC2, 0xBDC2, 0xBDC2 }, +{ 0xBDC3, 0xBDC3, 0xBDC3 }, +{ 0xBDC4, 0xBDC4, 0xBDC4 }, +{ 0xBDC5, 0xBDC5, 0xBDC5 }, +{ 0xBDC6, 0xBDC6, 0xBDC6 }, +{ 0xBDC7, 0xBDC7, 0xBDC7 }, +{ 0xBDC8, 0xBDC8, 0xBDC8 }, +{ 0xBDC9, 0xBDC9, 0xBDC9 }, +{ 0xBDCA, 0xBDCA, 0xBDCA }, +{ 0xBDCB, 0xBDCB, 0xBDCB }, +{ 0xBDCC, 0xBDCC, 0xBDCC }, +{ 0xBDCD, 0xBDCD, 0xBDCD }, +{ 0xBDCE, 0xBDCE, 0xBDCE }, +{ 0xBDCF, 0xBDCF, 0xBDCF }, +{ 0xBDD0, 0xBDD0, 0xBDD0 }, +{ 0xBDD1, 0xBDD1, 0xBDD1 }, +{ 0xBDD2, 0xBDD2, 0xBDD2 }, +{ 0xBDD3, 0xBDD3, 0xBDD3 }, +{ 0xBDD4, 0xBDD4, 0xBDD4 }, +{ 0xBDD5, 0xBDD5, 0xBDD5 }, +{ 0xBDD6, 0xBDD6, 0xBDD6 }, +{ 0xBDD7, 0xBDD7, 0xBDD7 }, +{ 0xBDD8, 0xBDD8, 0xBDD8 }, +{ 0xBDD9, 0xBDD9, 0xBDD9 }, +{ 0xBDDA, 0xBDDA, 0xBDDA }, +{ 0xBDDB, 0xBDDB, 0xBDDB }, +{ 0xBDDC, 0xBDDC, 0xBDDC }, +{ 0xBDDD, 0xBDDD, 0xBDDD }, +{ 0xBDDE, 0xBDDE, 0xBDDE }, +{ 0xBDDF, 0xBDDF, 0xBDDF }, +{ 0xBDE0, 0xBDE0, 0xBDE0 }, +{ 0xBDE1, 0xBDE1, 0xBDE1 }, +{ 0xBDE2, 0xBDE2, 0xBDE2 }, +{ 0xBDE3, 0xBDE3, 0xBDE3 }, +{ 0xBDE4, 0xBDE4, 0xBDE4 }, +{ 0xBDE5, 0xBDE5, 0xBDE5 }, +{ 0xBDE6, 0xBDE6, 0xBDE6 }, +{ 0xBDE7, 0xBDE7, 0xBDE7 }, +{ 0xBDE8, 0xBDE8, 0xBDE8 }, +{ 0xBDE9, 0xBDE9, 0xBDE9 }, +{ 0xBDEA, 0xBDEA, 0xBDEA }, +{ 0xBDEB, 0xBDEB, 0xBDEB }, +{ 0xBDEC, 0xBDEC, 0xBDEC }, +{ 0xBDED, 0xBDED, 0xBDED }, +{ 0xBDEE, 0xBDEE, 0xBDEE }, +{ 0xBDEF, 0xBDEF, 0xBDEF }, +{ 0xBDF0, 0xBDF0, 0xBDF0 }, +{ 0xBDF1, 0xBDF1, 0xBDF1 }, +{ 0xBDF2, 0xBDF2, 0xBDF2 }, +{ 0xBDF3, 0xBDF3, 0xBDF3 }, +{ 0xBDF4, 0xBDF4, 0xBDF4 }, +{ 0xBDF5, 0xBDF5, 0xBDF5 }, +{ 0xBDF6, 0xBDF6, 0xBDF6 }, +{ 0xBDF7, 0xBDF7, 0xBDF7 }, +{ 0xBDF8, 0xBDF8, 0xBDF8 }, +{ 0xBDF9, 0xBDF9, 0xBDF9 }, +{ 0xBDFA, 0xBDFA, 0xBDFA }, +{ 0xBDFB, 0xBDFB, 0xBDFB }, +{ 0xBDFC, 0xBDFC, 0xBDFC }, +{ 0xBDFD, 0xBDFD, 0xBDFD }, +{ 0xBDFE, 0xBDFE, 0xBDFE }, +{ 0xBDFF, 0xBDFF, 0xBDFF }, +{ 0xBE00, 0xBE00, 0xBE00 }, +{ 0xBE01, 0xBE01, 0xBE01 }, +{ 0xBE02, 0xBE02, 0xBE02 }, +{ 0xBE03, 0xBE03, 0xBE03 }, +{ 0xBE04, 0xBE04, 0xBE04 }, +{ 0xBE05, 0xBE05, 0xBE05 }, +{ 0xBE06, 0xBE06, 0xBE06 }, +{ 0xBE07, 0xBE07, 0xBE07 }, +{ 0xBE08, 0xBE08, 0xBE08 }, +{ 0xBE09, 0xBE09, 0xBE09 }, +{ 0xBE0A, 0xBE0A, 0xBE0A }, +{ 0xBE0B, 0xBE0B, 0xBE0B }, +{ 0xBE0C, 0xBE0C, 0xBE0C }, +{ 0xBE0D, 0xBE0D, 0xBE0D }, +{ 0xBE0E, 0xBE0E, 0xBE0E }, +{ 0xBE0F, 0xBE0F, 0xBE0F }, +{ 0xBE10, 0xBE10, 0xBE10 }, +{ 0xBE11, 0xBE11, 0xBE11 }, +{ 0xBE12, 0xBE12, 0xBE12 }, +{ 0xBE13, 0xBE13, 0xBE13 }, +{ 0xBE14, 0xBE14, 0xBE14 }, +{ 0xBE15, 0xBE15, 0xBE15 }, +{ 0xBE16, 0xBE16, 0xBE16 }, +{ 0xBE17, 0xBE17, 0xBE17 }, +{ 0xBE18, 0xBE18, 0xBE18 }, +{ 0xBE19, 0xBE19, 0xBE19 }, +{ 0xBE1A, 0xBE1A, 0xBE1A }, +{ 0xBE1B, 0xBE1B, 0xBE1B }, +{ 0xBE1C, 0xBE1C, 0xBE1C }, +{ 0xBE1D, 0xBE1D, 0xBE1D }, +{ 0xBE1E, 0xBE1E, 0xBE1E }, +{ 0xBE1F, 0xBE1F, 0xBE1F }, +{ 0xBE20, 0xBE20, 0xBE20 }, +{ 0xBE21, 0xBE21, 0xBE21 }, +{ 0xBE22, 0xBE22, 0xBE22 }, +{ 0xBE23, 0xBE23, 0xBE23 }, +{ 0xBE24, 0xBE24, 0xBE24 }, +{ 0xBE25, 0xBE25, 0xBE25 }, +{ 0xBE26, 0xBE26, 0xBE26 }, +{ 0xBE27, 0xBE27, 0xBE27 }, +{ 0xBE28, 0xBE28, 0xBE28 }, +{ 0xBE29, 0xBE29, 0xBE29 }, +{ 0xBE2A, 0xBE2A, 0xBE2A }, +{ 0xBE2B, 0xBE2B, 0xBE2B }, +{ 0xBE2C, 0xBE2C, 0xBE2C }, +{ 0xBE2D, 0xBE2D, 0xBE2D }, +{ 0xBE2E, 0xBE2E, 0xBE2E }, +{ 0xBE2F, 0xBE2F, 0xBE2F }, +{ 0xBE30, 0xBE30, 0xBE30 }, +{ 0xBE31, 0xBE31, 0xBE31 }, +{ 0xBE32, 0xBE32, 0xBE32 }, +{ 0xBE33, 0xBE33, 0xBE33 }, +{ 0xBE34, 0xBE34, 0xBE34 }, +{ 0xBE35, 0xBE35, 0xBE35 }, +{ 0xBE36, 0xBE36, 0xBE36 }, +{ 0xBE37, 0xBE37, 0xBE37 }, +{ 0xBE38, 0xBE38, 0xBE38 }, +{ 0xBE39, 0xBE39, 0xBE39 }, +{ 0xBE3A, 0xBE3A, 0xBE3A }, +{ 0xBE3B, 0xBE3B, 0xBE3B }, +{ 0xBE3C, 0xBE3C, 0xBE3C }, +{ 0xBE3D, 0xBE3D, 0xBE3D }, +{ 0xBE3E, 0xBE3E, 0xBE3E }, +{ 0xBE3F, 0xBE3F, 0xBE3F }, +{ 0xBE40, 0xBE40, 0xBE40 }, +{ 0xBE41, 0xBE41, 0xBE41 }, +{ 0xBE42, 0xBE42, 0xBE42 }, +{ 0xBE43, 0xBE43, 0xBE43 }, +{ 0xBE44, 0xBE44, 0xBE44 }, +{ 0xBE45, 0xBE45, 0xBE45 }, +{ 0xBE46, 0xBE46, 0xBE46 }, +{ 0xBE47, 0xBE47, 0xBE47 }, +{ 0xBE48, 0xBE48, 0xBE48 }, +{ 0xBE49, 0xBE49, 0xBE49 }, +{ 0xBE4A, 0xBE4A, 0xBE4A }, +{ 0xBE4B, 0xBE4B, 0xBE4B }, +{ 0xBE4C, 0xBE4C, 0xBE4C }, +{ 0xBE4D, 0xBE4D, 0xBE4D }, +{ 0xBE4E, 0xBE4E, 0xBE4E }, +{ 0xBE4F, 0xBE4F, 0xBE4F }, +{ 0xBE50, 0xBE50, 0xBE50 }, +{ 0xBE51, 0xBE51, 0xBE51 }, +{ 0xBE52, 0xBE52, 0xBE52 }, +{ 0xBE53, 0xBE53, 0xBE53 }, +{ 0xBE54, 0xBE54, 0xBE54 }, +{ 0xBE55, 0xBE55, 0xBE55 }, +{ 0xBE56, 0xBE56, 0xBE56 }, +{ 0xBE57, 0xBE57, 0xBE57 }, +{ 0xBE58, 0xBE58, 0xBE58 }, +{ 0xBE59, 0xBE59, 0xBE59 }, +{ 0xBE5A, 0xBE5A, 0xBE5A }, +{ 0xBE5B, 0xBE5B, 0xBE5B }, +{ 0xBE5C, 0xBE5C, 0xBE5C }, +{ 0xBE5D, 0xBE5D, 0xBE5D }, +{ 0xBE5E, 0xBE5E, 0xBE5E }, +{ 0xBE5F, 0xBE5F, 0xBE5F }, +{ 0xBE60, 0xBE60, 0xBE60 }, +{ 0xBE61, 0xBE61, 0xBE61 }, +{ 0xBE62, 0xBE62, 0xBE62 }, +{ 0xBE63, 0xBE63, 0xBE63 }, +{ 0xBE64, 0xBE64, 0xBE64 }, +{ 0xBE65, 0xBE65, 0xBE65 }, +{ 0xBE66, 0xBE66, 0xBE66 }, +{ 0xBE67, 0xBE67, 0xBE67 }, +{ 0xBE68, 0xBE68, 0xBE68 }, +{ 0xBE69, 0xBE69, 0xBE69 }, +{ 0xBE6A, 0xBE6A, 0xBE6A }, +{ 0xBE6B, 0xBE6B, 0xBE6B }, +{ 0xBE6C, 0xBE6C, 0xBE6C }, +{ 0xBE6D, 0xBE6D, 0xBE6D }, +{ 0xBE6E, 0xBE6E, 0xBE6E }, +{ 0xBE6F, 0xBE6F, 0xBE6F }, +{ 0xBE70, 0xBE70, 0xBE70 }, +{ 0xBE71, 0xBE71, 0xBE71 }, +{ 0xBE72, 0xBE72, 0xBE72 }, +{ 0xBE73, 0xBE73, 0xBE73 }, +{ 0xBE74, 0xBE74, 0xBE74 }, +{ 0xBE75, 0xBE75, 0xBE75 }, +{ 0xBE76, 0xBE76, 0xBE76 }, +{ 0xBE77, 0xBE77, 0xBE77 }, +{ 0xBE78, 0xBE78, 0xBE78 }, +{ 0xBE79, 0xBE79, 0xBE79 }, +{ 0xBE7A, 0xBE7A, 0xBE7A }, +{ 0xBE7B, 0xBE7B, 0xBE7B }, +{ 0xBE7C, 0xBE7C, 0xBE7C }, +{ 0xBE7D, 0xBE7D, 0xBE7D }, +{ 0xBE7E, 0xBE7E, 0xBE7E }, +{ 0xBE7F, 0xBE7F, 0xBE7F }, +{ 0xBE80, 0xBE80, 0xBE80 }, +{ 0xBE81, 0xBE81, 0xBE81 }, +{ 0xBE82, 0xBE82, 0xBE82 }, +{ 0xBE83, 0xBE83, 0xBE83 }, +{ 0xBE84, 0xBE84, 0xBE84 }, +{ 0xBE85, 0xBE85, 0xBE85 }, +{ 0xBE86, 0xBE86, 0xBE86 }, +{ 0xBE87, 0xBE87, 0xBE87 }, +{ 0xBE88, 0xBE88, 0xBE88 }, +{ 0xBE89, 0xBE89, 0xBE89 }, +{ 0xBE8A, 0xBE8A, 0xBE8A }, +{ 0xBE8B, 0xBE8B, 0xBE8B }, +{ 0xBE8C, 0xBE8C, 0xBE8C }, +{ 0xBE8D, 0xBE8D, 0xBE8D }, +{ 0xBE8E, 0xBE8E, 0xBE8E }, +{ 0xBE8F, 0xBE8F, 0xBE8F }, +{ 0xBE90, 0xBE90, 0xBE90 }, +{ 0xBE91, 0xBE91, 0xBE91 }, +{ 0xBE92, 0xBE92, 0xBE92 }, +{ 0xBE93, 0xBE93, 0xBE93 }, +{ 0xBE94, 0xBE94, 0xBE94 }, +{ 0xBE95, 0xBE95, 0xBE95 }, +{ 0xBE96, 0xBE96, 0xBE96 }, +{ 0xBE97, 0xBE97, 0xBE97 }, +{ 0xBE98, 0xBE98, 0xBE98 }, +{ 0xBE99, 0xBE99, 0xBE99 }, +{ 0xBE9A, 0xBE9A, 0xBE9A }, +{ 0xBE9B, 0xBE9B, 0xBE9B }, +{ 0xBE9C, 0xBE9C, 0xBE9C }, +{ 0xBE9D, 0xBE9D, 0xBE9D }, +{ 0xBE9E, 0xBE9E, 0xBE9E }, +{ 0xBE9F, 0xBE9F, 0xBE9F }, +{ 0xBEA0, 0xBEA0, 0xBEA0 }, +{ 0xBEA1, 0xBEA1, 0xBEA1 }, +{ 0xBEA2, 0xBEA2, 0xBEA2 }, +{ 0xBEA3, 0xBEA3, 0xBEA3 }, +{ 0xBEA4, 0xBEA4, 0xBEA4 }, +{ 0xBEA5, 0xBEA5, 0xBEA5 }, +{ 0xBEA6, 0xBEA6, 0xBEA6 }, +{ 0xBEA7, 0xBEA7, 0xBEA7 }, +{ 0xBEA8, 0xBEA8, 0xBEA8 }, +{ 0xBEA9, 0xBEA9, 0xBEA9 }, +{ 0xBEAA, 0xBEAA, 0xBEAA }, +{ 0xBEAB, 0xBEAB, 0xBEAB }, +{ 0xBEAC, 0xBEAC, 0xBEAC }, +{ 0xBEAD, 0xBEAD, 0xBEAD }, +{ 0xBEAE, 0xBEAE, 0xBEAE }, +{ 0xBEAF, 0xBEAF, 0xBEAF }, +{ 0xBEB0, 0xBEB0, 0xBEB0 }, +{ 0xBEB1, 0xBEB1, 0xBEB1 }, +{ 0xBEB2, 0xBEB2, 0xBEB2 }, +{ 0xBEB3, 0xBEB3, 0xBEB3 }, +{ 0xBEB4, 0xBEB4, 0xBEB4 }, +{ 0xBEB5, 0xBEB5, 0xBEB5 }, +{ 0xBEB6, 0xBEB6, 0xBEB6 }, +{ 0xBEB7, 0xBEB7, 0xBEB7 }, +{ 0xBEB8, 0xBEB8, 0xBEB8 }, +{ 0xBEB9, 0xBEB9, 0xBEB9 }, +{ 0xBEBA, 0xBEBA, 0xBEBA }, +{ 0xBEBB, 0xBEBB, 0xBEBB }, +{ 0xBEBC, 0xBEBC, 0xBEBC }, +{ 0xBEBD, 0xBEBD, 0xBEBD }, +{ 0xBEBE, 0xBEBE, 0xBEBE }, +{ 0xBEBF, 0xBEBF, 0xBEBF }, +{ 0xBEC0, 0xBEC0, 0xBEC0 }, +{ 0xBEC1, 0xBEC1, 0xBEC1 }, +{ 0xBEC2, 0xBEC2, 0xBEC2 }, +{ 0xBEC3, 0xBEC3, 0xBEC3 }, +{ 0xBEC4, 0xBEC4, 0xBEC4 }, +{ 0xBEC5, 0xBEC5, 0xBEC5 }, +{ 0xBEC6, 0xBEC6, 0xBEC6 }, +{ 0xBEC7, 0xBEC7, 0xBEC7 }, +{ 0xBEC8, 0xBEC8, 0xBEC8 }, +{ 0xBEC9, 0xBEC9, 0xBEC9 }, +{ 0xBECA, 0xBECA, 0xBECA }, +{ 0xBECB, 0xBECB, 0xBECB }, +{ 0xBECC, 0xBECC, 0xBECC }, +{ 0xBECD, 0xBECD, 0xBECD }, +{ 0xBECE, 0xBECE, 0xBECE }, +{ 0xBECF, 0xBECF, 0xBECF }, +{ 0xBED0, 0xBED0, 0xBED0 }, +{ 0xBED1, 0xBED1, 0xBED1 }, +{ 0xBED2, 0xBED2, 0xBED2 }, +{ 0xBED3, 0xBED3, 0xBED3 }, +{ 0xBED4, 0xBED4, 0xBED4 }, +{ 0xBED5, 0xBED5, 0xBED5 }, +{ 0xBED6, 0xBED6, 0xBED6 }, +{ 0xBED7, 0xBED7, 0xBED7 }, +{ 0xBED8, 0xBED8, 0xBED8 }, +{ 0xBED9, 0xBED9, 0xBED9 }, +{ 0xBEDA, 0xBEDA, 0xBEDA }, +{ 0xBEDB, 0xBEDB, 0xBEDB }, +{ 0xBEDC, 0xBEDC, 0xBEDC }, +{ 0xBEDD, 0xBEDD, 0xBEDD }, +{ 0xBEDE, 0xBEDE, 0xBEDE }, +{ 0xBEDF, 0xBEDF, 0xBEDF }, +{ 0xBEE0, 0xBEE0, 0xBEE0 }, +{ 0xBEE1, 0xBEE1, 0xBEE1 }, +{ 0xBEE2, 0xBEE2, 0xBEE2 }, +{ 0xBEE3, 0xBEE3, 0xBEE3 }, +{ 0xBEE4, 0xBEE4, 0xBEE4 }, +{ 0xBEE5, 0xBEE5, 0xBEE5 }, +{ 0xBEE6, 0xBEE6, 0xBEE6 }, +{ 0xBEE7, 0xBEE7, 0xBEE7 }, +{ 0xBEE8, 0xBEE8, 0xBEE8 }, +{ 0xBEE9, 0xBEE9, 0xBEE9 }, +{ 0xBEEA, 0xBEEA, 0xBEEA }, +{ 0xBEEB, 0xBEEB, 0xBEEB }, +{ 0xBEEC, 0xBEEC, 0xBEEC }, +{ 0xBEED, 0xBEED, 0xBEED }, +{ 0xBEEE, 0xBEEE, 0xBEEE }, +{ 0xBEEF, 0xBEEF, 0xBEEF }, +{ 0xBEF0, 0xBEF0, 0xBEF0 }, +{ 0xBEF1, 0xBEF1, 0xBEF1 }, +{ 0xBEF2, 0xBEF2, 0xBEF2 }, +{ 0xBEF3, 0xBEF3, 0xBEF3 }, +{ 0xBEF4, 0xBEF4, 0xBEF4 }, +{ 0xBEF5, 0xBEF5, 0xBEF5 }, +{ 0xBEF6, 0xBEF6, 0xBEF6 }, +{ 0xBEF7, 0xBEF7, 0xBEF7 }, +{ 0xBEF8, 0xBEF8, 0xBEF8 }, +{ 0xBEF9, 0xBEF9, 0xBEF9 }, +{ 0xBEFA, 0xBEFA, 0xBEFA }, +{ 0xBEFB, 0xBEFB, 0xBEFB }, +{ 0xBEFC, 0xBEFC, 0xBEFC }, +{ 0xBEFD, 0xBEFD, 0xBEFD }, +{ 0xBEFE, 0xBEFE, 0xBEFE }, +{ 0xBEFF, 0xBEFF, 0xBEFF }, +{ 0xBF00, 0xBF00, 0xBF00 }, +{ 0xBF01, 0xBF01, 0xBF01 }, +{ 0xBF02, 0xBF02, 0xBF02 }, +{ 0xBF03, 0xBF03, 0xBF03 }, +{ 0xBF04, 0xBF04, 0xBF04 }, +{ 0xBF05, 0xBF05, 0xBF05 }, +{ 0xBF06, 0xBF06, 0xBF06 }, +{ 0xBF07, 0xBF07, 0xBF07 }, +{ 0xBF08, 0xBF08, 0xBF08 }, +{ 0xBF09, 0xBF09, 0xBF09 }, +{ 0xBF0A, 0xBF0A, 0xBF0A }, +{ 0xBF0B, 0xBF0B, 0xBF0B }, +{ 0xBF0C, 0xBF0C, 0xBF0C }, +{ 0xBF0D, 0xBF0D, 0xBF0D }, +{ 0xBF0E, 0xBF0E, 0xBF0E }, +{ 0xBF0F, 0xBF0F, 0xBF0F }, +{ 0xBF10, 0xBF10, 0xBF10 }, +{ 0xBF11, 0xBF11, 0xBF11 }, +{ 0xBF12, 0xBF12, 0xBF12 }, +{ 0xBF13, 0xBF13, 0xBF13 }, +{ 0xBF14, 0xBF14, 0xBF14 }, +{ 0xBF15, 0xBF15, 0xBF15 }, +{ 0xBF16, 0xBF16, 0xBF16 }, +{ 0xBF17, 0xBF17, 0xBF17 }, +{ 0xBF18, 0xBF18, 0xBF18 }, +{ 0xBF19, 0xBF19, 0xBF19 }, +{ 0xBF1A, 0xBF1A, 0xBF1A }, +{ 0xBF1B, 0xBF1B, 0xBF1B }, +{ 0xBF1C, 0xBF1C, 0xBF1C }, +{ 0xBF1D, 0xBF1D, 0xBF1D }, +{ 0xBF1E, 0xBF1E, 0xBF1E }, +{ 0xBF1F, 0xBF1F, 0xBF1F }, +{ 0xBF20, 0xBF20, 0xBF20 }, +{ 0xBF21, 0xBF21, 0xBF21 }, +{ 0xBF22, 0xBF22, 0xBF22 }, +{ 0xBF23, 0xBF23, 0xBF23 }, +{ 0xBF24, 0xBF24, 0xBF24 }, +{ 0xBF25, 0xBF25, 0xBF25 }, +{ 0xBF26, 0xBF26, 0xBF26 }, +{ 0xBF27, 0xBF27, 0xBF27 }, +{ 0xBF28, 0xBF28, 0xBF28 }, +{ 0xBF29, 0xBF29, 0xBF29 }, +{ 0xBF2A, 0xBF2A, 0xBF2A }, +{ 0xBF2B, 0xBF2B, 0xBF2B }, +{ 0xBF2C, 0xBF2C, 0xBF2C }, +{ 0xBF2D, 0xBF2D, 0xBF2D }, +{ 0xBF2E, 0xBF2E, 0xBF2E }, +{ 0xBF2F, 0xBF2F, 0xBF2F }, +{ 0xBF30, 0xBF30, 0xBF30 }, +{ 0xBF31, 0xBF31, 0xBF31 }, +{ 0xBF32, 0xBF32, 0xBF32 }, +{ 0xBF33, 0xBF33, 0xBF33 }, +{ 0xBF34, 0xBF34, 0xBF34 }, +{ 0xBF35, 0xBF35, 0xBF35 }, +{ 0xBF36, 0xBF36, 0xBF36 }, +{ 0xBF37, 0xBF37, 0xBF37 }, +{ 0xBF38, 0xBF38, 0xBF38 }, +{ 0xBF39, 0xBF39, 0xBF39 }, +{ 0xBF3A, 0xBF3A, 0xBF3A }, +{ 0xBF3B, 0xBF3B, 0xBF3B }, +{ 0xBF3C, 0xBF3C, 0xBF3C }, +{ 0xBF3D, 0xBF3D, 0xBF3D }, +{ 0xBF3E, 0xBF3E, 0xBF3E }, +{ 0xBF3F, 0xBF3F, 0xBF3F }, +{ 0xBF40, 0xBF40, 0xBF40 }, +{ 0xBF41, 0xBF41, 0xBF41 }, +{ 0xBF42, 0xBF42, 0xBF42 }, +{ 0xBF43, 0xBF43, 0xBF43 }, +{ 0xBF44, 0xBF44, 0xBF44 }, +{ 0xBF45, 0xBF45, 0xBF45 }, +{ 0xBF46, 0xBF46, 0xBF46 }, +{ 0xBF47, 0xBF47, 0xBF47 }, +{ 0xBF48, 0xBF48, 0xBF48 }, +{ 0xBF49, 0xBF49, 0xBF49 }, +{ 0xBF4A, 0xBF4A, 0xBF4A }, +{ 0xBF4B, 0xBF4B, 0xBF4B }, +{ 0xBF4C, 0xBF4C, 0xBF4C }, +{ 0xBF4D, 0xBF4D, 0xBF4D }, +{ 0xBF4E, 0xBF4E, 0xBF4E }, +{ 0xBF4F, 0xBF4F, 0xBF4F }, +{ 0xBF50, 0xBF50, 0xBF50 }, +{ 0xBF51, 0xBF51, 0xBF51 }, +{ 0xBF52, 0xBF52, 0xBF52 }, +{ 0xBF53, 0xBF53, 0xBF53 }, +{ 0xBF54, 0xBF54, 0xBF54 }, +{ 0xBF55, 0xBF55, 0xBF55 }, +{ 0xBF56, 0xBF56, 0xBF56 }, +{ 0xBF57, 0xBF57, 0xBF57 }, +{ 0xBF58, 0xBF58, 0xBF58 }, +{ 0xBF59, 0xBF59, 0xBF59 }, +{ 0xBF5A, 0xBF5A, 0xBF5A }, +{ 0xBF5B, 0xBF5B, 0xBF5B }, +{ 0xBF5C, 0xBF5C, 0xBF5C }, +{ 0xBF5D, 0xBF5D, 0xBF5D }, +{ 0xBF5E, 0xBF5E, 0xBF5E }, +{ 0xBF5F, 0xBF5F, 0xBF5F }, +{ 0xBF60, 0xBF60, 0xBF60 }, +{ 0xBF61, 0xBF61, 0xBF61 }, +{ 0xBF62, 0xBF62, 0xBF62 }, +{ 0xBF63, 0xBF63, 0xBF63 }, +{ 0xBF64, 0xBF64, 0xBF64 }, +{ 0xBF65, 0xBF65, 0xBF65 }, +{ 0xBF66, 0xBF66, 0xBF66 }, +{ 0xBF67, 0xBF67, 0xBF67 }, +{ 0xBF68, 0xBF68, 0xBF68 }, +{ 0xBF69, 0xBF69, 0xBF69 }, +{ 0xBF6A, 0xBF6A, 0xBF6A }, +{ 0xBF6B, 0xBF6B, 0xBF6B }, +{ 0xBF6C, 0xBF6C, 0xBF6C }, +{ 0xBF6D, 0xBF6D, 0xBF6D }, +{ 0xBF6E, 0xBF6E, 0xBF6E }, +{ 0xBF6F, 0xBF6F, 0xBF6F }, +{ 0xBF70, 0xBF70, 0xBF70 }, +{ 0xBF71, 0xBF71, 0xBF71 }, +{ 0xBF72, 0xBF72, 0xBF72 }, +{ 0xBF73, 0xBF73, 0xBF73 }, +{ 0xBF74, 0xBF74, 0xBF74 }, +{ 0xBF75, 0xBF75, 0xBF75 }, +{ 0xBF76, 0xBF76, 0xBF76 }, +{ 0xBF77, 0xBF77, 0xBF77 }, +{ 0xBF78, 0xBF78, 0xBF78 }, +{ 0xBF79, 0xBF79, 0xBF79 }, +{ 0xBF7A, 0xBF7A, 0xBF7A }, +{ 0xBF7B, 0xBF7B, 0xBF7B }, +{ 0xBF7C, 0xBF7C, 0xBF7C }, +{ 0xBF7D, 0xBF7D, 0xBF7D }, +{ 0xBF7E, 0xBF7E, 0xBF7E }, +{ 0xBF7F, 0xBF7F, 0xBF7F }, +{ 0xBF80, 0xBF80, 0xBF80 }, +{ 0xBF81, 0xBF81, 0xBF81 }, +{ 0xBF82, 0xBF82, 0xBF82 }, +{ 0xBF83, 0xBF83, 0xBF83 }, +{ 0xBF84, 0xBF84, 0xBF84 }, +{ 0xBF85, 0xBF85, 0xBF85 }, +{ 0xBF86, 0xBF86, 0xBF86 }, +{ 0xBF87, 0xBF87, 0xBF87 }, +{ 0xBF88, 0xBF88, 0xBF88 }, +{ 0xBF89, 0xBF89, 0xBF89 }, +{ 0xBF8A, 0xBF8A, 0xBF8A }, +{ 0xBF8B, 0xBF8B, 0xBF8B }, +{ 0xBF8C, 0xBF8C, 0xBF8C }, +{ 0xBF8D, 0xBF8D, 0xBF8D }, +{ 0xBF8E, 0xBF8E, 0xBF8E }, +{ 0xBF8F, 0xBF8F, 0xBF8F }, +{ 0xBF90, 0xBF90, 0xBF90 }, +{ 0xBF91, 0xBF91, 0xBF91 }, +{ 0xBF92, 0xBF92, 0xBF92 }, +{ 0xBF93, 0xBF93, 0xBF93 }, +{ 0xBF94, 0xBF94, 0xBF94 }, +{ 0xBF95, 0xBF95, 0xBF95 }, +{ 0xBF96, 0xBF96, 0xBF96 }, +{ 0xBF97, 0xBF97, 0xBF97 }, +{ 0xBF98, 0xBF98, 0xBF98 }, +{ 0xBF99, 0xBF99, 0xBF99 }, +{ 0xBF9A, 0xBF9A, 0xBF9A }, +{ 0xBF9B, 0xBF9B, 0xBF9B }, +{ 0xBF9C, 0xBF9C, 0xBF9C }, +{ 0xBF9D, 0xBF9D, 0xBF9D }, +{ 0xBF9E, 0xBF9E, 0xBF9E }, +{ 0xBF9F, 0xBF9F, 0xBF9F }, +{ 0xBFA0, 0xBFA0, 0xBFA0 }, +{ 0xBFA1, 0xBFA1, 0xBFA1 }, +{ 0xBFA2, 0xBFA2, 0xBFA2 }, +{ 0xBFA3, 0xBFA3, 0xBFA3 }, +{ 0xBFA4, 0xBFA4, 0xBFA4 }, +{ 0xBFA5, 0xBFA5, 0xBFA5 }, +{ 0xBFA6, 0xBFA6, 0xBFA6 }, +{ 0xBFA7, 0xBFA7, 0xBFA7 }, +{ 0xBFA8, 0xBFA8, 0xBFA8 }, +{ 0xBFA9, 0xBFA9, 0xBFA9 }, +{ 0xBFAA, 0xBFAA, 0xBFAA }, +{ 0xBFAB, 0xBFAB, 0xBFAB }, +{ 0xBFAC, 0xBFAC, 0xBFAC }, +{ 0xBFAD, 0xBFAD, 0xBFAD }, +{ 0xBFAE, 0xBFAE, 0xBFAE }, +{ 0xBFAF, 0xBFAF, 0xBFAF }, +{ 0xBFB0, 0xBFB0, 0xBFB0 }, +{ 0xBFB1, 0xBFB1, 0xBFB1 }, +{ 0xBFB2, 0xBFB2, 0xBFB2 }, +{ 0xBFB3, 0xBFB3, 0xBFB3 }, +{ 0xBFB4, 0xBFB4, 0xBFB4 }, +{ 0xBFB5, 0xBFB5, 0xBFB5 }, +{ 0xBFB6, 0xBFB6, 0xBFB6 }, +{ 0xBFB7, 0xBFB7, 0xBFB7 }, +{ 0xBFB8, 0xBFB8, 0xBFB8 }, +{ 0xBFB9, 0xBFB9, 0xBFB9 }, +{ 0xBFBA, 0xBFBA, 0xBFBA }, +{ 0xBFBB, 0xBFBB, 0xBFBB }, +{ 0xBFBC, 0xBFBC, 0xBFBC }, +{ 0xBFBD, 0xBFBD, 0xBFBD }, +{ 0xBFBE, 0xBFBE, 0xBFBE }, +{ 0xBFBF, 0xBFBF, 0xBFBF }, +{ 0xBFC0, 0xBFC0, 0xBFC0 }, +{ 0xBFC1, 0xBFC1, 0xBFC1 }, +{ 0xBFC2, 0xBFC2, 0xBFC2 }, +{ 0xBFC3, 0xBFC3, 0xBFC3 }, +{ 0xBFC4, 0xBFC4, 0xBFC4 }, +{ 0xBFC5, 0xBFC5, 0xBFC5 }, +{ 0xBFC6, 0xBFC6, 0xBFC6 }, +{ 0xBFC7, 0xBFC7, 0xBFC7 }, +{ 0xBFC8, 0xBFC8, 0xBFC8 }, +{ 0xBFC9, 0xBFC9, 0xBFC9 }, +{ 0xBFCA, 0xBFCA, 0xBFCA }, +{ 0xBFCB, 0xBFCB, 0xBFCB }, +{ 0xBFCC, 0xBFCC, 0xBFCC }, +{ 0xBFCD, 0xBFCD, 0xBFCD }, +{ 0xBFCE, 0xBFCE, 0xBFCE }, +{ 0xBFCF, 0xBFCF, 0xBFCF }, +{ 0xBFD0, 0xBFD0, 0xBFD0 }, +{ 0xBFD1, 0xBFD1, 0xBFD1 }, +{ 0xBFD2, 0xBFD2, 0xBFD2 }, +{ 0xBFD3, 0xBFD3, 0xBFD3 }, +{ 0xBFD4, 0xBFD4, 0xBFD4 }, +{ 0xBFD5, 0xBFD5, 0xBFD5 }, +{ 0xBFD6, 0xBFD6, 0xBFD6 }, +{ 0xBFD7, 0xBFD7, 0xBFD7 }, +{ 0xBFD8, 0xBFD8, 0xBFD8 }, +{ 0xBFD9, 0xBFD9, 0xBFD9 }, +{ 0xBFDA, 0xBFDA, 0xBFDA }, +{ 0xBFDB, 0xBFDB, 0xBFDB }, +{ 0xBFDC, 0xBFDC, 0xBFDC }, +{ 0xBFDD, 0xBFDD, 0xBFDD }, +{ 0xBFDE, 0xBFDE, 0xBFDE }, +{ 0xBFDF, 0xBFDF, 0xBFDF }, +{ 0xBFE0, 0xBFE0, 0xBFE0 }, +{ 0xBFE1, 0xBFE1, 0xBFE1 }, +{ 0xBFE2, 0xBFE2, 0xBFE2 }, +{ 0xBFE3, 0xBFE3, 0xBFE3 }, +{ 0xBFE4, 0xBFE4, 0xBFE4 }, +{ 0xBFE5, 0xBFE5, 0xBFE5 }, +{ 0xBFE6, 0xBFE6, 0xBFE6 }, +{ 0xBFE7, 0xBFE7, 0xBFE7 }, +{ 0xBFE8, 0xBFE8, 0xBFE8 }, +{ 0xBFE9, 0xBFE9, 0xBFE9 }, +{ 0xBFEA, 0xBFEA, 0xBFEA }, +{ 0xBFEB, 0xBFEB, 0xBFEB }, +{ 0xBFEC, 0xBFEC, 0xBFEC }, +{ 0xBFED, 0xBFED, 0xBFED }, +{ 0xBFEE, 0xBFEE, 0xBFEE }, +{ 0xBFEF, 0xBFEF, 0xBFEF }, +{ 0xBFF0, 0xBFF0, 0xBFF0 }, +{ 0xBFF1, 0xBFF1, 0xBFF1 }, +{ 0xBFF2, 0xBFF2, 0xBFF2 }, +{ 0xBFF3, 0xBFF3, 0xBFF3 }, +{ 0xBFF4, 0xBFF4, 0xBFF4 }, +{ 0xBFF5, 0xBFF5, 0xBFF5 }, +{ 0xBFF6, 0xBFF6, 0xBFF6 }, +{ 0xBFF7, 0xBFF7, 0xBFF7 }, +{ 0xBFF8, 0xBFF8, 0xBFF8 }, +{ 0xBFF9, 0xBFF9, 0xBFF9 }, +{ 0xBFFA, 0xBFFA, 0xBFFA }, +{ 0xBFFB, 0xBFFB, 0xBFFB }, +{ 0xBFFC, 0xBFFC, 0xBFFC }, +{ 0xBFFD, 0xBFFD, 0xBFFD }, +{ 0xBFFE, 0xBFFE, 0xBFFE }, +{ 0xBFFF, 0xBFFF, 0xBFFF }, +{ 0xC000, 0xC000, 0xC000 }, +{ 0xC001, 0xC001, 0xC001 }, +{ 0xC002, 0xC002, 0xC002 }, +{ 0xC003, 0xC003, 0xC003 }, +{ 0xC004, 0xC004, 0xC004 }, +{ 0xC005, 0xC005, 0xC005 }, +{ 0xC006, 0xC006, 0xC006 }, +{ 0xC007, 0xC007, 0xC007 }, +{ 0xC008, 0xC008, 0xC008 }, +{ 0xC009, 0xC009, 0xC009 }, +{ 0xC00A, 0xC00A, 0xC00A }, +{ 0xC00B, 0xC00B, 0xC00B }, +{ 0xC00C, 0xC00C, 0xC00C }, +{ 0xC00D, 0xC00D, 0xC00D }, +{ 0xC00E, 0xC00E, 0xC00E }, +{ 0xC00F, 0xC00F, 0xC00F }, +{ 0xC010, 0xC010, 0xC010 }, +{ 0xC011, 0xC011, 0xC011 }, +{ 0xC012, 0xC012, 0xC012 }, +{ 0xC013, 0xC013, 0xC013 }, +{ 0xC014, 0xC014, 0xC014 }, +{ 0xC015, 0xC015, 0xC015 }, +{ 0xC016, 0xC016, 0xC016 }, +{ 0xC017, 0xC017, 0xC017 }, +{ 0xC018, 0xC018, 0xC018 }, +{ 0xC019, 0xC019, 0xC019 }, +{ 0xC01A, 0xC01A, 0xC01A }, +{ 0xC01B, 0xC01B, 0xC01B }, +{ 0xC01C, 0xC01C, 0xC01C }, +{ 0xC01D, 0xC01D, 0xC01D }, +{ 0xC01E, 0xC01E, 0xC01E }, +{ 0xC01F, 0xC01F, 0xC01F }, +{ 0xC020, 0xC020, 0xC020 }, +{ 0xC021, 0xC021, 0xC021 }, +{ 0xC022, 0xC022, 0xC022 }, +{ 0xC023, 0xC023, 0xC023 }, +{ 0xC024, 0xC024, 0xC024 }, +{ 0xC025, 0xC025, 0xC025 }, +{ 0xC026, 0xC026, 0xC026 }, +{ 0xC027, 0xC027, 0xC027 }, +{ 0xC028, 0xC028, 0xC028 }, +{ 0xC029, 0xC029, 0xC029 }, +{ 0xC02A, 0xC02A, 0xC02A }, +{ 0xC02B, 0xC02B, 0xC02B }, +{ 0xC02C, 0xC02C, 0xC02C }, +{ 0xC02D, 0xC02D, 0xC02D }, +{ 0xC02E, 0xC02E, 0xC02E }, +{ 0xC02F, 0xC02F, 0xC02F }, +{ 0xC030, 0xC030, 0xC030 }, +{ 0xC031, 0xC031, 0xC031 }, +{ 0xC032, 0xC032, 0xC032 }, +{ 0xC033, 0xC033, 0xC033 }, +{ 0xC034, 0xC034, 0xC034 }, +{ 0xC035, 0xC035, 0xC035 }, +{ 0xC036, 0xC036, 0xC036 }, +{ 0xC037, 0xC037, 0xC037 }, +{ 0xC038, 0xC038, 0xC038 }, +{ 0xC039, 0xC039, 0xC039 }, +{ 0xC03A, 0xC03A, 0xC03A }, +{ 0xC03B, 0xC03B, 0xC03B }, +{ 0xC03C, 0xC03C, 0xC03C }, +{ 0xC03D, 0xC03D, 0xC03D }, +{ 0xC03E, 0xC03E, 0xC03E }, +{ 0xC03F, 0xC03F, 0xC03F }, +{ 0xC040, 0xC040, 0xC040 }, +{ 0xC041, 0xC041, 0xC041 }, +{ 0xC042, 0xC042, 0xC042 }, +{ 0xC043, 0xC043, 0xC043 }, +{ 0xC044, 0xC044, 0xC044 }, +{ 0xC045, 0xC045, 0xC045 }, +{ 0xC046, 0xC046, 0xC046 }, +{ 0xC047, 0xC047, 0xC047 }, +{ 0xC048, 0xC048, 0xC048 }, +{ 0xC049, 0xC049, 0xC049 }, +{ 0xC04A, 0xC04A, 0xC04A }, +{ 0xC04B, 0xC04B, 0xC04B }, +{ 0xC04C, 0xC04C, 0xC04C }, +{ 0xC04D, 0xC04D, 0xC04D }, +{ 0xC04E, 0xC04E, 0xC04E }, +{ 0xC04F, 0xC04F, 0xC04F }, +{ 0xC050, 0xC050, 0xC050 }, +{ 0xC051, 0xC051, 0xC051 }, +{ 0xC052, 0xC052, 0xC052 }, +{ 0xC053, 0xC053, 0xC053 }, +{ 0xC054, 0xC054, 0xC054 }, +{ 0xC055, 0xC055, 0xC055 }, +{ 0xC056, 0xC056, 0xC056 }, +{ 0xC057, 0xC057, 0xC057 }, +{ 0xC058, 0xC058, 0xC058 }, +{ 0xC059, 0xC059, 0xC059 }, +{ 0xC05A, 0xC05A, 0xC05A }, +{ 0xC05B, 0xC05B, 0xC05B }, +{ 0xC05C, 0xC05C, 0xC05C }, +{ 0xC05D, 0xC05D, 0xC05D }, +{ 0xC05E, 0xC05E, 0xC05E }, +{ 0xC05F, 0xC05F, 0xC05F }, +{ 0xC060, 0xC060, 0xC060 }, +{ 0xC061, 0xC061, 0xC061 }, +{ 0xC062, 0xC062, 0xC062 }, +{ 0xC063, 0xC063, 0xC063 }, +{ 0xC064, 0xC064, 0xC064 }, +{ 0xC065, 0xC065, 0xC065 }, +{ 0xC066, 0xC066, 0xC066 }, +{ 0xC067, 0xC067, 0xC067 }, +{ 0xC068, 0xC068, 0xC068 }, +{ 0xC069, 0xC069, 0xC069 }, +{ 0xC06A, 0xC06A, 0xC06A }, +{ 0xC06B, 0xC06B, 0xC06B }, +{ 0xC06C, 0xC06C, 0xC06C }, +{ 0xC06D, 0xC06D, 0xC06D }, +{ 0xC06E, 0xC06E, 0xC06E }, +{ 0xC06F, 0xC06F, 0xC06F }, +{ 0xC070, 0xC070, 0xC070 }, +{ 0xC071, 0xC071, 0xC071 }, +{ 0xC072, 0xC072, 0xC072 }, +{ 0xC073, 0xC073, 0xC073 }, +{ 0xC074, 0xC074, 0xC074 }, +{ 0xC075, 0xC075, 0xC075 }, +{ 0xC076, 0xC076, 0xC076 }, +{ 0xC077, 0xC077, 0xC077 }, +{ 0xC078, 0xC078, 0xC078 }, +{ 0xC079, 0xC079, 0xC079 }, +{ 0xC07A, 0xC07A, 0xC07A }, +{ 0xC07B, 0xC07B, 0xC07B }, +{ 0xC07C, 0xC07C, 0xC07C }, +{ 0xC07D, 0xC07D, 0xC07D }, +{ 0xC07E, 0xC07E, 0xC07E }, +{ 0xC07F, 0xC07F, 0xC07F }, +{ 0xC080, 0xC080, 0xC080 }, +{ 0xC081, 0xC081, 0xC081 }, +{ 0xC082, 0xC082, 0xC082 }, +{ 0xC083, 0xC083, 0xC083 }, +{ 0xC084, 0xC084, 0xC084 }, +{ 0xC085, 0xC085, 0xC085 }, +{ 0xC086, 0xC086, 0xC086 }, +{ 0xC087, 0xC087, 0xC087 }, +{ 0xC088, 0xC088, 0xC088 }, +{ 0xC089, 0xC089, 0xC089 }, +{ 0xC08A, 0xC08A, 0xC08A }, +{ 0xC08B, 0xC08B, 0xC08B }, +{ 0xC08C, 0xC08C, 0xC08C }, +{ 0xC08D, 0xC08D, 0xC08D }, +{ 0xC08E, 0xC08E, 0xC08E }, +{ 0xC08F, 0xC08F, 0xC08F }, +{ 0xC090, 0xC090, 0xC090 }, +{ 0xC091, 0xC091, 0xC091 }, +{ 0xC092, 0xC092, 0xC092 }, +{ 0xC093, 0xC093, 0xC093 }, +{ 0xC094, 0xC094, 0xC094 }, +{ 0xC095, 0xC095, 0xC095 }, +{ 0xC096, 0xC096, 0xC096 }, +{ 0xC097, 0xC097, 0xC097 }, +{ 0xC098, 0xC098, 0xC098 }, +{ 0xC099, 0xC099, 0xC099 }, +{ 0xC09A, 0xC09A, 0xC09A }, +{ 0xC09B, 0xC09B, 0xC09B }, +{ 0xC09C, 0xC09C, 0xC09C }, +{ 0xC09D, 0xC09D, 0xC09D }, +{ 0xC09E, 0xC09E, 0xC09E }, +{ 0xC09F, 0xC09F, 0xC09F }, +{ 0xC0A0, 0xC0A0, 0xC0A0 }, +{ 0xC0A1, 0xC0A1, 0xC0A1 }, +{ 0xC0A2, 0xC0A2, 0xC0A2 }, +{ 0xC0A3, 0xC0A3, 0xC0A3 }, +{ 0xC0A4, 0xC0A4, 0xC0A4 }, +{ 0xC0A5, 0xC0A5, 0xC0A5 }, +{ 0xC0A6, 0xC0A6, 0xC0A6 }, +{ 0xC0A7, 0xC0A7, 0xC0A7 }, +{ 0xC0A8, 0xC0A8, 0xC0A8 }, +{ 0xC0A9, 0xC0A9, 0xC0A9 }, +{ 0xC0AA, 0xC0AA, 0xC0AA }, +{ 0xC0AB, 0xC0AB, 0xC0AB }, +{ 0xC0AC, 0xC0AC, 0xC0AC }, +{ 0xC0AD, 0xC0AD, 0xC0AD }, +{ 0xC0AE, 0xC0AE, 0xC0AE }, +{ 0xC0AF, 0xC0AF, 0xC0AF }, +{ 0xC0B0, 0xC0B0, 0xC0B0 }, +{ 0xC0B1, 0xC0B1, 0xC0B1 }, +{ 0xC0B2, 0xC0B2, 0xC0B2 }, +{ 0xC0B3, 0xC0B3, 0xC0B3 }, +{ 0xC0B4, 0xC0B4, 0xC0B4 }, +{ 0xC0B5, 0xC0B5, 0xC0B5 }, +{ 0xC0B6, 0xC0B6, 0xC0B6 }, +{ 0xC0B7, 0xC0B7, 0xC0B7 }, +{ 0xC0B8, 0xC0B8, 0xC0B8 }, +{ 0xC0B9, 0xC0B9, 0xC0B9 }, +{ 0xC0BA, 0xC0BA, 0xC0BA }, +{ 0xC0BB, 0xC0BB, 0xC0BB }, +{ 0xC0BC, 0xC0BC, 0xC0BC }, +{ 0xC0BD, 0xC0BD, 0xC0BD }, +{ 0xC0BE, 0xC0BE, 0xC0BE }, +{ 0xC0BF, 0xC0BF, 0xC0BF }, +{ 0xC0C0, 0xC0C0, 0xC0C0 }, +{ 0xC0C1, 0xC0C1, 0xC0C1 }, +{ 0xC0C2, 0xC0C2, 0xC0C2 }, +{ 0xC0C3, 0xC0C3, 0xC0C3 }, +{ 0xC0C4, 0xC0C4, 0xC0C4 }, +{ 0xC0C5, 0xC0C5, 0xC0C5 }, +{ 0xC0C6, 0xC0C6, 0xC0C6 }, +{ 0xC0C7, 0xC0C7, 0xC0C7 }, +{ 0xC0C8, 0xC0C8, 0xC0C8 }, +{ 0xC0C9, 0xC0C9, 0xC0C9 }, +{ 0xC0CA, 0xC0CA, 0xC0CA }, +{ 0xC0CB, 0xC0CB, 0xC0CB }, +{ 0xC0CC, 0xC0CC, 0xC0CC }, +{ 0xC0CD, 0xC0CD, 0xC0CD }, +{ 0xC0CE, 0xC0CE, 0xC0CE }, +{ 0xC0CF, 0xC0CF, 0xC0CF }, +{ 0xC0D0, 0xC0D0, 0xC0D0 }, +{ 0xC0D1, 0xC0D1, 0xC0D1 }, +{ 0xC0D2, 0xC0D2, 0xC0D2 }, +{ 0xC0D3, 0xC0D3, 0xC0D3 }, +{ 0xC0D4, 0xC0D4, 0xC0D4 }, +{ 0xC0D5, 0xC0D5, 0xC0D5 }, +{ 0xC0D6, 0xC0D6, 0xC0D6 }, +{ 0xC0D7, 0xC0D7, 0xC0D7 }, +{ 0xC0D8, 0xC0D8, 0xC0D8 }, +{ 0xC0D9, 0xC0D9, 0xC0D9 }, +{ 0xC0DA, 0xC0DA, 0xC0DA }, +{ 0xC0DB, 0xC0DB, 0xC0DB }, +{ 0xC0DC, 0xC0DC, 0xC0DC }, +{ 0xC0DD, 0xC0DD, 0xC0DD }, +{ 0xC0DE, 0xC0DE, 0xC0DE }, +{ 0xC0DF, 0xC0DF, 0xC0DF }, +{ 0xC0E0, 0xC0E0, 0xC0E0 }, +{ 0xC0E1, 0xC0E1, 0xC0E1 }, +{ 0xC0E2, 0xC0E2, 0xC0E2 }, +{ 0xC0E3, 0xC0E3, 0xC0E3 }, +{ 0xC0E4, 0xC0E4, 0xC0E4 }, +{ 0xC0E5, 0xC0E5, 0xC0E5 }, +{ 0xC0E6, 0xC0E6, 0xC0E6 }, +{ 0xC0E7, 0xC0E7, 0xC0E7 }, +{ 0xC0E8, 0xC0E8, 0xC0E8 }, +{ 0xC0E9, 0xC0E9, 0xC0E9 }, +{ 0xC0EA, 0xC0EA, 0xC0EA }, +{ 0xC0EB, 0xC0EB, 0xC0EB }, +{ 0xC0EC, 0xC0EC, 0xC0EC }, +{ 0xC0ED, 0xC0ED, 0xC0ED }, +{ 0xC0EE, 0xC0EE, 0xC0EE }, +{ 0xC0EF, 0xC0EF, 0xC0EF }, +{ 0xC0F0, 0xC0F0, 0xC0F0 }, +{ 0xC0F1, 0xC0F1, 0xC0F1 }, +{ 0xC0F2, 0xC0F2, 0xC0F2 }, +{ 0xC0F3, 0xC0F3, 0xC0F3 }, +{ 0xC0F4, 0xC0F4, 0xC0F4 }, +{ 0xC0F5, 0xC0F5, 0xC0F5 }, +{ 0xC0F6, 0xC0F6, 0xC0F6 }, +{ 0xC0F7, 0xC0F7, 0xC0F7 }, +{ 0xC0F8, 0xC0F8, 0xC0F8 }, +{ 0xC0F9, 0xC0F9, 0xC0F9 }, +{ 0xC0FA, 0xC0FA, 0xC0FA }, +{ 0xC0FB, 0xC0FB, 0xC0FB }, +{ 0xC0FC, 0xC0FC, 0xC0FC }, +{ 0xC0FD, 0xC0FD, 0xC0FD }, +{ 0xC0FE, 0xC0FE, 0xC0FE }, +{ 0xC0FF, 0xC0FF, 0xC0FF }, +{ 0xC100, 0xC100, 0xC100 }, +{ 0xC101, 0xC101, 0xC101 }, +{ 0xC102, 0xC102, 0xC102 }, +{ 0xC103, 0xC103, 0xC103 }, +{ 0xC104, 0xC104, 0xC104 }, +{ 0xC105, 0xC105, 0xC105 }, +{ 0xC106, 0xC106, 0xC106 }, +{ 0xC107, 0xC107, 0xC107 }, +{ 0xC108, 0xC108, 0xC108 }, +{ 0xC109, 0xC109, 0xC109 }, +{ 0xC10A, 0xC10A, 0xC10A }, +{ 0xC10B, 0xC10B, 0xC10B }, +{ 0xC10C, 0xC10C, 0xC10C }, +{ 0xC10D, 0xC10D, 0xC10D }, +{ 0xC10E, 0xC10E, 0xC10E }, +{ 0xC10F, 0xC10F, 0xC10F }, +{ 0xC110, 0xC110, 0xC110 }, +{ 0xC111, 0xC111, 0xC111 }, +{ 0xC112, 0xC112, 0xC112 }, +{ 0xC113, 0xC113, 0xC113 }, +{ 0xC114, 0xC114, 0xC114 }, +{ 0xC115, 0xC115, 0xC115 }, +{ 0xC116, 0xC116, 0xC116 }, +{ 0xC117, 0xC117, 0xC117 }, +{ 0xC118, 0xC118, 0xC118 }, +{ 0xC119, 0xC119, 0xC119 }, +{ 0xC11A, 0xC11A, 0xC11A }, +{ 0xC11B, 0xC11B, 0xC11B }, +{ 0xC11C, 0xC11C, 0xC11C }, +{ 0xC11D, 0xC11D, 0xC11D }, +{ 0xC11E, 0xC11E, 0xC11E }, +{ 0xC11F, 0xC11F, 0xC11F }, +{ 0xC120, 0xC120, 0xC120 }, +{ 0xC121, 0xC121, 0xC121 }, +{ 0xC122, 0xC122, 0xC122 }, +{ 0xC123, 0xC123, 0xC123 }, +{ 0xC124, 0xC124, 0xC124 }, +{ 0xC125, 0xC125, 0xC125 }, +{ 0xC126, 0xC126, 0xC126 }, +{ 0xC127, 0xC127, 0xC127 }, +{ 0xC128, 0xC128, 0xC128 }, +{ 0xC129, 0xC129, 0xC129 }, +{ 0xC12A, 0xC12A, 0xC12A }, +{ 0xC12B, 0xC12B, 0xC12B }, +{ 0xC12C, 0xC12C, 0xC12C }, +{ 0xC12D, 0xC12D, 0xC12D }, +{ 0xC12E, 0xC12E, 0xC12E }, +{ 0xC12F, 0xC12F, 0xC12F }, +{ 0xC130, 0xC130, 0xC130 }, +{ 0xC131, 0xC131, 0xC131 }, +{ 0xC132, 0xC132, 0xC132 }, +{ 0xC133, 0xC133, 0xC133 }, +{ 0xC134, 0xC134, 0xC134 }, +{ 0xC135, 0xC135, 0xC135 }, +{ 0xC136, 0xC136, 0xC136 }, +{ 0xC137, 0xC137, 0xC137 }, +{ 0xC138, 0xC138, 0xC138 }, +{ 0xC139, 0xC139, 0xC139 }, +{ 0xC13A, 0xC13A, 0xC13A }, +{ 0xC13B, 0xC13B, 0xC13B }, +{ 0xC13C, 0xC13C, 0xC13C }, +{ 0xC13D, 0xC13D, 0xC13D }, +{ 0xC13E, 0xC13E, 0xC13E }, +{ 0xC13F, 0xC13F, 0xC13F }, +{ 0xC140, 0xC140, 0xC140 }, +{ 0xC141, 0xC141, 0xC141 }, +{ 0xC142, 0xC142, 0xC142 }, +{ 0xC143, 0xC143, 0xC143 }, +{ 0xC144, 0xC144, 0xC144 }, +{ 0xC145, 0xC145, 0xC145 }, +{ 0xC146, 0xC146, 0xC146 }, +{ 0xC147, 0xC147, 0xC147 }, +{ 0xC148, 0xC148, 0xC148 }, +{ 0xC149, 0xC149, 0xC149 }, +{ 0xC14A, 0xC14A, 0xC14A }, +{ 0xC14B, 0xC14B, 0xC14B }, +{ 0xC14C, 0xC14C, 0xC14C }, +{ 0xC14D, 0xC14D, 0xC14D }, +{ 0xC14E, 0xC14E, 0xC14E }, +{ 0xC14F, 0xC14F, 0xC14F }, +{ 0xC150, 0xC150, 0xC150 }, +{ 0xC151, 0xC151, 0xC151 }, +{ 0xC152, 0xC152, 0xC152 }, +{ 0xC153, 0xC153, 0xC153 }, +{ 0xC154, 0xC154, 0xC154 }, +{ 0xC155, 0xC155, 0xC155 }, +{ 0xC156, 0xC156, 0xC156 }, +{ 0xC157, 0xC157, 0xC157 }, +{ 0xC158, 0xC158, 0xC158 }, +{ 0xC159, 0xC159, 0xC159 }, +{ 0xC15A, 0xC15A, 0xC15A }, +{ 0xC15B, 0xC15B, 0xC15B }, +{ 0xC15C, 0xC15C, 0xC15C }, +{ 0xC15D, 0xC15D, 0xC15D }, +{ 0xC15E, 0xC15E, 0xC15E }, +{ 0xC15F, 0xC15F, 0xC15F }, +{ 0xC160, 0xC160, 0xC160 }, +{ 0xC161, 0xC161, 0xC161 }, +{ 0xC162, 0xC162, 0xC162 }, +{ 0xC163, 0xC163, 0xC163 }, +{ 0xC164, 0xC164, 0xC164 }, +{ 0xC165, 0xC165, 0xC165 }, +{ 0xC166, 0xC166, 0xC166 }, +{ 0xC167, 0xC167, 0xC167 }, +{ 0xC168, 0xC168, 0xC168 }, +{ 0xC169, 0xC169, 0xC169 }, +{ 0xC16A, 0xC16A, 0xC16A }, +{ 0xC16B, 0xC16B, 0xC16B }, +{ 0xC16C, 0xC16C, 0xC16C }, +{ 0xC16D, 0xC16D, 0xC16D }, +{ 0xC16E, 0xC16E, 0xC16E }, +{ 0xC16F, 0xC16F, 0xC16F }, +{ 0xC170, 0xC170, 0xC170 }, +{ 0xC171, 0xC171, 0xC171 }, +{ 0xC172, 0xC172, 0xC172 }, +{ 0xC173, 0xC173, 0xC173 }, +{ 0xC174, 0xC174, 0xC174 }, +{ 0xC175, 0xC175, 0xC175 }, +{ 0xC176, 0xC176, 0xC176 }, +{ 0xC177, 0xC177, 0xC177 }, +{ 0xC178, 0xC178, 0xC178 }, +{ 0xC179, 0xC179, 0xC179 }, +{ 0xC17A, 0xC17A, 0xC17A }, +{ 0xC17B, 0xC17B, 0xC17B }, +{ 0xC17C, 0xC17C, 0xC17C }, +{ 0xC17D, 0xC17D, 0xC17D }, +{ 0xC17E, 0xC17E, 0xC17E }, +{ 0xC17F, 0xC17F, 0xC17F }, +{ 0xC180, 0xC180, 0xC180 }, +{ 0xC181, 0xC181, 0xC181 }, +{ 0xC182, 0xC182, 0xC182 }, +{ 0xC183, 0xC183, 0xC183 }, +{ 0xC184, 0xC184, 0xC184 }, +{ 0xC185, 0xC185, 0xC185 }, +{ 0xC186, 0xC186, 0xC186 }, +{ 0xC187, 0xC187, 0xC187 }, +{ 0xC188, 0xC188, 0xC188 }, +{ 0xC189, 0xC189, 0xC189 }, +{ 0xC18A, 0xC18A, 0xC18A }, +{ 0xC18B, 0xC18B, 0xC18B }, +{ 0xC18C, 0xC18C, 0xC18C }, +{ 0xC18D, 0xC18D, 0xC18D }, +{ 0xC18E, 0xC18E, 0xC18E }, +{ 0xC18F, 0xC18F, 0xC18F }, +{ 0xC190, 0xC190, 0xC190 }, +{ 0xC191, 0xC191, 0xC191 }, +{ 0xC192, 0xC192, 0xC192 }, +{ 0xC193, 0xC193, 0xC193 }, +{ 0xC194, 0xC194, 0xC194 }, +{ 0xC195, 0xC195, 0xC195 }, +{ 0xC196, 0xC196, 0xC196 }, +{ 0xC197, 0xC197, 0xC197 }, +{ 0xC198, 0xC198, 0xC198 }, +{ 0xC199, 0xC199, 0xC199 }, +{ 0xC19A, 0xC19A, 0xC19A }, +{ 0xC19B, 0xC19B, 0xC19B }, +{ 0xC19C, 0xC19C, 0xC19C }, +{ 0xC19D, 0xC19D, 0xC19D }, +{ 0xC19E, 0xC19E, 0xC19E }, +{ 0xC19F, 0xC19F, 0xC19F }, +{ 0xC1A0, 0xC1A0, 0xC1A0 }, +{ 0xC1A1, 0xC1A1, 0xC1A1 }, +{ 0xC1A2, 0xC1A2, 0xC1A2 }, +{ 0xC1A3, 0xC1A3, 0xC1A3 }, +{ 0xC1A4, 0xC1A4, 0xC1A4 }, +{ 0xC1A5, 0xC1A5, 0xC1A5 }, +{ 0xC1A6, 0xC1A6, 0xC1A6 }, +{ 0xC1A7, 0xC1A7, 0xC1A7 }, +{ 0xC1A8, 0xC1A8, 0xC1A8 }, +{ 0xC1A9, 0xC1A9, 0xC1A9 }, +{ 0xC1AA, 0xC1AA, 0xC1AA }, +{ 0xC1AB, 0xC1AB, 0xC1AB }, +{ 0xC1AC, 0xC1AC, 0xC1AC }, +{ 0xC1AD, 0xC1AD, 0xC1AD }, +{ 0xC1AE, 0xC1AE, 0xC1AE }, +{ 0xC1AF, 0xC1AF, 0xC1AF }, +{ 0xC1B0, 0xC1B0, 0xC1B0 }, +{ 0xC1B1, 0xC1B1, 0xC1B1 }, +{ 0xC1B2, 0xC1B2, 0xC1B2 }, +{ 0xC1B3, 0xC1B3, 0xC1B3 }, +{ 0xC1B4, 0xC1B4, 0xC1B4 }, +{ 0xC1B5, 0xC1B5, 0xC1B5 }, +{ 0xC1B6, 0xC1B6, 0xC1B6 }, +{ 0xC1B7, 0xC1B7, 0xC1B7 }, +{ 0xC1B8, 0xC1B8, 0xC1B8 }, +{ 0xC1B9, 0xC1B9, 0xC1B9 }, +{ 0xC1BA, 0xC1BA, 0xC1BA }, +{ 0xC1BB, 0xC1BB, 0xC1BB }, +{ 0xC1BC, 0xC1BC, 0xC1BC }, +{ 0xC1BD, 0xC1BD, 0xC1BD }, +{ 0xC1BE, 0xC1BE, 0xC1BE }, +{ 0xC1BF, 0xC1BF, 0xC1BF }, +{ 0xC1C0, 0xC1C0, 0xC1C0 }, +{ 0xC1C1, 0xC1C1, 0xC1C1 }, +{ 0xC1C2, 0xC1C2, 0xC1C2 }, +{ 0xC1C3, 0xC1C3, 0xC1C3 }, +{ 0xC1C4, 0xC1C4, 0xC1C4 }, +{ 0xC1C5, 0xC1C5, 0xC1C5 }, +{ 0xC1C6, 0xC1C6, 0xC1C6 }, +{ 0xC1C7, 0xC1C7, 0xC1C7 }, +{ 0xC1C8, 0xC1C8, 0xC1C8 }, +{ 0xC1C9, 0xC1C9, 0xC1C9 }, +{ 0xC1CA, 0xC1CA, 0xC1CA }, +{ 0xC1CB, 0xC1CB, 0xC1CB }, +{ 0xC1CC, 0xC1CC, 0xC1CC }, +{ 0xC1CD, 0xC1CD, 0xC1CD }, +{ 0xC1CE, 0xC1CE, 0xC1CE }, +{ 0xC1CF, 0xC1CF, 0xC1CF }, +{ 0xC1D0, 0xC1D0, 0xC1D0 }, +{ 0xC1D1, 0xC1D1, 0xC1D1 }, +{ 0xC1D2, 0xC1D2, 0xC1D2 }, +{ 0xC1D3, 0xC1D3, 0xC1D3 }, +{ 0xC1D4, 0xC1D4, 0xC1D4 }, +{ 0xC1D5, 0xC1D5, 0xC1D5 }, +{ 0xC1D6, 0xC1D6, 0xC1D6 }, +{ 0xC1D7, 0xC1D7, 0xC1D7 }, +{ 0xC1D8, 0xC1D8, 0xC1D8 }, +{ 0xC1D9, 0xC1D9, 0xC1D9 }, +{ 0xC1DA, 0xC1DA, 0xC1DA }, +{ 0xC1DB, 0xC1DB, 0xC1DB }, +{ 0xC1DC, 0xC1DC, 0xC1DC }, +{ 0xC1DD, 0xC1DD, 0xC1DD }, +{ 0xC1DE, 0xC1DE, 0xC1DE }, +{ 0xC1DF, 0xC1DF, 0xC1DF }, +{ 0xC1E0, 0xC1E0, 0xC1E0 }, +{ 0xC1E1, 0xC1E1, 0xC1E1 }, +{ 0xC1E2, 0xC1E2, 0xC1E2 }, +{ 0xC1E3, 0xC1E3, 0xC1E3 }, +{ 0xC1E4, 0xC1E4, 0xC1E4 }, +{ 0xC1E5, 0xC1E5, 0xC1E5 }, +{ 0xC1E6, 0xC1E6, 0xC1E6 }, +{ 0xC1E7, 0xC1E7, 0xC1E7 }, +{ 0xC1E8, 0xC1E8, 0xC1E8 }, +{ 0xC1E9, 0xC1E9, 0xC1E9 }, +{ 0xC1EA, 0xC1EA, 0xC1EA }, +{ 0xC1EB, 0xC1EB, 0xC1EB }, +{ 0xC1EC, 0xC1EC, 0xC1EC }, +{ 0xC1ED, 0xC1ED, 0xC1ED }, +{ 0xC1EE, 0xC1EE, 0xC1EE }, +{ 0xC1EF, 0xC1EF, 0xC1EF }, +{ 0xC1F0, 0xC1F0, 0xC1F0 }, +{ 0xC1F1, 0xC1F1, 0xC1F1 }, +{ 0xC1F2, 0xC1F2, 0xC1F2 }, +{ 0xC1F3, 0xC1F3, 0xC1F3 }, +{ 0xC1F4, 0xC1F4, 0xC1F4 }, +{ 0xC1F5, 0xC1F5, 0xC1F5 }, +{ 0xC1F6, 0xC1F6, 0xC1F6 }, +{ 0xC1F7, 0xC1F7, 0xC1F7 }, +{ 0xC1F8, 0xC1F8, 0xC1F8 }, +{ 0xC1F9, 0xC1F9, 0xC1F9 }, +{ 0xC1FA, 0xC1FA, 0xC1FA }, +{ 0xC1FB, 0xC1FB, 0xC1FB }, +{ 0xC1FC, 0xC1FC, 0xC1FC }, +{ 0xC1FD, 0xC1FD, 0xC1FD }, +{ 0xC1FE, 0xC1FE, 0xC1FE }, +{ 0xC1FF, 0xC1FF, 0xC1FF }, +{ 0xC200, 0xC200, 0xC200 }, +{ 0xC201, 0xC201, 0xC201 }, +{ 0xC202, 0xC202, 0xC202 }, +{ 0xC203, 0xC203, 0xC203 }, +{ 0xC204, 0xC204, 0xC204 }, +{ 0xC205, 0xC205, 0xC205 }, +{ 0xC206, 0xC206, 0xC206 }, +{ 0xC207, 0xC207, 0xC207 }, +{ 0xC208, 0xC208, 0xC208 }, +{ 0xC209, 0xC209, 0xC209 }, +{ 0xC20A, 0xC20A, 0xC20A }, +{ 0xC20B, 0xC20B, 0xC20B }, +{ 0xC20C, 0xC20C, 0xC20C }, +{ 0xC20D, 0xC20D, 0xC20D }, +{ 0xC20E, 0xC20E, 0xC20E }, +{ 0xC20F, 0xC20F, 0xC20F }, +{ 0xC210, 0xC210, 0xC210 }, +{ 0xC211, 0xC211, 0xC211 }, +{ 0xC212, 0xC212, 0xC212 }, +{ 0xC213, 0xC213, 0xC213 }, +{ 0xC214, 0xC214, 0xC214 }, +{ 0xC215, 0xC215, 0xC215 }, +{ 0xC216, 0xC216, 0xC216 }, +{ 0xC217, 0xC217, 0xC217 }, +{ 0xC218, 0xC218, 0xC218 }, +{ 0xC219, 0xC219, 0xC219 }, +{ 0xC21A, 0xC21A, 0xC21A }, +{ 0xC21B, 0xC21B, 0xC21B }, +{ 0xC21C, 0xC21C, 0xC21C }, +{ 0xC21D, 0xC21D, 0xC21D }, +{ 0xC21E, 0xC21E, 0xC21E }, +{ 0xC21F, 0xC21F, 0xC21F }, +{ 0xC220, 0xC220, 0xC220 }, +{ 0xC221, 0xC221, 0xC221 }, +{ 0xC222, 0xC222, 0xC222 }, +{ 0xC223, 0xC223, 0xC223 }, +{ 0xC224, 0xC224, 0xC224 }, +{ 0xC225, 0xC225, 0xC225 }, +{ 0xC226, 0xC226, 0xC226 }, +{ 0xC227, 0xC227, 0xC227 }, +{ 0xC228, 0xC228, 0xC228 }, +{ 0xC229, 0xC229, 0xC229 }, +{ 0xC22A, 0xC22A, 0xC22A }, +{ 0xC22B, 0xC22B, 0xC22B }, +{ 0xC22C, 0xC22C, 0xC22C }, +{ 0xC22D, 0xC22D, 0xC22D }, +{ 0xC22E, 0xC22E, 0xC22E }, +{ 0xC22F, 0xC22F, 0xC22F }, +{ 0xC230, 0xC230, 0xC230 }, +{ 0xC231, 0xC231, 0xC231 }, +{ 0xC232, 0xC232, 0xC232 }, +{ 0xC233, 0xC233, 0xC233 }, +{ 0xC234, 0xC234, 0xC234 }, +{ 0xC235, 0xC235, 0xC235 }, +{ 0xC236, 0xC236, 0xC236 }, +{ 0xC237, 0xC237, 0xC237 }, +{ 0xC238, 0xC238, 0xC238 }, +{ 0xC239, 0xC239, 0xC239 }, +{ 0xC23A, 0xC23A, 0xC23A }, +{ 0xC23B, 0xC23B, 0xC23B }, +{ 0xC23C, 0xC23C, 0xC23C }, +{ 0xC23D, 0xC23D, 0xC23D }, +{ 0xC23E, 0xC23E, 0xC23E }, +{ 0xC23F, 0xC23F, 0xC23F }, +{ 0xC240, 0xC240, 0xC240 }, +{ 0xC241, 0xC241, 0xC241 }, +{ 0xC242, 0xC242, 0xC242 }, +{ 0xC243, 0xC243, 0xC243 }, +{ 0xC244, 0xC244, 0xC244 }, +{ 0xC245, 0xC245, 0xC245 }, +{ 0xC246, 0xC246, 0xC246 }, +{ 0xC247, 0xC247, 0xC247 }, +{ 0xC248, 0xC248, 0xC248 }, +{ 0xC249, 0xC249, 0xC249 }, +{ 0xC24A, 0xC24A, 0xC24A }, +{ 0xC24B, 0xC24B, 0xC24B }, +{ 0xC24C, 0xC24C, 0xC24C }, +{ 0xC24D, 0xC24D, 0xC24D }, +{ 0xC24E, 0xC24E, 0xC24E }, +{ 0xC24F, 0xC24F, 0xC24F }, +{ 0xC250, 0xC250, 0xC250 }, +{ 0xC251, 0xC251, 0xC251 }, +{ 0xC252, 0xC252, 0xC252 }, +{ 0xC253, 0xC253, 0xC253 }, +{ 0xC254, 0xC254, 0xC254 }, +{ 0xC255, 0xC255, 0xC255 }, +{ 0xC256, 0xC256, 0xC256 }, +{ 0xC257, 0xC257, 0xC257 }, +{ 0xC258, 0xC258, 0xC258 }, +{ 0xC259, 0xC259, 0xC259 }, +{ 0xC25A, 0xC25A, 0xC25A }, +{ 0xC25B, 0xC25B, 0xC25B }, +{ 0xC25C, 0xC25C, 0xC25C }, +{ 0xC25D, 0xC25D, 0xC25D }, +{ 0xC25E, 0xC25E, 0xC25E }, +{ 0xC25F, 0xC25F, 0xC25F }, +{ 0xC260, 0xC260, 0xC260 }, +{ 0xC261, 0xC261, 0xC261 }, +{ 0xC262, 0xC262, 0xC262 }, +{ 0xC263, 0xC263, 0xC263 }, +{ 0xC264, 0xC264, 0xC264 }, +{ 0xC265, 0xC265, 0xC265 }, +{ 0xC266, 0xC266, 0xC266 }, +{ 0xC267, 0xC267, 0xC267 }, +{ 0xC268, 0xC268, 0xC268 }, +{ 0xC269, 0xC269, 0xC269 }, +{ 0xC26A, 0xC26A, 0xC26A }, +{ 0xC26B, 0xC26B, 0xC26B }, +{ 0xC26C, 0xC26C, 0xC26C }, +{ 0xC26D, 0xC26D, 0xC26D }, +{ 0xC26E, 0xC26E, 0xC26E }, +{ 0xC26F, 0xC26F, 0xC26F }, +{ 0xC270, 0xC270, 0xC270 }, +{ 0xC271, 0xC271, 0xC271 }, +{ 0xC272, 0xC272, 0xC272 }, +{ 0xC273, 0xC273, 0xC273 }, +{ 0xC274, 0xC274, 0xC274 }, +{ 0xC275, 0xC275, 0xC275 }, +{ 0xC276, 0xC276, 0xC276 }, +{ 0xC277, 0xC277, 0xC277 }, +{ 0xC278, 0xC278, 0xC278 }, +{ 0xC279, 0xC279, 0xC279 }, +{ 0xC27A, 0xC27A, 0xC27A }, +{ 0xC27B, 0xC27B, 0xC27B }, +{ 0xC27C, 0xC27C, 0xC27C }, +{ 0xC27D, 0xC27D, 0xC27D }, +{ 0xC27E, 0xC27E, 0xC27E }, +{ 0xC27F, 0xC27F, 0xC27F }, +{ 0xC280, 0xC280, 0xC280 }, +{ 0xC281, 0xC281, 0xC281 }, +{ 0xC282, 0xC282, 0xC282 }, +{ 0xC283, 0xC283, 0xC283 }, +{ 0xC284, 0xC284, 0xC284 }, +{ 0xC285, 0xC285, 0xC285 }, +{ 0xC286, 0xC286, 0xC286 }, +{ 0xC287, 0xC287, 0xC287 }, +{ 0xC288, 0xC288, 0xC288 }, +{ 0xC289, 0xC289, 0xC289 }, +{ 0xC28A, 0xC28A, 0xC28A }, +{ 0xC28B, 0xC28B, 0xC28B }, +{ 0xC28C, 0xC28C, 0xC28C }, +{ 0xC28D, 0xC28D, 0xC28D }, +{ 0xC28E, 0xC28E, 0xC28E }, +{ 0xC28F, 0xC28F, 0xC28F }, +{ 0xC290, 0xC290, 0xC290 }, +{ 0xC291, 0xC291, 0xC291 }, +{ 0xC292, 0xC292, 0xC292 }, +{ 0xC293, 0xC293, 0xC293 }, +{ 0xC294, 0xC294, 0xC294 }, +{ 0xC295, 0xC295, 0xC295 }, +{ 0xC296, 0xC296, 0xC296 }, +{ 0xC297, 0xC297, 0xC297 }, +{ 0xC298, 0xC298, 0xC298 }, +{ 0xC299, 0xC299, 0xC299 }, +{ 0xC29A, 0xC29A, 0xC29A }, +{ 0xC29B, 0xC29B, 0xC29B }, +{ 0xC29C, 0xC29C, 0xC29C }, +{ 0xC29D, 0xC29D, 0xC29D }, +{ 0xC29E, 0xC29E, 0xC29E }, +{ 0xC29F, 0xC29F, 0xC29F }, +{ 0xC2A0, 0xC2A0, 0xC2A0 }, +{ 0xC2A1, 0xC2A1, 0xC2A1 }, +{ 0xC2A2, 0xC2A2, 0xC2A2 }, +{ 0xC2A3, 0xC2A3, 0xC2A3 }, +{ 0xC2A4, 0xC2A4, 0xC2A4 }, +{ 0xC2A5, 0xC2A5, 0xC2A5 }, +{ 0xC2A6, 0xC2A6, 0xC2A6 }, +{ 0xC2A7, 0xC2A7, 0xC2A7 }, +{ 0xC2A8, 0xC2A8, 0xC2A8 }, +{ 0xC2A9, 0xC2A9, 0xC2A9 }, +{ 0xC2AA, 0xC2AA, 0xC2AA }, +{ 0xC2AB, 0xC2AB, 0xC2AB }, +{ 0xC2AC, 0xC2AC, 0xC2AC }, +{ 0xC2AD, 0xC2AD, 0xC2AD }, +{ 0xC2AE, 0xC2AE, 0xC2AE }, +{ 0xC2AF, 0xC2AF, 0xC2AF }, +{ 0xC2B0, 0xC2B0, 0xC2B0 }, +{ 0xC2B1, 0xC2B1, 0xC2B1 }, +{ 0xC2B2, 0xC2B2, 0xC2B2 }, +{ 0xC2B3, 0xC2B3, 0xC2B3 }, +{ 0xC2B4, 0xC2B4, 0xC2B4 }, +{ 0xC2B5, 0xC2B5, 0xC2B5 }, +{ 0xC2B6, 0xC2B6, 0xC2B6 }, +{ 0xC2B7, 0xC2B7, 0xC2B7 }, +{ 0xC2B8, 0xC2B8, 0xC2B8 }, +{ 0xC2B9, 0xC2B9, 0xC2B9 }, +{ 0xC2BA, 0xC2BA, 0xC2BA }, +{ 0xC2BB, 0xC2BB, 0xC2BB }, +{ 0xC2BC, 0xC2BC, 0xC2BC }, +{ 0xC2BD, 0xC2BD, 0xC2BD }, +{ 0xC2BE, 0xC2BE, 0xC2BE }, +{ 0xC2BF, 0xC2BF, 0xC2BF }, +{ 0xC2C0, 0xC2C0, 0xC2C0 }, +{ 0xC2C1, 0xC2C1, 0xC2C1 }, +{ 0xC2C2, 0xC2C2, 0xC2C2 }, +{ 0xC2C3, 0xC2C3, 0xC2C3 }, +{ 0xC2C4, 0xC2C4, 0xC2C4 }, +{ 0xC2C5, 0xC2C5, 0xC2C5 }, +{ 0xC2C6, 0xC2C6, 0xC2C6 }, +{ 0xC2C7, 0xC2C7, 0xC2C7 }, +{ 0xC2C8, 0xC2C8, 0xC2C8 }, +{ 0xC2C9, 0xC2C9, 0xC2C9 }, +{ 0xC2CA, 0xC2CA, 0xC2CA }, +{ 0xC2CB, 0xC2CB, 0xC2CB }, +{ 0xC2CC, 0xC2CC, 0xC2CC }, +{ 0xC2CD, 0xC2CD, 0xC2CD }, +{ 0xC2CE, 0xC2CE, 0xC2CE }, +{ 0xC2CF, 0xC2CF, 0xC2CF }, +{ 0xC2D0, 0xC2D0, 0xC2D0 }, +{ 0xC2D1, 0xC2D1, 0xC2D1 }, +{ 0xC2D2, 0xC2D2, 0xC2D2 }, +{ 0xC2D3, 0xC2D3, 0xC2D3 }, +{ 0xC2D4, 0xC2D4, 0xC2D4 }, +{ 0xC2D5, 0xC2D5, 0xC2D5 }, +{ 0xC2D6, 0xC2D6, 0xC2D6 }, +{ 0xC2D7, 0xC2D7, 0xC2D7 }, +{ 0xC2D8, 0xC2D8, 0xC2D8 }, +{ 0xC2D9, 0xC2D9, 0xC2D9 }, +{ 0xC2DA, 0xC2DA, 0xC2DA }, +{ 0xC2DB, 0xC2DB, 0xC2DB }, +{ 0xC2DC, 0xC2DC, 0xC2DC }, +{ 0xC2DD, 0xC2DD, 0xC2DD }, +{ 0xC2DE, 0xC2DE, 0xC2DE }, +{ 0xC2DF, 0xC2DF, 0xC2DF }, +{ 0xC2E0, 0xC2E0, 0xC2E0 }, +{ 0xC2E1, 0xC2E1, 0xC2E1 }, +{ 0xC2E2, 0xC2E2, 0xC2E2 }, +{ 0xC2E3, 0xC2E3, 0xC2E3 }, +{ 0xC2E4, 0xC2E4, 0xC2E4 }, +{ 0xC2E5, 0xC2E5, 0xC2E5 }, +{ 0xC2E6, 0xC2E6, 0xC2E6 }, +{ 0xC2E7, 0xC2E7, 0xC2E7 }, +{ 0xC2E8, 0xC2E8, 0xC2E8 }, +{ 0xC2E9, 0xC2E9, 0xC2E9 }, +{ 0xC2EA, 0xC2EA, 0xC2EA }, +{ 0xC2EB, 0xC2EB, 0xC2EB }, +{ 0xC2EC, 0xC2EC, 0xC2EC }, +{ 0xC2ED, 0xC2ED, 0xC2ED }, +{ 0xC2EE, 0xC2EE, 0xC2EE }, +{ 0xC2EF, 0xC2EF, 0xC2EF }, +{ 0xC2F0, 0xC2F0, 0xC2F0 }, +{ 0xC2F1, 0xC2F1, 0xC2F1 }, +{ 0xC2F2, 0xC2F2, 0xC2F2 }, +{ 0xC2F3, 0xC2F3, 0xC2F3 }, +{ 0xC2F4, 0xC2F4, 0xC2F4 }, +{ 0xC2F5, 0xC2F5, 0xC2F5 }, +{ 0xC2F6, 0xC2F6, 0xC2F6 }, +{ 0xC2F7, 0xC2F7, 0xC2F7 }, +{ 0xC2F8, 0xC2F8, 0xC2F8 }, +{ 0xC2F9, 0xC2F9, 0xC2F9 }, +{ 0xC2FA, 0xC2FA, 0xC2FA }, +{ 0xC2FB, 0xC2FB, 0xC2FB }, +{ 0xC2FC, 0xC2FC, 0xC2FC }, +{ 0xC2FD, 0xC2FD, 0xC2FD }, +{ 0xC2FE, 0xC2FE, 0xC2FE }, +{ 0xC2FF, 0xC2FF, 0xC2FF }, +{ 0xC300, 0xC300, 0xC300 }, +{ 0xC301, 0xC301, 0xC301 }, +{ 0xC302, 0xC302, 0xC302 }, +{ 0xC303, 0xC303, 0xC303 }, +{ 0xC304, 0xC304, 0xC304 }, +{ 0xC305, 0xC305, 0xC305 }, +{ 0xC306, 0xC306, 0xC306 }, +{ 0xC307, 0xC307, 0xC307 }, +{ 0xC308, 0xC308, 0xC308 }, +{ 0xC309, 0xC309, 0xC309 }, +{ 0xC30A, 0xC30A, 0xC30A }, +{ 0xC30B, 0xC30B, 0xC30B }, +{ 0xC30C, 0xC30C, 0xC30C }, +{ 0xC30D, 0xC30D, 0xC30D }, +{ 0xC30E, 0xC30E, 0xC30E }, +{ 0xC30F, 0xC30F, 0xC30F }, +{ 0xC310, 0xC310, 0xC310 }, +{ 0xC311, 0xC311, 0xC311 }, +{ 0xC312, 0xC312, 0xC312 }, +{ 0xC313, 0xC313, 0xC313 }, +{ 0xC314, 0xC314, 0xC314 }, +{ 0xC315, 0xC315, 0xC315 }, +{ 0xC316, 0xC316, 0xC316 }, +{ 0xC317, 0xC317, 0xC317 }, +{ 0xC318, 0xC318, 0xC318 }, +{ 0xC319, 0xC319, 0xC319 }, +{ 0xC31A, 0xC31A, 0xC31A }, +{ 0xC31B, 0xC31B, 0xC31B }, +{ 0xC31C, 0xC31C, 0xC31C }, +{ 0xC31D, 0xC31D, 0xC31D }, +{ 0xC31E, 0xC31E, 0xC31E }, +{ 0xC31F, 0xC31F, 0xC31F }, +{ 0xC320, 0xC320, 0xC320 }, +{ 0xC321, 0xC321, 0xC321 }, +{ 0xC322, 0xC322, 0xC322 }, +{ 0xC323, 0xC323, 0xC323 }, +{ 0xC324, 0xC324, 0xC324 }, +{ 0xC325, 0xC325, 0xC325 }, +{ 0xC326, 0xC326, 0xC326 }, +{ 0xC327, 0xC327, 0xC327 }, +{ 0xC328, 0xC328, 0xC328 }, +{ 0xC329, 0xC329, 0xC329 }, +{ 0xC32A, 0xC32A, 0xC32A }, +{ 0xC32B, 0xC32B, 0xC32B }, +{ 0xC32C, 0xC32C, 0xC32C }, +{ 0xC32D, 0xC32D, 0xC32D }, +{ 0xC32E, 0xC32E, 0xC32E }, +{ 0xC32F, 0xC32F, 0xC32F }, +{ 0xC330, 0xC330, 0xC330 }, +{ 0xC331, 0xC331, 0xC331 }, +{ 0xC332, 0xC332, 0xC332 }, +{ 0xC333, 0xC333, 0xC333 }, +{ 0xC334, 0xC334, 0xC334 }, +{ 0xC335, 0xC335, 0xC335 }, +{ 0xC336, 0xC336, 0xC336 }, +{ 0xC337, 0xC337, 0xC337 }, +{ 0xC338, 0xC338, 0xC338 }, +{ 0xC339, 0xC339, 0xC339 }, +{ 0xC33A, 0xC33A, 0xC33A }, +{ 0xC33B, 0xC33B, 0xC33B }, +{ 0xC33C, 0xC33C, 0xC33C }, +{ 0xC33D, 0xC33D, 0xC33D }, +{ 0xC33E, 0xC33E, 0xC33E }, +{ 0xC33F, 0xC33F, 0xC33F }, +{ 0xC340, 0xC340, 0xC340 }, +{ 0xC341, 0xC341, 0xC341 }, +{ 0xC342, 0xC342, 0xC342 }, +{ 0xC343, 0xC343, 0xC343 }, +{ 0xC344, 0xC344, 0xC344 }, +{ 0xC345, 0xC345, 0xC345 }, +{ 0xC346, 0xC346, 0xC346 }, +{ 0xC347, 0xC347, 0xC347 }, +{ 0xC348, 0xC348, 0xC348 }, +{ 0xC349, 0xC349, 0xC349 }, +{ 0xC34A, 0xC34A, 0xC34A }, +{ 0xC34B, 0xC34B, 0xC34B }, +{ 0xC34C, 0xC34C, 0xC34C }, +{ 0xC34D, 0xC34D, 0xC34D }, +{ 0xC34E, 0xC34E, 0xC34E }, +{ 0xC34F, 0xC34F, 0xC34F }, +{ 0xC350, 0xC350, 0xC350 }, +{ 0xC351, 0xC351, 0xC351 }, +{ 0xC352, 0xC352, 0xC352 }, +{ 0xC353, 0xC353, 0xC353 }, +{ 0xC354, 0xC354, 0xC354 }, +{ 0xC355, 0xC355, 0xC355 }, +{ 0xC356, 0xC356, 0xC356 }, +{ 0xC357, 0xC357, 0xC357 }, +{ 0xC358, 0xC358, 0xC358 }, +{ 0xC359, 0xC359, 0xC359 }, +{ 0xC35A, 0xC35A, 0xC35A }, +{ 0xC35B, 0xC35B, 0xC35B }, +{ 0xC35C, 0xC35C, 0xC35C }, +{ 0xC35D, 0xC35D, 0xC35D }, +{ 0xC35E, 0xC35E, 0xC35E }, +{ 0xC35F, 0xC35F, 0xC35F }, +{ 0xC360, 0xC360, 0xC360 }, +{ 0xC361, 0xC361, 0xC361 }, +{ 0xC362, 0xC362, 0xC362 }, +{ 0xC363, 0xC363, 0xC363 }, +{ 0xC364, 0xC364, 0xC364 }, +{ 0xC365, 0xC365, 0xC365 }, +{ 0xC366, 0xC366, 0xC366 }, +{ 0xC367, 0xC367, 0xC367 }, +{ 0xC368, 0xC368, 0xC368 }, +{ 0xC369, 0xC369, 0xC369 }, +{ 0xC36A, 0xC36A, 0xC36A }, +{ 0xC36B, 0xC36B, 0xC36B }, +{ 0xC36C, 0xC36C, 0xC36C }, +{ 0xC36D, 0xC36D, 0xC36D }, +{ 0xC36E, 0xC36E, 0xC36E }, +{ 0xC36F, 0xC36F, 0xC36F }, +{ 0xC370, 0xC370, 0xC370 }, +{ 0xC371, 0xC371, 0xC371 }, +{ 0xC372, 0xC372, 0xC372 }, +{ 0xC373, 0xC373, 0xC373 }, +{ 0xC374, 0xC374, 0xC374 }, +{ 0xC375, 0xC375, 0xC375 }, +{ 0xC376, 0xC376, 0xC376 }, +{ 0xC377, 0xC377, 0xC377 }, +{ 0xC378, 0xC378, 0xC378 }, +{ 0xC379, 0xC379, 0xC379 }, +{ 0xC37A, 0xC37A, 0xC37A }, +{ 0xC37B, 0xC37B, 0xC37B }, +{ 0xC37C, 0xC37C, 0xC37C }, +{ 0xC37D, 0xC37D, 0xC37D }, +{ 0xC37E, 0xC37E, 0xC37E }, +{ 0xC37F, 0xC37F, 0xC37F }, +{ 0xC380, 0xC380, 0xC380 }, +{ 0xC381, 0xC381, 0xC381 }, +{ 0xC382, 0xC382, 0xC382 }, +{ 0xC383, 0xC383, 0xC383 }, +{ 0xC384, 0xC384, 0xC384 }, +{ 0xC385, 0xC385, 0xC385 }, +{ 0xC386, 0xC386, 0xC386 }, +{ 0xC387, 0xC387, 0xC387 }, +{ 0xC388, 0xC388, 0xC388 }, +{ 0xC389, 0xC389, 0xC389 }, +{ 0xC38A, 0xC38A, 0xC38A }, +{ 0xC38B, 0xC38B, 0xC38B }, +{ 0xC38C, 0xC38C, 0xC38C }, +{ 0xC38D, 0xC38D, 0xC38D }, +{ 0xC38E, 0xC38E, 0xC38E }, +{ 0xC38F, 0xC38F, 0xC38F }, +{ 0xC390, 0xC390, 0xC390 }, +{ 0xC391, 0xC391, 0xC391 }, +{ 0xC392, 0xC392, 0xC392 }, +{ 0xC393, 0xC393, 0xC393 }, +{ 0xC394, 0xC394, 0xC394 }, +{ 0xC395, 0xC395, 0xC395 }, +{ 0xC396, 0xC396, 0xC396 }, +{ 0xC397, 0xC397, 0xC397 }, +{ 0xC398, 0xC398, 0xC398 }, +{ 0xC399, 0xC399, 0xC399 }, +{ 0xC39A, 0xC39A, 0xC39A }, +{ 0xC39B, 0xC39B, 0xC39B }, +{ 0xC39C, 0xC39C, 0xC39C }, +{ 0xC39D, 0xC39D, 0xC39D }, +{ 0xC39E, 0xC39E, 0xC39E }, +{ 0xC39F, 0xC39F, 0xC39F }, +{ 0xC3A0, 0xC3A0, 0xC3A0 }, +{ 0xC3A1, 0xC3A1, 0xC3A1 }, +{ 0xC3A2, 0xC3A2, 0xC3A2 }, +{ 0xC3A3, 0xC3A3, 0xC3A3 }, +{ 0xC3A4, 0xC3A4, 0xC3A4 }, +{ 0xC3A5, 0xC3A5, 0xC3A5 }, +{ 0xC3A6, 0xC3A6, 0xC3A6 }, +{ 0xC3A7, 0xC3A7, 0xC3A7 }, +{ 0xC3A8, 0xC3A8, 0xC3A8 }, +{ 0xC3A9, 0xC3A9, 0xC3A9 }, +{ 0xC3AA, 0xC3AA, 0xC3AA }, +{ 0xC3AB, 0xC3AB, 0xC3AB }, +{ 0xC3AC, 0xC3AC, 0xC3AC }, +{ 0xC3AD, 0xC3AD, 0xC3AD }, +{ 0xC3AE, 0xC3AE, 0xC3AE }, +{ 0xC3AF, 0xC3AF, 0xC3AF }, +{ 0xC3B0, 0xC3B0, 0xC3B0 }, +{ 0xC3B1, 0xC3B1, 0xC3B1 }, +{ 0xC3B2, 0xC3B2, 0xC3B2 }, +{ 0xC3B3, 0xC3B3, 0xC3B3 }, +{ 0xC3B4, 0xC3B4, 0xC3B4 }, +{ 0xC3B5, 0xC3B5, 0xC3B5 }, +{ 0xC3B6, 0xC3B6, 0xC3B6 }, +{ 0xC3B7, 0xC3B7, 0xC3B7 }, +{ 0xC3B8, 0xC3B8, 0xC3B8 }, +{ 0xC3B9, 0xC3B9, 0xC3B9 }, +{ 0xC3BA, 0xC3BA, 0xC3BA }, +{ 0xC3BB, 0xC3BB, 0xC3BB }, +{ 0xC3BC, 0xC3BC, 0xC3BC }, +{ 0xC3BD, 0xC3BD, 0xC3BD }, +{ 0xC3BE, 0xC3BE, 0xC3BE }, +{ 0xC3BF, 0xC3BF, 0xC3BF }, +{ 0xC3C0, 0xC3C0, 0xC3C0 }, +{ 0xC3C1, 0xC3C1, 0xC3C1 }, +{ 0xC3C2, 0xC3C2, 0xC3C2 }, +{ 0xC3C3, 0xC3C3, 0xC3C3 }, +{ 0xC3C4, 0xC3C4, 0xC3C4 }, +{ 0xC3C5, 0xC3C5, 0xC3C5 }, +{ 0xC3C6, 0xC3C6, 0xC3C6 }, +{ 0xC3C7, 0xC3C7, 0xC3C7 }, +{ 0xC3C8, 0xC3C8, 0xC3C8 }, +{ 0xC3C9, 0xC3C9, 0xC3C9 }, +{ 0xC3CA, 0xC3CA, 0xC3CA }, +{ 0xC3CB, 0xC3CB, 0xC3CB }, +{ 0xC3CC, 0xC3CC, 0xC3CC }, +{ 0xC3CD, 0xC3CD, 0xC3CD }, +{ 0xC3CE, 0xC3CE, 0xC3CE }, +{ 0xC3CF, 0xC3CF, 0xC3CF }, +{ 0xC3D0, 0xC3D0, 0xC3D0 }, +{ 0xC3D1, 0xC3D1, 0xC3D1 }, +{ 0xC3D2, 0xC3D2, 0xC3D2 }, +{ 0xC3D3, 0xC3D3, 0xC3D3 }, +{ 0xC3D4, 0xC3D4, 0xC3D4 }, +{ 0xC3D5, 0xC3D5, 0xC3D5 }, +{ 0xC3D6, 0xC3D6, 0xC3D6 }, +{ 0xC3D7, 0xC3D7, 0xC3D7 }, +{ 0xC3D8, 0xC3D8, 0xC3D8 }, +{ 0xC3D9, 0xC3D9, 0xC3D9 }, +{ 0xC3DA, 0xC3DA, 0xC3DA }, +{ 0xC3DB, 0xC3DB, 0xC3DB }, +{ 0xC3DC, 0xC3DC, 0xC3DC }, +{ 0xC3DD, 0xC3DD, 0xC3DD }, +{ 0xC3DE, 0xC3DE, 0xC3DE }, +{ 0xC3DF, 0xC3DF, 0xC3DF }, +{ 0xC3E0, 0xC3E0, 0xC3E0 }, +{ 0xC3E1, 0xC3E1, 0xC3E1 }, +{ 0xC3E2, 0xC3E2, 0xC3E2 }, +{ 0xC3E3, 0xC3E3, 0xC3E3 }, +{ 0xC3E4, 0xC3E4, 0xC3E4 }, +{ 0xC3E5, 0xC3E5, 0xC3E5 }, +{ 0xC3E6, 0xC3E6, 0xC3E6 }, +{ 0xC3E7, 0xC3E7, 0xC3E7 }, +{ 0xC3E8, 0xC3E8, 0xC3E8 }, +{ 0xC3E9, 0xC3E9, 0xC3E9 }, +{ 0xC3EA, 0xC3EA, 0xC3EA }, +{ 0xC3EB, 0xC3EB, 0xC3EB }, +{ 0xC3EC, 0xC3EC, 0xC3EC }, +{ 0xC3ED, 0xC3ED, 0xC3ED }, +{ 0xC3EE, 0xC3EE, 0xC3EE }, +{ 0xC3EF, 0xC3EF, 0xC3EF }, +{ 0xC3F0, 0xC3F0, 0xC3F0 }, +{ 0xC3F1, 0xC3F1, 0xC3F1 }, +{ 0xC3F2, 0xC3F2, 0xC3F2 }, +{ 0xC3F3, 0xC3F3, 0xC3F3 }, +{ 0xC3F4, 0xC3F4, 0xC3F4 }, +{ 0xC3F5, 0xC3F5, 0xC3F5 }, +{ 0xC3F6, 0xC3F6, 0xC3F6 }, +{ 0xC3F7, 0xC3F7, 0xC3F7 }, +{ 0xC3F8, 0xC3F8, 0xC3F8 }, +{ 0xC3F9, 0xC3F9, 0xC3F9 }, +{ 0xC3FA, 0xC3FA, 0xC3FA }, +{ 0xC3FB, 0xC3FB, 0xC3FB }, +{ 0xC3FC, 0xC3FC, 0xC3FC }, +{ 0xC3FD, 0xC3FD, 0xC3FD }, +{ 0xC3FE, 0xC3FE, 0xC3FE }, +{ 0xC3FF, 0xC3FF, 0xC3FF }, +{ 0xC400, 0xC400, 0xC400 }, +{ 0xC401, 0xC401, 0xC401 }, +{ 0xC402, 0xC402, 0xC402 }, +{ 0xC403, 0xC403, 0xC403 }, +{ 0xC404, 0xC404, 0xC404 }, +{ 0xC405, 0xC405, 0xC405 }, +{ 0xC406, 0xC406, 0xC406 }, +{ 0xC407, 0xC407, 0xC407 }, +{ 0xC408, 0xC408, 0xC408 }, +{ 0xC409, 0xC409, 0xC409 }, +{ 0xC40A, 0xC40A, 0xC40A }, +{ 0xC40B, 0xC40B, 0xC40B }, +{ 0xC40C, 0xC40C, 0xC40C }, +{ 0xC40D, 0xC40D, 0xC40D }, +{ 0xC40E, 0xC40E, 0xC40E }, +{ 0xC40F, 0xC40F, 0xC40F }, +{ 0xC410, 0xC410, 0xC410 }, +{ 0xC411, 0xC411, 0xC411 }, +{ 0xC412, 0xC412, 0xC412 }, +{ 0xC413, 0xC413, 0xC413 }, +{ 0xC414, 0xC414, 0xC414 }, +{ 0xC415, 0xC415, 0xC415 }, +{ 0xC416, 0xC416, 0xC416 }, +{ 0xC417, 0xC417, 0xC417 }, +{ 0xC418, 0xC418, 0xC418 }, +{ 0xC419, 0xC419, 0xC419 }, +{ 0xC41A, 0xC41A, 0xC41A }, +{ 0xC41B, 0xC41B, 0xC41B }, +{ 0xC41C, 0xC41C, 0xC41C }, +{ 0xC41D, 0xC41D, 0xC41D }, +{ 0xC41E, 0xC41E, 0xC41E }, +{ 0xC41F, 0xC41F, 0xC41F }, +{ 0xC420, 0xC420, 0xC420 }, +{ 0xC421, 0xC421, 0xC421 }, +{ 0xC422, 0xC422, 0xC422 }, +{ 0xC423, 0xC423, 0xC423 }, +{ 0xC424, 0xC424, 0xC424 }, +{ 0xC425, 0xC425, 0xC425 }, +{ 0xC426, 0xC426, 0xC426 }, +{ 0xC427, 0xC427, 0xC427 }, +{ 0xC428, 0xC428, 0xC428 }, +{ 0xC429, 0xC429, 0xC429 }, +{ 0xC42A, 0xC42A, 0xC42A }, +{ 0xC42B, 0xC42B, 0xC42B }, +{ 0xC42C, 0xC42C, 0xC42C }, +{ 0xC42D, 0xC42D, 0xC42D }, +{ 0xC42E, 0xC42E, 0xC42E }, +{ 0xC42F, 0xC42F, 0xC42F }, +{ 0xC430, 0xC430, 0xC430 }, +{ 0xC431, 0xC431, 0xC431 }, +{ 0xC432, 0xC432, 0xC432 }, +{ 0xC433, 0xC433, 0xC433 }, +{ 0xC434, 0xC434, 0xC434 }, +{ 0xC435, 0xC435, 0xC435 }, +{ 0xC436, 0xC436, 0xC436 }, +{ 0xC437, 0xC437, 0xC437 }, +{ 0xC438, 0xC438, 0xC438 }, +{ 0xC439, 0xC439, 0xC439 }, +{ 0xC43A, 0xC43A, 0xC43A }, +{ 0xC43B, 0xC43B, 0xC43B }, +{ 0xC43C, 0xC43C, 0xC43C }, +{ 0xC43D, 0xC43D, 0xC43D }, +{ 0xC43E, 0xC43E, 0xC43E }, +{ 0xC43F, 0xC43F, 0xC43F }, +{ 0xC440, 0xC440, 0xC440 }, +{ 0xC441, 0xC441, 0xC441 }, +{ 0xC442, 0xC442, 0xC442 }, +{ 0xC443, 0xC443, 0xC443 }, +{ 0xC444, 0xC444, 0xC444 }, +{ 0xC445, 0xC445, 0xC445 }, +{ 0xC446, 0xC446, 0xC446 }, +{ 0xC447, 0xC447, 0xC447 }, +{ 0xC448, 0xC448, 0xC448 }, +{ 0xC449, 0xC449, 0xC449 }, +{ 0xC44A, 0xC44A, 0xC44A }, +{ 0xC44B, 0xC44B, 0xC44B }, +{ 0xC44C, 0xC44C, 0xC44C }, +{ 0xC44D, 0xC44D, 0xC44D }, +{ 0xC44E, 0xC44E, 0xC44E }, +{ 0xC44F, 0xC44F, 0xC44F }, +{ 0xC450, 0xC450, 0xC450 }, +{ 0xC451, 0xC451, 0xC451 }, +{ 0xC452, 0xC452, 0xC452 }, +{ 0xC453, 0xC453, 0xC453 }, +{ 0xC454, 0xC454, 0xC454 }, +{ 0xC455, 0xC455, 0xC455 }, +{ 0xC456, 0xC456, 0xC456 }, +{ 0xC457, 0xC457, 0xC457 }, +{ 0xC458, 0xC458, 0xC458 }, +{ 0xC459, 0xC459, 0xC459 }, +{ 0xC45A, 0xC45A, 0xC45A }, +{ 0xC45B, 0xC45B, 0xC45B }, +{ 0xC45C, 0xC45C, 0xC45C }, +{ 0xC45D, 0xC45D, 0xC45D }, +{ 0xC45E, 0xC45E, 0xC45E }, +{ 0xC45F, 0xC45F, 0xC45F }, +{ 0xC460, 0xC460, 0xC460 }, +{ 0xC461, 0xC461, 0xC461 }, +{ 0xC462, 0xC462, 0xC462 }, +{ 0xC463, 0xC463, 0xC463 }, +{ 0xC464, 0xC464, 0xC464 }, +{ 0xC465, 0xC465, 0xC465 }, +{ 0xC466, 0xC466, 0xC466 }, +{ 0xC467, 0xC467, 0xC467 }, +{ 0xC468, 0xC468, 0xC468 }, +{ 0xC469, 0xC469, 0xC469 }, +{ 0xC46A, 0xC46A, 0xC46A }, +{ 0xC46B, 0xC46B, 0xC46B }, +{ 0xC46C, 0xC46C, 0xC46C }, +{ 0xC46D, 0xC46D, 0xC46D }, +{ 0xC46E, 0xC46E, 0xC46E }, +{ 0xC46F, 0xC46F, 0xC46F }, +{ 0xC470, 0xC470, 0xC470 }, +{ 0xC471, 0xC471, 0xC471 }, +{ 0xC472, 0xC472, 0xC472 }, +{ 0xC473, 0xC473, 0xC473 }, +{ 0xC474, 0xC474, 0xC474 }, +{ 0xC475, 0xC475, 0xC475 }, +{ 0xC476, 0xC476, 0xC476 }, +{ 0xC477, 0xC477, 0xC477 }, +{ 0xC478, 0xC478, 0xC478 }, +{ 0xC479, 0xC479, 0xC479 }, +{ 0xC47A, 0xC47A, 0xC47A }, +{ 0xC47B, 0xC47B, 0xC47B }, +{ 0xC47C, 0xC47C, 0xC47C }, +{ 0xC47D, 0xC47D, 0xC47D }, +{ 0xC47E, 0xC47E, 0xC47E }, +{ 0xC47F, 0xC47F, 0xC47F }, +{ 0xC480, 0xC480, 0xC480 }, +{ 0xC481, 0xC481, 0xC481 }, +{ 0xC482, 0xC482, 0xC482 }, +{ 0xC483, 0xC483, 0xC483 }, +{ 0xC484, 0xC484, 0xC484 }, +{ 0xC485, 0xC485, 0xC485 }, +{ 0xC486, 0xC486, 0xC486 }, +{ 0xC487, 0xC487, 0xC487 }, +{ 0xC488, 0xC488, 0xC488 }, +{ 0xC489, 0xC489, 0xC489 }, +{ 0xC48A, 0xC48A, 0xC48A }, +{ 0xC48B, 0xC48B, 0xC48B }, +{ 0xC48C, 0xC48C, 0xC48C }, +{ 0xC48D, 0xC48D, 0xC48D }, +{ 0xC48E, 0xC48E, 0xC48E }, +{ 0xC48F, 0xC48F, 0xC48F }, +{ 0xC490, 0xC490, 0xC490 }, +{ 0xC491, 0xC491, 0xC491 }, +{ 0xC492, 0xC492, 0xC492 }, +{ 0xC493, 0xC493, 0xC493 }, +{ 0xC494, 0xC494, 0xC494 }, +{ 0xC495, 0xC495, 0xC495 }, +{ 0xC496, 0xC496, 0xC496 }, +{ 0xC497, 0xC497, 0xC497 }, +{ 0xC498, 0xC498, 0xC498 }, +{ 0xC499, 0xC499, 0xC499 }, +{ 0xC49A, 0xC49A, 0xC49A }, +{ 0xC49B, 0xC49B, 0xC49B }, +{ 0xC49C, 0xC49C, 0xC49C }, +{ 0xC49D, 0xC49D, 0xC49D }, +{ 0xC49E, 0xC49E, 0xC49E }, +{ 0xC49F, 0xC49F, 0xC49F }, +{ 0xC4A0, 0xC4A0, 0xC4A0 }, +{ 0xC4A1, 0xC4A1, 0xC4A1 }, +{ 0xC4A2, 0xC4A2, 0xC4A2 }, +{ 0xC4A3, 0xC4A3, 0xC4A3 }, +{ 0xC4A4, 0xC4A4, 0xC4A4 }, +{ 0xC4A5, 0xC4A5, 0xC4A5 }, +{ 0xC4A6, 0xC4A6, 0xC4A6 }, +{ 0xC4A7, 0xC4A7, 0xC4A7 }, +{ 0xC4A8, 0xC4A8, 0xC4A8 }, +{ 0xC4A9, 0xC4A9, 0xC4A9 }, +{ 0xC4AA, 0xC4AA, 0xC4AA }, +{ 0xC4AB, 0xC4AB, 0xC4AB }, +{ 0xC4AC, 0xC4AC, 0xC4AC }, +{ 0xC4AD, 0xC4AD, 0xC4AD }, +{ 0xC4AE, 0xC4AE, 0xC4AE }, +{ 0xC4AF, 0xC4AF, 0xC4AF }, +{ 0xC4B0, 0xC4B0, 0xC4B0 }, +{ 0xC4B1, 0xC4B1, 0xC4B1 }, +{ 0xC4B2, 0xC4B2, 0xC4B2 }, +{ 0xC4B3, 0xC4B3, 0xC4B3 }, +{ 0xC4B4, 0xC4B4, 0xC4B4 }, +{ 0xC4B5, 0xC4B5, 0xC4B5 }, +{ 0xC4B6, 0xC4B6, 0xC4B6 }, +{ 0xC4B7, 0xC4B7, 0xC4B7 }, +{ 0xC4B8, 0xC4B8, 0xC4B8 }, +{ 0xC4B9, 0xC4B9, 0xC4B9 }, +{ 0xC4BA, 0xC4BA, 0xC4BA }, +{ 0xC4BB, 0xC4BB, 0xC4BB }, +{ 0xC4BC, 0xC4BC, 0xC4BC }, +{ 0xC4BD, 0xC4BD, 0xC4BD }, +{ 0xC4BE, 0xC4BE, 0xC4BE }, +{ 0xC4BF, 0xC4BF, 0xC4BF }, +{ 0xC4C0, 0xC4C0, 0xC4C0 }, +{ 0xC4C1, 0xC4C1, 0xC4C1 }, +{ 0xC4C2, 0xC4C2, 0xC4C2 }, +{ 0xC4C3, 0xC4C3, 0xC4C3 }, +{ 0xC4C4, 0xC4C4, 0xC4C4 }, +{ 0xC4C5, 0xC4C5, 0xC4C5 }, +{ 0xC4C6, 0xC4C6, 0xC4C6 }, +{ 0xC4C7, 0xC4C7, 0xC4C7 }, +{ 0xC4C8, 0xC4C8, 0xC4C8 }, +{ 0xC4C9, 0xC4C9, 0xC4C9 }, +{ 0xC4CA, 0xC4CA, 0xC4CA }, +{ 0xC4CB, 0xC4CB, 0xC4CB }, +{ 0xC4CC, 0xC4CC, 0xC4CC }, +{ 0xC4CD, 0xC4CD, 0xC4CD }, +{ 0xC4CE, 0xC4CE, 0xC4CE }, +{ 0xC4CF, 0xC4CF, 0xC4CF }, +{ 0xC4D0, 0xC4D0, 0xC4D0 }, +{ 0xC4D1, 0xC4D1, 0xC4D1 }, +{ 0xC4D2, 0xC4D2, 0xC4D2 }, +{ 0xC4D3, 0xC4D3, 0xC4D3 }, +{ 0xC4D4, 0xC4D4, 0xC4D4 }, +{ 0xC4D5, 0xC4D5, 0xC4D5 }, +{ 0xC4D6, 0xC4D6, 0xC4D6 }, +{ 0xC4D7, 0xC4D7, 0xC4D7 }, +{ 0xC4D8, 0xC4D8, 0xC4D8 }, +{ 0xC4D9, 0xC4D9, 0xC4D9 }, +{ 0xC4DA, 0xC4DA, 0xC4DA }, +{ 0xC4DB, 0xC4DB, 0xC4DB }, +{ 0xC4DC, 0xC4DC, 0xC4DC }, +{ 0xC4DD, 0xC4DD, 0xC4DD }, +{ 0xC4DE, 0xC4DE, 0xC4DE }, +{ 0xC4DF, 0xC4DF, 0xC4DF }, +{ 0xC4E0, 0xC4E0, 0xC4E0 }, +{ 0xC4E1, 0xC4E1, 0xC4E1 }, +{ 0xC4E2, 0xC4E2, 0xC4E2 }, +{ 0xC4E3, 0xC4E3, 0xC4E3 }, +{ 0xC4E4, 0xC4E4, 0xC4E4 }, +{ 0xC4E5, 0xC4E5, 0xC4E5 }, +{ 0xC4E6, 0xC4E6, 0xC4E6 }, +{ 0xC4E7, 0xC4E7, 0xC4E7 }, +{ 0xC4E8, 0xC4E8, 0xC4E8 }, +{ 0xC4E9, 0xC4E9, 0xC4E9 }, +{ 0xC4EA, 0xC4EA, 0xC4EA }, +{ 0xC4EB, 0xC4EB, 0xC4EB }, +{ 0xC4EC, 0xC4EC, 0xC4EC }, +{ 0xC4ED, 0xC4ED, 0xC4ED }, +{ 0xC4EE, 0xC4EE, 0xC4EE }, +{ 0xC4EF, 0xC4EF, 0xC4EF }, +{ 0xC4F0, 0xC4F0, 0xC4F0 }, +{ 0xC4F1, 0xC4F1, 0xC4F1 }, +{ 0xC4F2, 0xC4F2, 0xC4F2 }, +{ 0xC4F3, 0xC4F3, 0xC4F3 }, +{ 0xC4F4, 0xC4F4, 0xC4F4 }, +{ 0xC4F5, 0xC4F5, 0xC4F5 }, +{ 0xC4F6, 0xC4F6, 0xC4F6 }, +{ 0xC4F7, 0xC4F7, 0xC4F7 }, +{ 0xC4F8, 0xC4F8, 0xC4F8 }, +{ 0xC4F9, 0xC4F9, 0xC4F9 }, +{ 0xC4FA, 0xC4FA, 0xC4FA }, +{ 0xC4FB, 0xC4FB, 0xC4FB }, +{ 0xC4FC, 0xC4FC, 0xC4FC }, +{ 0xC4FD, 0xC4FD, 0xC4FD }, +{ 0xC4FE, 0xC4FE, 0xC4FE }, +{ 0xC4FF, 0xC4FF, 0xC4FF }, +{ 0xC500, 0xC500, 0xC500 }, +{ 0xC501, 0xC501, 0xC501 }, +{ 0xC502, 0xC502, 0xC502 }, +{ 0xC503, 0xC503, 0xC503 }, +{ 0xC504, 0xC504, 0xC504 }, +{ 0xC505, 0xC505, 0xC505 }, +{ 0xC506, 0xC506, 0xC506 }, +{ 0xC507, 0xC507, 0xC507 }, +{ 0xC508, 0xC508, 0xC508 }, +{ 0xC509, 0xC509, 0xC509 }, +{ 0xC50A, 0xC50A, 0xC50A }, +{ 0xC50B, 0xC50B, 0xC50B }, +{ 0xC50C, 0xC50C, 0xC50C }, +{ 0xC50D, 0xC50D, 0xC50D }, +{ 0xC50E, 0xC50E, 0xC50E }, +{ 0xC50F, 0xC50F, 0xC50F }, +{ 0xC510, 0xC510, 0xC510 }, +{ 0xC511, 0xC511, 0xC511 }, +{ 0xC512, 0xC512, 0xC512 }, +{ 0xC513, 0xC513, 0xC513 }, +{ 0xC514, 0xC514, 0xC514 }, +{ 0xC515, 0xC515, 0xC515 }, +{ 0xC516, 0xC516, 0xC516 }, +{ 0xC517, 0xC517, 0xC517 }, +{ 0xC518, 0xC518, 0xC518 }, +{ 0xC519, 0xC519, 0xC519 }, +{ 0xC51A, 0xC51A, 0xC51A }, +{ 0xC51B, 0xC51B, 0xC51B }, +{ 0xC51C, 0xC51C, 0xC51C }, +{ 0xC51D, 0xC51D, 0xC51D }, +{ 0xC51E, 0xC51E, 0xC51E }, +{ 0xC51F, 0xC51F, 0xC51F }, +{ 0xC520, 0xC520, 0xC520 }, +{ 0xC521, 0xC521, 0xC521 }, +{ 0xC522, 0xC522, 0xC522 }, +{ 0xC523, 0xC523, 0xC523 }, +{ 0xC524, 0xC524, 0xC524 }, +{ 0xC525, 0xC525, 0xC525 }, +{ 0xC526, 0xC526, 0xC526 }, +{ 0xC527, 0xC527, 0xC527 }, +{ 0xC528, 0xC528, 0xC528 }, +{ 0xC529, 0xC529, 0xC529 }, +{ 0xC52A, 0xC52A, 0xC52A }, +{ 0xC52B, 0xC52B, 0xC52B }, +{ 0xC52C, 0xC52C, 0xC52C }, +{ 0xC52D, 0xC52D, 0xC52D }, +{ 0xC52E, 0xC52E, 0xC52E }, +{ 0xC52F, 0xC52F, 0xC52F }, +{ 0xC530, 0xC530, 0xC530 }, +{ 0xC531, 0xC531, 0xC531 }, +{ 0xC532, 0xC532, 0xC532 }, +{ 0xC533, 0xC533, 0xC533 }, +{ 0xC534, 0xC534, 0xC534 }, +{ 0xC535, 0xC535, 0xC535 }, +{ 0xC536, 0xC536, 0xC536 }, +{ 0xC537, 0xC537, 0xC537 }, +{ 0xC538, 0xC538, 0xC538 }, +{ 0xC539, 0xC539, 0xC539 }, +{ 0xC53A, 0xC53A, 0xC53A }, +{ 0xC53B, 0xC53B, 0xC53B }, +{ 0xC53C, 0xC53C, 0xC53C }, +{ 0xC53D, 0xC53D, 0xC53D }, +{ 0xC53E, 0xC53E, 0xC53E }, +{ 0xC53F, 0xC53F, 0xC53F }, +{ 0xC540, 0xC540, 0xC540 }, +{ 0xC541, 0xC541, 0xC541 }, +{ 0xC542, 0xC542, 0xC542 }, +{ 0xC543, 0xC543, 0xC543 }, +{ 0xC544, 0xC544, 0xC544 }, +{ 0xC545, 0xC545, 0xC545 }, +{ 0xC546, 0xC546, 0xC546 }, +{ 0xC547, 0xC547, 0xC547 }, +{ 0xC548, 0xC548, 0xC548 }, +{ 0xC549, 0xC549, 0xC549 }, +{ 0xC54A, 0xC54A, 0xC54A }, +{ 0xC54B, 0xC54B, 0xC54B }, +{ 0xC54C, 0xC54C, 0xC54C }, +{ 0xC54D, 0xC54D, 0xC54D }, +{ 0xC54E, 0xC54E, 0xC54E }, +{ 0xC54F, 0xC54F, 0xC54F }, +{ 0xC550, 0xC550, 0xC550 }, +{ 0xC551, 0xC551, 0xC551 }, +{ 0xC552, 0xC552, 0xC552 }, +{ 0xC553, 0xC553, 0xC553 }, +{ 0xC554, 0xC554, 0xC554 }, +{ 0xC555, 0xC555, 0xC555 }, +{ 0xC556, 0xC556, 0xC556 }, +{ 0xC557, 0xC557, 0xC557 }, +{ 0xC558, 0xC558, 0xC558 }, +{ 0xC559, 0xC559, 0xC559 }, +{ 0xC55A, 0xC55A, 0xC55A }, +{ 0xC55B, 0xC55B, 0xC55B }, +{ 0xC55C, 0xC55C, 0xC55C }, +{ 0xC55D, 0xC55D, 0xC55D }, +{ 0xC55E, 0xC55E, 0xC55E }, +{ 0xC55F, 0xC55F, 0xC55F }, +{ 0xC560, 0xC560, 0xC560 }, +{ 0xC561, 0xC561, 0xC561 }, +{ 0xC562, 0xC562, 0xC562 }, +{ 0xC563, 0xC563, 0xC563 }, +{ 0xC564, 0xC564, 0xC564 }, +{ 0xC565, 0xC565, 0xC565 }, +{ 0xC566, 0xC566, 0xC566 }, +{ 0xC567, 0xC567, 0xC567 }, +{ 0xC568, 0xC568, 0xC568 }, +{ 0xC569, 0xC569, 0xC569 }, +{ 0xC56A, 0xC56A, 0xC56A }, +{ 0xC56B, 0xC56B, 0xC56B }, +{ 0xC56C, 0xC56C, 0xC56C }, +{ 0xC56D, 0xC56D, 0xC56D }, +{ 0xC56E, 0xC56E, 0xC56E }, +{ 0xC56F, 0xC56F, 0xC56F }, +{ 0xC570, 0xC570, 0xC570 }, +{ 0xC571, 0xC571, 0xC571 }, +{ 0xC572, 0xC572, 0xC572 }, +{ 0xC573, 0xC573, 0xC573 }, +{ 0xC574, 0xC574, 0xC574 }, +{ 0xC575, 0xC575, 0xC575 }, +{ 0xC576, 0xC576, 0xC576 }, +{ 0xC577, 0xC577, 0xC577 }, +{ 0xC578, 0xC578, 0xC578 }, +{ 0xC579, 0xC579, 0xC579 }, +{ 0xC57A, 0xC57A, 0xC57A }, +{ 0xC57B, 0xC57B, 0xC57B }, +{ 0xC57C, 0xC57C, 0xC57C }, +{ 0xC57D, 0xC57D, 0xC57D }, +{ 0xC57E, 0xC57E, 0xC57E }, +{ 0xC57F, 0xC57F, 0xC57F }, +{ 0xC580, 0xC580, 0xC580 }, +{ 0xC581, 0xC581, 0xC581 }, +{ 0xC582, 0xC582, 0xC582 }, +{ 0xC583, 0xC583, 0xC583 }, +{ 0xC584, 0xC584, 0xC584 }, +{ 0xC585, 0xC585, 0xC585 }, +{ 0xC586, 0xC586, 0xC586 }, +{ 0xC587, 0xC587, 0xC587 }, +{ 0xC588, 0xC588, 0xC588 }, +{ 0xC589, 0xC589, 0xC589 }, +{ 0xC58A, 0xC58A, 0xC58A }, +{ 0xC58B, 0xC58B, 0xC58B }, +{ 0xC58C, 0xC58C, 0xC58C }, +{ 0xC58D, 0xC58D, 0xC58D }, +{ 0xC58E, 0xC58E, 0xC58E }, +{ 0xC58F, 0xC58F, 0xC58F }, +{ 0xC590, 0xC590, 0xC590 }, +{ 0xC591, 0xC591, 0xC591 }, +{ 0xC592, 0xC592, 0xC592 }, +{ 0xC593, 0xC593, 0xC593 }, +{ 0xC594, 0xC594, 0xC594 }, +{ 0xC595, 0xC595, 0xC595 }, +{ 0xC596, 0xC596, 0xC596 }, +{ 0xC597, 0xC597, 0xC597 }, +{ 0xC598, 0xC598, 0xC598 }, +{ 0xC599, 0xC599, 0xC599 }, +{ 0xC59A, 0xC59A, 0xC59A }, +{ 0xC59B, 0xC59B, 0xC59B }, +{ 0xC59C, 0xC59C, 0xC59C }, +{ 0xC59D, 0xC59D, 0xC59D }, +{ 0xC59E, 0xC59E, 0xC59E }, +{ 0xC59F, 0xC59F, 0xC59F }, +{ 0xC5A0, 0xC5A0, 0xC5A0 }, +{ 0xC5A1, 0xC5A1, 0xC5A1 }, +{ 0xC5A2, 0xC5A2, 0xC5A2 }, +{ 0xC5A3, 0xC5A3, 0xC5A3 }, +{ 0xC5A4, 0xC5A4, 0xC5A4 }, +{ 0xC5A5, 0xC5A5, 0xC5A5 }, +{ 0xC5A6, 0xC5A6, 0xC5A6 }, +{ 0xC5A7, 0xC5A7, 0xC5A7 }, +{ 0xC5A8, 0xC5A8, 0xC5A8 }, +{ 0xC5A9, 0xC5A9, 0xC5A9 }, +{ 0xC5AA, 0xC5AA, 0xC5AA }, +{ 0xC5AB, 0xC5AB, 0xC5AB }, +{ 0xC5AC, 0xC5AC, 0xC5AC }, +{ 0xC5AD, 0xC5AD, 0xC5AD }, +{ 0xC5AE, 0xC5AE, 0xC5AE }, +{ 0xC5AF, 0xC5AF, 0xC5AF }, +{ 0xC5B0, 0xC5B0, 0xC5B0 }, +{ 0xC5B1, 0xC5B1, 0xC5B1 }, +{ 0xC5B2, 0xC5B2, 0xC5B2 }, +{ 0xC5B3, 0xC5B3, 0xC5B3 }, +{ 0xC5B4, 0xC5B4, 0xC5B4 }, +{ 0xC5B5, 0xC5B5, 0xC5B5 }, +{ 0xC5B6, 0xC5B6, 0xC5B6 }, +{ 0xC5B7, 0xC5B7, 0xC5B7 }, +{ 0xC5B8, 0xC5B8, 0xC5B8 }, +{ 0xC5B9, 0xC5B9, 0xC5B9 }, +{ 0xC5BA, 0xC5BA, 0xC5BA }, +{ 0xC5BB, 0xC5BB, 0xC5BB }, +{ 0xC5BC, 0xC5BC, 0xC5BC }, +{ 0xC5BD, 0xC5BD, 0xC5BD }, +{ 0xC5BE, 0xC5BE, 0xC5BE }, +{ 0xC5BF, 0xC5BF, 0xC5BF }, +{ 0xC5C0, 0xC5C0, 0xC5C0 }, +{ 0xC5C1, 0xC5C1, 0xC5C1 }, +{ 0xC5C2, 0xC5C2, 0xC5C2 }, +{ 0xC5C3, 0xC5C3, 0xC5C3 }, +{ 0xC5C4, 0xC5C4, 0xC5C4 }, +{ 0xC5C5, 0xC5C5, 0xC5C5 }, +{ 0xC5C6, 0xC5C6, 0xC5C6 }, +{ 0xC5C7, 0xC5C7, 0xC5C7 }, +{ 0xC5C8, 0xC5C8, 0xC5C8 }, +{ 0xC5C9, 0xC5C9, 0xC5C9 }, +{ 0xC5CA, 0xC5CA, 0xC5CA }, +{ 0xC5CB, 0xC5CB, 0xC5CB }, +{ 0xC5CC, 0xC5CC, 0xC5CC }, +{ 0xC5CD, 0xC5CD, 0xC5CD }, +{ 0xC5CE, 0xC5CE, 0xC5CE }, +{ 0xC5CF, 0xC5CF, 0xC5CF }, +{ 0xC5D0, 0xC5D0, 0xC5D0 }, +{ 0xC5D1, 0xC5D1, 0xC5D1 }, +{ 0xC5D2, 0xC5D2, 0xC5D2 }, +{ 0xC5D3, 0xC5D3, 0xC5D3 }, +{ 0xC5D4, 0xC5D4, 0xC5D4 }, +{ 0xC5D5, 0xC5D5, 0xC5D5 }, +{ 0xC5D6, 0xC5D6, 0xC5D6 }, +{ 0xC5D7, 0xC5D7, 0xC5D7 }, +{ 0xC5D8, 0xC5D8, 0xC5D8 }, +{ 0xC5D9, 0xC5D9, 0xC5D9 }, +{ 0xC5DA, 0xC5DA, 0xC5DA }, +{ 0xC5DB, 0xC5DB, 0xC5DB }, +{ 0xC5DC, 0xC5DC, 0xC5DC }, +{ 0xC5DD, 0xC5DD, 0xC5DD }, +{ 0xC5DE, 0xC5DE, 0xC5DE }, +{ 0xC5DF, 0xC5DF, 0xC5DF }, +{ 0xC5E0, 0xC5E0, 0xC5E0 }, +{ 0xC5E1, 0xC5E1, 0xC5E1 }, +{ 0xC5E2, 0xC5E2, 0xC5E2 }, +{ 0xC5E3, 0xC5E3, 0xC5E3 }, +{ 0xC5E4, 0xC5E4, 0xC5E4 }, +{ 0xC5E5, 0xC5E5, 0xC5E5 }, +{ 0xC5E6, 0xC5E6, 0xC5E6 }, +{ 0xC5E7, 0xC5E7, 0xC5E7 }, +{ 0xC5E8, 0xC5E8, 0xC5E8 }, +{ 0xC5E9, 0xC5E9, 0xC5E9 }, +{ 0xC5EA, 0xC5EA, 0xC5EA }, +{ 0xC5EB, 0xC5EB, 0xC5EB }, +{ 0xC5EC, 0xC5EC, 0xC5EC }, +{ 0xC5ED, 0xC5ED, 0xC5ED }, +{ 0xC5EE, 0xC5EE, 0xC5EE }, +{ 0xC5EF, 0xC5EF, 0xC5EF }, +{ 0xC5F0, 0xC5F0, 0xC5F0 }, +{ 0xC5F1, 0xC5F1, 0xC5F1 }, +{ 0xC5F2, 0xC5F2, 0xC5F2 }, +{ 0xC5F3, 0xC5F3, 0xC5F3 }, +{ 0xC5F4, 0xC5F4, 0xC5F4 }, +{ 0xC5F5, 0xC5F5, 0xC5F5 }, +{ 0xC5F6, 0xC5F6, 0xC5F6 }, +{ 0xC5F7, 0xC5F7, 0xC5F7 }, +{ 0xC5F8, 0xC5F8, 0xC5F8 }, +{ 0xC5F9, 0xC5F9, 0xC5F9 }, +{ 0xC5FA, 0xC5FA, 0xC5FA }, +{ 0xC5FB, 0xC5FB, 0xC5FB }, +{ 0xC5FC, 0xC5FC, 0xC5FC }, +{ 0xC5FD, 0xC5FD, 0xC5FD }, +{ 0xC5FE, 0xC5FE, 0xC5FE }, +{ 0xC5FF, 0xC5FF, 0xC5FF }, +{ 0xC600, 0xC600, 0xC600 }, +{ 0xC601, 0xC601, 0xC601 }, +{ 0xC602, 0xC602, 0xC602 }, +{ 0xC603, 0xC603, 0xC603 }, +{ 0xC604, 0xC604, 0xC604 }, +{ 0xC605, 0xC605, 0xC605 }, +{ 0xC606, 0xC606, 0xC606 }, +{ 0xC607, 0xC607, 0xC607 }, +{ 0xC608, 0xC608, 0xC608 }, +{ 0xC609, 0xC609, 0xC609 }, +{ 0xC60A, 0xC60A, 0xC60A }, +{ 0xC60B, 0xC60B, 0xC60B }, +{ 0xC60C, 0xC60C, 0xC60C }, +{ 0xC60D, 0xC60D, 0xC60D }, +{ 0xC60E, 0xC60E, 0xC60E }, +{ 0xC60F, 0xC60F, 0xC60F }, +{ 0xC610, 0xC610, 0xC610 }, +{ 0xC611, 0xC611, 0xC611 }, +{ 0xC612, 0xC612, 0xC612 }, +{ 0xC613, 0xC613, 0xC613 }, +{ 0xC614, 0xC614, 0xC614 }, +{ 0xC615, 0xC615, 0xC615 }, +{ 0xC616, 0xC616, 0xC616 }, +{ 0xC617, 0xC617, 0xC617 }, +{ 0xC618, 0xC618, 0xC618 }, +{ 0xC619, 0xC619, 0xC619 }, +{ 0xC61A, 0xC61A, 0xC61A }, +{ 0xC61B, 0xC61B, 0xC61B }, +{ 0xC61C, 0xC61C, 0xC61C }, +{ 0xC61D, 0xC61D, 0xC61D }, +{ 0xC61E, 0xC61E, 0xC61E }, +{ 0xC61F, 0xC61F, 0xC61F }, +{ 0xC620, 0xC620, 0xC620 }, +{ 0xC621, 0xC621, 0xC621 }, +{ 0xC622, 0xC622, 0xC622 }, +{ 0xC623, 0xC623, 0xC623 }, +{ 0xC624, 0xC624, 0xC624 }, +{ 0xC625, 0xC625, 0xC625 }, +{ 0xC626, 0xC626, 0xC626 }, +{ 0xC627, 0xC627, 0xC627 }, +{ 0xC628, 0xC628, 0xC628 }, +{ 0xC629, 0xC629, 0xC629 }, +{ 0xC62A, 0xC62A, 0xC62A }, +{ 0xC62B, 0xC62B, 0xC62B }, +{ 0xC62C, 0xC62C, 0xC62C }, +{ 0xC62D, 0xC62D, 0xC62D }, +{ 0xC62E, 0xC62E, 0xC62E }, +{ 0xC62F, 0xC62F, 0xC62F }, +{ 0xC630, 0xC630, 0xC630 }, +{ 0xC631, 0xC631, 0xC631 }, +{ 0xC632, 0xC632, 0xC632 }, +{ 0xC633, 0xC633, 0xC633 }, +{ 0xC634, 0xC634, 0xC634 }, +{ 0xC635, 0xC635, 0xC635 }, +{ 0xC636, 0xC636, 0xC636 }, +{ 0xC637, 0xC637, 0xC637 }, +{ 0xC638, 0xC638, 0xC638 }, +{ 0xC639, 0xC639, 0xC639 }, +{ 0xC63A, 0xC63A, 0xC63A }, +{ 0xC63B, 0xC63B, 0xC63B }, +{ 0xC63C, 0xC63C, 0xC63C }, +{ 0xC63D, 0xC63D, 0xC63D }, +{ 0xC63E, 0xC63E, 0xC63E }, +{ 0xC63F, 0xC63F, 0xC63F }, +{ 0xC640, 0xC640, 0xC640 }, +{ 0xC641, 0xC641, 0xC641 }, +{ 0xC642, 0xC642, 0xC642 }, +{ 0xC643, 0xC643, 0xC643 }, +{ 0xC644, 0xC644, 0xC644 }, +{ 0xC645, 0xC645, 0xC645 }, +{ 0xC646, 0xC646, 0xC646 }, +{ 0xC647, 0xC647, 0xC647 }, +{ 0xC648, 0xC648, 0xC648 }, +{ 0xC649, 0xC649, 0xC649 }, +{ 0xC64A, 0xC64A, 0xC64A }, +{ 0xC64B, 0xC64B, 0xC64B }, +{ 0xC64C, 0xC64C, 0xC64C }, +{ 0xC64D, 0xC64D, 0xC64D }, +{ 0xC64E, 0xC64E, 0xC64E }, +{ 0xC64F, 0xC64F, 0xC64F }, +{ 0xC650, 0xC650, 0xC650 }, +{ 0xC651, 0xC651, 0xC651 }, +{ 0xC652, 0xC652, 0xC652 }, +{ 0xC653, 0xC653, 0xC653 }, +{ 0xC654, 0xC654, 0xC654 }, +{ 0xC655, 0xC655, 0xC655 }, +{ 0xC656, 0xC656, 0xC656 }, +{ 0xC657, 0xC657, 0xC657 }, +{ 0xC658, 0xC658, 0xC658 }, +{ 0xC659, 0xC659, 0xC659 }, +{ 0xC65A, 0xC65A, 0xC65A }, +{ 0xC65B, 0xC65B, 0xC65B }, +{ 0xC65C, 0xC65C, 0xC65C }, +{ 0xC65D, 0xC65D, 0xC65D }, +{ 0xC65E, 0xC65E, 0xC65E }, +{ 0xC65F, 0xC65F, 0xC65F }, +{ 0xC660, 0xC660, 0xC660 }, +{ 0xC661, 0xC661, 0xC661 }, +{ 0xC662, 0xC662, 0xC662 }, +{ 0xC663, 0xC663, 0xC663 }, +{ 0xC664, 0xC664, 0xC664 }, +{ 0xC665, 0xC665, 0xC665 }, +{ 0xC666, 0xC666, 0xC666 }, +{ 0xC667, 0xC667, 0xC667 }, +{ 0xC668, 0xC668, 0xC668 }, +{ 0xC669, 0xC669, 0xC669 }, +{ 0xC66A, 0xC66A, 0xC66A }, +{ 0xC66B, 0xC66B, 0xC66B }, +{ 0xC66C, 0xC66C, 0xC66C }, +{ 0xC66D, 0xC66D, 0xC66D }, +{ 0xC66E, 0xC66E, 0xC66E }, +{ 0xC66F, 0xC66F, 0xC66F }, +{ 0xC670, 0xC670, 0xC670 }, +{ 0xC671, 0xC671, 0xC671 }, +{ 0xC672, 0xC672, 0xC672 }, +{ 0xC673, 0xC673, 0xC673 }, +{ 0xC674, 0xC674, 0xC674 }, +{ 0xC675, 0xC675, 0xC675 }, +{ 0xC676, 0xC676, 0xC676 }, +{ 0xC677, 0xC677, 0xC677 }, +{ 0xC678, 0xC678, 0xC678 }, +{ 0xC679, 0xC679, 0xC679 }, +{ 0xC67A, 0xC67A, 0xC67A }, +{ 0xC67B, 0xC67B, 0xC67B }, +{ 0xC67C, 0xC67C, 0xC67C }, +{ 0xC67D, 0xC67D, 0xC67D }, +{ 0xC67E, 0xC67E, 0xC67E }, +{ 0xC67F, 0xC67F, 0xC67F }, +{ 0xC680, 0xC680, 0xC680 }, +{ 0xC681, 0xC681, 0xC681 }, +{ 0xC682, 0xC682, 0xC682 }, +{ 0xC683, 0xC683, 0xC683 }, +{ 0xC684, 0xC684, 0xC684 }, +{ 0xC685, 0xC685, 0xC685 }, +{ 0xC686, 0xC686, 0xC686 }, +{ 0xC687, 0xC687, 0xC687 }, +{ 0xC688, 0xC688, 0xC688 }, +{ 0xC689, 0xC689, 0xC689 }, +{ 0xC68A, 0xC68A, 0xC68A }, +{ 0xC68B, 0xC68B, 0xC68B }, +{ 0xC68C, 0xC68C, 0xC68C }, +{ 0xC68D, 0xC68D, 0xC68D }, +{ 0xC68E, 0xC68E, 0xC68E }, +{ 0xC68F, 0xC68F, 0xC68F }, +{ 0xC690, 0xC690, 0xC690 }, +{ 0xC691, 0xC691, 0xC691 }, +{ 0xC692, 0xC692, 0xC692 }, +{ 0xC693, 0xC693, 0xC693 }, +{ 0xC694, 0xC694, 0xC694 }, +{ 0xC695, 0xC695, 0xC695 }, +{ 0xC696, 0xC696, 0xC696 }, +{ 0xC697, 0xC697, 0xC697 }, +{ 0xC698, 0xC698, 0xC698 }, +{ 0xC699, 0xC699, 0xC699 }, +{ 0xC69A, 0xC69A, 0xC69A }, +{ 0xC69B, 0xC69B, 0xC69B }, +{ 0xC69C, 0xC69C, 0xC69C }, +{ 0xC69D, 0xC69D, 0xC69D }, +{ 0xC69E, 0xC69E, 0xC69E }, +{ 0xC69F, 0xC69F, 0xC69F }, +{ 0xC6A0, 0xC6A0, 0xC6A0 }, +{ 0xC6A1, 0xC6A1, 0xC6A1 }, +{ 0xC6A2, 0xC6A2, 0xC6A2 }, +{ 0xC6A3, 0xC6A3, 0xC6A3 }, +{ 0xC6A4, 0xC6A4, 0xC6A4 }, +{ 0xC6A5, 0xC6A5, 0xC6A5 }, +{ 0xC6A6, 0xC6A6, 0xC6A6 }, +{ 0xC6A7, 0xC6A7, 0xC6A7 }, +{ 0xC6A8, 0xC6A8, 0xC6A8 }, +{ 0xC6A9, 0xC6A9, 0xC6A9 }, +{ 0xC6AA, 0xC6AA, 0xC6AA }, +{ 0xC6AB, 0xC6AB, 0xC6AB }, +{ 0xC6AC, 0xC6AC, 0xC6AC }, +{ 0xC6AD, 0xC6AD, 0xC6AD }, +{ 0xC6AE, 0xC6AE, 0xC6AE }, +{ 0xC6AF, 0xC6AF, 0xC6AF }, +{ 0xC6B0, 0xC6B0, 0xC6B0 }, +{ 0xC6B1, 0xC6B1, 0xC6B1 }, +{ 0xC6B2, 0xC6B2, 0xC6B2 }, +{ 0xC6B3, 0xC6B3, 0xC6B3 }, +{ 0xC6B4, 0xC6B4, 0xC6B4 }, +{ 0xC6B5, 0xC6B5, 0xC6B5 }, +{ 0xC6B6, 0xC6B6, 0xC6B6 }, +{ 0xC6B7, 0xC6B7, 0xC6B7 }, +{ 0xC6B8, 0xC6B8, 0xC6B8 }, +{ 0xC6B9, 0xC6B9, 0xC6B9 }, +{ 0xC6BA, 0xC6BA, 0xC6BA }, +{ 0xC6BB, 0xC6BB, 0xC6BB }, +{ 0xC6BC, 0xC6BC, 0xC6BC }, +{ 0xC6BD, 0xC6BD, 0xC6BD }, +{ 0xC6BE, 0xC6BE, 0xC6BE }, +{ 0xC6BF, 0xC6BF, 0xC6BF }, +{ 0xC6C0, 0xC6C0, 0xC6C0 }, +{ 0xC6C1, 0xC6C1, 0xC6C1 }, +{ 0xC6C2, 0xC6C2, 0xC6C2 }, +{ 0xC6C3, 0xC6C3, 0xC6C3 }, +{ 0xC6C4, 0xC6C4, 0xC6C4 }, +{ 0xC6C5, 0xC6C5, 0xC6C5 }, +{ 0xC6C6, 0xC6C6, 0xC6C6 }, +{ 0xC6C7, 0xC6C7, 0xC6C7 }, +{ 0xC6C8, 0xC6C8, 0xC6C8 }, +{ 0xC6C9, 0xC6C9, 0xC6C9 }, +{ 0xC6CA, 0xC6CA, 0xC6CA }, +{ 0xC6CB, 0xC6CB, 0xC6CB }, +{ 0xC6CC, 0xC6CC, 0xC6CC }, +{ 0xC6CD, 0xC6CD, 0xC6CD }, +{ 0xC6CE, 0xC6CE, 0xC6CE }, +{ 0xC6CF, 0xC6CF, 0xC6CF }, +{ 0xC6D0, 0xC6D0, 0xC6D0 }, +{ 0xC6D1, 0xC6D1, 0xC6D1 }, +{ 0xC6D2, 0xC6D2, 0xC6D2 }, +{ 0xC6D3, 0xC6D3, 0xC6D3 }, +{ 0xC6D4, 0xC6D4, 0xC6D4 }, +{ 0xC6D5, 0xC6D5, 0xC6D5 }, +{ 0xC6D6, 0xC6D6, 0xC6D6 }, +{ 0xC6D7, 0xC6D7, 0xC6D7 }, +{ 0xC6D8, 0xC6D8, 0xC6D8 }, +{ 0xC6D9, 0xC6D9, 0xC6D9 }, +{ 0xC6DA, 0xC6DA, 0xC6DA }, +{ 0xC6DB, 0xC6DB, 0xC6DB }, +{ 0xC6DC, 0xC6DC, 0xC6DC }, +{ 0xC6DD, 0xC6DD, 0xC6DD }, +{ 0xC6DE, 0xC6DE, 0xC6DE }, +{ 0xC6DF, 0xC6DF, 0xC6DF }, +{ 0xC6E0, 0xC6E0, 0xC6E0 }, +{ 0xC6E1, 0xC6E1, 0xC6E1 }, +{ 0xC6E2, 0xC6E2, 0xC6E2 }, +{ 0xC6E3, 0xC6E3, 0xC6E3 }, +{ 0xC6E4, 0xC6E4, 0xC6E4 }, +{ 0xC6E5, 0xC6E5, 0xC6E5 }, +{ 0xC6E6, 0xC6E6, 0xC6E6 }, +{ 0xC6E7, 0xC6E7, 0xC6E7 }, +{ 0xC6E8, 0xC6E8, 0xC6E8 }, +{ 0xC6E9, 0xC6E9, 0xC6E9 }, +{ 0xC6EA, 0xC6EA, 0xC6EA }, +{ 0xC6EB, 0xC6EB, 0xC6EB }, +{ 0xC6EC, 0xC6EC, 0xC6EC }, +{ 0xC6ED, 0xC6ED, 0xC6ED }, +{ 0xC6EE, 0xC6EE, 0xC6EE }, +{ 0xC6EF, 0xC6EF, 0xC6EF }, +{ 0xC6F0, 0xC6F0, 0xC6F0 }, +{ 0xC6F1, 0xC6F1, 0xC6F1 }, +{ 0xC6F2, 0xC6F2, 0xC6F2 }, +{ 0xC6F3, 0xC6F3, 0xC6F3 }, +{ 0xC6F4, 0xC6F4, 0xC6F4 }, +{ 0xC6F5, 0xC6F5, 0xC6F5 }, +{ 0xC6F6, 0xC6F6, 0xC6F6 }, +{ 0xC6F7, 0xC6F7, 0xC6F7 }, +{ 0xC6F8, 0xC6F8, 0xC6F8 }, +{ 0xC6F9, 0xC6F9, 0xC6F9 }, +{ 0xC6FA, 0xC6FA, 0xC6FA }, +{ 0xC6FB, 0xC6FB, 0xC6FB }, +{ 0xC6FC, 0xC6FC, 0xC6FC }, +{ 0xC6FD, 0xC6FD, 0xC6FD }, +{ 0xC6FE, 0xC6FE, 0xC6FE }, +{ 0xC6FF, 0xC6FF, 0xC6FF }, +{ 0xC700, 0xC700, 0xC700 }, +{ 0xC701, 0xC701, 0xC701 }, +{ 0xC702, 0xC702, 0xC702 }, +{ 0xC703, 0xC703, 0xC703 }, +{ 0xC704, 0xC704, 0xC704 }, +{ 0xC705, 0xC705, 0xC705 }, +{ 0xC706, 0xC706, 0xC706 }, +{ 0xC707, 0xC707, 0xC707 }, +{ 0xC708, 0xC708, 0xC708 }, +{ 0xC709, 0xC709, 0xC709 }, +{ 0xC70A, 0xC70A, 0xC70A }, +{ 0xC70B, 0xC70B, 0xC70B }, +{ 0xC70C, 0xC70C, 0xC70C }, +{ 0xC70D, 0xC70D, 0xC70D }, +{ 0xC70E, 0xC70E, 0xC70E }, +{ 0xC70F, 0xC70F, 0xC70F }, +{ 0xC710, 0xC710, 0xC710 }, +{ 0xC711, 0xC711, 0xC711 }, +{ 0xC712, 0xC712, 0xC712 }, +{ 0xC713, 0xC713, 0xC713 }, +{ 0xC714, 0xC714, 0xC714 }, +{ 0xC715, 0xC715, 0xC715 }, +{ 0xC716, 0xC716, 0xC716 }, +{ 0xC717, 0xC717, 0xC717 }, +{ 0xC718, 0xC718, 0xC718 }, +{ 0xC719, 0xC719, 0xC719 }, +{ 0xC71A, 0xC71A, 0xC71A }, +{ 0xC71B, 0xC71B, 0xC71B }, +{ 0xC71C, 0xC71C, 0xC71C }, +{ 0xC71D, 0xC71D, 0xC71D }, +{ 0xC71E, 0xC71E, 0xC71E }, +{ 0xC71F, 0xC71F, 0xC71F }, +{ 0xC720, 0xC720, 0xC720 }, +{ 0xC721, 0xC721, 0xC721 }, +{ 0xC722, 0xC722, 0xC722 }, +{ 0xC723, 0xC723, 0xC723 }, +{ 0xC724, 0xC724, 0xC724 }, +{ 0xC725, 0xC725, 0xC725 }, +{ 0xC726, 0xC726, 0xC726 }, +{ 0xC727, 0xC727, 0xC727 }, +{ 0xC728, 0xC728, 0xC728 }, +{ 0xC729, 0xC729, 0xC729 }, +{ 0xC72A, 0xC72A, 0xC72A }, +{ 0xC72B, 0xC72B, 0xC72B }, +{ 0xC72C, 0xC72C, 0xC72C }, +{ 0xC72D, 0xC72D, 0xC72D }, +{ 0xC72E, 0xC72E, 0xC72E }, +{ 0xC72F, 0xC72F, 0xC72F }, +{ 0xC730, 0xC730, 0xC730 }, +{ 0xC731, 0xC731, 0xC731 }, +{ 0xC732, 0xC732, 0xC732 }, +{ 0xC733, 0xC733, 0xC733 }, +{ 0xC734, 0xC734, 0xC734 }, +{ 0xC735, 0xC735, 0xC735 }, +{ 0xC736, 0xC736, 0xC736 }, +{ 0xC737, 0xC737, 0xC737 }, +{ 0xC738, 0xC738, 0xC738 }, +{ 0xC739, 0xC739, 0xC739 }, +{ 0xC73A, 0xC73A, 0xC73A }, +{ 0xC73B, 0xC73B, 0xC73B }, +{ 0xC73C, 0xC73C, 0xC73C }, +{ 0xC73D, 0xC73D, 0xC73D }, +{ 0xC73E, 0xC73E, 0xC73E }, +{ 0xC73F, 0xC73F, 0xC73F }, +{ 0xC740, 0xC740, 0xC740 }, +{ 0xC741, 0xC741, 0xC741 }, +{ 0xC742, 0xC742, 0xC742 }, +{ 0xC743, 0xC743, 0xC743 }, +{ 0xC744, 0xC744, 0xC744 }, +{ 0xC745, 0xC745, 0xC745 }, +{ 0xC746, 0xC746, 0xC746 }, +{ 0xC747, 0xC747, 0xC747 }, +{ 0xC748, 0xC748, 0xC748 }, +{ 0xC749, 0xC749, 0xC749 }, +{ 0xC74A, 0xC74A, 0xC74A }, +{ 0xC74B, 0xC74B, 0xC74B }, +{ 0xC74C, 0xC74C, 0xC74C }, +{ 0xC74D, 0xC74D, 0xC74D }, +{ 0xC74E, 0xC74E, 0xC74E }, +{ 0xC74F, 0xC74F, 0xC74F }, +{ 0xC750, 0xC750, 0xC750 }, +{ 0xC751, 0xC751, 0xC751 }, +{ 0xC752, 0xC752, 0xC752 }, +{ 0xC753, 0xC753, 0xC753 }, +{ 0xC754, 0xC754, 0xC754 }, +{ 0xC755, 0xC755, 0xC755 }, +{ 0xC756, 0xC756, 0xC756 }, +{ 0xC757, 0xC757, 0xC757 }, +{ 0xC758, 0xC758, 0xC758 }, +{ 0xC759, 0xC759, 0xC759 }, +{ 0xC75A, 0xC75A, 0xC75A }, +{ 0xC75B, 0xC75B, 0xC75B }, +{ 0xC75C, 0xC75C, 0xC75C }, +{ 0xC75D, 0xC75D, 0xC75D }, +{ 0xC75E, 0xC75E, 0xC75E }, +{ 0xC75F, 0xC75F, 0xC75F }, +{ 0xC760, 0xC760, 0xC760 }, +{ 0xC761, 0xC761, 0xC761 }, +{ 0xC762, 0xC762, 0xC762 }, +{ 0xC763, 0xC763, 0xC763 }, +{ 0xC764, 0xC764, 0xC764 }, +{ 0xC765, 0xC765, 0xC765 }, +{ 0xC766, 0xC766, 0xC766 }, +{ 0xC767, 0xC767, 0xC767 }, +{ 0xC768, 0xC768, 0xC768 }, +{ 0xC769, 0xC769, 0xC769 }, +{ 0xC76A, 0xC76A, 0xC76A }, +{ 0xC76B, 0xC76B, 0xC76B }, +{ 0xC76C, 0xC76C, 0xC76C }, +{ 0xC76D, 0xC76D, 0xC76D }, +{ 0xC76E, 0xC76E, 0xC76E }, +{ 0xC76F, 0xC76F, 0xC76F }, +{ 0xC770, 0xC770, 0xC770 }, +{ 0xC771, 0xC771, 0xC771 }, +{ 0xC772, 0xC772, 0xC772 }, +{ 0xC773, 0xC773, 0xC773 }, +{ 0xC774, 0xC774, 0xC774 }, +{ 0xC775, 0xC775, 0xC775 }, +{ 0xC776, 0xC776, 0xC776 }, +{ 0xC777, 0xC777, 0xC777 }, +{ 0xC778, 0xC778, 0xC778 }, +{ 0xC779, 0xC779, 0xC779 }, +{ 0xC77A, 0xC77A, 0xC77A }, +{ 0xC77B, 0xC77B, 0xC77B }, +{ 0xC77C, 0xC77C, 0xC77C }, +{ 0xC77D, 0xC77D, 0xC77D }, +{ 0xC77E, 0xC77E, 0xC77E }, +{ 0xC77F, 0xC77F, 0xC77F }, +{ 0xC780, 0xC780, 0xC780 }, +{ 0xC781, 0xC781, 0xC781 }, +{ 0xC782, 0xC782, 0xC782 }, +{ 0xC783, 0xC783, 0xC783 }, +{ 0xC784, 0xC784, 0xC784 }, +{ 0xC785, 0xC785, 0xC785 }, +{ 0xC786, 0xC786, 0xC786 }, +{ 0xC787, 0xC787, 0xC787 }, +{ 0xC788, 0xC788, 0xC788 }, +{ 0xC789, 0xC789, 0xC789 }, +{ 0xC78A, 0xC78A, 0xC78A }, +{ 0xC78B, 0xC78B, 0xC78B }, +{ 0xC78C, 0xC78C, 0xC78C }, +{ 0xC78D, 0xC78D, 0xC78D }, +{ 0xC78E, 0xC78E, 0xC78E }, +{ 0xC78F, 0xC78F, 0xC78F }, +{ 0xC790, 0xC790, 0xC790 }, +{ 0xC791, 0xC791, 0xC791 }, +{ 0xC792, 0xC792, 0xC792 }, +{ 0xC793, 0xC793, 0xC793 }, +{ 0xC794, 0xC794, 0xC794 }, +{ 0xC795, 0xC795, 0xC795 }, +{ 0xC796, 0xC796, 0xC796 }, +{ 0xC797, 0xC797, 0xC797 }, +{ 0xC798, 0xC798, 0xC798 }, +{ 0xC799, 0xC799, 0xC799 }, +{ 0xC79A, 0xC79A, 0xC79A }, +{ 0xC79B, 0xC79B, 0xC79B }, +{ 0xC79C, 0xC79C, 0xC79C }, +{ 0xC79D, 0xC79D, 0xC79D }, +{ 0xC79E, 0xC79E, 0xC79E }, +{ 0xC79F, 0xC79F, 0xC79F }, +{ 0xC7A0, 0xC7A0, 0xC7A0 }, +{ 0xC7A1, 0xC7A1, 0xC7A1 }, +{ 0xC7A2, 0xC7A2, 0xC7A2 }, +{ 0xC7A3, 0xC7A3, 0xC7A3 }, +{ 0xC7A4, 0xC7A4, 0xC7A4 }, +{ 0xC7A5, 0xC7A5, 0xC7A5 }, +{ 0xC7A6, 0xC7A6, 0xC7A6 }, +{ 0xC7A7, 0xC7A7, 0xC7A7 }, +{ 0xC7A8, 0xC7A8, 0xC7A8 }, +{ 0xC7A9, 0xC7A9, 0xC7A9 }, +{ 0xC7AA, 0xC7AA, 0xC7AA }, +{ 0xC7AB, 0xC7AB, 0xC7AB }, +{ 0xC7AC, 0xC7AC, 0xC7AC }, +{ 0xC7AD, 0xC7AD, 0xC7AD }, +{ 0xC7AE, 0xC7AE, 0xC7AE }, +{ 0xC7AF, 0xC7AF, 0xC7AF }, +{ 0xC7B0, 0xC7B0, 0xC7B0 }, +{ 0xC7B1, 0xC7B1, 0xC7B1 }, +{ 0xC7B2, 0xC7B2, 0xC7B2 }, +{ 0xC7B3, 0xC7B3, 0xC7B3 }, +{ 0xC7B4, 0xC7B4, 0xC7B4 }, +{ 0xC7B5, 0xC7B5, 0xC7B5 }, +{ 0xC7B6, 0xC7B6, 0xC7B6 }, +{ 0xC7B7, 0xC7B7, 0xC7B7 }, +{ 0xC7B8, 0xC7B8, 0xC7B8 }, +{ 0xC7B9, 0xC7B9, 0xC7B9 }, +{ 0xC7BA, 0xC7BA, 0xC7BA }, +{ 0xC7BB, 0xC7BB, 0xC7BB }, +{ 0xC7BC, 0xC7BC, 0xC7BC }, +{ 0xC7BD, 0xC7BD, 0xC7BD }, +{ 0xC7BE, 0xC7BE, 0xC7BE }, +{ 0xC7BF, 0xC7BF, 0xC7BF }, +{ 0xC7C0, 0xC7C0, 0xC7C0 }, +{ 0xC7C1, 0xC7C1, 0xC7C1 }, +{ 0xC7C2, 0xC7C2, 0xC7C2 }, +{ 0xC7C3, 0xC7C3, 0xC7C3 }, +{ 0xC7C4, 0xC7C4, 0xC7C4 }, +{ 0xC7C5, 0xC7C5, 0xC7C5 }, +{ 0xC7C6, 0xC7C6, 0xC7C6 }, +{ 0xC7C7, 0xC7C7, 0xC7C7 }, +{ 0xC7C8, 0xC7C8, 0xC7C8 }, +{ 0xC7C9, 0xC7C9, 0xC7C9 }, +{ 0xC7CA, 0xC7CA, 0xC7CA }, +{ 0xC7CB, 0xC7CB, 0xC7CB }, +{ 0xC7CC, 0xC7CC, 0xC7CC }, +{ 0xC7CD, 0xC7CD, 0xC7CD }, +{ 0xC7CE, 0xC7CE, 0xC7CE }, +{ 0xC7CF, 0xC7CF, 0xC7CF }, +{ 0xC7D0, 0xC7D0, 0xC7D0 }, +{ 0xC7D1, 0xC7D1, 0xC7D1 }, +{ 0xC7D2, 0xC7D2, 0xC7D2 }, +{ 0xC7D3, 0xC7D3, 0xC7D3 }, +{ 0xC7D4, 0xC7D4, 0xC7D4 }, +{ 0xC7D5, 0xC7D5, 0xC7D5 }, +{ 0xC7D6, 0xC7D6, 0xC7D6 }, +{ 0xC7D7, 0xC7D7, 0xC7D7 }, +{ 0xC7D8, 0xC7D8, 0xC7D8 }, +{ 0xC7D9, 0xC7D9, 0xC7D9 }, +{ 0xC7DA, 0xC7DA, 0xC7DA }, +{ 0xC7DB, 0xC7DB, 0xC7DB }, +{ 0xC7DC, 0xC7DC, 0xC7DC }, +{ 0xC7DD, 0xC7DD, 0xC7DD }, +{ 0xC7DE, 0xC7DE, 0xC7DE }, +{ 0xC7DF, 0xC7DF, 0xC7DF }, +{ 0xC7E0, 0xC7E0, 0xC7E0 }, +{ 0xC7E1, 0xC7E1, 0xC7E1 }, +{ 0xC7E2, 0xC7E2, 0xC7E2 }, +{ 0xC7E3, 0xC7E3, 0xC7E3 }, +{ 0xC7E4, 0xC7E4, 0xC7E4 }, +{ 0xC7E5, 0xC7E5, 0xC7E5 }, +{ 0xC7E6, 0xC7E6, 0xC7E6 }, +{ 0xC7E7, 0xC7E7, 0xC7E7 }, +{ 0xC7E8, 0xC7E8, 0xC7E8 }, +{ 0xC7E9, 0xC7E9, 0xC7E9 }, +{ 0xC7EA, 0xC7EA, 0xC7EA }, +{ 0xC7EB, 0xC7EB, 0xC7EB }, +{ 0xC7EC, 0xC7EC, 0xC7EC }, +{ 0xC7ED, 0xC7ED, 0xC7ED }, +{ 0xC7EE, 0xC7EE, 0xC7EE }, +{ 0xC7EF, 0xC7EF, 0xC7EF }, +{ 0xC7F0, 0xC7F0, 0xC7F0 }, +{ 0xC7F1, 0xC7F1, 0xC7F1 }, +{ 0xC7F2, 0xC7F2, 0xC7F2 }, +{ 0xC7F3, 0xC7F3, 0xC7F3 }, +{ 0xC7F4, 0xC7F4, 0xC7F4 }, +{ 0xC7F5, 0xC7F5, 0xC7F5 }, +{ 0xC7F6, 0xC7F6, 0xC7F6 }, +{ 0xC7F7, 0xC7F7, 0xC7F7 }, +{ 0xC7F8, 0xC7F8, 0xC7F8 }, +{ 0xC7F9, 0xC7F9, 0xC7F9 }, +{ 0xC7FA, 0xC7FA, 0xC7FA }, +{ 0xC7FB, 0xC7FB, 0xC7FB }, +{ 0xC7FC, 0xC7FC, 0xC7FC }, +{ 0xC7FD, 0xC7FD, 0xC7FD }, +{ 0xC7FE, 0xC7FE, 0xC7FE }, +{ 0xC7FF, 0xC7FF, 0xC7FF }, +{ 0xC800, 0xC800, 0xC800 }, +{ 0xC801, 0xC801, 0xC801 }, +{ 0xC802, 0xC802, 0xC802 }, +{ 0xC803, 0xC803, 0xC803 }, +{ 0xC804, 0xC804, 0xC804 }, +{ 0xC805, 0xC805, 0xC805 }, +{ 0xC806, 0xC806, 0xC806 }, +{ 0xC807, 0xC807, 0xC807 }, +{ 0xC808, 0xC808, 0xC808 }, +{ 0xC809, 0xC809, 0xC809 }, +{ 0xC80A, 0xC80A, 0xC80A }, +{ 0xC80B, 0xC80B, 0xC80B }, +{ 0xC80C, 0xC80C, 0xC80C }, +{ 0xC80D, 0xC80D, 0xC80D }, +{ 0xC80E, 0xC80E, 0xC80E }, +{ 0xC80F, 0xC80F, 0xC80F }, +{ 0xC810, 0xC810, 0xC810 }, +{ 0xC811, 0xC811, 0xC811 }, +{ 0xC812, 0xC812, 0xC812 }, +{ 0xC813, 0xC813, 0xC813 }, +{ 0xC814, 0xC814, 0xC814 }, +{ 0xC815, 0xC815, 0xC815 }, +{ 0xC816, 0xC816, 0xC816 }, +{ 0xC817, 0xC817, 0xC817 }, +{ 0xC818, 0xC818, 0xC818 }, +{ 0xC819, 0xC819, 0xC819 }, +{ 0xC81A, 0xC81A, 0xC81A }, +{ 0xC81B, 0xC81B, 0xC81B }, +{ 0xC81C, 0xC81C, 0xC81C }, +{ 0xC81D, 0xC81D, 0xC81D }, +{ 0xC81E, 0xC81E, 0xC81E }, +{ 0xC81F, 0xC81F, 0xC81F }, +{ 0xC820, 0xC820, 0xC820 }, +{ 0xC821, 0xC821, 0xC821 }, +{ 0xC822, 0xC822, 0xC822 }, +{ 0xC823, 0xC823, 0xC823 }, +{ 0xC824, 0xC824, 0xC824 }, +{ 0xC825, 0xC825, 0xC825 }, +{ 0xC826, 0xC826, 0xC826 }, +{ 0xC827, 0xC827, 0xC827 }, +{ 0xC828, 0xC828, 0xC828 }, +{ 0xC829, 0xC829, 0xC829 }, +{ 0xC82A, 0xC82A, 0xC82A }, +{ 0xC82B, 0xC82B, 0xC82B }, +{ 0xC82C, 0xC82C, 0xC82C }, +{ 0xC82D, 0xC82D, 0xC82D }, +{ 0xC82E, 0xC82E, 0xC82E }, +{ 0xC82F, 0xC82F, 0xC82F }, +{ 0xC830, 0xC830, 0xC830 }, +{ 0xC831, 0xC831, 0xC831 }, +{ 0xC832, 0xC832, 0xC832 }, +{ 0xC833, 0xC833, 0xC833 }, +{ 0xC834, 0xC834, 0xC834 }, +{ 0xC835, 0xC835, 0xC835 }, +{ 0xC836, 0xC836, 0xC836 }, +{ 0xC837, 0xC837, 0xC837 }, +{ 0xC838, 0xC838, 0xC838 }, +{ 0xC839, 0xC839, 0xC839 }, +{ 0xC83A, 0xC83A, 0xC83A }, +{ 0xC83B, 0xC83B, 0xC83B }, +{ 0xC83C, 0xC83C, 0xC83C }, +{ 0xC83D, 0xC83D, 0xC83D }, +{ 0xC83E, 0xC83E, 0xC83E }, +{ 0xC83F, 0xC83F, 0xC83F }, +{ 0xC840, 0xC840, 0xC840 }, +{ 0xC841, 0xC841, 0xC841 }, +{ 0xC842, 0xC842, 0xC842 }, +{ 0xC843, 0xC843, 0xC843 }, +{ 0xC844, 0xC844, 0xC844 }, +{ 0xC845, 0xC845, 0xC845 }, +{ 0xC846, 0xC846, 0xC846 }, +{ 0xC847, 0xC847, 0xC847 }, +{ 0xC848, 0xC848, 0xC848 }, +{ 0xC849, 0xC849, 0xC849 }, +{ 0xC84A, 0xC84A, 0xC84A }, +{ 0xC84B, 0xC84B, 0xC84B }, +{ 0xC84C, 0xC84C, 0xC84C }, +{ 0xC84D, 0xC84D, 0xC84D }, +{ 0xC84E, 0xC84E, 0xC84E }, +{ 0xC84F, 0xC84F, 0xC84F }, +{ 0xC850, 0xC850, 0xC850 }, +{ 0xC851, 0xC851, 0xC851 }, +{ 0xC852, 0xC852, 0xC852 }, +{ 0xC853, 0xC853, 0xC853 }, +{ 0xC854, 0xC854, 0xC854 }, +{ 0xC855, 0xC855, 0xC855 }, +{ 0xC856, 0xC856, 0xC856 }, +{ 0xC857, 0xC857, 0xC857 }, +{ 0xC858, 0xC858, 0xC858 }, +{ 0xC859, 0xC859, 0xC859 }, +{ 0xC85A, 0xC85A, 0xC85A }, +{ 0xC85B, 0xC85B, 0xC85B }, +{ 0xC85C, 0xC85C, 0xC85C }, +{ 0xC85D, 0xC85D, 0xC85D }, +{ 0xC85E, 0xC85E, 0xC85E }, +{ 0xC85F, 0xC85F, 0xC85F }, +{ 0xC860, 0xC860, 0xC860 }, +{ 0xC861, 0xC861, 0xC861 }, +{ 0xC862, 0xC862, 0xC862 }, +{ 0xC863, 0xC863, 0xC863 }, +{ 0xC864, 0xC864, 0xC864 }, +{ 0xC865, 0xC865, 0xC865 }, +{ 0xC866, 0xC866, 0xC866 }, +{ 0xC867, 0xC867, 0xC867 }, +{ 0xC868, 0xC868, 0xC868 }, +{ 0xC869, 0xC869, 0xC869 }, +{ 0xC86A, 0xC86A, 0xC86A }, +{ 0xC86B, 0xC86B, 0xC86B }, +{ 0xC86C, 0xC86C, 0xC86C }, +{ 0xC86D, 0xC86D, 0xC86D }, +{ 0xC86E, 0xC86E, 0xC86E }, +{ 0xC86F, 0xC86F, 0xC86F }, +{ 0xC870, 0xC870, 0xC870 }, +{ 0xC871, 0xC871, 0xC871 }, +{ 0xC872, 0xC872, 0xC872 }, +{ 0xC873, 0xC873, 0xC873 }, +{ 0xC874, 0xC874, 0xC874 }, +{ 0xC875, 0xC875, 0xC875 }, +{ 0xC876, 0xC876, 0xC876 }, +{ 0xC877, 0xC877, 0xC877 }, +{ 0xC878, 0xC878, 0xC878 }, +{ 0xC879, 0xC879, 0xC879 }, +{ 0xC87A, 0xC87A, 0xC87A }, +{ 0xC87B, 0xC87B, 0xC87B }, +{ 0xC87C, 0xC87C, 0xC87C }, +{ 0xC87D, 0xC87D, 0xC87D }, +{ 0xC87E, 0xC87E, 0xC87E }, +{ 0xC87F, 0xC87F, 0xC87F }, +{ 0xC880, 0xC880, 0xC880 }, +{ 0xC881, 0xC881, 0xC881 }, +{ 0xC882, 0xC882, 0xC882 }, +{ 0xC883, 0xC883, 0xC883 }, +{ 0xC884, 0xC884, 0xC884 }, +{ 0xC885, 0xC885, 0xC885 }, +{ 0xC886, 0xC886, 0xC886 }, +{ 0xC887, 0xC887, 0xC887 }, +{ 0xC888, 0xC888, 0xC888 }, +{ 0xC889, 0xC889, 0xC889 }, +{ 0xC88A, 0xC88A, 0xC88A }, +{ 0xC88B, 0xC88B, 0xC88B }, +{ 0xC88C, 0xC88C, 0xC88C }, +{ 0xC88D, 0xC88D, 0xC88D }, +{ 0xC88E, 0xC88E, 0xC88E }, +{ 0xC88F, 0xC88F, 0xC88F }, +{ 0xC890, 0xC890, 0xC890 }, +{ 0xC891, 0xC891, 0xC891 }, +{ 0xC892, 0xC892, 0xC892 }, +{ 0xC893, 0xC893, 0xC893 }, +{ 0xC894, 0xC894, 0xC894 }, +{ 0xC895, 0xC895, 0xC895 }, +{ 0xC896, 0xC896, 0xC896 }, +{ 0xC897, 0xC897, 0xC897 }, +{ 0xC898, 0xC898, 0xC898 }, +{ 0xC899, 0xC899, 0xC899 }, +{ 0xC89A, 0xC89A, 0xC89A }, +{ 0xC89B, 0xC89B, 0xC89B }, +{ 0xC89C, 0xC89C, 0xC89C }, +{ 0xC89D, 0xC89D, 0xC89D }, +{ 0xC89E, 0xC89E, 0xC89E }, +{ 0xC89F, 0xC89F, 0xC89F }, +{ 0xC8A0, 0xC8A0, 0xC8A0 }, +{ 0xC8A1, 0xC8A1, 0xC8A1 }, +{ 0xC8A2, 0xC8A2, 0xC8A2 }, +{ 0xC8A3, 0xC8A3, 0xC8A3 }, +{ 0xC8A4, 0xC8A4, 0xC8A4 }, +{ 0xC8A5, 0xC8A5, 0xC8A5 }, +{ 0xC8A6, 0xC8A6, 0xC8A6 }, +{ 0xC8A7, 0xC8A7, 0xC8A7 }, +{ 0xC8A8, 0xC8A8, 0xC8A8 }, +{ 0xC8A9, 0xC8A9, 0xC8A9 }, +{ 0xC8AA, 0xC8AA, 0xC8AA }, +{ 0xC8AB, 0xC8AB, 0xC8AB }, +{ 0xC8AC, 0xC8AC, 0xC8AC }, +{ 0xC8AD, 0xC8AD, 0xC8AD }, +{ 0xC8AE, 0xC8AE, 0xC8AE }, +{ 0xC8AF, 0xC8AF, 0xC8AF }, +{ 0xC8B0, 0xC8B0, 0xC8B0 }, +{ 0xC8B1, 0xC8B1, 0xC8B1 }, +{ 0xC8B2, 0xC8B2, 0xC8B2 }, +{ 0xC8B3, 0xC8B3, 0xC8B3 }, +{ 0xC8B4, 0xC8B4, 0xC8B4 }, +{ 0xC8B5, 0xC8B5, 0xC8B5 }, +{ 0xC8B6, 0xC8B6, 0xC8B6 }, +{ 0xC8B7, 0xC8B7, 0xC8B7 }, +{ 0xC8B8, 0xC8B8, 0xC8B8 }, +{ 0xC8B9, 0xC8B9, 0xC8B9 }, +{ 0xC8BA, 0xC8BA, 0xC8BA }, +{ 0xC8BB, 0xC8BB, 0xC8BB }, +{ 0xC8BC, 0xC8BC, 0xC8BC }, +{ 0xC8BD, 0xC8BD, 0xC8BD }, +{ 0xC8BE, 0xC8BE, 0xC8BE }, +{ 0xC8BF, 0xC8BF, 0xC8BF }, +{ 0xC8C0, 0xC8C0, 0xC8C0 }, +{ 0xC8C1, 0xC8C1, 0xC8C1 }, +{ 0xC8C2, 0xC8C2, 0xC8C2 }, +{ 0xC8C3, 0xC8C3, 0xC8C3 }, +{ 0xC8C4, 0xC8C4, 0xC8C4 }, +{ 0xC8C5, 0xC8C5, 0xC8C5 }, +{ 0xC8C6, 0xC8C6, 0xC8C6 }, +{ 0xC8C7, 0xC8C7, 0xC8C7 }, +{ 0xC8C8, 0xC8C8, 0xC8C8 }, +{ 0xC8C9, 0xC8C9, 0xC8C9 }, +{ 0xC8CA, 0xC8CA, 0xC8CA }, +{ 0xC8CB, 0xC8CB, 0xC8CB }, +{ 0xC8CC, 0xC8CC, 0xC8CC }, +{ 0xC8CD, 0xC8CD, 0xC8CD }, +{ 0xC8CE, 0xC8CE, 0xC8CE }, +{ 0xC8CF, 0xC8CF, 0xC8CF }, +{ 0xC8D0, 0xC8D0, 0xC8D0 }, +{ 0xC8D1, 0xC8D1, 0xC8D1 }, +{ 0xC8D2, 0xC8D2, 0xC8D2 }, +{ 0xC8D3, 0xC8D3, 0xC8D3 }, +{ 0xC8D4, 0xC8D4, 0xC8D4 }, +{ 0xC8D5, 0xC8D5, 0xC8D5 }, +{ 0xC8D6, 0xC8D6, 0xC8D6 }, +{ 0xC8D7, 0xC8D7, 0xC8D7 }, +{ 0xC8D8, 0xC8D8, 0xC8D8 }, +{ 0xC8D9, 0xC8D9, 0xC8D9 }, +{ 0xC8DA, 0xC8DA, 0xC8DA }, +{ 0xC8DB, 0xC8DB, 0xC8DB }, +{ 0xC8DC, 0xC8DC, 0xC8DC }, +{ 0xC8DD, 0xC8DD, 0xC8DD }, +{ 0xC8DE, 0xC8DE, 0xC8DE }, +{ 0xC8DF, 0xC8DF, 0xC8DF }, +{ 0xC8E0, 0xC8E0, 0xC8E0 }, +{ 0xC8E1, 0xC8E1, 0xC8E1 }, +{ 0xC8E2, 0xC8E2, 0xC8E2 }, +{ 0xC8E3, 0xC8E3, 0xC8E3 }, +{ 0xC8E4, 0xC8E4, 0xC8E4 }, +{ 0xC8E5, 0xC8E5, 0xC8E5 }, +{ 0xC8E6, 0xC8E6, 0xC8E6 }, +{ 0xC8E7, 0xC8E7, 0xC8E7 }, +{ 0xC8E8, 0xC8E8, 0xC8E8 }, +{ 0xC8E9, 0xC8E9, 0xC8E9 }, +{ 0xC8EA, 0xC8EA, 0xC8EA }, +{ 0xC8EB, 0xC8EB, 0xC8EB }, +{ 0xC8EC, 0xC8EC, 0xC8EC }, +{ 0xC8ED, 0xC8ED, 0xC8ED }, +{ 0xC8EE, 0xC8EE, 0xC8EE }, +{ 0xC8EF, 0xC8EF, 0xC8EF }, +{ 0xC8F0, 0xC8F0, 0xC8F0 }, +{ 0xC8F1, 0xC8F1, 0xC8F1 }, +{ 0xC8F2, 0xC8F2, 0xC8F2 }, +{ 0xC8F3, 0xC8F3, 0xC8F3 }, +{ 0xC8F4, 0xC8F4, 0xC8F4 }, +{ 0xC8F5, 0xC8F5, 0xC8F5 }, +{ 0xC8F6, 0xC8F6, 0xC8F6 }, +{ 0xC8F7, 0xC8F7, 0xC8F7 }, +{ 0xC8F8, 0xC8F8, 0xC8F8 }, +{ 0xC8F9, 0xC8F9, 0xC8F9 }, +{ 0xC8FA, 0xC8FA, 0xC8FA }, +{ 0xC8FB, 0xC8FB, 0xC8FB }, +{ 0xC8FC, 0xC8FC, 0xC8FC }, +{ 0xC8FD, 0xC8FD, 0xC8FD }, +{ 0xC8FE, 0xC8FE, 0xC8FE }, +{ 0xC8FF, 0xC8FF, 0xC8FF }, +{ 0xC900, 0xC900, 0xC900 }, +{ 0xC901, 0xC901, 0xC901 }, +{ 0xC902, 0xC902, 0xC902 }, +{ 0xC903, 0xC903, 0xC903 }, +{ 0xC904, 0xC904, 0xC904 }, +{ 0xC905, 0xC905, 0xC905 }, +{ 0xC906, 0xC906, 0xC906 }, +{ 0xC907, 0xC907, 0xC907 }, +{ 0xC908, 0xC908, 0xC908 }, +{ 0xC909, 0xC909, 0xC909 }, +{ 0xC90A, 0xC90A, 0xC90A }, +{ 0xC90B, 0xC90B, 0xC90B }, +{ 0xC90C, 0xC90C, 0xC90C }, +{ 0xC90D, 0xC90D, 0xC90D }, +{ 0xC90E, 0xC90E, 0xC90E }, +{ 0xC90F, 0xC90F, 0xC90F }, +{ 0xC910, 0xC910, 0xC910 }, +{ 0xC911, 0xC911, 0xC911 }, +{ 0xC912, 0xC912, 0xC912 }, +{ 0xC913, 0xC913, 0xC913 }, +{ 0xC914, 0xC914, 0xC914 }, +{ 0xC915, 0xC915, 0xC915 }, +{ 0xC916, 0xC916, 0xC916 }, +{ 0xC917, 0xC917, 0xC917 }, +{ 0xC918, 0xC918, 0xC918 }, +{ 0xC919, 0xC919, 0xC919 }, +{ 0xC91A, 0xC91A, 0xC91A }, +{ 0xC91B, 0xC91B, 0xC91B }, +{ 0xC91C, 0xC91C, 0xC91C }, +{ 0xC91D, 0xC91D, 0xC91D }, +{ 0xC91E, 0xC91E, 0xC91E }, +{ 0xC91F, 0xC91F, 0xC91F }, +{ 0xC920, 0xC920, 0xC920 }, +{ 0xC921, 0xC921, 0xC921 }, +{ 0xC922, 0xC922, 0xC922 }, +{ 0xC923, 0xC923, 0xC923 }, +{ 0xC924, 0xC924, 0xC924 }, +{ 0xC925, 0xC925, 0xC925 }, +{ 0xC926, 0xC926, 0xC926 }, +{ 0xC927, 0xC927, 0xC927 }, +{ 0xC928, 0xC928, 0xC928 }, +{ 0xC929, 0xC929, 0xC929 }, +{ 0xC92A, 0xC92A, 0xC92A }, +{ 0xC92B, 0xC92B, 0xC92B }, +{ 0xC92C, 0xC92C, 0xC92C }, +{ 0xC92D, 0xC92D, 0xC92D }, +{ 0xC92E, 0xC92E, 0xC92E }, +{ 0xC92F, 0xC92F, 0xC92F }, +{ 0xC930, 0xC930, 0xC930 }, +{ 0xC931, 0xC931, 0xC931 }, +{ 0xC932, 0xC932, 0xC932 }, +{ 0xC933, 0xC933, 0xC933 }, +{ 0xC934, 0xC934, 0xC934 }, +{ 0xC935, 0xC935, 0xC935 }, +{ 0xC936, 0xC936, 0xC936 }, +{ 0xC937, 0xC937, 0xC937 }, +{ 0xC938, 0xC938, 0xC938 }, +{ 0xC939, 0xC939, 0xC939 }, +{ 0xC93A, 0xC93A, 0xC93A }, +{ 0xC93B, 0xC93B, 0xC93B }, +{ 0xC93C, 0xC93C, 0xC93C }, +{ 0xC93D, 0xC93D, 0xC93D }, +{ 0xC93E, 0xC93E, 0xC93E }, +{ 0xC93F, 0xC93F, 0xC93F }, +{ 0xC940, 0xC940, 0xC940 }, +{ 0xC941, 0xC941, 0xC941 }, +{ 0xC942, 0xC942, 0xC942 }, +{ 0xC943, 0xC943, 0xC943 }, +{ 0xC944, 0xC944, 0xC944 }, +{ 0xC945, 0xC945, 0xC945 }, +{ 0xC946, 0xC946, 0xC946 }, +{ 0xC947, 0xC947, 0xC947 }, +{ 0xC948, 0xC948, 0xC948 }, +{ 0xC949, 0xC949, 0xC949 }, +{ 0xC94A, 0xC94A, 0xC94A }, +{ 0xC94B, 0xC94B, 0xC94B }, +{ 0xC94C, 0xC94C, 0xC94C }, +{ 0xC94D, 0xC94D, 0xC94D }, +{ 0xC94E, 0xC94E, 0xC94E }, +{ 0xC94F, 0xC94F, 0xC94F }, +{ 0xC950, 0xC950, 0xC950 }, +{ 0xC951, 0xC951, 0xC951 }, +{ 0xC952, 0xC952, 0xC952 }, +{ 0xC953, 0xC953, 0xC953 }, +{ 0xC954, 0xC954, 0xC954 }, +{ 0xC955, 0xC955, 0xC955 }, +{ 0xC956, 0xC956, 0xC956 }, +{ 0xC957, 0xC957, 0xC957 }, +{ 0xC958, 0xC958, 0xC958 }, +{ 0xC959, 0xC959, 0xC959 }, +{ 0xC95A, 0xC95A, 0xC95A }, +{ 0xC95B, 0xC95B, 0xC95B }, +{ 0xC95C, 0xC95C, 0xC95C }, +{ 0xC95D, 0xC95D, 0xC95D }, +{ 0xC95E, 0xC95E, 0xC95E }, +{ 0xC95F, 0xC95F, 0xC95F }, +{ 0xC960, 0xC960, 0xC960 }, +{ 0xC961, 0xC961, 0xC961 }, +{ 0xC962, 0xC962, 0xC962 }, +{ 0xC963, 0xC963, 0xC963 }, +{ 0xC964, 0xC964, 0xC964 }, +{ 0xC965, 0xC965, 0xC965 }, +{ 0xC966, 0xC966, 0xC966 }, +{ 0xC967, 0xC967, 0xC967 }, +{ 0xC968, 0xC968, 0xC968 }, +{ 0xC969, 0xC969, 0xC969 }, +{ 0xC96A, 0xC96A, 0xC96A }, +{ 0xC96B, 0xC96B, 0xC96B }, +{ 0xC96C, 0xC96C, 0xC96C }, +{ 0xC96D, 0xC96D, 0xC96D }, +{ 0xC96E, 0xC96E, 0xC96E }, +{ 0xC96F, 0xC96F, 0xC96F }, +{ 0xC970, 0xC970, 0xC970 }, +{ 0xC971, 0xC971, 0xC971 }, +{ 0xC972, 0xC972, 0xC972 }, +{ 0xC973, 0xC973, 0xC973 }, +{ 0xC974, 0xC974, 0xC974 }, +{ 0xC975, 0xC975, 0xC975 }, +{ 0xC976, 0xC976, 0xC976 }, +{ 0xC977, 0xC977, 0xC977 }, +{ 0xC978, 0xC978, 0xC978 }, +{ 0xC979, 0xC979, 0xC979 }, +{ 0xC97A, 0xC97A, 0xC97A }, +{ 0xC97B, 0xC97B, 0xC97B }, +{ 0xC97C, 0xC97C, 0xC97C }, +{ 0xC97D, 0xC97D, 0xC97D }, +{ 0xC97E, 0xC97E, 0xC97E }, +{ 0xC97F, 0xC97F, 0xC97F }, +{ 0xC980, 0xC980, 0xC980 }, +{ 0xC981, 0xC981, 0xC981 }, +{ 0xC982, 0xC982, 0xC982 }, +{ 0xC983, 0xC983, 0xC983 }, +{ 0xC984, 0xC984, 0xC984 }, +{ 0xC985, 0xC985, 0xC985 }, +{ 0xC986, 0xC986, 0xC986 }, +{ 0xC987, 0xC987, 0xC987 }, +{ 0xC988, 0xC988, 0xC988 }, +{ 0xC989, 0xC989, 0xC989 }, +{ 0xC98A, 0xC98A, 0xC98A }, +{ 0xC98B, 0xC98B, 0xC98B }, +{ 0xC98C, 0xC98C, 0xC98C }, +{ 0xC98D, 0xC98D, 0xC98D }, +{ 0xC98E, 0xC98E, 0xC98E }, +{ 0xC98F, 0xC98F, 0xC98F }, +{ 0xC990, 0xC990, 0xC990 }, +{ 0xC991, 0xC991, 0xC991 }, +{ 0xC992, 0xC992, 0xC992 }, +{ 0xC993, 0xC993, 0xC993 }, +{ 0xC994, 0xC994, 0xC994 }, +{ 0xC995, 0xC995, 0xC995 }, +{ 0xC996, 0xC996, 0xC996 }, +{ 0xC997, 0xC997, 0xC997 }, +{ 0xC998, 0xC998, 0xC998 }, +{ 0xC999, 0xC999, 0xC999 }, +{ 0xC99A, 0xC99A, 0xC99A }, +{ 0xC99B, 0xC99B, 0xC99B }, +{ 0xC99C, 0xC99C, 0xC99C }, +{ 0xC99D, 0xC99D, 0xC99D }, +{ 0xC99E, 0xC99E, 0xC99E }, +{ 0xC99F, 0xC99F, 0xC99F }, +{ 0xC9A0, 0xC9A0, 0xC9A0 }, +{ 0xC9A1, 0xC9A1, 0xC9A1 }, +{ 0xC9A2, 0xC9A2, 0xC9A2 }, +{ 0xC9A3, 0xC9A3, 0xC9A3 }, +{ 0xC9A4, 0xC9A4, 0xC9A4 }, +{ 0xC9A5, 0xC9A5, 0xC9A5 }, +{ 0xC9A6, 0xC9A6, 0xC9A6 }, +{ 0xC9A7, 0xC9A7, 0xC9A7 }, +{ 0xC9A8, 0xC9A8, 0xC9A8 }, +{ 0xC9A9, 0xC9A9, 0xC9A9 }, +{ 0xC9AA, 0xC9AA, 0xC9AA }, +{ 0xC9AB, 0xC9AB, 0xC9AB }, +{ 0xC9AC, 0xC9AC, 0xC9AC }, +{ 0xC9AD, 0xC9AD, 0xC9AD }, +{ 0xC9AE, 0xC9AE, 0xC9AE }, +{ 0xC9AF, 0xC9AF, 0xC9AF }, +{ 0xC9B0, 0xC9B0, 0xC9B0 }, +{ 0xC9B1, 0xC9B1, 0xC9B1 }, +{ 0xC9B2, 0xC9B2, 0xC9B2 }, +{ 0xC9B3, 0xC9B3, 0xC9B3 }, +{ 0xC9B4, 0xC9B4, 0xC9B4 }, +{ 0xC9B5, 0xC9B5, 0xC9B5 }, +{ 0xC9B6, 0xC9B6, 0xC9B6 }, +{ 0xC9B7, 0xC9B7, 0xC9B7 }, +{ 0xC9B8, 0xC9B8, 0xC9B8 }, +{ 0xC9B9, 0xC9B9, 0xC9B9 }, +{ 0xC9BA, 0xC9BA, 0xC9BA }, +{ 0xC9BB, 0xC9BB, 0xC9BB }, +{ 0xC9BC, 0xC9BC, 0xC9BC }, +{ 0xC9BD, 0xC9BD, 0xC9BD }, +{ 0xC9BE, 0xC9BE, 0xC9BE }, +{ 0xC9BF, 0xC9BF, 0xC9BF }, +{ 0xC9C0, 0xC9C0, 0xC9C0 }, +{ 0xC9C1, 0xC9C1, 0xC9C1 }, +{ 0xC9C2, 0xC9C2, 0xC9C2 }, +{ 0xC9C3, 0xC9C3, 0xC9C3 }, +{ 0xC9C4, 0xC9C4, 0xC9C4 }, +{ 0xC9C5, 0xC9C5, 0xC9C5 }, +{ 0xC9C6, 0xC9C6, 0xC9C6 }, +{ 0xC9C7, 0xC9C7, 0xC9C7 }, +{ 0xC9C8, 0xC9C8, 0xC9C8 }, +{ 0xC9C9, 0xC9C9, 0xC9C9 }, +{ 0xC9CA, 0xC9CA, 0xC9CA }, +{ 0xC9CB, 0xC9CB, 0xC9CB }, +{ 0xC9CC, 0xC9CC, 0xC9CC }, +{ 0xC9CD, 0xC9CD, 0xC9CD }, +{ 0xC9CE, 0xC9CE, 0xC9CE }, +{ 0xC9CF, 0xC9CF, 0xC9CF }, +{ 0xC9D0, 0xC9D0, 0xC9D0 }, +{ 0xC9D1, 0xC9D1, 0xC9D1 }, +{ 0xC9D2, 0xC9D2, 0xC9D2 }, +{ 0xC9D3, 0xC9D3, 0xC9D3 }, +{ 0xC9D4, 0xC9D4, 0xC9D4 }, +{ 0xC9D5, 0xC9D5, 0xC9D5 }, +{ 0xC9D6, 0xC9D6, 0xC9D6 }, +{ 0xC9D7, 0xC9D7, 0xC9D7 }, +{ 0xC9D8, 0xC9D8, 0xC9D8 }, +{ 0xC9D9, 0xC9D9, 0xC9D9 }, +{ 0xC9DA, 0xC9DA, 0xC9DA }, +{ 0xC9DB, 0xC9DB, 0xC9DB }, +{ 0xC9DC, 0xC9DC, 0xC9DC }, +{ 0xC9DD, 0xC9DD, 0xC9DD }, +{ 0xC9DE, 0xC9DE, 0xC9DE }, +{ 0xC9DF, 0xC9DF, 0xC9DF }, +{ 0xC9E0, 0xC9E0, 0xC9E0 }, +{ 0xC9E1, 0xC9E1, 0xC9E1 }, +{ 0xC9E2, 0xC9E2, 0xC9E2 }, +{ 0xC9E3, 0xC9E3, 0xC9E3 }, +{ 0xC9E4, 0xC9E4, 0xC9E4 }, +{ 0xC9E5, 0xC9E5, 0xC9E5 }, +{ 0xC9E6, 0xC9E6, 0xC9E6 }, +{ 0xC9E7, 0xC9E7, 0xC9E7 }, +{ 0xC9E8, 0xC9E8, 0xC9E8 }, +{ 0xC9E9, 0xC9E9, 0xC9E9 }, +{ 0xC9EA, 0xC9EA, 0xC9EA }, +{ 0xC9EB, 0xC9EB, 0xC9EB }, +{ 0xC9EC, 0xC9EC, 0xC9EC }, +{ 0xC9ED, 0xC9ED, 0xC9ED }, +{ 0xC9EE, 0xC9EE, 0xC9EE }, +{ 0xC9EF, 0xC9EF, 0xC9EF }, +{ 0xC9F0, 0xC9F0, 0xC9F0 }, +{ 0xC9F1, 0xC9F1, 0xC9F1 }, +{ 0xC9F2, 0xC9F2, 0xC9F2 }, +{ 0xC9F3, 0xC9F3, 0xC9F3 }, +{ 0xC9F4, 0xC9F4, 0xC9F4 }, +{ 0xC9F5, 0xC9F5, 0xC9F5 }, +{ 0xC9F6, 0xC9F6, 0xC9F6 }, +{ 0xC9F7, 0xC9F7, 0xC9F7 }, +{ 0xC9F8, 0xC9F8, 0xC9F8 }, +{ 0xC9F9, 0xC9F9, 0xC9F9 }, +{ 0xC9FA, 0xC9FA, 0xC9FA }, +{ 0xC9FB, 0xC9FB, 0xC9FB }, +{ 0xC9FC, 0xC9FC, 0xC9FC }, +{ 0xC9FD, 0xC9FD, 0xC9FD }, +{ 0xC9FE, 0xC9FE, 0xC9FE }, +{ 0xC9FF, 0xC9FF, 0xC9FF }, +{ 0xCA00, 0xCA00, 0xCA00 }, +{ 0xCA01, 0xCA01, 0xCA01 }, +{ 0xCA02, 0xCA02, 0xCA02 }, +{ 0xCA03, 0xCA03, 0xCA03 }, +{ 0xCA04, 0xCA04, 0xCA04 }, +{ 0xCA05, 0xCA05, 0xCA05 }, +{ 0xCA06, 0xCA06, 0xCA06 }, +{ 0xCA07, 0xCA07, 0xCA07 }, +{ 0xCA08, 0xCA08, 0xCA08 }, +{ 0xCA09, 0xCA09, 0xCA09 }, +{ 0xCA0A, 0xCA0A, 0xCA0A }, +{ 0xCA0B, 0xCA0B, 0xCA0B }, +{ 0xCA0C, 0xCA0C, 0xCA0C }, +{ 0xCA0D, 0xCA0D, 0xCA0D }, +{ 0xCA0E, 0xCA0E, 0xCA0E }, +{ 0xCA0F, 0xCA0F, 0xCA0F }, +{ 0xCA10, 0xCA10, 0xCA10 }, +{ 0xCA11, 0xCA11, 0xCA11 }, +{ 0xCA12, 0xCA12, 0xCA12 }, +{ 0xCA13, 0xCA13, 0xCA13 }, +{ 0xCA14, 0xCA14, 0xCA14 }, +{ 0xCA15, 0xCA15, 0xCA15 }, +{ 0xCA16, 0xCA16, 0xCA16 }, +{ 0xCA17, 0xCA17, 0xCA17 }, +{ 0xCA18, 0xCA18, 0xCA18 }, +{ 0xCA19, 0xCA19, 0xCA19 }, +{ 0xCA1A, 0xCA1A, 0xCA1A }, +{ 0xCA1B, 0xCA1B, 0xCA1B }, +{ 0xCA1C, 0xCA1C, 0xCA1C }, +{ 0xCA1D, 0xCA1D, 0xCA1D }, +{ 0xCA1E, 0xCA1E, 0xCA1E }, +{ 0xCA1F, 0xCA1F, 0xCA1F }, +{ 0xCA20, 0xCA20, 0xCA20 }, +{ 0xCA21, 0xCA21, 0xCA21 }, +{ 0xCA22, 0xCA22, 0xCA22 }, +{ 0xCA23, 0xCA23, 0xCA23 }, +{ 0xCA24, 0xCA24, 0xCA24 }, +{ 0xCA25, 0xCA25, 0xCA25 }, +{ 0xCA26, 0xCA26, 0xCA26 }, +{ 0xCA27, 0xCA27, 0xCA27 }, +{ 0xCA28, 0xCA28, 0xCA28 }, +{ 0xCA29, 0xCA29, 0xCA29 }, +{ 0xCA2A, 0xCA2A, 0xCA2A }, +{ 0xCA2B, 0xCA2B, 0xCA2B }, +{ 0xCA2C, 0xCA2C, 0xCA2C }, +{ 0xCA2D, 0xCA2D, 0xCA2D }, +{ 0xCA2E, 0xCA2E, 0xCA2E }, +{ 0xCA2F, 0xCA2F, 0xCA2F }, +{ 0xCA30, 0xCA30, 0xCA30 }, +{ 0xCA31, 0xCA31, 0xCA31 }, +{ 0xCA32, 0xCA32, 0xCA32 }, +{ 0xCA33, 0xCA33, 0xCA33 }, +{ 0xCA34, 0xCA34, 0xCA34 }, +{ 0xCA35, 0xCA35, 0xCA35 }, +{ 0xCA36, 0xCA36, 0xCA36 }, +{ 0xCA37, 0xCA37, 0xCA37 }, +{ 0xCA38, 0xCA38, 0xCA38 }, +{ 0xCA39, 0xCA39, 0xCA39 }, +{ 0xCA3A, 0xCA3A, 0xCA3A }, +{ 0xCA3B, 0xCA3B, 0xCA3B }, +{ 0xCA3C, 0xCA3C, 0xCA3C }, +{ 0xCA3D, 0xCA3D, 0xCA3D }, +{ 0xCA3E, 0xCA3E, 0xCA3E }, +{ 0xCA3F, 0xCA3F, 0xCA3F }, +{ 0xCA40, 0xCA40, 0xCA40 }, +{ 0xCA41, 0xCA41, 0xCA41 }, +{ 0xCA42, 0xCA42, 0xCA42 }, +{ 0xCA43, 0xCA43, 0xCA43 }, +{ 0xCA44, 0xCA44, 0xCA44 }, +{ 0xCA45, 0xCA45, 0xCA45 }, +{ 0xCA46, 0xCA46, 0xCA46 }, +{ 0xCA47, 0xCA47, 0xCA47 }, +{ 0xCA48, 0xCA48, 0xCA48 }, +{ 0xCA49, 0xCA49, 0xCA49 }, +{ 0xCA4A, 0xCA4A, 0xCA4A }, +{ 0xCA4B, 0xCA4B, 0xCA4B }, +{ 0xCA4C, 0xCA4C, 0xCA4C }, +{ 0xCA4D, 0xCA4D, 0xCA4D }, +{ 0xCA4E, 0xCA4E, 0xCA4E }, +{ 0xCA4F, 0xCA4F, 0xCA4F }, +{ 0xCA50, 0xCA50, 0xCA50 }, +{ 0xCA51, 0xCA51, 0xCA51 }, +{ 0xCA52, 0xCA52, 0xCA52 }, +{ 0xCA53, 0xCA53, 0xCA53 }, +{ 0xCA54, 0xCA54, 0xCA54 }, +{ 0xCA55, 0xCA55, 0xCA55 }, +{ 0xCA56, 0xCA56, 0xCA56 }, +{ 0xCA57, 0xCA57, 0xCA57 }, +{ 0xCA58, 0xCA58, 0xCA58 }, +{ 0xCA59, 0xCA59, 0xCA59 }, +{ 0xCA5A, 0xCA5A, 0xCA5A }, +{ 0xCA5B, 0xCA5B, 0xCA5B }, +{ 0xCA5C, 0xCA5C, 0xCA5C }, +{ 0xCA5D, 0xCA5D, 0xCA5D }, +{ 0xCA5E, 0xCA5E, 0xCA5E }, +{ 0xCA5F, 0xCA5F, 0xCA5F }, +{ 0xCA60, 0xCA60, 0xCA60 }, +{ 0xCA61, 0xCA61, 0xCA61 }, +{ 0xCA62, 0xCA62, 0xCA62 }, +{ 0xCA63, 0xCA63, 0xCA63 }, +{ 0xCA64, 0xCA64, 0xCA64 }, +{ 0xCA65, 0xCA65, 0xCA65 }, +{ 0xCA66, 0xCA66, 0xCA66 }, +{ 0xCA67, 0xCA67, 0xCA67 }, +{ 0xCA68, 0xCA68, 0xCA68 }, +{ 0xCA69, 0xCA69, 0xCA69 }, +{ 0xCA6A, 0xCA6A, 0xCA6A }, +{ 0xCA6B, 0xCA6B, 0xCA6B }, +{ 0xCA6C, 0xCA6C, 0xCA6C }, +{ 0xCA6D, 0xCA6D, 0xCA6D }, +{ 0xCA6E, 0xCA6E, 0xCA6E }, +{ 0xCA6F, 0xCA6F, 0xCA6F }, +{ 0xCA70, 0xCA70, 0xCA70 }, +{ 0xCA71, 0xCA71, 0xCA71 }, +{ 0xCA72, 0xCA72, 0xCA72 }, +{ 0xCA73, 0xCA73, 0xCA73 }, +{ 0xCA74, 0xCA74, 0xCA74 }, +{ 0xCA75, 0xCA75, 0xCA75 }, +{ 0xCA76, 0xCA76, 0xCA76 }, +{ 0xCA77, 0xCA77, 0xCA77 }, +{ 0xCA78, 0xCA78, 0xCA78 }, +{ 0xCA79, 0xCA79, 0xCA79 }, +{ 0xCA7A, 0xCA7A, 0xCA7A }, +{ 0xCA7B, 0xCA7B, 0xCA7B }, +{ 0xCA7C, 0xCA7C, 0xCA7C }, +{ 0xCA7D, 0xCA7D, 0xCA7D }, +{ 0xCA7E, 0xCA7E, 0xCA7E }, +{ 0xCA7F, 0xCA7F, 0xCA7F }, +{ 0xCA80, 0xCA80, 0xCA80 }, +{ 0xCA81, 0xCA81, 0xCA81 }, +{ 0xCA82, 0xCA82, 0xCA82 }, +{ 0xCA83, 0xCA83, 0xCA83 }, +{ 0xCA84, 0xCA84, 0xCA84 }, +{ 0xCA85, 0xCA85, 0xCA85 }, +{ 0xCA86, 0xCA86, 0xCA86 }, +{ 0xCA87, 0xCA87, 0xCA87 }, +{ 0xCA88, 0xCA88, 0xCA88 }, +{ 0xCA89, 0xCA89, 0xCA89 }, +{ 0xCA8A, 0xCA8A, 0xCA8A }, +{ 0xCA8B, 0xCA8B, 0xCA8B }, +{ 0xCA8C, 0xCA8C, 0xCA8C }, +{ 0xCA8D, 0xCA8D, 0xCA8D }, +{ 0xCA8E, 0xCA8E, 0xCA8E }, +{ 0xCA8F, 0xCA8F, 0xCA8F }, +{ 0xCA90, 0xCA90, 0xCA90 }, +{ 0xCA91, 0xCA91, 0xCA91 }, +{ 0xCA92, 0xCA92, 0xCA92 }, +{ 0xCA93, 0xCA93, 0xCA93 }, +{ 0xCA94, 0xCA94, 0xCA94 }, +{ 0xCA95, 0xCA95, 0xCA95 }, +{ 0xCA96, 0xCA96, 0xCA96 }, +{ 0xCA97, 0xCA97, 0xCA97 }, +{ 0xCA98, 0xCA98, 0xCA98 }, +{ 0xCA99, 0xCA99, 0xCA99 }, +{ 0xCA9A, 0xCA9A, 0xCA9A }, +{ 0xCA9B, 0xCA9B, 0xCA9B }, +{ 0xCA9C, 0xCA9C, 0xCA9C }, +{ 0xCA9D, 0xCA9D, 0xCA9D }, +{ 0xCA9E, 0xCA9E, 0xCA9E }, +{ 0xCA9F, 0xCA9F, 0xCA9F }, +{ 0xCAA0, 0xCAA0, 0xCAA0 }, +{ 0xCAA1, 0xCAA1, 0xCAA1 }, +{ 0xCAA2, 0xCAA2, 0xCAA2 }, +{ 0xCAA3, 0xCAA3, 0xCAA3 }, +{ 0xCAA4, 0xCAA4, 0xCAA4 }, +{ 0xCAA5, 0xCAA5, 0xCAA5 }, +{ 0xCAA6, 0xCAA6, 0xCAA6 }, +{ 0xCAA7, 0xCAA7, 0xCAA7 }, +{ 0xCAA8, 0xCAA8, 0xCAA8 }, +{ 0xCAA9, 0xCAA9, 0xCAA9 }, +{ 0xCAAA, 0xCAAA, 0xCAAA }, +{ 0xCAAB, 0xCAAB, 0xCAAB }, +{ 0xCAAC, 0xCAAC, 0xCAAC }, +{ 0xCAAD, 0xCAAD, 0xCAAD }, +{ 0xCAAE, 0xCAAE, 0xCAAE }, +{ 0xCAAF, 0xCAAF, 0xCAAF }, +{ 0xCAB0, 0xCAB0, 0xCAB0 }, +{ 0xCAB1, 0xCAB1, 0xCAB1 }, +{ 0xCAB2, 0xCAB2, 0xCAB2 }, +{ 0xCAB3, 0xCAB3, 0xCAB3 }, +{ 0xCAB4, 0xCAB4, 0xCAB4 }, +{ 0xCAB5, 0xCAB5, 0xCAB5 }, +{ 0xCAB6, 0xCAB6, 0xCAB6 }, +{ 0xCAB7, 0xCAB7, 0xCAB7 }, +{ 0xCAB8, 0xCAB8, 0xCAB8 }, +{ 0xCAB9, 0xCAB9, 0xCAB9 }, +{ 0xCABA, 0xCABA, 0xCABA }, +{ 0xCABB, 0xCABB, 0xCABB }, +{ 0xCABC, 0xCABC, 0xCABC }, +{ 0xCABD, 0xCABD, 0xCABD }, +{ 0xCABE, 0xCABE, 0xCABE }, +{ 0xCABF, 0xCABF, 0xCABF }, +{ 0xCAC0, 0xCAC0, 0xCAC0 }, +{ 0xCAC1, 0xCAC1, 0xCAC1 }, +{ 0xCAC2, 0xCAC2, 0xCAC2 }, +{ 0xCAC3, 0xCAC3, 0xCAC3 }, +{ 0xCAC4, 0xCAC4, 0xCAC4 }, +{ 0xCAC5, 0xCAC5, 0xCAC5 }, +{ 0xCAC6, 0xCAC6, 0xCAC6 }, +{ 0xCAC7, 0xCAC7, 0xCAC7 }, +{ 0xCAC8, 0xCAC8, 0xCAC8 }, +{ 0xCAC9, 0xCAC9, 0xCAC9 }, +{ 0xCACA, 0xCACA, 0xCACA }, +{ 0xCACB, 0xCACB, 0xCACB }, +{ 0xCACC, 0xCACC, 0xCACC }, +{ 0xCACD, 0xCACD, 0xCACD }, +{ 0xCACE, 0xCACE, 0xCACE }, +{ 0xCACF, 0xCACF, 0xCACF }, +{ 0xCAD0, 0xCAD0, 0xCAD0 }, +{ 0xCAD1, 0xCAD1, 0xCAD1 }, +{ 0xCAD2, 0xCAD2, 0xCAD2 }, +{ 0xCAD3, 0xCAD3, 0xCAD3 }, +{ 0xCAD4, 0xCAD4, 0xCAD4 }, +{ 0xCAD5, 0xCAD5, 0xCAD5 }, +{ 0xCAD6, 0xCAD6, 0xCAD6 }, +{ 0xCAD7, 0xCAD7, 0xCAD7 }, +{ 0xCAD8, 0xCAD8, 0xCAD8 }, +{ 0xCAD9, 0xCAD9, 0xCAD9 }, +{ 0xCADA, 0xCADA, 0xCADA }, +{ 0xCADB, 0xCADB, 0xCADB }, +{ 0xCADC, 0xCADC, 0xCADC }, +{ 0xCADD, 0xCADD, 0xCADD }, +{ 0xCADE, 0xCADE, 0xCADE }, +{ 0xCADF, 0xCADF, 0xCADF }, +{ 0xCAE0, 0xCAE0, 0xCAE0 }, +{ 0xCAE1, 0xCAE1, 0xCAE1 }, +{ 0xCAE2, 0xCAE2, 0xCAE2 }, +{ 0xCAE3, 0xCAE3, 0xCAE3 }, +{ 0xCAE4, 0xCAE4, 0xCAE4 }, +{ 0xCAE5, 0xCAE5, 0xCAE5 }, +{ 0xCAE6, 0xCAE6, 0xCAE6 }, +{ 0xCAE7, 0xCAE7, 0xCAE7 }, +{ 0xCAE8, 0xCAE8, 0xCAE8 }, +{ 0xCAE9, 0xCAE9, 0xCAE9 }, +{ 0xCAEA, 0xCAEA, 0xCAEA }, +{ 0xCAEB, 0xCAEB, 0xCAEB }, +{ 0xCAEC, 0xCAEC, 0xCAEC }, +{ 0xCAED, 0xCAED, 0xCAED }, +{ 0xCAEE, 0xCAEE, 0xCAEE }, +{ 0xCAEF, 0xCAEF, 0xCAEF }, +{ 0xCAF0, 0xCAF0, 0xCAF0 }, +{ 0xCAF1, 0xCAF1, 0xCAF1 }, +{ 0xCAF2, 0xCAF2, 0xCAF2 }, +{ 0xCAF3, 0xCAF3, 0xCAF3 }, +{ 0xCAF4, 0xCAF4, 0xCAF4 }, +{ 0xCAF5, 0xCAF5, 0xCAF5 }, +{ 0xCAF6, 0xCAF6, 0xCAF6 }, +{ 0xCAF7, 0xCAF7, 0xCAF7 }, +{ 0xCAF8, 0xCAF8, 0xCAF8 }, +{ 0xCAF9, 0xCAF9, 0xCAF9 }, +{ 0xCAFA, 0xCAFA, 0xCAFA }, +{ 0xCAFB, 0xCAFB, 0xCAFB }, +{ 0xCAFC, 0xCAFC, 0xCAFC }, +{ 0xCAFD, 0xCAFD, 0xCAFD }, +{ 0xCAFE, 0xCAFE, 0xCAFE }, +{ 0xCAFF, 0xCAFF, 0xCAFF }, +{ 0xCB00, 0xCB00, 0xCB00 }, +{ 0xCB01, 0xCB01, 0xCB01 }, +{ 0xCB02, 0xCB02, 0xCB02 }, +{ 0xCB03, 0xCB03, 0xCB03 }, +{ 0xCB04, 0xCB04, 0xCB04 }, +{ 0xCB05, 0xCB05, 0xCB05 }, +{ 0xCB06, 0xCB06, 0xCB06 }, +{ 0xCB07, 0xCB07, 0xCB07 }, +{ 0xCB08, 0xCB08, 0xCB08 }, +{ 0xCB09, 0xCB09, 0xCB09 }, +{ 0xCB0A, 0xCB0A, 0xCB0A }, +{ 0xCB0B, 0xCB0B, 0xCB0B }, +{ 0xCB0C, 0xCB0C, 0xCB0C }, +{ 0xCB0D, 0xCB0D, 0xCB0D }, +{ 0xCB0E, 0xCB0E, 0xCB0E }, +{ 0xCB0F, 0xCB0F, 0xCB0F }, +{ 0xCB10, 0xCB10, 0xCB10 }, +{ 0xCB11, 0xCB11, 0xCB11 }, +{ 0xCB12, 0xCB12, 0xCB12 }, +{ 0xCB13, 0xCB13, 0xCB13 }, +{ 0xCB14, 0xCB14, 0xCB14 }, +{ 0xCB15, 0xCB15, 0xCB15 }, +{ 0xCB16, 0xCB16, 0xCB16 }, +{ 0xCB17, 0xCB17, 0xCB17 }, +{ 0xCB18, 0xCB18, 0xCB18 }, +{ 0xCB19, 0xCB19, 0xCB19 }, +{ 0xCB1A, 0xCB1A, 0xCB1A }, +{ 0xCB1B, 0xCB1B, 0xCB1B }, +{ 0xCB1C, 0xCB1C, 0xCB1C }, +{ 0xCB1D, 0xCB1D, 0xCB1D }, +{ 0xCB1E, 0xCB1E, 0xCB1E }, +{ 0xCB1F, 0xCB1F, 0xCB1F }, +{ 0xCB20, 0xCB20, 0xCB20 }, +{ 0xCB21, 0xCB21, 0xCB21 }, +{ 0xCB22, 0xCB22, 0xCB22 }, +{ 0xCB23, 0xCB23, 0xCB23 }, +{ 0xCB24, 0xCB24, 0xCB24 }, +{ 0xCB25, 0xCB25, 0xCB25 }, +{ 0xCB26, 0xCB26, 0xCB26 }, +{ 0xCB27, 0xCB27, 0xCB27 }, +{ 0xCB28, 0xCB28, 0xCB28 }, +{ 0xCB29, 0xCB29, 0xCB29 }, +{ 0xCB2A, 0xCB2A, 0xCB2A }, +{ 0xCB2B, 0xCB2B, 0xCB2B }, +{ 0xCB2C, 0xCB2C, 0xCB2C }, +{ 0xCB2D, 0xCB2D, 0xCB2D }, +{ 0xCB2E, 0xCB2E, 0xCB2E }, +{ 0xCB2F, 0xCB2F, 0xCB2F }, +{ 0xCB30, 0xCB30, 0xCB30 }, +{ 0xCB31, 0xCB31, 0xCB31 }, +{ 0xCB32, 0xCB32, 0xCB32 }, +{ 0xCB33, 0xCB33, 0xCB33 }, +{ 0xCB34, 0xCB34, 0xCB34 }, +{ 0xCB35, 0xCB35, 0xCB35 }, +{ 0xCB36, 0xCB36, 0xCB36 }, +{ 0xCB37, 0xCB37, 0xCB37 }, +{ 0xCB38, 0xCB38, 0xCB38 }, +{ 0xCB39, 0xCB39, 0xCB39 }, +{ 0xCB3A, 0xCB3A, 0xCB3A }, +{ 0xCB3B, 0xCB3B, 0xCB3B }, +{ 0xCB3C, 0xCB3C, 0xCB3C }, +{ 0xCB3D, 0xCB3D, 0xCB3D }, +{ 0xCB3E, 0xCB3E, 0xCB3E }, +{ 0xCB3F, 0xCB3F, 0xCB3F }, +{ 0xCB40, 0xCB40, 0xCB40 }, +{ 0xCB41, 0xCB41, 0xCB41 }, +{ 0xCB42, 0xCB42, 0xCB42 }, +{ 0xCB43, 0xCB43, 0xCB43 }, +{ 0xCB44, 0xCB44, 0xCB44 }, +{ 0xCB45, 0xCB45, 0xCB45 }, +{ 0xCB46, 0xCB46, 0xCB46 }, +{ 0xCB47, 0xCB47, 0xCB47 }, +{ 0xCB48, 0xCB48, 0xCB48 }, +{ 0xCB49, 0xCB49, 0xCB49 }, +{ 0xCB4A, 0xCB4A, 0xCB4A }, +{ 0xCB4B, 0xCB4B, 0xCB4B }, +{ 0xCB4C, 0xCB4C, 0xCB4C }, +{ 0xCB4D, 0xCB4D, 0xCB4D }, +{ 0xCB4E, 0xCB4E, 0xCB4E }, +{ 0xCB4F, 0xCB4F, 0xCB4F }, +{ 0xCB50, 0xCB50, 0xCB50 }, +{ 0xCB51, 0xCB51, 0xCB51 }, +{ 0xCB52, 0xCB52, 0xCB52 }, +{ 0xCB53, 0xCB53, 0xCB53 }, +{ 0xCB54, 0xCB54, 0xCB54 }, +{ 0xCB55, 0xCB55, 0xCB55 }, +{ 0xCB56, 0xCB56, 0xCB56 }, +{ 0xCB57, 0xCB57, 0xCB57 }, +{ 0xCB58, 0xCB58, 0xCB58 }, +{ 0xCB59, 0xCB59, 0xCB59 }, +{ 0xCB5A, 0xCB5A, 0xCB5A }, +{ 0xCB5B, 0xCB5B, 0xCB5B }, +{ 0xCB5C, 0xCB5C, 0xCB5C }, +{ 0xCB5D, 0xCB5D, 0xCB5D }, +{ 0xCB5E, 0xCB5E, 0xCB5E }, +{ 0xCB5F, 0xCB5F, 0xCB5F }, +{ 0xCB60, 0xCB60, 0xCB60 }, +{ 0xCB61, 0xCB61, 0xCB61 }, +{ 0xCB62, 0xCB62, 0xCB62 }, +{ 0xCB63, 0xCB63, 0xCB63 }, +{ 0xCB64, 0xCB64, 0xCB64 }, +{ 0xCB65, 0xCB65, 0xCB65 }, +{ 0xCB66, 0xCB66, 0xCB66 }, +{ 0xCB67, 0xCB67, 0xCB67 }, +{ 0xCB68, 0xCB68, 0xCB68 }, +{ 0xCB69, 0xCB69, 0xCB69 }, +{ 0xCB6A, 0xCB6A, 0xCB6A }, +{ 0xCB6B, 0xCB6B, 0xCB6B }, +{ 0xCB6C, 0xCB6C, 0xCB6C }, +{ 0xCB6D, 0xCB6D, 0xCB6D }, +{ 0xCB6E, 0xCB6E, 0xCB6E }, +{ 0xCB6F, 0xCB6F, 0xCB6F }, +{ 0xCB70, 0xCB70, 0xCB70 }, +{ 0xCB71, 0xCB71, 0xCB71 }, +{ 0xCB72, 0xCB72, 0xCB72 }, +{ 0xCB73, 0xCB73, 0xCB73 }, +{ 0xCB74, 0xCB74, 0xCB74 }, +{ 0xCB75, 0xCB75, 0xCB75 }, +{ 0xCB76, 0xCB76, 0xCB76 }, +{ 0xCB77, 0xCB77, 0xCB77 }, +{ 0xCB78, 0xCB78, 0xCB78 }, +{ 0xCB79, 0xCB79, 0xCB79 }, +{ 0xCB7A, 0xCB7A, 0xCB7A }, +{ 0xCB7B, 0xCB7B, 0xCB7B }, +{ 0xCB7C, 0xCB7C, 0xCB7C }, +{ 0xCB7D, 0xCB7D, 0xCB7D }, +{ 0xCB7E, 0xCB7E, 0xCB7E }, +{ 0xCB7F, 0xCB7F, 0xCB7F }, +{ 0xCB80, 0xCB80, 0xCB80 }, +{ 0xCB81, 0xCB81, 0xCB81 }, +{ 0xCB82, 0xCB82, 0xCB82 }, +{ 0xCB83, 0xCB83, 0xCB83 }, +{ 0xCB84, 0xCB84, 0xCB84 }, +{ 0xCB85, 0xCB85, 0xCB85 }, +{ 0xCB86, 0xCB86, 0xCB86 }, +{ 0xCB87, 0xCB87, 0xCB87 }, +{ 0xCB88, 0xCB88, 0xCB88 }, +{ 0xCB89, 0xCB89, 0xCB89 }, +{ 0xCB8A, 0xCB8A, 0xCB8A }, +{ 0xCB8B, 0xCB8B, 0xCB8B }, +{ 0xCB8C, 0xCB8C, 0xCB8C }, +{ 0xCB8D, 0xCB8D, 0xCB8D }, +{ 0xCB8E, 0xCB8E, 0xCB8E }, +{ 0xCB8F, 0xCB8F, 0xCB8F }, +{ 0xCB90, 0xCB90, 0xCB90 }, +{ 0xCB91, 0xCB91, 0xCB91 }, +{ 0xCB92, 0xCB92, 0xCB92 }, +{ 0xCB93, 0xCB93, 0xCB93 }, +{ 0xCB94, 0xCB94, 0xCB94 }, +{ 0xCB95, 0xCB95, 0xCB95 }, +{ 0xCB96, 0xCB96, 0xCB96 }, +{ 0xCB97, 0xCB97, 0xCB97 }, +{ 0xCB98, 0xCB98, 0xCB98 }, +{ 0xCB99, 0xCB99, 0xCB99 }, +{ 0xCB9A, 0xCB9A, 0xCB9A }, +{ 0xCB9B, 0xCB9B, 0xCB9B }, +{ 0xCB9C, 0xCB9C, 0xCB9C }, +{ 0xCB9D, 0xCB9D, 0xCB9D }, +{ 0xCB9E, 0xCB9E, 0xCB9E }, +{ 0xCB9F, 0xCB9F, 0xCB9F }, +{ 0xCBA0, 0xCBA0, 0xCBA0 }, +{ 0xCBA1, 0xCBA1, 0xCBA1 }, +{ 0xCBA2, 0xCBA2, 0xCBA2 }, +{ 0xCBA3, 0xCBA3, 0xCBA3 }, +{ 0xCBA4, 0xCBA4, 0xCBA4 }, +{ 0xCBA5, 0xCBA5, 0xCBA5 }, +{ 0xCBA6, 0xCBA6, 0xCBA6 }, +{ 0xCBA7, 0xCBA7, 0xCBA7 }, +{ 0xCBA8, 0xCBA8, 0xCBA8 }, +{ 0xCBA9, 0xCBA9, 0xCBA9 }, +{ 0xCBAA, 0xCBAA, 0xCBAA }, +{ 0xCBAB, 0xCBAB, 0xCBAB }, +{ 0xCBAC, 0xCBAC, 0xCBAC }, +{ 0xCBAD, 0xCBAD, 0xCBAD }, +{ 0xCBAE, 0xCBAE, 0xCBAE }, +{ 0xCBAF, 0xCBAF, 0xCBAF }, +{ 0xCBB0, 0xCBB0, 0xCBB0 }, +{ 0xCBB1, 0xCBB1, 0xCBB1 }, +{ 0xCBB2, 0xCBB2, 0xCBB2 }, +{ 0xCBB3, 0xCBB3, 0xCBB3 }, +{ 0xCBB4, 0xCBB4, 0xCBB4 }, +{ 0xCBB5, 0xCBB5, 0xCBB5 }, +{ 0xCBB6, 0xCBB6, 0xCBB6 }, +{ 0xCBB7, 0xCBB7, 0xCBB7 }, +{ 0xCBB8, 0xCBB8, 0xCBB8 }, +{ 0xCBB9, 0xCBB9, 0xCBB9 }, +{ 0xCBBA, 0xCBBA, 0xCBBA }, +{ 0xCBBB, 0xCBBB, 0xCBBB }, +{ 0xCBBC, 0xCBBC, 0xCBBC }, +{ 0xCBBD, 0xCBBD, 0xCBBD }, +{ 0xCBBE, 0xCBBE, 0xCBBE }, +{ 0xCBBF, 0xCBBF, 0xCBBF }, +{ 0xCBC0, 0xCBC0, 0xCBC0 }, +{ 0xCBC1, 0xCBC1, 0xCBC1 }, +{ 0xCBC2, 0xCBC2, 0xCBC2 }, +{ 0xCBC3, 0xCBC3, 0xCBC3 }, +{ 0xCBC4, 0xCBC4, 0xCBC4 }, +{ 0xCBC5, 0xCBC5, 0xCBC5 }, +{ 0xCBC6, 0xCBC6, 0xCBC6 }, +{ 0xCBC7, 0xCBC7, 0xCBC7 }, +{ 0xCBC8, 0xCBC8, 0xCBC8 }, +{ 0xCBC9, 0xCBC9, 0xCBC9 }, +{ 0xCBCA, 0xCBCA, 0xCBCA }, +{ 0xCBCB, 0xCBCB, 0xCBCB }, +{ 0xCBCC, 0xCBCC, 0xCBCC }, +{ 0xCBCD, 0xCBCD, 0xCBCD }, +{ 0xCBCE, 0xCBCE, 0xCBCE }, +{ 0xCBCF, 0xCBCF, 0xCBCF }, +{ 0xCBD0, 0xCBD0, 0xCBD0 }, +{ 0xCBD1, 0xCBD1, 0xCBD1 }, +{ 0xCBD2, 0xCBD2, 0xCBD2 }, +{ 0xCBD3, 0xCBD3, 0xCBD3 }, +{ 0xCBD4, 0xCBD4, 0xCBD4 }, +{ 0xCBD5, 0xCBD5, 0xCBD5 }, +{ 0xCBD6, 0xCBD6, 0xCBD6 }, +{ 0xCBD7, 0xCBD7, 0xCBD7 }, +{ 0xCBD8, 0xCBD8, 0xCBD8 }, +{ 0xCBD9, 0xCBD9, 0xCBD9 }, +{ 0xCBDA, 0xCBDA, 0xCBDA }, +{ 0xCBDB, 0xCBDB, 0xCBDB }, +{ 0xCBDC, 0xCBDC, 0xCBDC }, +{ 0xCBDD, 0xCBDD, 0xCBDD }, +{ 0xCBDE, 0xCBDE, 0xCBDE }, +{ 0xCBDF, 0xCBDF, 0xCBDF }, +{ 0xCBE0, 0xCBE0, 0xCBE0 }, +{ 0xCBE1, 0xCBE1, 0xCBE1 }, +{ 0xCBE2, 0xCBE2, 0xCBE2 }, +{ 0xCBE3, 0xCBE3, 0xCBE3 }, +{ 0xCBE4, 0xCBE4, 0xCBE4 }, +{ 0xCBE5, 0xCBE5, 0xCBE5 }, +{ 0xCBE6, 0xCBE6, 0xCBE6 }, +{ 0xCBE7, 0xCBE7, 0xCBE7 }, +{ 0xCBE8, 0xCBE8, 0xCBE8 }, +{ 0xCBE9, 0xCBE9, 0xCBE9 }, +{ 0xCBEA, 0xCBEA, 0xCBEA }, +{ 0xCBEB, 0xCBEB, 0xCBEB }, +{ 0xCBEC, 0xCBEC, 0xCBEC }, +{ 0xCBED, 0xCBED, 0xCBED }, +{ 0xCBEE, 0xCBEE, 0xCBEE }, +{ 0xCBEF, 0xCBEF, 0xCBEF }, +{ 0xCBF0, 0xCBF0, 0xCBF0 }, +{ 0xCBF1, 0xCBF1, 0xCBF1 }, +{ 0xCBF2, 0xCBF2, 0xCBF2 }, +{ 0xCBF3, 0xCBF3, 0xCBF3 }, +{ 0xCBF4, 0xCBF4, 0xCBF4 }, +{ 0xCBF5, 0xCBF5, 0xCBF5 }, +{ 0xCBF6, 0xCBF6, 0xCBF6 }, +{ 0xCBF7, 0xCBF7, 0xCBF7 }, +{ 0xCBF8, 0xCBF8, 0xCBF8 }, +{ 0xCBF9, 0xCBF9, 0xCBF9 }, +{ 0xCBFA, 0xCBFA, 0xCBFA }, +{ 0xCBFB, 0xCBFB, 0xCBFB }, +{ 0xCBFC, 0xCBFC, 0xCBFC }, +{ 0xCBFD, 0xCBFD, 0xCBFD }, +{ 0xCBFE, 0xCBFE, 0xCBFE }, +{ 0xCBFF, 0xCBFF, 0xCBFF }, +{ 0xCC00, 0xCC00, 0xCC00 }, +{ 0xCC01, 0xCC01, 0xCC01 }, +{ 0xCC02, 0xCC02, 0xCC02 }, +{ 0xCC03, 0xCC03, 0xCC03 }, +{ 0xCC04, 0xCC04, 0xCC04 }, +{ 0xCC05, 0xCC05, 0xCC05 }, +{ 0xCC06, 0xCC06, 0xCC06 }, +{ 0xCC07, 0xCC07, 0xCC07 }, +{ 0xCC08, 0xCC08, 0xCC08 }, +{ 0xCC09, 0xCC09, 0xCC09 }, +{ 0xCC0A, 0xCC0A, 0xCC0A }, +{ 0xCC0B, 0xCC0B, 0xCC0B }, +{ 0xCC0C, 0xCC0C, 0xCC0C }, +{ 0xCC0D, 0xCC0D, 0xCC0D }, +{ 0xCC0E, 0xCC0E, 0xCC0E }, +{ 0xCC0F, 0xCC0F, 0xCC0F }, +{ 0xCC10, 0xCC10, 0xCC10 }, +{ 0xCC11, 0xCC11, 0xCC11 }, +{ 0xCC12, 0xCC12, 0xCC12 }, +{ 0xCC13, 0xCC13, 0xCC13 }, +{ 0xCC14, 0xCC14, 0xCC14 }, +{ 0xCC15, 0xCC15, 0xCC15 }, +{ 0xCC16, 0xCC16, 0xCC16 }, +{ 0xCC17, 0xCC17, 0xCC17 }, +{ 0xCC18, 0xCC18, 0xCC18 }, +{ 0xCC19, 0xCC19, 0xCC19 }, +{ 0xCC1A, 0xCC1A, 0xCC1A }, +{ 0xCC1B, 0xCC1B, 0xCC1B }, +{ 0xCC1C, 0xCC1C, 0xCC1C }, +{ 0xCC1D, 0xCC1D, 0xCC1D }, +{ 0xCC1E, 0xCC1E, 0xCC1E }, +{ 0xCC1F, 0xCC1F, 0xCC1F }, +{ 0xCC20, 0xCC20, 0xCC20 }, +{ 0xCC21, 0xCC21, 0xCC21 }, +{ 0xCC22, 0xCC22, 0xCC22 }, +{ 0xCC23, 0xCC23, 0xCC23 }, +{ 0xCC24, 0xCC24, 0xCC24 }, +{ 0xCC25, 0xCC25, 0xCC25 }, +{ 0xCC26, 0xCC26, 0xCC26 }, +{ 0xCC27, 0xCC27, 0xCC27 }, +{ 0xCC28, 0xCC28, 0xCC28 }, +{ 0xCC29, 0xCC29, 0xCC29 }, +{ 0xCC2A, 0xCC2A, 0xCC2A }, +{ 0xCC2B, 0xCC2B, 0xCC2B }, +{ 0xCC2C, 0xCC2C, 0xCC2C }, +{ 0xCC2D, 0xCC2D, 0xCC2D }, +{ 0xCC2E, 0xCC2E, 0xCC2E }, +{ 0xCC2F, 0xCC2F, 0xCC2F }, +{ 0xCC30, 0xCC30, 0xCC30 }, +{ 0xCC31, 0xCC31, 0xCC31 }, +{ 0xCC32, 0xCC32, 0xCC32 }, +{ 0xCC33, 0xCC33, 0xCC33 }, +{ 0xCC34, 0xCC34, 0xCC34 }, +{ 0xCC35, 0xCC35, 0xCC35 }, +{ 0xCC36, 0xCC36, 0xCC36 }, +{ 0xCC37, 0xCC37, 0xCC37 }, +{ 0xCC38, 0xCC38, 0xCC38 }, +{ 0xCC39, 0xCC39, 0xCC39 }, +{ 0xCC3A, 0xCC3A, 0xCC3A }, +{ 0xCC3B, 0xCC3B, 0xCC3B }, +{ 0xCC3C, 0xCC3C, 0xCC3C }, +{ 0xCC3D, 0xCC3D, 0xCC3D }, +{ 0xCC3E, 0xCC3E, 0xCC3E }, +{ 0xCC3F, 0xCC3F, 0xCC3F }, +{ 0xCC40, 0xCC40, 0xCC40 }, +{ 0xCC41, 0xCC41, 0xCC41 }, +{ 0xCC42, 0xCC42, 0xCC42 }, +{ 0xCC43, 0xCC43, 0xCC43 }, +{ 0xCC44, 0xCC44, 0xCC44 }, +{ 0xCC45, 0xCC45, 0xCC45 }, +{ 0xCC46, 0xCC46, 0xCC46 }, +{ 0xCC47, 0xCC47, 0xCC47 }, +{ 0xCC48, 0xCC48, 0xCC48 }, +{ 0xCC49, 0xCC49, 0xCC49 }, +{ 0xCC4A, 0xCC4A, 0xCC4A }, +{ 0xCC4B, 0xCC4B, 0xCC4B }, +{ 0xCC4C, 0xCC4C, 0xCC4C }, +{ 0xCC4D, 0xCC4D, 0xCC4D }, +{ 0xCC4E, 0xCC4E, 0xCC4E }, +{ 0xCC4F, 0xCC4F, 0xCC4F }, +{ 0xCC50, 0xCC50, 0xCC50 }, +{ 0xCC51, 0xCC51, 0xCC51 }, +{ 0xCC52, 0xCC52, 0xCC52 }, +{ 0xCC53, 0xCC53, 0xCC53 }, +{ 0xCC54, 0xCC54, 0xCC54 }, +{ 0xCC55, 0xCC55, 0xCC55 }, +{ 0xCC56, 0xCC56, 0xCC56 }, +{ 0xCC57, 0xCC57, 0xCC57 }, +{ 0xCC58, 0xCC58, 0xCC58 }, +{ 0xCC59, 0xCC59, 0xCC59 }, +{ 0xCC5A, 0xCC5A, 0xCC5A }, +{ 0xCC5B, 0xCC5B, 0xCC5B }, +{ 0xCC5C, 0xCC5C, 0xCC5C }, +{ 0xCC5D, 0xCC5D, 0xCC5D }, +{ 0xCC5E, 0xCC5E, 0xCC5E }, +{ 0xCC5F, 0xCC5F, 0xCC5F }, +{ 0xCC60, 0xCC60, 0xCC60 }, +{ 0xCC61, 0xCC61, 0xCC61 }, +{ 0xCC62, 0xCC62, 0xCC62 }, +{ 0xCC63, 0xCC63, 0xCC63 }, +{ 0xCC64, 0xCC64, 0xCC64 }, +{ 0xCC65, 0xCC65, 0xCC65 }, +{ 0xCC66, 0xCC66, 0xCC66 }, +{ 0xCC67, 0xCC67, 0xCC67 }, +{ 0xCC68, 0xCC68, 0xCC68 }, +{ 0xCC69, 0xCC69, 0xCC69 }, +{ 0xCC6A, 0xCC6A, 0xCC6A }, +{ 0xCC6B, 0xCC6B, 0xCC6B }, +{ 0xCC6C, 0xCC6C, 0xCC6C }, +{ 0xCC6D, 0xCC6D, 0xCC6D }, +{ 0xCC6E, 0xCC6E, 0xCC6E }, +{ 0xCC6F, 0xCC6F, 0xCC6F }, +{ 0xCC70, 0xCC70, 0xCC70 }, +{ 0xCC71, 0xCC71, 0xCC71 }, +{ 0xCC72, 0xCC72, 0xCC72 }, +{ 0xCC73, 0xCC73, 0xCC73 }, +{ 0xCC74, 0xCC74, 0xCC74 }, +{ 0xCC75, 0xCC75, 0xCC75 }, +{ 0xCC76, 0xCC76, 0xCC76 }, +{ 0xCC77, 0xCC77, 0xCC77 }, +{ 0xCC78, 0xCC78, 0xCC78 }, +{ 0xCC79, 0xCC79, 0xCC79 }, +{ 0xCC7A, 0xCC7A, 0xCC7A }, +{ 0xCC7B, 0xCC7B, 0xCC7B }, +{ 0xCC7C, 0xCC7C, 0xCC7C }, +{ 0xCC7D, 0xCC7D, 0xCC7D }, +{ 0xCC7E, 0xCC7E, 0xCC7E }, +{ 0xCC7F, 0xCC7F, 0xCC7F }, +{ 0xCC80, 0xCC80, 0xCC80 }, +{ 0xCC81, 0xCC81, 0xCC81 }, +{ 0xCC82, 0xCC82, 0xCC82 }, +{ 0xCC83, 0xCC83, 0xCC83 }, +{ 0xCC84, 0xCC84, 0xCC84 }, +{ 0xCC85, 0xCC85, 0xCC85 }, +{ 0xCC86, 0xCC86, 0xCC86 }, +{ 0xCC87, 0xCC87, 0xCC87 }, +{ 0xCC88, 0xCC88, 0xCC88 }, +{ 0xCC89, 0xCC89, 0xCC89 }, +{ 0xCC8A, 0xCC8A, 0xCC8A }, +{ 0xCC8B, 0xCC8B, 0xCC8B }, +{ 0xCC8C, 0xCC8C, 0xCC8C }, +{ 0xCC8D, 0xCC8D, 0xCC8D }, +{ 0xCC8E, 0xCC8E, 0xCC8E }, +{ 0xCC8F, 0xCC8F, 0xCC8F }, +{ 0xCC90, 0xCC90, 0xCC90 }, +{ 0xCC91, 0xCC91, 0xCC91 }, +{ 0xCC92, 0xCC92, 0xCC92 }, +{ 0xCC93, 0xCC93, 0xCC93 }, +{ 0xCC94, 0xCC94, 0xCC94 }, +{ 0xCC95, 0xCC95, 0xCC95 }, +{ 0xCC96, 0xCC96, 0xCC96 }, +{ 0xCC97, 0xCC97, 0xCC97 }, +{ 0xCC98, 0xCC98, 0xCC98 }, +{ 0xCC99, 0xCC99, 0xCC99 }, +{ 0xCC9A, 0xCC9A, 0xCC9A }, +{ 0xCC9B, 0xCC9B, 0xCC9B }, +{ 0xCC9C, 0xCC9C, 0xCC9C }, +{ 0xCC9D, 0xCC9D, 0xCC9D }, +{ 0xCC9E, 0xCC9E, 0xCC9E }, +{ 0xCC9F, 0xCC9F, 0xCC9F }, +{ 0xCCA0, 0xCCA0, 0xCCA0 }, +{ 0xCCA1, 0xCCA1, 0xCCA1 }, +{ 0xCCA2, 0xCCA2, 0xCCA2 }, +{ 0xCCA3, 0xCCA3, 0xCCA3 }, +{ 0xCCA4, 0xCCA4, 0xCCA4 }, +{ 0xCCA5, 0xCCA5, 0xCCA5 }, +{ 0xCCA6, 0xCCA6, 0xCCA6 }, +{ 0xCCA7, 0xCCA7, 0xCCA7 }, +{ 0xCCA8, 0xCCA8, 0xCCA8 }, +{ 0xCCA9, 0xCCA9, 0xCCA9 }, +{ 0xCCAA, 0xCCAA, 0xCCAA }, +{ 0xCCAB, 0xCCAB, 0xCCAB }, +{ 0xCCAC, 0xCCAC, 0xCCAC }, +{ 0xCCAD, 0xCCAD, 0xCCAD }, +{ 0xCCAE, 0xCCAE, 0xCCAE }, +{ 0xCCAF, 0xCCAF, 0xCCAF }, +{ 0xCCB0, 0xCCB0, 0xCCB0 }, +{ 0xCCB1, 0xCCB1, 0xCCB1 }, +{ 0xCCB2, 0xCCB2, 0xCCB2 }, +{ 0xCCB3, 0xCCB3, 0xCCB3 }, +{ 0xCCB4, 0xCCB4, 0xCCB4 }, +{ 0xCCB5, 0xCCB5, 0xCCB5 }, +{ 0xCCB6, 0xCCB6, 0xCCB6 }, +{ 0xCCB7, 0xCCB7, 0xCCB7 }, +{ 0xCCB8, 0xCCB8, 0xCCB8 }, +{ 0xCCB9, 0xCCB9, 0xCCB9 }, +{ 0xCCBA, 0xCCBA, 0xCCBA }, +{ 0xCCBB, 0xCCBB, 0xCCBB }, +{ 0xCCBC, 0xCCBC, 0xCCBC }, +{ 0xCCBD, 0xCCBD, 0xCCBD }, +{ 0xCCBE, 0xCCBE, 0xCCBE }, +{ 0xCCBF, 0xCCBF, 0xCCBF }, +{ 0xCCC0, 0xCCC0, 0xCCC0 }, +{ 0xCCC1, 0xCCC1, 0xCCC1 }, +{ 0xCCC2, 0xCCC2, 0xCCC2 }, +{ 0xCCC3, 0xCCC3, 0xCCC3 }, +{ 0xCCC4, 0xCCC4, 0xCCC4 }, +{ 0xCCC5, 0xCCC5, 0xCCC5 }, +{ 0xCCC6, 0xCCC6, 0xCCC6 }, +{ 0xCCC7, 0xCCC7, 0xCCC7 }, +{ 0xCCC8, 0xCCC8, 0xCCC8 }, +{ 0xCCC9, 0xCCC9, 0xCCC9 }, +{ 0xCCCA, 0xCCCA, 0xCCCA }, +{ 0xCCCB, 0xCCCB, 0xCCCB }, +{ 0xCCCC, 0xCCCC, 0xCCCC }, +{ 0xCCCD, 0xCCCD, 0xCCCD }, +{ 0xCCCE, 0xCCCE, 0xCCCE }, +{ 0xCCCF, 0xCCCF, 0xCCCF }, +{ 0xCCD0, 0xCCD0, 0xCCD0 }, +{ 0xCCD1, 0xCCD1, 0xCCD1 }, +{ 0xCCD2, 0xCCD2, 0xCCD2 }, +{ 0xCCD3, 0xCCD3, 0xCCD3 }, +{ 0xCCD4, 0xCCD4, 0xCCD4 }, +{ 0xCCD5, 0xCCD5, 0xCCD5 }, +{ 0xCCD6, 0xCCD6, 0xCCD6 }, +{ 0xCCD7, 0xCCD7, 0xCCD7 }, +{ 0xCCD8, 0xCCD8, 0xCCD8 }, +{ 0xCCD9, 0xCCD9, 0xCCD9 }, +{ 0xCCDA, 0xCCDA, 0xCCDA }, +{ 0xCCDB, 0xCCDB, 0xCCDB }, +{ 0xCCDC, 0xCCDC, 0xCCDC }, +{ 0xCCDD, 0xCCDD, 0xCCDD }, +{ 0xCCDE, 0xCCDE, 0xCCDE }, +{ 0xCCDF, 0xCCDF, 0xCCDF }, +{ 0xCCE0, 0xCCE0, 0xCCE0 }, +{ 0xCCE1, 0xCCE1, 0xCCE1 }, +{ 0xCCE2, 0xCCE2, 0xCCE2 }, +{ 0xCCE3, 0xCCE3, 0xCCE3 }, +{ 0xCCE4, 0xCCE4, 0xCCE4 }, +{ 0xCCE5, 0xCCE5, 0xCCE5 }, +{ 0xCCE6, 0xCCE6, 0xCCE6 }, +{ 0xCCE7, 0xCCE7, 0xCCE7 }, +{ 0xCCE8, 0xCCE8, 0xCCE8 }, +{ 0xCCE9, 0xCCE9, 0xCCE9 }, +{ 0xCCEA, 0xCCEA, 0xCCEA }, +{ 0xCCEB, 0xCCEB, 0xCCEB }, +{ 0xCCEC, 0xCCEC, 0xCCEC }, +{ 0xCCED, 0xCCED, 0xCCED }, +{ 0xCCEE, 0xCCEE, 0xCCEE }, +{ 0xCCEF, 0xCCEF, 0xCCEF }, +{ 0xCCF0, 0xCCF0, 0xCCF0 }, +{ 0xCCF1, 0xCCF1, 0xCCF1 }, +{ 0xCCF2, 0xCCF2, 0xCCF2 }, +{ 0xCCF3, 0xCCF3, 0xCCF3 }, +{ 0xCCF4, 0xCCF4, 0xCCF4 }, +{ 0xCCF5, 0xCCF5, 0xCCF5 }, +{ 0xCCF6, 0xCCF6, 0xCCF6 }, +{ 0xCCF7, 0xCCF7, 0xCCF7 }, +{ 0xCCF8, 0xCCF8, 0xCCF8 }, +{ 0xCCF9, 0xCCF9, 0xCCF9 }, +{ 0xCCFA, 0xCCFA, 0xCCFA }, +{ 0xCCFB, 0xCCFB, 0xCCFB }, +{ 0xCCFC, 0xCCFC, 0xCCFC }, +{ 0xCCFD, 0xCCFD, 0xCCFD }, +{ 0xCCFE, 0xCCFE, 0xCCFE }, +{ 0xCCFF, 0xCCFF, 0xCCFF }, +{ 0xCD00, 0xCD00, 0xCD00 }, +{ 0xCD01, 0xCD01, 0xCD01 }, +{ 0xCD02, 0xCD02, 0xCD02 }, +{ 0xCD03, 0xCD03, 0xCD03 }, +{ 0xCD04, 0xCD04, 0xCD04 }, +{ 0xCD05, 0xCD05, 0xCD05 }, +{ 0xCD06, 0xCD06, 0xCD06 }, +{ 0xCD07, 0xCD07, 0xCD07 }, +{ 0xCD08, 0xCD08, 0xCD08 }, +{ 0xCD09, 0xCD09, 0xCD09 }, +{ 0xCD0A, 0xCD0A, 0xCD0A }, +{ 0xCD0B, 0xCD0B, 0xCD0B }, +{ 0xCD0C, 0xCD0C, 0xCD0C }, +{ 0xCD0D, 0xCD0D, 0xCD0D }, +{ 0xCD0E, 0xCD0E, 0xCD0E }, +{ 0xCD0F, 0xCD0F, 0xCD0F }, +{ 0xCD10, 0xCD10, 0xCD10 }, +{ 0xCD11, 0xCD11, 0xCD11 }, +{ 0xCD12, 0xCD12, 0xCD12 }, +{ 0xCD13, 0xCD13, 0xCD13 }, +{ 0xCD14, 0xCD14, 0xCD14 }, +{ 0xCD15, 0xCD15, 0xCD15 }, +{ 0xCD16, 0xCD16, 0xCD16 }, +{ 0xCD17, 0xCD17, 0xCD17 }, +{ 0xCD18, 0xCD18, 0xCD18 }, +{ 0xCD19, 0xCD19, 0xCD19 }, +{ 0xCD1A, 0xCD1A, 0xCD1A }, +{ 0xCD1B, 0xCD1B, 0xCD1B }, +{ 0xCD1C, 0xCD1C, 0xCD1C }, +{ 0xCD1D, 0xCD1D, 0xCD1D }, +{ 0xCD1E, 0xCD1E, 0xCD1E }, +{ 0xCD1F, 0xCD1F, 0xCD1F }, +{ 0xCD20, 0xCD20, 0xCD20 }, +{ 0xCD21, 0xCD21, 0xCD21 }, +{ 0xCD22, 0xCD22, 0xCD22 }, +{ 0xCD23, 0xCD23, 0xCD23 }, +{ 0xCD24, 0xCD24, 0xCD24 }, +{ 0xCD25, 0xCD25, 0xCD25 }, +{ 0xCD26, 0xCD26, 0xCD26 }, +{ 0xCD27, 0xCD27, 0xCD27 }, +{ 0xCD28, 0xCD28, 0xCD28 }, +{ 0xCD29, 0xCD29, 0xCD29 }, +{ 0xCD2A, 0xCD2A, 0xCD2A }, +{ 0xCD2B, 0xCD2B, 0xCD2B }, +{ 0xCD2C, 0xCD2C, 0xCD2C }, +{ 0xCD2D, 0xCD2D, 0xCD2D }, +{ 0xCD2E, 0xCD2E, 0xCD2E }, +{ 0xCD2F, 0xCD2F, 0xCD2F }, +{ 0xCD30, 0xCD30, 0xCD30 }, +{ 0xCD31, 0xCD31, 0xCD31 }, +{ 0xCD32, 0xCD32, 0xCD32 }, +{ 0xCD33, 0xCD33, 0xCD33 }, +{ 0xCD34, 0xCD34, 0xCD34 }, +{ 0xCD35, 0xCD35, 0xCD35 }, +{ 0xCD36, 0xCD36, 0xCD36 }, +{ 0xCD37, 0xCD37, 0xCD37 }, +{ 0xCD38, 0xCD38, 0xCD38 }, +{ 0xCD39, 0xCD39, 0xCD39 }, +{ 0xCD3A, 0xCD3A, 0xCD3A }, +{ 0xCD3B, 0xCD3B, 0xCD3B }, +{ 0xCD3C, 0xCD3C, 0xCD3C }, +{ 0xCD3D, 0xCD3D, 0xCD3D }, +{ 0xCD3E, 0xCD3E, 0xCD3E }, +{ 0xCD3F, 0xCD3F, 0xCD3F }, +{ 0xCD40, 0xCD40, 0xCD40 }, +{ 0xCD41, 0xCD41, 0xCD41 }, +{ 0xCD42, 0xCD42, 0xCD42 }, +{ 0xCD43, 0xCD43, 0xCD43 }, +{ 0xCD44, 0xCD44, 0xCD44 }, +{ 0xCD45, 0xCD45, 0xCD45 }, +{ 0xCD46, 0xCD46, 0xCD46 }, +{ 0xCD47, 0xCD47, 0xCD47 }, +{ 0xCD48, 0xCD48, 0xCD48 }, +{ 0xCD49, 0xCD49, 0xCD49 }, +{ 0xCD4A, 0xCD4A, 0xCD4A }, +{ 0xCD4B, 0xCD4B, 0xCD4B }, +{ 0xCD4C, 0xCD4C, 0xCD4C }, +{ 0xCD4D, 0xCD4D, 0xCD4D }, +{ 0xCD4E, 0xCD4E, 0xCD4E }, +{ 0xCD4F, 0xCD4F, 0xCD4F }, +{ 0xCD50, 0xCD50, 0xCD50 }, +{ 0xCD51, 0xCD51, 0xCD51 }, +{ 0xCD52, 0xCD52, 0xCD52 }, +{ 0xCD53, 0xCD53, 0xCD53 }, +{ 0xCD54, 0xCD54, 0xCD54 }, +{ 0xCD55, 0xCD55, 0xCD55 }, +{ 0xCD56, 0xCD56, 0xCD56 }, +{ 0xCD57, 0xCD57, 0xCD57 }, +{ 0xCD58, 0xCD58, 0xCD58 }, +{ 0xCD59, 0xCD59, 0xCD59 }, +{ 0xCD5A, 0xCD5A, 0xCD5A }, +{ 0xCD5B, 0xCD5B, 0xCD5B }, +{ 0xCD5C, 0xCD5C, 0xCD5C }, +{ 0xCD5D, 0xCD5D, 0xCD5D }, +{ 0xCD5E, 0xCD5E, 0xCD5E }, +{ 0xCD5F, 0xCD5F, 0xCD5F }, +{ 0xCD60, 0xCD60, 0xCD60 }, +{ 0xCD61, 0xCD61, 0xCD61 }, +{ 0xCD62, 0xCD62, 0xCD62 }, +{ 0xCD63, 0xCD63, 0xCD63 }, +{ 0xCD64, 0xCD64, 0xCD64 }, +{ 0xCD65, 0xCD65, 0xCD65 }, +{ 0xCD66, 0xCD66, 0xCD66 }, +{ 0xCD67, 0xCD67, 0xCD67 }, +{ 0xCD68, 0xCD68, 0xCD68 }, +{ 0xCD69, 0xCD69, 0xCD69 }, +{ 0xCD6A, 0xCD6A, 0xCD6A }, +{ 0xCD6B, 0xCD6B, 0xCD6B }, +{ 0xCD6C, 0xCD6C, 0xCD6C }, +{ 0xCD6D, 0xCD6D, 0xCD6D }, +{ 0xCD6E, 0xCD6E, 0xCD6E }, +{ 0xCD6F, 0xCD6F, 0xCD6F }, +{ 0xCD70, 0xCD70, 0xCD70 }, +{ 0xCD71, 0xCD71, 0xCD71 }, +{ 0xCD72, 0xCD72, 0xCD72 }, +{ 0xCD73, 0xCD73, 0xCD73 }, +{ 0xCD74, 0xCD74, 0xCD74 }, +{ 0xCD75, 0xCD75, 0xCD75 }, +{ 0xCD76, 0xCD76, 0xCD76 }, +{ 0xCD77, 0xCD77, 0xCD77 }, +{ 0xCD78, 0xCD78, 0xCD78 }, +{ 0xCD79, 0xCD79, 0xCD79 }, +{ 0xCD7A, 0xCD7A, 0xCD7A }, +{ 0xCD7B, 0xCD7B, 0xCD7B }, +{ 0xCD7C, 0xCD7C, 0xCD7C }, +{ 0xCD7D, 0xCD7D, 0xCD7D }, +{ 0xCD7E, 0xCD7E, 0xCD7E }, +{ 0xCD7F, 0xCD7F, 0xCD7F }, +{ 0xCD80, 0xCD80, 0xCD80 }, +{ 0xCD81, 0xCD81, 0xCD81 }, +{ 0xCD82, 0xCD82, 0xCD82 }, +{ 0xCD83, 0xCD83, 0xCD83 }, +{ 0xCD84, 0xCD84, 0xCD84 }, +{ 0xCD85, 0xCD85, 0xCD85 }, +{ 0xCD86, 0xCD86, 0xCD86 }, +{ 0xCD87, 0xCD87, 0xCD87 }, +{ 0xCD88, 0xCD88, 0xCD88 }, +{ 0xCD89, 0xCD89, 0xCD89 }, +{ 0xCD8A, 0xCD8A, 0xCD8A }, +{ 0xCD8B, 0xCD8B, 0xCD8B }, +{ 0xCD8C, 0xCD8C, 0xCD8C }, +{ 0xCD8D, 0xCD8D, 0xCD8D }, +{ 0xCD8E, 0xCD8E, 0xCD8E }, +{ 0xCD8F, 0xCD8F, 0xCD8F }, +{ 0xCD90, 0xCD90, 0xCD90 }, +{ 0xCD91, 0xCD91, 0xCD91 }, +{ 0xCD92, 0xCD92, 0xCD92 }, +{ 0xCD93, 0xCD93, 0xCD93 }, +{ 0xCD94, 0xCD94, 0xCD94 }, +{ 0xCD95, 0xCD95, 0xCD95 }, +{ 0xCD96, 0xCD96, 0xCD96 }, +{ 0xCD97, 0xCD97, 0xCD97 }, +{ 0xCD98, 0xCD98, 0xCD98 }, +{ 0xCD99, 0xCD99, 0xCD99 }, +{ 0xCD9A, 0xCD9A, 0xCD9A }, +{ 0xCD9B, 0xCD9B, 0xCD9B }, +{ 0xCD9C, 0xCD9C, 0xCD9C }, +{ 0xCD9D, 0xCD9D, 0xCD9D }, +{ 0xCD9E, 0xCD9E, 0xCD9E }, +{ 0xCD9F, 0xCD9F, 0xCD9F }, +{ 0xCDA0, 0xCDA0, 0xCDA0 }, +{ 0xCDA1, 0xCDA1, 0xCDA1 }, +{ 0xCDA2, 0xCDA2, 0xCDA2 }, +{ 0xCDA3, 0xCDA3, 0xCDA3 }, +{ 0xCDA4, 0xCDA4, 0xCDA4 }, +{ 0xCDA5, 0xCDA5, 0xCDA5 }, +{ 0xCDA6, 0xCDA6, 0xCDA6 }, +{ 0xCDA7, 0xCDA7, 0xCDA7 }, +{ 0xCDA8, 0xCDA8, 0xCDA8 }, +{ 0xCDA9, 0xCDA9, 0xCDA9 }, +{ 0xCDAA, 0xCDAA, 0xCDAA }, +{ 0xCDAB, 0xCDAB, 0xCDAB }, +{ 0xCDAC, 0xCDAC, 0xCDAC }, +{ 0xCDAD, 0xCDAD, 0xCDAD }, +{ 0xCDAE, 0xCDAE, 0xCDAE }, +{ 0xCDAF, 0xCDAF, 0xCDAF }, +{ 0xCDB0, 0xCDB0, 0xCDB0 }, +{ 0xCDB1, 0xCDB1, 0xCDB1 }, +{ 0xCDB2, 0xCDB2, 0xCDB2 }, +{ 0xCDB3, 0xCDB3, 0xCDB3 }, +{ 0xCDB4, 0xCDB4, 0xCDB4 }, +{ 0xCDB5, 0xCDB5, 0xCDB5 }, +{ 0xCDB6, 0xCDB6, 0xCDB6 }, +{ 0xCDB7, 0xCDB7, 0xCDB7 }, +{ 0xCDB8, 0xCDB8, 0xCDB8 }, +{ 0xCDB9, 0xCDB9, 0xCDB9 }, +{ 0xCDBA, 0xCDBA, 0xCDBA }, +{ 0xCDBB, 0xCDBB, 0xCDBB }, +{ 0xCDBC, 0xCDBC, 0xCDBC }, +{ 0xCDBD, 0xCDBD, 0xCDBD }, +{ 0xCDBE, 0xCDBE, 0xCDBE }, +{ 0xCDBF, 0xCDBF, 0xCDBF }, +{ 0xCDC0, 0xCDC0, 0xCDC0 }, +{ 0xCDC1, 0xCDC1, 0xCDC1 }, +{ 0xCDC2, 0xCDC2, 0xCDC2 }, +{ 0xCDC3, 0xCDC3, 0xCDC3 }, +{ 0xCDC4, 0xCDC4, 0xCDC4 }, +{ 0xCDC5, 0xCDC5, 0xCDC5 }, +{ 0xCDC6, 0xCDC6, 0xCDC6 }, +{ 0xCDC7, 0xCDC7, 0xCDC7 }, +{ 0xCDC8, 0xCDC8, 0xCDC8 }, +{ 0xCDC9, 0xCDC9, 0xCDC9 }, +{ 0xCDCA, 0xCDCA, 0xCDCA }, +{ 0xCDCB, 0xCDCB, 0xCDCB }, +{ 0xCDCC, 0xCDCC, 0xCDCC }, +{ 0xCDCD, 0xCDCD, 0xCDCD }, +{ 0xCDCE, 0xCDCE, 0xCDCE }, +{ 0xCDCF, 0xCDCF, 0xCDCF }, +{ 0xCDD0, 0xCDD0, 0xCDD0 }, +{ 0xCDD1, 0xCDD1, 0xCDD1 }, +{ 0xCDD2, 0xCDD2, 0xCDD2 }, +{ 0xCDD3, 0xCDD3, 0xCDD3 }, +{ 0xCDD4, 0xCDD4, 0xCDD4 }, +{ 0xCDD5, 0xCDD5, 0xCDD5 }, +{ 0xCDD6, 0xCDD6, 0xCDD6 }, +{ 0xCDD7, 0xCDD7, 0xCDD7 }, +{ 0xCDD8, 0xCDD8, 0xCDD8 }, +{ 0xCDD9, 0xCDD9, 0xCDD9 }, +{ 0xCDDA, 0xCDDA, 0xCDDA }, +{ 0xCDDB, 0xCDDB, 0xCDDB }, +{ 0xCDDC, 0xCDDC, 0xCDDC }, +{ 0xCDDD, 0xCDDD, 0xCDDD }, +{ 0xCDDE, 0xCDDE, 0xCDDE }, +{ 0xCDDF, 0xCDDF, 0xCDDF }, +{ 0xCDE0, 0xCDE0, 0xCDE0 }, +{ 0xCDE1, 0xCDE1, 0xCDE1 }, +{ 0xCDE2, 0xCDE2, 0xCDE2 }, +{ 0xCDE3, 0xCDE3, 0xCDE3 }, +{ 0xCDE4, 0xCDE4, 0xCDE4 }, +{ 0xCDE5, 0xCDE5, 0xCDE5 }, +{ 0xCDE6, 0xCDE6, 0xCDE6 }, +{ 0xCDE7, 0xCDE7, 0xCDE7 }, +{ 0xCDE8, 0xCDE8, 0xCDE8 }, +{ 0xCDE9, 0xCDE9, 0xCDE9 }, +{ 0xCDEA, 0xCDEA, 0xCDEA }, +{ 0xCDEB, 0xCDEB, 0xCDEB }, +{ 0xCDEC, 0xCDEC, 0xCDEC }, +{ 0xCDED, 0xCDED, 0xCDED }, +{ 0xCDEE, 0xCDEE, 0xCDEE }, +{ 0xCDEF, 0xCDEF, 0xCDEF }, +{ 0xCDF0, 0xCDF0, 0xCDF0 }, +{ 0xCDF1, 0xCDF1, 0xCDF1 }, +{ 0xCDF2, 0xCDF2, 0xCDF2 }, +{ 0xCDF3, 0xCDF3, 0xCDF3 }, +{ 0xCDF4, 0xCDF4, 0xCDF4 }, +{ 0xCDF5, 0xCDF5, 0xCDF5 }, +{ 0xCDF6, 0xCDF6, 0xCDF6 }, +{ 0xCDF7, 0xCDF7, 0xCDF7 }, +{ 0xCDF8, 0xCDF8, 0xCDF8 }, +{ 0xCDF9, 0xCDF9, 0xCDF9 }, +{ 0xCDFA, 0xCDFA, 0xCDFA }, +{ 0xCDFB, 0xCDFB, 0xCDFB }, +{ 0xCDFC, 0xCDFC, 0xCDFC }, +{ 0xCDFD, 0xCDFD, 0xCDFD }, +{ 0xCDFE, 0xCDFE, 0xCDFE }, +{ 0xCDFF, 0xCDFF, 0xCDFF }, +{ 0xCE00, 0xCE00, 0xCE00 }, +{ 0xCE01, 0xCE01, 0xCE01 }, +{ 0xCE02, 0xCE02, 0xCE02 }, +{ 0xCE03, 0xCE03, 0xCE03 }, +{ 0xCE04, 0xCE04, 0xCE04 }, +{ 0xCE05, 0xCE05, 0xCE05 }, +{ 0xCE06, 0xCE06, 0xCE06 }, +{ 0xCE07, 0xCE07, 0xCE07 }, +{ 0xCE08, 0xCE08, 0xCE08 }, +{ 0xCE09, 0xCE09, 0xCE09 }, +{ 0xCE0A, 0xCE0A, 0xCE0A }, +{ 0xCE0B, 0xCE0B, 0xCE0B }, +{ 0xCE0C, 0xCE0C, 0xCE0C }, +{ 0xCE0D, 0xCE0D, 0xCE0D }, +{ 0xCE0E, 0xCE0E, 0xCE0E }, +{ 0xCE0F, 0xCE0F, 0xCE0F }, +{ 0xCE10, 0xCE10, 0xCE10 }, +{ 0xCE11, 0xCE11, 0xCE11 }, +{ 0xCE12, 0xCE12, 0xCE12 }, +{ 0xCE13, 0xCE13, 0xCE13 }, +{ 0xCE14, 0xCE14, 0xCE14 }, +{ 0xCE15, 0xCE15, 0xCE15 }, +{ 0xCE16, 0xCE16, 0xCE16 }, +{ 0xCE17, 0xCE17, 0xCE17 }, +{ 0xCE18, 0xCE18, 0xCE18 }, +{ 0xCE19, 0xCE19, 0xCE19 }, +{ 0xCE1A, 0xCE1A, 0xCE1A }, +{ 0xCE1B, 0xCE1B, 0xCE1B }, +{ 0xCE1C, 0xCE1C, 0xCE1C }, +{ 0xCE1D, 0xCE1D, 0xCE1D }, +{ 0xCE1E, 0xCE1E, 0xCE1E }, +{ 0xCE1F, 0xCE1F, 0xCE1F }, +{ 0xCE20, 0xCE20, 0xCE20 }, +{ 0xCE21, 0xCE21, 0xCE21 }, +{ 0xCE22, 0xCE22, 0xCE22 }, +{ 0xCE23, 0xCE23, 0xCE23 }, +{ 0xCE24, 0xCE24, 0xCE24 }, +{ 0xCE25, 0xCE25, 0xCE25 }, +{ 0xCE26, 0xCE26, 0xCE26 }, +{ 0xCE27, 0xCE27, 0xCE27 }, +{ 0xCE28, 0xCE28, 0xCE28 }, +{ 0xCE29, 0xCE29, 0xCE29 }, +{ 0xCE2A, 0xCE2A, 0xCE2A }, +{ 0xCE2B, 0xCE2B, 0xCE2B }, +{ 0xCE2C, 0xCE2C, 0xCE2C }, +{ 0xCE2D, 0xCE2D, 0xCE2D }, +{ 0xCE2E, 0xCE2E, 0xCE2E }, +{ 0xCE2F, 0xCE2F, 0xCE2F }, +{ 0xCE30, 0xCE30, 0xCE30 }, +{ 0xCE31, 0xCE31, 0xCE31 }, +{ 0xCE32, 0xCE32, 0xCE32 }, +{ 0xCE33, 0xCE33, 0xCE33 }, +{ 0xCE34, 0xCE34, 0xCE34 }, +{ 0xCE35, 0xCE35, 0xCE35 }, +{ 0xCE36, 0xCE36, 0xCE36 }, +{ 0xCE37, 0xCE37, 0xCE37 }, +{ 0xCE38, 0xCE38, 0xCE38 }, +{ 0xCE39, 0xCE39, 0xCE39 }, +{ 0xCE3A, 0xCE3A, 0xCE3A }, +{ 0xCE3B, 0xCE3B, 0xCE3B }, +{ 0xCE3C, 0xCE3C, 0xCE3C }, +{ 0xCE3D, 0xCE3D, 0xCE3D }, +{ 0xCE3E, 0xCE3E, 0xCE3E }, +{ 0xCE3F, 0xCE3F, 0xCE3F }, +{ 0xCE40, 0xCE40, 0xCE40 }, +{ 0xCE41, 0xCE41, 0xCE41 }, +{ 0xCE42, 0xCE42, 0xCE42 }, +{ 0xCE43, 0xCE43, 0xCE43 }, +{ 0xCE44, 0xCE44, 0xCE44 }, +{ 0xCE45, 0xCE45, 0xCE45 }, +{ 0xCE46, 0xCE46, 0xCE46 }, +{ 0xCE47, 0xCE47, 0xCE47 }, +{ 0xCE48, 0xCE48, 0xCE48 }, +{ 0xCE49, 0xCE49, 0xCE49 }, +{ 0xCE4A, 0xCE4A, 0xCE4A }, +{ 0xCE4B, 0xCE4B, 0xCE4B }, +{ 0xCE4C, 0xCE4C, 0xCE4C }, +{ 0xCE4D, 0xCE4D, 0xCE4D }, +{ 0xCE4E, 0xCE4E, 0xCE4E }, +{ 0xCE4F, 0xCE4F, 0xCE4F }, +{ 0xCE50, 0xCE50, 0xCE50 }, +{ 0xCE51, 0xCE51, 0xCE51 }, +{ 0xCE52, 0xCE52, 0xCE52 }, +{ 0xCE53, 0xCE53, 0xCE53 }, +{ 0xCE54, 0xCE54, 0xCE54 }, +{ 0xCE55, 0xCE55, 0xCE55 }, +{ 0xCE56, 0xCE56, 0xCE56 }, +{ 0xCE57, 0xCE57, 0xCE57 }, +{ 0xCE58, 0xCE58, 0xCE58 }, +{ 0xCE59, 0xCE59, 0xCE59 }, +{ 0xCE5A, 0xCE5A, 0xCE5A }, +{ 0xCE5B, 0xCE5B, 0xCE5B }, +{ 0xCE5C, 0xCE5C, 0xCE5C }, +{ 0xCE5D, 0xCE5D, 0xCE5D }, +{ 0xCE5E, 0xCE5E, 0xCE5E }, +{ 0xCE5F, 0xCE5F, 0xCE5F }, +{ 0xCE60, 0xCE60, 0xCE60 }, +{ 0xCE61, 0xCE61, 0xCE61 }, +{ 0xCE62, 0xCE62, 0xCE62 }, +{ 0xCE63, 0xCE63, 0xCE63 }, +{ 0xCE64, 0xCE64, 0xCE64 }, +{ 0xCE65, 0xCE65, 0xCE65 }, +{ 0xCE66, 0xCE66, 0xCE66 }, +{ 0xCE67, 0xCE67, 0xCE67 }, +{ 0xCE68, 0xCE68, 0xCE68 }, +{ 0xCE69, 0xCE69, 0xCE69 }, +{ 0xCE6A, 0xCE6A, 0xCE6A }, +{ 0xCE6B, 0xCE6B, 0xCE6B }, +{ 0xCE6C, 0xCE6C, 0xCE6C }, +{ 0xCE6D, 0xCE6D, 0xCE6D }, +{ 0xCE6E, 0xCE6E, 0xCE6E }, +{ 0xCE6F, 0xCE6F, 0xCE6F }, +{ 0xCE70, 0xCE70, 0xCE70 }, +{ 0xCE71, 0xCE71, 0xCE71 }, +{ 0xCE72, 0xCE72, 0xCE72 }, +{ 0xCE73, 0xCE73, 0xCE73 }, +{ 0xCE74, 0xCE74, 0xCE74 }, +{ 0xCE75, 0xCE75, 0xCE75 }, +{ 0xCE76, 0xCE76, 0xCE76 }, +{ 0xCE77, 0xCE77, 0xCE77 }, +{ 0xCE78, 0xCE78, 0xCE78 }, +{ 0xCE79, 0xCE79, 0xCE79 }, +{ 0xCE7A, 0xCE7A, 0xCE7A }, +{ 0xCE7B, 0xCE7B, 0xCE7B }, +{ 0xCE7C, 0xCE7C, 0xCE7C }, +{ 0xCE7D, 0xCE7D, 0xCE7D }, +{ 0xCE7E, 0xCE7E, 0xCE7E }, +{ 0xCE7F, 0xCE7F, 0xCE7F }, +{ 0xCE80, 0xCE80, 0xCE80 }, +{ 0xCE81, 0xCE81, 0xCE81 }, +{ 0xCE82, 0xCE82, 0xCE82 }, +{ 0xCE83, 0xCE83, 0xCE83 }, +{ 0xCE84, 0xCE84, 0xCE84 }, +{ 0xCE85, 0xCE85, 0xCE85 }, +{ 0xCE86, 0xCE86, 0xCE86 }, +{ 0xCE87, 0xCE87, 0xCE87 }, +{ 0xCE88, 0xCE88, 0xCE88 }, +{ 0xCE89, 0xCE89, 0xCE89 }, +{ 0xCE8A, 0xCE8A, 0xCE8A }, +{ 0xCE8B, 0xCE8B, 0xCE8B }, +{ 0xCE8C, 0xCE8C, 0xCE8C }, +{ 0xCE8D, 0xCE8D, 0xCE8D }, +{ 0xCE8E, 0xCE8E, 0xCE8E }, +{ 0xCE8F, 0xCE8F, 0xCE8F }, +{ 0xCE90, 0xCE90, 0xCE90 }, +{ 0xCE91, 0xCE91, 0xCE91 }, +{ 0xCE92, 0xCE92, 0xCE92 }, +{ 0xCE93, 0xCE93, 0xCE93 }, +{ 0xCE94, 0xCE94, 0xCE94 }, +{ 0xCE95, 0xCE95, 0xCE95 }, +{ 0xCE96, 0xCE96, 0xCE96 }, +{ 0xCE97, 0xCE97, 0xCE97 }, +{ 0xCE98, 0xCE98, 0xCE98 }, +{ 0xCE99, 0xCE99, 0xCE99 }, +{ 0xCE9A, 0xCE9A, 0xCE9A }, +{ 0xCE9B, 0xCE9B, 0xCE9B }, +{ 0xCE9C, 0xCE9C, 0xCE9C }, +{ 0xCE9D, 0xCE9D, 0xCE9D }, +{ 0xCE9E, 0xCE9E, 0xCE9E }, +{ 0xCE9F, 0xCE9F, 0xCE9F }, +{ 0xCEA0, 0xCEA0, 0xCEA0 }, +{ 0xCEA1, 0xCEA1, 0xCEA1 }, +{ 0xCEA2, 0xCEA2, 0xCEA2 }, +{ 0xCEA3, 0xCEA3, 0xCEA3 }, +{ 0xCEA4, 0xCEA4, 0xCEA4 }, +{ 0xCEA5, 0xCEA5, 0xCEA5 }, +{ 0xCEA6, 0xCEA6, 0xCEA6 }, +{ 0xCEA7, 0xCEA7, 0xCEA7 }, +{ 0xCEA8, 0xCEA8, 0xCEA8 }, +{ 0xCEA9, 0xCEA9, 0xCEA9 }, +{ 0xCEAA, 0xCEAA, 0xCEAA }, +{ 0xCEAB, 0xCEAB, 0xCEAB }, +{ 0xCEAC, 0xCEAC, 0xCEAC }, +{ 0xCEAD, 0xCEAD, 0xCEAD }, +{ 0xCEAE, 0xCEAE, 0xCEAE }, +{ 0xCEAF, 0xCEAF, 0xCEAF }, +{ 0xCEB0, 0xCEB0, 0xCEB0 }, +{ 0xCEB1, 0xCEB1, 0xCEB1 }, +{ 0xCEB2, 0xCEB2, 0xCEB2 }, +{ 0xCEB3, 0xCEB3, 0xCEB3 }, +{ 0xCEB4, 0xCEB4, 0xCEB4 }, +{ 0xCEB5, 0xCEB5, 0xCEB5 }, +{ 0xCEB6, 0xCEB6, 0xCEB6 }, +{ 0xCEB7, 0xCEB7, 0xCEB7 }, +{ 0xCEB8, 0xCEB8, 0xCEB8 }, +{ 0xCEB9, 0xCEB9, 0xCEB9 }, +{ 0xCEBA, 0xCEBA, 0xCEBA }, +{ 0xCEBB, 0xCEBB, 0xCEBB }, +{ 0xCEBC, 0xCEBC, 0xCEBC }, +{ 0xCEBD, 0xCEBD, 0xCEBD }, +{ 0xCEBE, 0xCEBE, 0xCEBE }, +{ 0xCEBF, 0xCEBF, 0xCEBF }, +{ 0xCEC0, 0xCEC0, 0xCEC0 }, +{ 0xCEC1, 0xCEC1, 0xCEC1 }, +{ 0xCEC2, 0xCEC2, 0xCEC2 }, +{ 0xCEC3, 0xCEC3, 0xCEC3 }, +{ 0xCEC4, 0xCEC4, 0xCEC4 }, +{ 0xCEC5, 0xCEC5, 0xCEC5 }, +{ 0xCEC6, 0xCEC6, 0xCEC6 }, +{ 0xCEC7, 0xCEC7, 0xCEC7 }, +{ 0xCEC8, 0xCEC8, 0xCEC8 }, +{ 0xCEC9, 0xCEC9, 0xCEC9 }, +{ 0xCECA, 0xCECA, 0xCECA }, +{ 0xCECB, 0xCECB, 0xCECB }, +{ 0xCECC, 0xCECC, 0xCECC }, +{ 0xCECD, 0xCECD, 0xCECD }, +{ 0xCECE, 0xCECE, 0xCECE }, +{ 0xCECF, 0xCECF, 0xCECF }, +{ 0xCED0, 0xCED0, 0xCED0 }, +{ 0xCED1, 0xCED1, 0xCED1 }, +{ 0xCED2, 0xCED2, 0xCED2 }, +{ 0xCED3, 0xCED3, 0xCED3 }, +{ 0xCED4, 0xCED4, 0xCED4 }, +{ 0xCED5, 0xCED5, 0xCED5 }, +{ 0xCED6, 0xCED6, 0xCED6 }, +{ 0xCED7, 0xCED7, 0xCED7 }, +{ 0xCED8, 0xCED8, 0xCED8 }, +{ 0xCED9, 0xCED9, 0xCED9 }, +{ 0xCEDA, 0xCEDA, 0xCEDA }, +{ 0xCEDB, 0xCEDB, 0xCEDB }, +{ 0xCEDC, 0xCEDC, 0xCEDC }, +{ 0xCEDD, 0xCEDD, 0xCEDD }, +{ 0xCEDE, 0xCEDE, 0xCEDE }, +{ 0xCEDF, 0xCEDF, 0xCEDF }, +{ 0xCEE0, 0xCEE0, 0xCEE0 }, +{ 0xCEE1, 0xCEE1, 0xCEE1 }, +{ 0xCEE2, 0xCEE2, 0xCEE2 }, +{ 0xCEE3, 0xCEE3, 0xCEE3 }, +{ 0xCEE4, 0xCEE4, 0xCEE4 }, +{ 0xCEE5, 0xCEE5, 0xCEE5 }, +{ 0xCEE6, 0xCEE6, 0xCEE6 }, +{ 0xCEE7, 0xCEE7, 0xCEE7 }, +{ 0xCEE8, 0xCEE8, 0xCEE8 }, +{ 0xCEE9, 0xCEE9, 0xCEE9 }, +{ 0xCEEA, 0xCEEA, 0xCEEA }, +{ 0xCEEB, 0xCEEB, 0xCEEB }, +{ 0xCEEC, 0xCEEC, 0xCEEC }, +{ 0xCEED, 0xCEED, 0xCEED }, +{ 0xCEEE, 0xCEEE, 0xCEEE }, +{ 0xCEEF, 0xCEEF, 0xCEEF }, +{ 0xCEF0, 0xCEF0, 0xCEF0 }, +{ 0xCEF1, 0xCEF1, 0xCEF1 }, +{ 0xCEF2, 0xCEF2, 0xCEF2 }, +{ 0xCEF3, 0xCEF3, 0xCEF3 }, +{ 0xCEF4, 0xCEF4, 0xCEF4 }, +{ 0xCEF5, 0xCEF5, 0xCEF5 }, +{ 0xCEF6, 0xCEF6, 0xCEF6 }, +{ 0xCEF7, 0xCEF7, 0xCEF7 }, +{ 0xCEF8, 0xCEF8, 0xCEF8 }, +{ 0xCEF9, 0xCEF9, 0xCEF9 }, +{ 0xCEFA, 0xCEFA, 0xCEFA }, +{ 0xCEFB, 0xCEFB, 0xCEFB }, +{ 0xCEFC, 0xCEFC, 0xCEFC }, +{ 0xCEFD, 0xCEFD, 0xCEFD }, +{ 0xCEFE, 0xCEFE, 0xCEFE }, +{ 0xCEFF, 0xCEFF, 0xCEFF }, +{ 0xCF00, 0xCF00, 0xCF00 }, +{ 0xCF01, 0xCF01, 0xCF01 }, +{ 0xCF02, 0xCF02, 0xCF02 }, +{ 0xCF03, 0xCF03, 0xCF03 }, +{ 0xCF04, 0xCF04, 0xCF04 }, +{ 0xCF05, 0xCF05, 0xCF05 }, +{ 0xCF06, 0xCF06, 0xCF06 }, +{ 0xCF07, 0xCF07, 0xCF07 }, +{ 0xCF08, 0xCF08, 0xCF08 }, +{ 0xCF09, 0xCF09, 0xCF09 }, +{ 0xCF0A, 0xCF0A, 0xCF0A }, +{ 0xCF0B, 0xCF0B, 0xCF0B }, +{ 0xCF0C, 0xCF0C, 0xCF0C }, +{ 0xCF0D, 0xCF0D, 0xCF0D }, +{ 0xCF0E, 0xCF0E, 0xCF0E }, +{ 0xCF0F, 0xCF0F, 0xCF0F }, +{ 0xCF10, 0xCF10, 0xCF10 }, +{ 0xCF11, 0xCF11, 0xCF11 }, +{ 0xCF12, 0xCF12, 0xCF12 }, +{ 0xCF13, 0xCF13, 0xCF13 }, +{ 0xCF14, 0xCF14, 0xCF14 }, +{ 0xCF15, 0xCF15, 0xCF15 }, +{ 0xCF16, 0xCF16, 0xCF16 }, +{ 0xCF17, 0xCF17, 0xCF17 }, +{ 0xCF18, 0xCF18, 0xCF18 }, +{ 0xCF19, 0xCF19, 0xCF19 }, +{ 0xCF1A, 0xCF1A, 0xCF1A }, +{ 0xCF1B, 0xCF1B, 0xCF1B }, +{ 0xCF1C, 0xCF1C, 0xCF1C }, +{ 0xCF1D, 0xCF1D, 0xCF1D }, +{ 0xCF1E, 0xCF1E, 0xCF1E }, +{ 0xCF1F, 0xCF1F, 0xCF1F }, +{ 0xCF20, 0xCF20, 0xCF20 }, +{ 0xCF21, 0xCF21, 0xCF21 }, +{ 0xCF22, 0xCF22, 0xCF22 }, +{ 0xCF23, 0xCF23, 0xCF23 }, +{ 0xCF24, 0xCF24, 0xCF24 }, +{ 0xCF25, 0xCF25, 0xCF25 }, +{ 0xCF26, 0xCF26, 0xCF26 }, +{ 0xCF27, 0xCF27, 0xCF27 }, +{ 0xCF28, 0xCF28, 0xCF28 }, +{ 0xCF29, 0xCF29, 0xCF29 }, +{ 0xCF2A, 0xCF2A, 0xCF2A }, +{ 0xCF2B, 0xCF2B, 0xCF2B }, +{ 0xCF2C, 0xCF2C, 0xCF2C }, +{ 0xCF2D, 0xCF2D, 0xCF2D }, +{ 0xCF2E, 0xCF2E, 0xCF2E }, +{ 0xCF2F, 0xCF2F, 0xCF2F }, +{ 0xCF30, 0xCF30, 0xCF30 }, +{ 0xCF31, 0xCF31, 0xCF31 }, +{ 0xCF32, 0xCF32, 0xCF32 }, +{ 0xCF33, 0xCF33, 0xCF33 }, +{ 0xCF34, 0xCF34, 0xCF34 }, +{ 0xCF35, 0xCF35, 0xCF35 }, +{ 0xCF36, 0xCF36, 0xCF36 }, +{ 0xCF37, 0xCF37, 0xCF37 }, +{ 0xCF38, 0xCF38, 0xCF38 }, +{ 0xCF39, 0xCF39, 0xCF39 }, +{ 0xCF3A, 0xCF3A, 0xCF3A }, +{ 0xCF3B, 0xCF3B, 0xCF3B }, +{ 0xCF3C, 0xCF3C, 0xCF3C }, +{ 0xCF3D, 0xCF3D, 0xCF3D }, +{ 0xCF3E, 0xCF3E, 0xCF3E }, +{ 0xCF3F, 0xCF3F, 0xCF3F }, +{ 0xCF40, 0xCF40, 0xCF40 }, +{ 0xCF41, 0xCF41, 0xCF41 }, +{ 0xCF42, 0xCF42, 0xCF42 }, +{ 0xCF43, 0xCF43, 0xCF43 }, +{ 0xCF44, 0xCF44, 0xCF44 }, +{ 0xCF45, 0xCF45, 0xCF45 }, +{ 0xCF46, 0xCF46, 0xCF46 }, +{ 0xCF47, 0xCF47, 0xCF47 }, +{ 0xCF48, 0xCF48, 0xCF48 }, +{ 0xCF49, 0xCF49, 0xCF49 }, +{ 0xCF4A, 0xCF4A, 0xCF4A }, +{ 0xCF4B, 0xCF4B, 0xCF4B }, +{ 0xCF4C, 0xCF4C, 0xCF4C }, +{ 0xCF4D, 0xCF4D, 0xCF4D }, +{ 0xCF4E, 0xCF4E, 0xCF4E }, +{ 0xCF4F, 0xCF4F, 0xCF4F }, +{ 0xCF50, 0xCF50, 0xCF50 }, +{ 0xCF51, 0xCF51, 0xCF51 }, +{ 0xCF52, 0xCF52, 0xCF52 }, +{ 0xCF53, 0xCF53, 0xCF53 }, +{ 0xCF54, 0xCF54, 0xCF54 }, +{ 0xCF55, 0xCF55, 0xCF55 }, +{ 0xCF56, 0xCF56, 0xCF56 }, +{ 0xCF57, 0xCF57, 0xCF57 }, +{ 0xCF58, 0xCF58, 0xCF58 }, +{ 0xCF59, 0xCF59, 0xCF59 }, +{ 0xCF5A, 0xCF5A, 0xCF5A }, +{ 0xCF5B, 0xCF5B, 0xCF5B }, +{ 0xCF5C, 0xCF5C, 0xCF5C }, +{ 0xCF5D, 0xCF5D, 0xCF5D }, +{ 0xCF5E, 0xCF5E, 0xCF5E }, +{ 0xCF5F, 0xCF5F, 0xCF5F }, +{ 0xCF60, 0xCF60, 0xCF60 }, +{ 0xCF61, 0xCF61, 0xCF61 }, +{ 0xCF62, 0xCF62, 0xCF62 }, +{ 0xCF63, 0xCF63, 0xCF63 }, +{ 0xCF64, 0xCF64, 0xCF64 }, +{ 0xCF65, 0xCF65, 0xCF65 }, +{ 0xCF66, 0xCF66, 0xCF66 }, +{ 0xCF67, 0xCF67, 0xCF67 }, +{ 0xCF68, 0xCF68, 0xCF68 }, +{ 0xCF69, 0xCF69, 0xCF69 }, +{ 0xCF6A, 0xCF6A, 0xCF6A }, +{ 0xCF6B, 0xCF6B, 0xCF6B }, +{ 0xCF6C, 0xCF6C, 0xCF6C }, +{ 0xCF6D, 0xCF6D, 0xCF6D }, +{ 0xCF6E, 0xCF6E, 0xCF6E }, +{ 0xCF6F, 0xCF6F, 0xCF6F }, +{ 0xCF70, 0xCF70, 0xCF70 }, +{ 0xCF71, 0xCF71, 0xCF71 }, +{ 0xCF72, 0xCF72, 0xCF72 }, +{ 0xCF73, 0xCF73, 0xCF73 }, +{ 0xCF74, 0xCF74, 0xCF74 }, +{ 0xCF75, 0xCF75, 0xCF75 }, +{ 0xCF76, 0xCF76, 0xCF76 }, +{ 0xCF77, 0xCF77, 0xCF77 }, +{ 0xCF78, 0xCF78, 0xCF78 }, +{ 0xCF79, 0xCF79, 0xCF79 }, +{ 0xCF7A, 0xCF7A, 0xCF7A }, +{ 0xCF7B, 0xCF7B, 0xCF7B }, +{ 0xCF7C, 0xCF7C, 0xCF7C }, +{ 0xCF7D, 0xCF7D, 0xCF7D }, +{ 0xCF7E, 0xCF7E, 0xCF7E }, +{ 0xCF7F, 0xCF7F, 0xCF7F }, +{ 0xCF80, 0xCF80, 0xCF80 }, +{ 0xCF81, 0xCF81, 0xCF81 }, +{ 0xCF82, 0xCF82, 0xCF82 }, +{ 0xCF83, 0xCF83, 0xCF83 }, +{ 0xCF84, 0xCF84, 0xCF84 }, +{ 0xCF85, 0xCF85, 0xCF85 }, +{ 0xCF86, 0xCF86, 0xCF86 }, +{ 0xCF87, 0xCF87, 0xCF87 }, +{ 0xCF88, 0xCF88, 0xCF88 }, +{ 0xCF89, 0xCF89, 0xCF89 }, +{ 0xCF8A, 0xCF8A, 0xCF8A }, +{ 0xCF8B, 0xCF8B, 0xCF8B }, +{ 0xCF8C, 0xCF8C, 0xCF8C }, +{ 0xCF8D, 0xCF8D, 0xCF8D }, +{ 0xCF8E, 0xCF8E, 0xCF8E }, +{ 0xCF8F, 0xCF8F, 0xCF8F }, +{ 0xCF90, 0xCF90, 0xCF90 }, +{ 0xCF91, 0xCF91, 0xCF91 }, +{ 0xCF92, 0xCF92, 0xCF92 }, +{ 0xCF93, 0xCF93, 0xCF93 }, +{ 0xCF94, 0xCF94, 0xCF94 }, +{ 0xCF95, 0xCF95, 0xCF95 }, +{ 0xCF96, 0xCF96, 0xCF96 }, +{ 0xCF97, 0xCF97, 0xCF97 }, +{ 0xCF98, 0xCF98, 0xCF98 }, +{ 0xCF99, 0xCF99, 0xCF99 }, +{ 0xCF9A, 0xCF9A, 0xCF9A }, +{ 0xCF9B, 0xCF9B, 0xCF9B }, +{ 0xCF9C, 0xCF9C, 0xCF9C }, +{ 0xCF9D, 0xCF9D, 0xCF9D }, +{ 0xCF9E, 0xCF9E, 0xCF9E }, +{ 0xCF9F, 0xCF9F, 0xCF9F }, +{ 0xCFA0, 0xCFA0, 0xCFA0 }, +{ 0xCFA1, 0xCFA1, 0xCFA1 }, +{ 0xCFA2, 0xCFA2, 0xCFA2 }, +{ 0xCFA3, 0xCFA3, 0xCFA3 }, +{ 0xCFA4, 0xCFA4, 0xCFA4 }, +{ 0xCFA5, 0xCFA5, 0xCFA5 }, +{ 0xCFA6, 0xCFA6, 0xCFA6 }, +{ 0xCFA7, 0xCFA7, 0xCFA7 }, +{ 0xCFA8, 0xCFA8, 0xCFA8 }, +{ 0xCFA9, 0xCFA9, 0xCFA9 }, +{ 0xCFAA, 0xCFAA, 0xCFAA }, +{ 0xCFAB, 0xCFAB, 0xCFAB }, +{ 0xCFAC, 0xCFAC, 0xCFAC }, +{ 0xCFAD, 0xCFAD, 0xCFAD }, +{ 0xCFAE, 0xCFAE, 0xCFAE }, +{ 0xCFAF, 0xCFAF, 0xCFAF }, +{ 0xCFB0, 0xCFB0, 0xCFB0 }, +{ 0xCFB1, 0xCFB1, 0xCFB1 }, +{ 0xCFB2, 0xCFB2, 0xCFB2 }, +{ 0xCFB3, 0xCFB3, 0xCFB3 }, +{ 0xCFB4, 0xCFB4, 0xCFB4 }, +{ 0xCFB5, 0xCFB5, 0xCFB5 }, +{ 0xCFB6, 0xCFB6, 0xCFB6 }, +{ 0xCFB7, 0xCFB7, 0xCFB7 }, +{ 0xCFB8, 0xCFB8, 0xCFB8 }, +{ 0xCFB9, 0xCFB9, 0xCFB9 }, +{ 0xCFBA, 0xCFBA, 0xCFBA }, +{ 0xCFBB, 0xCFBB, 0xCFBB }, +{ 0xCFBC, 0xCFBC, 0xCFBC }, +{ 0xCFBD, 0xCFBD, 0xCFBD }, +{ 0xCFBE, 0xCFBE, 0xCFBE }, +{ 0xCFBF, 0xCFBF, 0xCFBF }, +{ 0xCFC0, 0xCFC0, 0xCFC0 }, +{ 0xCFC1, 0xCFC1, 0xCFC1 }, +{ 0xCFC2, 0xCFC2, 0xCFC2 }, +{ 0xCFC3, 0xCFC3, 0xCFC3 }, +{ 0xCFC4, 0xCFC4, 0xCFC4 }, +{ 0xCFC5, 0xCFC5, 0xCFC5 }, +{ 0xCFC6, 0xCFC6, 0xCFC6 }, +{ 0xCFC7, 0xCFC7, 0xCFC7 }, +{ 0xCFC8, 0xCFC8, 0xCFC8 }, +{ 0xCFC9, 0xCFC9, 0xCFC9 }, +{ 0xCFCA, 0xCFCA, 0xCFCA }, +{ 0xCFCB, 0xCFCB, 0xCFCB }, +{ 0xCFCC, 0xCFCC, 0xCFCC }, +{ 0xCFCD, 0xCFCD, 0xCFCD }, +{ 0xCFCE, 0xCFCE, 0xCFCE }, +{ 0xCFCF, 0xCFCF, 0xCFCF }, +{ 0xCFD0, 0xCFD0, 0xCFD0 }, +{ 0xCFD1, 0xCFD1, 0xCFD1 }, +{ 0xCFD2, 0xCFD2, 0xCFD2 }, +{ 0xCFD3, 0xCFD3, 0xCFD3 }, +{ 0xCFD4, 0xCFD4, 0xCFD4 }, +{ 0xCFD5, 0xCFD5, 0xCFD5 }, +{ 0xCFD6, 0xCFD6, 0xCFD6 }, +{ 0xCFD7, 0xCFD7, 0xCFD7 }, +{ 0xCFD8, 0xCFD8, 0xCFD8 }, +{ 0xCFD9, 0xCFD9, 0xCFD9 }, +{ 0xCFDA, 0xCFDA, 0xCFDA }, +{ 0xCFDB, 0xCFDB, 0xCFDB }, +{ 0xCFDC, 0xCFDC, 0xCFDC }, +{ 0xCFDD, 0xCFDD, 0xCFDD }, +{ 0xCFDE, 0xCFDE, 0xCFDE }, +{ 0xCFDF, 0xCFDF, 0xCFDF }, +{ 0xCFE0, 0xCFE0, 0xCFE0 }, +{ 0xCFE1, 0xCFE1, 0xCFE1 }, +{ 0xCFE2, 0xCFE2, 0xCFE2 }, +{ 0xCFE3, 0xCFE3, 0xCFE3 }, +{ 0xCFE4, 0xCFE4, 0xCFE4 }, +{ 0xCFE5, 0xCFE5, 0xCFE5 }, +{ 0xCFE6, 0xCFE6, 0xCFE6 }, +{ 0xCFE7, 0xCFE7, 0xCFE7 }, +{ 0xCFE8, 0xCFE8, 0xCFE8 }, +{ 0xCFE9, 0xCFE9, 0xCFE9 }, +{ 0xCFEA, 0xCFEA, 0xCFEA }, +{ 0xCFEB, 0xCFEB, 0xCFEB }, +{ 0xCFEC, 0xCFEC, 0xCFEC }, +{ 0xCFED, 0xCFED, 0xCFED }, +{ 0xCFEE, 0xCFEE, 0xCFEE }, +{ 0xCFEF, 0xCFEF, 0xCFEF }, +{ 0xCFF0, 0xCFF0, 0xCFF0 }, +{ 0xCFF1, 0xCFF1, 0xCFF1 }, +{ 0xCFF2, 0xCFF2, 0xCFF2 }, +{ 0xCFF3, 0xCFF3, 0xCFF3 }, +{ 0xCFF4, 0xCFF4, 0xCFF4 }, +{ 0xCFF5, 0xCFF5, 0xCFF5 }, +{ 0xCFF6, 0xCFF6, 0xCFF6 }, +{ 0xCFF7, 0xCFF7, 0xCFF7 }, +{ 0xCFF8, 0xCFF8, 0xCFF8 }, +{ 0xCFF9, 0xCFF9, 0xCFF9 }, +{ 0xCFFA, 0xCFFA, 0xCFFA }, +{ 0xCFFB, 0xCFFB, 0xCFFB }, +{ 0xCFFC, 0xCFFC, 0xCFFC }, +{ 0xCFFD, 0xCFFD, 0xCFFD }, +{ 0xCFFE, 0xCFFE, 0xCFFE }, +{ 0xCFFF, 0xCFFF, 0xCFFF }, +{ 0xD000, 0xD000, 0xD000 }, +{ 0xD001, 0xD001, 0xD001 }, +{ 0xD002, 0xD002, 0xD002 }, +{ 0xD003, 0xD003, 0xD003 }, +{ 0xD004, 0xD004, 0xD004 }, +{ 0xD005, 0xD005, 0xD005 }, +{ 0xD006, 0xD006, 0xD006 }, +{ 0xD007, 0xD007, 0xD007 }, +{ 0xD008, 0xD008, 0xD008 }, +{ 0xD009, 0xD009, 0xD009 }, +{ 0xD00A, 0xD00A, 0xD00A }, +{ 0xD00B, 0xD00B, 0xD00B }, +{ 0xD00C, 0xD00C, 0xD00C }, +{ 0xD00D, 0xD00D, 0xD00D }, +{ 0xD00E, 0xD00E, 0xD00E }, +{ 0xD00F, 0xD00F, 0xD00F }, +{ 0xD010, 0xD010, 0xD010 }, +{ 0xD011, 0xD011, 0xD011 }, +{ 0xD012, 0xD012, 0xD012 }, +{ 0xD013, 0xD013, 0xD013 }, +{ 0xD014, 0xD014, 0xD014 }, +{ 0xD015, 0xD015, 0xD015 }, +{ 0xD016, 0xD016, 0xD016 }, +{ 0xD017, 0xD017, 0xD017 }, +{ 0xD018, 0xD018, 0xD018 }, +{ 0xD019, 0xD019, 0xD019 }, +{ 0xD01A, 0xD01A, 0xD01A }, +{ 0xD01B, 0xD01B, 0xD01B }, +{ 0xD01C, 0xD01C, 0xD01C }, +{ 0xD01D, 0xD01D, 0xD01D }, +{ 0xD01E, 0xD01E, 0xD01E }, +{ 0xD01F, 0xD01F, 0xD01F }, +{ 0xD020, 0xD020, 0xD020 }, +{ 0xD021, 0xD021, 0xD021 }, +{ 0xD022, 0xD022, 0xD022 }, +{ 0xD023, 0xD023, 0xD023 }, +{ 0xD024, 0xD024, 0xD024 }, +{ 0xD025, 0xD025, 0xD025 }, +{ 0xD026, 0xD026, 0xD026 }, +{ 0xD027, 0xD027, 0xD027 }, +{ 0xD028, 0xD028, 0xD028 }, +{ 0xD029, 0xD029, 0xD029 }, +{ 0xD02A, 0xD02A, 0xD02A }, +{ 0xD02B, 0xD02B, 0xD02B }, +{ 0xD02C, 0xD02C, 0xD02C }, +{ 0xD02D, 0xD02D, 0xD02D }, +{ 0xD02E, 0xD02E, 0xD02E }, +{ 0xD02F, 0xD02F, 0xD02F }, +{ 0xD030, 0xD030, 0xD030 }, +{ 0xD031, 0xD031, 0xD031 }, +{ 0xD032, 0xD032, 0xD032 }, +{ 0xD033, 0xD033, 0xD033 }, +{ 0xD034, 0xD034, 0xD034 }, +{ 0xD035, 0xD035, 0xD035 }, +{ 0xD036, 0xD036, 0xD036 }, +{ 0xD037, 0xD037, 0xD037 }, +{ 0xD038, 0xD038, 0xD038 }, +{ 0xD039, 0xD039, 0xD039 }, +{ 0xD03A, 0xD03A, 0xD03A }, +{ 0xD03B, 0xD03B, 0xD03B }, +{ 0xD03C, 0xD03C, 0xD03C }, +{ 0xD03D, 0xD03D, 0xD03D }, +{ 0xD03E, 0xD03E, 0xD03E }, +{ 0xD03F, 0xD03F, 0xD03F }, +{ 0xD040, 0xD040, 0xD040 }, +{ 0xD041, 0xD041, 0xD041 }, +{ 0xD042, 0xD042, 0xD042 }, +{ 0xD043, 0xD043, 0xD043 }, +{ 0xD044, 0xD044, 0xD044 }, +{ 0xD045, 0xD045, 0xD045 }, +{ 0xD046, 0xD046, 0xD046 }, +{ 0xD047, 0xD047, 0xD047 }, +{ 0xD048, 0xD048, 0xD048 }, +{ 0xD049, 0xD049, 0xD049 }, +{ 0xD04A, 0xD04A, 0xD04A }, +{ 0xD04B, 0xD04B, 0xD04B }, +{ 0xD04C, 0xD04C, 0xD04C }, +{ 0xD04D, 0xD04D, 0xD04D }, +{ 0xD04E, 0xD04E, 0xD04E }, +{ 0xD04F, 0xD04F, 0xD04F }, +{ 0xD050, 0xD050, 0xD050 }, +{ 0xD051, 0xD051, 0xD051 }, +{ 0xD052, 0xD052, 0xD052 }, +{ 0xD053, 0xD053, 0xD053 }, +{ 0xD054, 0xD054, 0xD054 }, +{ 0xD055, 0xD055, 0xD055 }, +{ 0xD056, 0xD056, 0xD056 }, +{ 0xD057, 0xD057, 0xD057 }, +{ 0xD058, 0xD058, 0xD058 }, +{ 0xD059, 0xD059, 0xD059 }, +{ 0xD05A, 0xD05A, 0xD05A }, +{ 0xD05B, 0xD05B, 0xD05B }, +{ 0xD05C, 0xD05C, 0xD05C }, +{ 0xD05D, 0xD05D, 0xD05D }, +{ 0xD05E, 0xD05E, 0xD05E }, +{ 0xD05F, 0xD05F, 0xD05F }, +{ 0xD060, 0xD060, 0xD060 }, +{ 0xD061, 0xD061, 0xD061 }, +{ 0xD062, 0xD062, 0xD062 }, +{ 0xD063, 0xD063, 0xD063 }, +{ 0xD064, 0xD064, 0xD064 }, +{ 0xD065, 0xD065, 0xD065 }, +{ 0xD066, 0xD066, 0xD066 }, +{ 0xD067, 0xD067, 0xD067 }, +{ 0xD068, 0xD068, 0xD068 }, +{ 0xD069, 0xD069, 0xD069 }, +{ 0xD06A, 0xD06A, 0xD06A }, +{ 0xD06B, 0xD06B, 0xD06B }, +{ 0xD06C, 0xD06C, 0xD06C }, +{ 0xD06D, 0xD06D, 0xD06D }, +{ 0xD06E, 0xD06E, 0xD06E }, +{ 0xD06F, 0xD06F, 0xD06F }, +{ 0xD070, 0xD070, 0xD070 }, +{ 0xD071, 0xD071, 0xD071 }, +{ 0xD072, 0xD072, 0xD072 }, +{ 0xD073, 0xD073, 0xD073 }, +{ 0xD074, 0xD074, 0xD074 }, +{ 0xD075, 0xD075, 0xD075 }, +{ 0xD076, 0xD076, 0xD076 }, +{ 0xD077, 0xD077, 0xD077 }, +{ 0xD078, 0xD078, 0xD078 }, +{ 0xD079, 0xD079, 0xD079 }, +{ 0xD07A, 0xD07A, 0xD07A }, +{ 0xD07B, 0xD07B, 0xD07B }, +{ 0xD07C, 0xD07C, 0xD07C }, +{ 0xD07D, 0xD07D, 0xD07D }, +{ 0xD07E, 0xD07E, 0xD07E }, +{ 0xD07F, 0xD07F, 0xD07F }, +{ 0xD080, 0xD080, 0xD080 }, +{ 0xD081, 0xD081, 0xD081 }, +{ 0xD082, 0xD082, 0xD082 }, +{ 0xD083, 0xD083, 0xD083 }, +{ 0xD084, 0xD084, 0xD084 }, +{ 0xD085, 0xD085, 0xD085 }, +{ 0xD086, 0xD086, 0xD086 }, +{ 0xD087, 0xD087, 0xD087 }, +{ 0xD088, 0xD088, 0xD088 }, +{ 0xD089, 0xD089, 0xD089 }, +{ 0xD08A, 0xD08A, 0xD08A }, +{ 0xD08B, 0xD08B, 0xD08B }, +{ 0xD08C, 0xD08C, 0xD08C }, +{ 0xD08D, 0xD08D, 0xD08D }, +{ 0xD08E, 0xD08E, 0xD08E }, +{ 0xD08F, 0xD08F, 0xD08F }, +{ 0xD090, 0xD090, 0xD090 }, +{ 0xD091, 0xD091, 0xD091 }, +{ 0xD092, 0xD092, 0xD092 }, +{ 0xD093, 0xD093, 0xD093 }, +{ 0xD094, 0xD094, 0xD094 }, +{ 0xD095, 0xD095, 0xD095 }, +{ 0xD096, 0xD096, 0xD096 }, +{ 0xD097, 0xD097, 0xD097 }, +{ 0xD098, 0xD098, 0xD098 }, +{ 0xD099, 0xD099, 0xD099 }, +{ 0xD09A, 0xD09A, 0xD09A }, +{ 0xD09B, 0xD09B, 0xD09B }, +{ 0xD09C, 0xD09C, 0xD09C }, +{ 0xD09D, 0xD09D, 0xD09D }, +{ 0xD09E, 0xD09E, 0xD09E }, +{ 0xD09F, 0xD09F, 0xD09F }, +{ 0xD0A0, 0xD0A0, 0xD0A0 }, +{ 0xD0A1, 0xD0A1, 0xD0A1 }, +{ 0xD0A2, 0xD0A2, 0xD0A2 }, +{ 0xD0A3, 0xD0A3, 0xD0A3 }, +{ 0xD0A4, 0xD0A4, 0xD0A4 }, +{ 0xD0A5, 0xD0A5, 0xD0A5 }, +{ 0xD0A6, 0xD0A6, 0xD0A6 }, +{ 0xD0A7, 0xD0A7, 0xD0A7 }, +{ 0xD0A8, 0xD0A8, 0xD0A8 }, +{ 0xD0A9, 0xD0A9, 0xD0A9 }, +{ 0xD0AA, 0xD0AA, 0xD0AA }, +{ 0xD0AB, 0xD0AB, 0xD0AB }, +{ 0xD0AC, 0xD0AC, 0xD0AC }, +{ 0xD0AD, 0xD0AD, 0xD0AD }, +{ 0xD0AE, 0xD0AE, 0xD0AE }, +{ 0xD0AF, 0xD0AF, 0xD0AF }, +{ 0xD0B0, 0xD0B0, 0xD0B0 }, +{ 0xD0B1, 0xD0B1, 0xD0B1 }, +{ 0xD0B2, 0xD0B2, 0xD0B2 }, +{ 0xD0B3, 0xD0B3, 0xD0B3 }, +{ 0xD0B4, 0xD0B4, 0xD0B4 }, +{ 0xD0B5, 0xD0B5, 0xD0B5 }, +{ 0xD0B6, 0xD0B6, 0xD0B6 }, +{ 0xD0B7, 0xD0B7, 0xD0B7 }, +{ 0xD0B8, 0xD0B8, 0xD0B8 }, +{ 0xD0B9, 0xD0B9, 0xD0B9 }, +{ 0xD0BA, 0xD0BA, 0xD0BA }, +{ 0xD0BB, 0xD0BB, 0xD0BB }, +{ 0xD0BC, 0xD0BC, 0xD0BC }, +{ 0xD0BD, 0xD0BD, 0xD0BD }, +{ 0xD0BE, 0xD0BE, 0xD0BE }, +{ 0xD0BF, 0xD0BF, 0xD0BF }, +{ 0xD0C0, 0xD0C0, 0xD0C0 }, +{ 0xD0C1, 0xD0C1, 0xD0C1 }, +{ 0xD0C2, 0xD0C2, 0xD0C2 }, +{ 0xD0C3, 0xD0C3, 0xD0C3 }, +{ 0xD0C4, 0xD0C4, 0xD0C4 }, +{ 0xD0C5, 0xD0C5, 0xD0C5 }, +{ 0xD0C6, 0xD0C6, 0xD0C6 }, +{ 0xD0C7, 0xD0C7, 0xD0C7 }, +{ 0xD0C8, 0xD0C8, 0xD0C8 }, +{ 0xD0C9, 0xD0C9, 0xD0C9 }, +{ 0xD0CA, 0xD0CA, 0xD0CA }, +{ 0xD0CB, 0xD0CB, 0xD0CB }, +{ 0xD0CC, 0xD0CC, 0xD0CC }, +{ 0xD0CD, 0xD0CD, 0xD0CD }, +{ 0xD0CE, 0xD0CE, 0xD0CE }, +{ 0xD0CF, 0xD0CF, 0xD0CF }, +{ 0xD0D0, 0xD0D0, 0xD0D0 }, +{ 0xD0D1, 0xD0D1, 0xD0D1 }, +{ 0xD0D2, 0xD0D2, 0xD0D2 }, +{ 0xD0D3, 0xD0D3, 0xD0D3 }, +{ 0xD0D4, 0xD0D4, 0xD0D4 }, +{ 0xD0D5, 0xD0D5, 0xD0D5 }, +{ 0xD0D6, 0xD0D6, 0xD0D6 }, +{ 0xD0D7, 0xD0D7, 0xD0D7 }, +{ 0xD0D8, 0xD0D8, 0xD0D8 }, +{ 0xD0D9, 0xD0D9, 0xD0D9 }, +{ 0xD0DA, 0xD0DA, 0xD0DA }, +{ 0xD0DB, 0xD0DB, 0xD0DB }, +{ 0xD0DC, 0xD0DC, 0xD0DC }, +{ 0xD0DD, 0xD0DD, 0xD0DD }, +{ 0xD0DE, 0xD0DE, 0xD0DE }, +{ 0xD0DF, 0xD0DF, 0xD0DF }, +{ 0xD0E0, 0xD0E0, 0xD0E0 }, +{ 0xD0E1, 0xD0E1, 0xD0E1 }, +{ 0xD0E2, 0xD0E2, 0xD0E2 }, +{ 0xD0E3, 0xD0E3, 0xD0E3 }, +{ 0xD0E4, 0xD0E4, 0xD0E4 }, +{ 0xD0E5, 0xD0E5, 0xD0E5 }, +{ 0xD0E6, 0xD0E6, 0xD0E6 }, +{ 0xD0E7, 0xD0E7, 0xD0E7 }, +{ 0xD0E8, 0xD0E8, 0xD0E8 }, +{ 0xD0E9, 0xD0E9, 0xD0E9 }, +{ 0xD0EA, 0xD0EA, 0xD0EA }, +{ 0xD0EB, 0xD0EB, 0xD0EB }, +{ 0xD0EC, 0xD0EC, 0xD0EC }, +{ 0xD0ED, 0xD0ED, 0xD0ED }, +{ 0xD0EE, 0xD0EE, 0xD0EE }, +{ 0xD0EF, 0xD0EF, 0xD0EF }, +{ 0xD0F0, 0xD0F0, 0xD0F0 }, +{ 0xD0F1, 0xD0F1, 0xD0F1 }, +{ 0xD0F2, 0xD0F2, 0xD0F2 }, +{ 0xD0F3, 0xD0F3, 0xD0F3 }, +{ 0xD0F4, 0xD0F4, 0xD0F4 }, +{ 0xD0F5, 0xD0F5, 0xD0F5 }, +{ 0xD0F6, 0xD0F6, 0xD0F6 }, +{ 0xD0F7, 0xD0F7, 0xD0F7 }, +{ 0xD0F8, 0xD0F8, 0xD0F8 }, +{ 0xD0F9, 0xD0F9, 0xD0F9 }, +{ 0xD0FA, 0xD0FA, 0xD0FA }, +{ 0xD0FB, 0xD0FB, 0xD0FB }, +{ 0xD0FC, 0xD0FC, 0xD0FC }, +{ 0xD0FD, 0xD0FD, 0xD0FD }, +{ 0xD0FE, 0xD0FE, 0xD0FE }, +{ 0xD0FF, 0xD0FF, 0xD0FF }, +{ 0xD100, 0xD100, 0xD100 }, +{ 0xD101, 0xD101, 0xD101 }, +{ 0xD102, 0xD102, 0xD102 }, +{ 0xD103, 0xD103, 0xD103 }, +{ 0xD104, 0xD104, 0xD104 }, +{ 0xD105, 0xD105, 0xD105 }, +{ 0xD106, 0xD106, 0xD106 }, +{ 0xD107, 0xD107, 0xD107 }, +{ 0xD108, 0xD108, 0xD108 }, +{ 0xD109, 0xD109, 0xD109 }, +{ 0xD10A, 0xD10A, 0xD10A }, +{ 0xD10B, 0xD10B, 0xD10B }, +{ 0xD10C, 0xD10C, 0xD10C }, +{ 0xD10D, 0xD10D, 0xD10D }, +{ 0xD10E, 0xD10E, 0xD10E }, +{ 0xD10F, 0xD10F, 0xD10F }, +{ 0xD110, 0xD110, 0xD110 }, +{ 0xD111, 0xD111, 0xD111 }, +{ 0xD112, 0xD112, 0xD112 }, +{ 0xD113, 0xD113, 0xD113 }, +{ 0xD114, 0xD114, 0xD114 }, +{ 0xD115, 0xD115, 0xD115 }, +{ 0xD116, 0xD116, 0xD116 }, +{ 0xD117, 0xD117, 0xD117 }, +{ 0xD118, 0xD118, 0xD118 }, +{ 0xD119, 0xD119, 0xD119 }, +{ 0xD11A, 0xD11A, 0xD11A }, +{ 0xD11B, 0xD11B, 0xD11B }, +{ 0xD11C, 0xD11C, 0xD11C }, +{ 0xD11D, 0xD11D, 0xD11D }, +{ 0xD11E, 0xD11E, 0xD11E }, +{ 0xD11F, 0xD11F, 0xD11F }, +{ 0xD120, 0xD120, 0xD120 }, +{ 0xD121, 0xD121, 0xD121 }, +{ 0xD122, 0xD122, 0xD122 }, +{ 0xD123, 0xD123, 0xD123 }, +{ 0xD124, 0xD124, 0xD124 }, +{ 0xD125, 0xD125, 0xD125 }, +{ 0xD126, 0xD126, 0xD126 }, +{ 0xD127, 0xD127, 0xD127 }, +{ 0xD128, 0xD128, 0xD128 }, +{ 0xD129, 0xD129, 0xD129 }, +{ 0xD12A, 0xD12A, 0xD12A }, +{ 0xD12B, 0xD12B, 0xD12B }, +{ 0xD12C, 0xD12C, 0xD12C }, +{ 0xD12D, 0xD12D, 0xD12D }, +{ 0xD12E, 0xD12E, 0xD12E }, +{ 0xD12F, 0xD12F, 0xD12F }, +{ 0xD130, 0xD130, 0xD130 }, +{ 0xD131, 0xD131, 0xD131 }, +{ 0xD132, 0xD132, 0xD132 }, +{ 0xD133, 0xD133, 0xD133 }, +{ 0xD134, 0xD134, 0xD134 }, +{ 0xD135, 0xD135, 0xD135 }, +{ 0xD136, 0xD136, 0xD136 }, +{ 0xD137, 0xD137, 0xD137 }, +{ 0xD138, 0xD138, 0xD138 }, +{ 0xD139, 0xD139, 0xD139 }, +{ 0xD13A, 0xD13A, 0xD13A }, +{ 0xD13B, 0xD13B, 0xD13B }, +{ 0xD13C, 0xD13C, 0xD13C }, +{ 0xD13D, 0xD13D, 0xD13D }, +{ 0xD13E, 0xD13E, 0xD13E }, +{ 0xD13F, 0xD13F, 0xD13F }, +{ 0xD140, 0xD140, 0xD140 }, +{ 0xD141, 0xD141, 0xD141 }, +{ 0xD142, 0xD142, 0xD142 }, +{ 0xD143, 0xD143, 0xD143 }, +{ 0xD144, 0xD144, 0xD144 }, +{ 0xD145, 0xD145, 0xD145 }, +{ 0xD146, 0xD146, 0xD146 }, +{ 0xD147, 0xD147, 0xD147 }, +{ 0xD148, 0xD148, 0xD148 }, +{ 0xD149, 0xD149, 0xD149 }, +{ 0xD14A, 0xD14A, 0xD14A }, +{ 0xD14B, 0xD14B, 0xD14B }, +{ 0xD14C, 0xD14C, 0xD14C }, +{ 0xD14D, 0xD14D, 0xD14D }, +{ 0xD14E, 0xD14E, 0xD14E }, +{ 0xD14F, 0xD14F, 0xD14F }, +{ 0xD150, 0xD150, 0xD150 }, +{ 0xD151, 0xD151, 0xD151 }, +{ 0xD152, 0xD152, 0xD152 }, +{ 0xD153, 0xD153, 0xD153 }, +{ 0xD154, 0xD154, 0xD154 }, +{ 0xD155, 0xD155, 0xD155 }, +{ 0xD156, 0xD156, 0xD156 }, +{ 0xD157, 0xD157, 0xD157 }, +{ 0xD158, 0xD158, 0xD158 }, +{ 0xD159, 0xD159, 0xD159 }, +{ 0xD15A, 0xD15A, 0xD15A }, +{ 0xD15B, 0xD15B, 0xD15B }, +{ 0xD15C, 0xD15C, 0xD15C }, +{ 0xD15D, 0xD15D, 0xD15D }, +{ 0xD15E, 0xD15E, 0xD15E }, +{ 0xD15F, 0xD15F, 0xD15F }, +{ 0xD160, 0xD160, 0xD160 }, +{ 0xD161, 0xD161, 0xD161 }, +{ 0xD162, 0xD162, 0xD162 }, +{ 0xD163, 0xD163, 0xD163 }, +{ 0xD164, 0xD164, 0xD164 }, +{ 0xD165, 0xD165, 0xD165 }, +{ 0xD166, 0xD166, 0xD166 }, +{ 0xD167, 0xD167, 0xD167 }, +{ 0xD168, 0xD168, 0xD168 }, +{ 0xD169, 0xD169, 0xD169 }, +{ 0xD16A, 0xD16A, 0xD16A }, +{ 0xD16B, 0xD16B, 0xD16B }, +{ 0xD16C, 0xD16C, 0xD16C }, +{ 0xD16D, 0xD16D, 0xD16D }, +{ 0xD16E, 0xD16E, 0xD16E }, +{ 0xD16F, 0xD16F, 0xD16F }, +{ 0xD170, 0xD170, 0xD170 }, +{ 0xD171, 0xD171, 0xD171 }, +{ 0xD172, 0xD172, 0xD172 }, +{ 0xD173, 0xD173, 0xD173 }, +{ 0xD174, 0xD174, 0xD174 }, +{ 0xD175, 0xD175, 0xD175 }, +{ 0xD176, 0xD176, 0xD176 }, +{ 0xD177, 0xD177, 0xD177 }, +{ 0xD178, 0xD178, 0xD178 }, +{ 0xD179, 0xD179, 0xD179 }, +{ 0xD17A, 0xD17A, 0xD17A }, +{ 0xD17B, 0xD17B, 0xD17B }, +{ 0xD17C, 0xD17C, 0xD17C }, +{ 0xD17D, 0xD17D, 0xD17D }, +{ 0xD17E, 0xD17E, 0xD17E }, +{ 0xD17F, 0xD17F, 0xD17F }, +{ 0xD180, 0xD180, 0xD180 }, +{ 0xD181, 0xD181, 0xD181 }, +{ 0xD182, 0xD182, 0xD182 }, +{ 0xD183, 0xD183, 0xD183 }, +{ 0xD184, 0xD184, 0xD184 }, +{ 0xD185, 0xD185, 0xD185 }, +{ 0xD186, 0xD186, 0xD186 }, +{ 0xD187, 0xD187, 0xD187 }, +{ 0xD188, 0xD188, 0xD188 }, +{ 0xD189, 0xD189, 0xD189 }, +{ 0xD18A, 0xD18A, 0xD18A }, +{ 0xD18B, 0xD18B, 0xD18B }, +{ 0xD18C, 0xD18C, 0xD18C }, +{ 0xD18D, 0xD18D, 0xD18D }, +{ 0xD18E, 0xD18E, 0xD18E }, +{ 0xD18F, 0xD18F, 0xD18F }, +{ 0xD190, 0xD190, 0xD190 }, +{ 0xD191, 0xD191, 0xD191 }, +{ 0xD192, 0xD192, 0xD192 }, +{ 0xD193, 0xD193, 0xD193 }, +{ 0xD194, 0xD194, 0xD194 }, +{ 0xD195, 0xD195, 0xD195 }, +{ 0xD196, 0xD196, 0xD196 }, +{ 0xD197, 0xD197, 0xD197 }, +{ 0xD198, 0xD198, 0xD198 }, +{ 0xD199, 0xD199, 0xD199 }, +{ 0xD19A, 0xD19A, 0xD19A }, +{ 0xD19B, 0xD19B, 0xD19B }, +{ 0xD19C, 0xD19C, 0xD19C }, +{ 0xD19D, 0xD19D, 0xD19D }, +{ 0xD19E, 0xD19E, 0xD19E }, +{ 0xD19F, 0xD19F, 0xD19F }, +{ 0xD1A0, 0xD1A0, 0xD1A0 }, +{ 0xD1A1, 0xD1A1, 0xD1A1 }, +{ 0xD1A2, 0xD1A2, 0xD1A2 }, +{ 0xD1A3, 0xD1A3, 0xD1A3 }, +{ 0xD1A4, 0xD1A4, 0xD1A4 }, +{ 0xD1A5, 0xD1A5, 0xD1A5 }, +{ 0xD1A6, 0xD1A6, 0xD1A6 }, +{ 0xD1A7, 0xD1A7, 0xD1A7 }, +{ 0xD1A8, 0xD1A8, 0xD1A8 }, +{ 0xD1A9, 0xD1A9, 0xD1A9 }, +{ 0xD1AA, 0xD1AA, 0xD1AA }, +{ 0xD1AB, 0xD1AB, 0xD1AB }, +{ 0xD1AC, 0xD1AC, 0xD1AC }, +{ 0xD1AD, 0xD1AD, 0xD1AD }, +{ 0xD1AE, 0xD1AE, 0xD1AE }, +{ 0xD1AF, 0xD1AF, 0xD1AF }, +{ 0xD1B0, 0xD1B0, 0xD1B0 }, +{ 0xD1B1, 0xD1B1, 0xD1B1 }, +{ 0xD1B2, 0xD1B2, 0xD1B2 }, +{ 0xD1B3, 0xD1B3, 0xD1B3 }, +{ 0xD1B4, 0xD1B4, 0xD1B4 }, +{ 0xD1B5, 0xD1B5, 0xD1B5 }, +{ 0xD1B6, 0xD1B6, 0xD1B6 }, +{ 0xD1B7, 0xD1B7, 0xD1B7 }, +{ 0xD1B8, 0xD1B8, 0xD1B8 }, +{ 0xD1B9, 0xD1B9, 0xD1B9 }, +{ 0xD1BA, 0xD1BA, 0xD1BA }, +{ 0xD1BB, 0xD1BB, 0xD1BB }, +{ 0xD1BC, 0xD1BC, 0xD1BC }, +{ 0xD1BD, 0xD1BD, 0xD1BD }, +{ 0xD1BE, 0xD1BE, 0xD1BE }, +{ 0xD1BF, 0xD1BF, 0xD1BF }, +{ 0xD1C0, 0xD1C0, 0xD1C0 }, +{ 0xD1C1, 0xD1C1, 0xD1C1 }, +{ 0xD1C2, 0xD1C2, 0xD1C2 }, +{ 0xD1C3, 0xD1C3, 0xD1C3 }, +{ 0xD1C4, 0xD1C4, 0xD1C4 }, +{ 0xD1C5, 0xD1C5, 0xD1C5 }, +{ 0xD1C6, 0xD1C6, 0xD1C6 }, +{ 0xD1C7, 0xD1C7, 0xD1C7 }, +{ 0xD1C8, 0xD1C8, 0xD1C8 }, +{ 0xD1C9, 0xD1C9, 0xD1C9 }, +{ 0xD1CA, 0xD1CA, 0xD1CA }, +{ 0xD1CB, 0xD1CB, 0xD1CB }, +{ 0xD1CC, 0xD1CC, 0xD1CC }, +{ 0xD1CD, 0xD1CD, 0xD1CD }, +{ 0xD1CE, 0xD1CE, 0xD1CE }, +{ 0xD1CF, 0xD1CF, 0xD1CF }, +{ 0xD1D0, 0xD1D0, 0xD1D0 }, +{ 0xD1D1, 0xD1D1, 0xD1D1 }, +{ 0xD1D2, 0xD1D2, 0xD1D2 }, +{ 0xD1D3, 0xD1D3, 0xD1D3 }, +{ 0xD1D4, 0xD1D4, 0xD1D4 }, +{ 0xD1D5, 0xD1D5, 0xD1D5 }, +{ 0xD1D6, 0xD1D6, 0xD1D6 }, +{ 0xD1D7, 0xD1D7, 0xD1D7 }, +{ 0xD1D8, 0xD1D8, 0xD1D8 }, +{ 0xD1D9, 0xD1D9, 0xD1D9 }, +{ 0xD1DA, 0xD1DA, 0xD1DA }, +{ 0xD1DB, 0xD1DB, 0xD1DB }, +{ 0xD1DC, 0xD1DC, 0xD1DC }, +{ 0xD1DD, 0xD1DD, 0xD1DD }, +{ 0xD1DE, 0xD1DE, 0xD1DE }, +{ 0xD1DF, 0xD1DF, 0xD1DF }, +{ 0xD1E0, 0xD1E0, 0xD1E0 }, +{ 0xD1E1, 0xD1E1, 0xD1E1 }, +{ 0xD1E2, 0xD1E2, 0xD1E2 }, +{ 0xD1E3, 0xD1E3, 0xD1E3 }, +{ 0xD1E4, 0xD1E4, 0xD1E4 }, +{ 0xD1E5, 0xD1E5, 0xD1E5 }, +{ 0xD1E6, 0xD1E6, 0xD1E6 }, +{ 0xD1E7, 0xD1E7, 0xD1E7 }, +{ 0xD1E8, 0xD1E8, 0xD1E8 }, +{ 0xD1E9, 0xD1E9, 0xD1E9 }, +{ 0xD1EA, 0xD1EA, 0xD1EA }, +{ 0xD1EB, 0xD1EB, 0xD1EB }, +{ 0xD1EC, 0xD1EC, 0xD1EC }, +{ 0xD1ED, 0xD1ED, 0xD1ED }, +{ 0xD1EE, 0xD1EE, 0xD1EE }, +{ 0xD1EF, 0xD1EF, 0xD1EF }, +{ 0xD1F0, 0xD1F0, 0xD1F0 }, +{ 0xD1F1, 0xD1F1, 0xD1F1 }, +{ 0xD1F2, 0xD1F2, 0xD1F2 }, +{ 0xD1F3, 0xD1F3, 0xD1F3 }, +{ 0xD1F4, 0xD1F4, 0xD1F4 }, +{ 0xD1F5, 0xD1F5, 0xD1F5 }, +{ 0xD1F6, 0xD1F6, 0xD1F6 }, +{ 0xD1F7, 0xD1F7, 0xD1F7 }, +{ 0xD1F8, 0xD1F8, 0xD1F8 }, +{ 0xD1F9, 0xD1F9, 0xD1F9 }, +{ 0xD1FA, 0xD1FA, 0xD1FA }, +{ 0xD1FB, 0xD1FB, 0xD1FB }, +{ 0xD1FC, 0xD1FC, 0xD1FC }, +{ 0xD1FD, 0xD1FD, 0xD1FD }, +{ 0xD1FE, 0xD1FE, 0xD1FE }, +{ 0xD1FF, 0xD1FF, 0xD1FF }, +{ 0xD200, 0xD200, 0xD200 }, +{ 0xD201, 0xD201, 0xD201 }, +{ 0xD202, 0xD202, 0xD202 }, +{ 0xD203, 0xD203, 0xD203 }, +{ 0xD204, 0xD204, 0xD204 }, +{ 0xD205, 0xD205, 0xD205 }, +{ 0xD206, 0xD206, 0xD206 }, +{ 0xD207, 0xD207, 0xD207 }, +{ 0xD208, 0xD208, 0xD208 }, +{ 0xD209, 0xD209, 0xD209 }, +{ 0xD20A, 0xD20A, 0xD20A }, +{ 0xD20B, 0xD20B, 0xD20B }, +{ 0xD20C, 0xD20C, 0xD20C }, +{ 0xD20D, 0xD20D, 0xD20D }, +{ 0xD20E, 0xD20E, 0xD20E }, +{ 0xD20F, 0xD20F, 0xD20F }, +{ 0xD210, 0xD210, 0xD210 }, +{ 0xD211, 0xD211, 0xD211 }, +{ 0xD212, 0xD212, 0xD212 }, +{ 0xD213, 0xD213, 0xD213 }, +{ 0xD214, 0xD214, 0xD214 }, +{ 0xD215, 0xD215, 0xD215 }, +{ 0xD216, 0xD216, 0xD216 }, +{ 0xD217, 0xD217, 0xD217 }, +{ 0xD218, 0xD218, 0xD218 }, +{ 0xD219, 0xD219, 0xD219 }, +{ 0xD21A, 0xD21A, 0xD21A }, +{ 0xD21B, 0xD21B, 0xD21B }, +{ 0xD21C, 0xD21C, 0xD21C }, +{ 0xD21D, 0xD21D, 0xD21D }, +{ 0xD21E, 0xD21E, 0xD21E }, +{ 0xD21F, 0xD21F, 0xD21F }, +{ 0xD220, 0xD220, 0xD220 }, +{ 0xD221, 0xD221, 0xD221 }, +{ 0xD222, 0xD222, 0xD222 }, +{ 0xD223, 0xD223, 0xD223 }, +{ 0xD224, 0xD224, 0xD224 }, +{ 0xD225, 0xD225, 0xD225 }, +{ 0xD226, 0xD226, 0xD226 }, +{ 0xD227, 0xD227, 0xD227 }, +{ 0xD228, 0xD228, 0xD228 }, +{ 0xD229, 0xD229, 0xD229 }, +{ 0xD22A, 0xD22A, 0xD22A }, +{ 0xD22B, 0xD22B, 0xD22B }, +{ 0xD22C, 0xD22C, 0xD22C }, +{ 0xD22D, 0xD22D, 0xD22D }, +{ 0xD22E, 0xD22E, 0xD22E }, +{ 0xD22F, 0xD22F, 0xD22F }, +{ 0xD230, 0xD230, 0xD230 }, +{ 0xD231, 0xD231, 0xD231 }, +{ 0xD232, 0xD232, 0xD232 }, +{ 0xD233, 0xD233, 0xD233 }, +{ 0xD234, 0xD234, 0xD234 }, +{ 0xD235, 0xD235, 0xD235 }, +{ 0xD236, 0xD236, 0xD236 }, +{ 0xD237, 0xD237, 0xD237 }, +{ 0xD238, 0xD238, 0xD238 }, +{ 0xD239, 0xD239, 0xD239 }, +{ 0xD23A, 0xD23A, 0xD23A }, +{ 0xD23B, 0xD23B, 0xD23B }, +{ 0xD23C, 0xD23C, 0xD23C }, +{ 0xD23D, 0xD23D, 0xD23D }, +{ 0xD23E, 0xD23E, 0xD23E }, +{ 0xD23F, 0xD23F, 0xD23F }, +{ 0xD240, 0xD240, 0xD240 }, +{ 0xD241, 0xD241, 0xD241 }, +{ 0xD242, 0xD242, 0xD242 }, +{ 0xD243, 0xD243, 0xD243 }, +{ 0xD244, 0xD244, 0xD244 }, +{ 0xD245, 0xD245, 0xD245 }, +{ 0xD246, 0xD246, 0xD246 }, +{ 0xD247, 0xD247, 0xD247 }, +{ 0xD248, 0xD248, 0xD248 }, +{ 0xD249, 0xD249, 0xD249 }, +{ 0xD24A, 0xD24A, 0xD24A }, +{ 0xD24B, 0xD24B, 0xD24B }, +{ 0xD24C, 0xD24C, 0xD24C }, +{ 0xD24D, 0xD24D, 0xD24D }, +{ 0xD24E, 0xD24E, 0xD24E }, +{ 0xD24F, 0xD24F, 0xD24F }, +{ 0xD250, 0xD250, 0xD250 }, +{ 0xD251, 0xD251, 0xD251 }, +{ 0xD252, 0xD252, 0xD252 }, +{ 0xD253, 0xD253, 0xD253 }, +{ 0xD254, 0xD254, 0xD254 }, +{ 0xD255, 0xD255, 0xD255 }, +{ 0xD256, 0xD256, 0xD256 }, +{ 0xD257, 0xD257, 0xD257 }, +{ 0xD258, 0xD258, 0xD258 }, +{ 0xD259, 0xD259, 0xD259 }, +{ 0xD25A, 0xD25A, 0xD25A }, +{ 0xD25B, 0xD25B, 0xD25B }, +{ 0xD25C, 0xD25C, 0xD25C }, +{ 0xD25D, 0xD25D, 0xD25D }, +{ 0xD25E, 0xD25E, 0xD25E }, +{ 0xD25F, 0xD25F, 0xD25F }, +{ 0xD260, 0xD260, 0xD260 }, +{ 0xD261, 0xD261, 0xD261 }, +{ 0xD262, 0xD262, 0xD262 }, +{ 0xD263, 0xD263, 0xD263 }, +{ 0xD264, 0xD264, 0xD264 }, +{ 0xD265, 0xD265, 0xD265 }, +{ 0xD266, 0xD266, 0xD266 }, +{ 0xD267, 0xD267, 0xD267 }, +{ 0xD268, 0xD268, 0xD268 }, +{ 0xD269, 0xD269, 0xD269 }, +{ 0xD26A, 0xD26A, 0xD26A }, +{ 0xD26B, 0xD26B, 0xD26B }, +{ 0xD26C, 0xD26C, 0xD26C }, +{ 0xD26D, 0xD26D, 0xD26D }, +{ 0xD26E, 0xD26E, 0xD26E }, +{ 0xD26F, 0xD26F, 0xD26F }, +{ 0xD270, 0xD270, 0xD270 }, +{ 0xD271, 0xD271, 0xD271 }, +{ 0xD272, 0xD272, 0xD272 }, +{ 0xD273, 0xD273, 0xD273 }, +{ 0xD274, 0xD274, 0xD274 }, +{ 0xD275, 0xD275, 0xD275 }, +{ 0xD276, 0xD276, 0xD276 }, +{ 0xD277, 0xD277, 0xD277 }, +{ 0xD278, 0xD278, 0xD278 }, +{ 0xD279, 0xD279, 0xD279 }, +{ 0xD27A, 0xD27A, 0xD27A }, +{ 0xD27B, 0xD27B, 0xD27B }, +{ 0xD27C, 0xD27C, 0xD27C }, +{ 0xD27D, 0xD27D, 0xD27D }, +{ 0xD27E, 0xD27E, 0xD27E }, +{ 0xD27F, 0xD27F, 0xD27F }, +{ 0xD280, 0xD280, 0xD280 }, +{ 0xD281, 0xD281, 0xD281 }, +{ 0xD282, 0xD282, 0xD282 }, +{ 0xD283, 0xD283, 0xD283 }, +{ 0xD284, 0xD284, 0xD284 }, +{ 0xD285, 0xD285, 0xD285 }, +{ 0xD286, 0xD286, 0xD286 }, +{ 0xD287, 0xD287, 0xD287 }, +{ 0xD288, 0xD288, 0xD288 }, +{ 0xD289, 0xD289, 0xD289 }, +{ 0xD28A, 0xD28A, 0xD28A }, +{ 0xD28B, 0xD28B, 0xD28B }, +{ 0xD28C, 0xD28C, 0xD28C }, +{ 0xD28D, 0xD28D, 0xD28D }, +{ 0xD28E, 0xD28E, 0xD28E }, +{ 0xD28F, 0xD28F, 0xD28F }, +{ 0xD290, 0xD290, 0xD290 }, +{ 0xD291, 0xD291, 0xD291 }, +{ 0xD292, 0xD292, 0xD292 }, +{ 0xD293, 0xD293, 0xD293 }, +{ 0xD294, 0xD294, 0xD294 }, +{ 0xD295, 0xD295, 0xD295 }, +{ 0xD296, 0xD296, 0xD296 }, +{ 0xD297, 0xD297, 0xD297 }, +{ 0xD298, 0xD298, 0xD298 }, +{ 0xD299, 0xD299, 0xD299 }, +{ 0xD29A, 0xD29A, 0xD29A }, +{ 0xD29B, 0xD29B, 0xD29B }, +{ 0xD29C, 0xD29C, 0xD29C }, +{ 0xD29D, 0xD29D, 0xD29D }, +{ 0xD29E, 0xD29E, 0xD29E }, +{ 0xD29F, 0xD29F, 0xD29F }, +{ 0xD2A0, 0xD2A0, 0xD2A0 }, +{ 0xD2A1, 0xD2A1, 0xD2A1 }, +{ 0xD2A2, 0xD2A2, 0xD2A2 }, +{ 0xD2A3, 0xD2A3, 0xD2A3 }, +{ 0xD2A4, 0xD2A4, 0xD2A4 }, +{ 0xD2A5, 0xD2A5, 0xD2A5 }, +{ 0xD2A6, 0xD2A6, 0xD2A6 }, +{ 0xD2A7, 0xD2A7, 0xD2A7 }, +{ 0xD2A8, 0xD2A8, 0xD2A8 }, +{ 0xD2A9, 0xD2A9, 0xD2A9 }, +{ 0xD2AA, 0xD2AA, 0xD2AA }, +{ 0xD2AB, 0xD2AB, 0xD2AB }, +{ 0xD2AC, 0xD2AC, 0xD2AC }, +{ 0xD2AD, 0xD2AD, 0xD2AD }, +{ 0xD2AE, 0xD2AE, 0xD2AE }, +{ 0xD2AF, 0xD2AF, 0xD2AF }, +{ 0xD2B0, 0xD2B0, 0xD2B0 }, +{ 0xD2B1, 0xD2B1, 0xD2B1 }, +{ 0xD2B2, 0xD2B2, 0xD2B2 }, +{ 0xD2B3, 0xD2B3, 0xD2B3 }, +{ 0xD2B4, 0xD2B4, 0xD2B4 }, +{ 0xD2B5, 0xD2B5, 0xD2B5 }, +{ 0xD2B6, 0xD2B6, 0xD2B6 }, +{ 0xD2B7, 0xD2B7, 0xD2B7 }, +{ 0xD2B8, 0xD2B8, 0xD2B8 }, +{ 0xD2B9, 0xD2B9, 0xD2B9 }, +{ 0xD2BA, 0xD2BA, 0xD2BA }, +{ 0xD2BB, 0xD2BB, 0xD2BB }, +{ 0xD2BC, 0xD2BC, 0xD2BC }, +{ 0xD2BD, 0xD2BD, 0xD2BD }, +{ 0xD2BE, 0xD2BE, 0xD2BE }, +{ 0xD2BF, 0xD2BF, 0xD2BF }, +{ 0xD2C0, 0xD2C0, 0xD2C0 }, +{ 0xD2C1, 0xD2C1, 0xD2C1 }, +{ 0xD2C2, 0xD2C2, 0xD2C2 }, +{ 0xD2C3, 0xD2C3, 0xD2C3 }, +{ 0xD2C4, 0xD2C4, 0xD2C4 }, +{ 0xD2C5, 0xD2C5, 0xD2C5 }, +{ 0xD2C6, 0xD2C6, 0xD2C6 }, +{ 0xD2C7, 0xD2C7, 0xD2C7 }, +{ 0xD2C8, 0xD2C8, 0xD2C8 }, +{ 0xD2C9, 0xD2C9, 0xD2C9 }, +{ 0xD2CA, 0xD2CA, 0xD2CA }, +{ 0xD2CB, 0xD2CB, 0xD2CB }, +{ 0xD2CC, 0xD2CC, 0xD2CC }, +{ 0xD2CD, 0xD2CD, 0xD2CD }, +{ 0xD2CE, 0xD2CE, 0xD2CE }, +{ 0xD2CF, 0xD2CF, 0xD2CF }, +{ 0xD2D0, 0xD2D0, 0xD2D0 }, +{ 0xD2D1, 0xD2D1, 0xD2D1 }, +{ 0xD2D2, 0xD2D2, 0xD2D2 }, +{ 0xD2D3, 0xD2D3, 0xD2D3 }, +{ 0xD2D4, 0xD2D4, 0xD2D4 }, +{ 0xD2D5, 0xD2D5, 0xD2D5 }, +{ 0xD2D6, 0xD2D6, 0xD2D6 }, +{ 0xD2D7, 0xD2D7, 0xD2D7 }, +{ 0xD2D8, 0xD2D8, 0xD2D8 }, +{ 0xD2D9, 0xD2D9, 0xD2D9 }, +{ 0xD2DA, 0xD2DA, 0xD2DA }, +{ 0xD2DB, 0xD2DB, 0xD2DB }, +{ 0xD2DC, 0xD2DC, 0xD2DC }, +{ 0xD2DD, 0xD2DD, 0xD2DD }, +{ 0xD2DE, 0xD2DE, 0xD2DE }, +{ 0xD2DF, 0xD2DF, 0xD2DF }, +{ 0xD2E0, 0xD2E0, 0xD2E0 }, +{ 0xD2E1, 0xD2E1, 0xD2E1 }, +{ 0xD2E2, 0xD2E2, 0xD2E2 }, +{ 0xD2E3, 0xD2E3, 0xD2E3 }, +{ 0xD2E4, 0xD2E4, 0xD2E4 }, +{ 0xD2E5, 0xD2E5, 0xD2E5 }, +{ 0xD2E6, 0xD2E6, 0xD2E6 }, +{ 0xD2E7, 0xD2E7, 0xD2E7 }, +{ 0xD2E8, 0xD2E8, 0xD2E8 }, +{ 0xD2E9, 0xD2E9, 0xD2E9 }, +{ 0xD2EA, 0xD2EA, 0xD2EA }, +{ 0xD2EB, 0xD2EB, 0xD2EB }, +{ 0xD2EC, 0xD2EC, 0xD2EC }, +{ 0xD2ED, 0xD2ED, 0xD2ED }, +{ 0xD2EE, 0xD2EE, 0xD2EE }, +{ 0xD2EF, 0xD2EF, 0xD2EF }, +{ 0xD2F0, 0xD2F0, 0xD2F0 }, +{ 0xD2F1, 0xD2F1, 0xD2F1 }, +{ 0xD2F2, 0xD2F2, 0xD2F2 }, +{ 0xD2F3, 0xD2F3, 0xD2F3 }, +{ 0xD2F4, 0xD2F4, 0xD2F4 }, +{ 0xD2F5, 0xD2F5, 0xD2F5 }, +{ 0xD2F6, 0xD2F6, 0xD2F6 }, +{ 0xD2F7, 0xD2F7, 0xD2F7 }, +{ 0xD2F8, 0xD2F8, 0xD2F8 }, +{ 0xD2F9, 0xD2F9, 0xD2F9 }, +{ 0xD2FA, 0xD2FA, 0xD2FA }, +{ 0xD2FB, 0xD2FB, 0xD2FB }, +{ 0xD2FC, 0xD2FC, 0xD2FC }, +{ 0xD2FD, 0xD2FD, 0xD2FD }, +{ 0xD2FE, 0xD2FE, 0xD2FE }, +{ 0xD2FF, 0xD2FF, 0xD2FF }, +{ 0xD300, 0xD300, 0xD300 }, +{ 0xD301, 0xD301, 0xD301 }, +{ 0xD302, 0xD302, 0xD302 }, +{ 0xD303, 0xD303, 0xD303 }, +{ 0xD304, 0xD304, 0xD304 }, +{ 0xD305, 0xD305, 0xD305 }, +{ 0xD306, 0xD306, 0xD306 }, +{ 0xD307, 0xD307, 0xD307 }, +{ 0xD308, 0xD308, 0xD308 }, +{ 0xD309, 0xD309, 0xD309 }, +{ 0xD30A, 0xD30A, 0xD30A }, +{ 0xD30B, 0xD30B, 0xD30B }, +{ 0xD30C, 0xD30C, 0xD30C }, +{ 0xD30D, 0xD30D, 0xD30D }, +{ 0xD30E, 0xD30E, 0xD30E }, +{ 0xD30F, 0xD30F, 0xD30F }, +{ 0xD310, 0xD310, 0xD310 }, +{ 0xD311, 0xD311, 0xD311 }, +{ 0xD312, 0xD312, 0xD312 }, +{ 0xD313, 0xD313, 0xD313 }, +{ 0xD314, 0xD314, 0xD314 }, +{ 0xD315, 0xD315, 0xD315 }, +{ 0xD316, 0xD316, 0xD316 }, +{ 0xD317, 0xD317, 0xD317 }, +{ 0xD318, 0xD318, 0xD318 }, +{ 0xD319, 0xD319, 0xD319 }, +{ 0xD31A, 0xD31A, 0xD31A }, +{ 0xD31B, 0xD31B, 0xD31B }, +{ 0xD31C, 0xD31C, 0xD31C }, +{ 0xD31D, 0xD31D, 0xD31D }, +{ 0xD31E, 0xD31E, 0xD31E }, +{ 0xD31F, 0xD31F, 0xD31F }, +{ 0xD320, 0xD320, 0xD320 }, +{ 0xD321, 0xD321, 0xD321 }, +{ 0xD322, 0xD322, 0xD322 }, +{ 0xD323, 0xD323, 0xD323 }, +{ 0xD324, 0xD324, 0xD324 }, +{ 0xD325, 0xD325, 0xD325 }, +{ 0xD326, 0xD326, 0xD326 }, +{ 0xD327, 0xD327, 0xD327 }, +{ 0xD328, 0xD328, 0xD328 }, +{ 0xD329, 0xD329, 0xD329 }, +{ 0xD32A, 0xD32A, 0xD32A }, +{ 0xD32B, 0xD32B, 0xD32B }, +{ 0xD32C, 0xD32C, 0xD32C }, +{ 0xD32D, 0xD32D, 0xD32D }, +{ 0xD32E, 0xD32E, 0xD32E }, +{ 0xD32F, 0xD32F, 0xD32F }, +{ 0xD330, 0xD330, 0xD330 }, +{ 0xD331, 0xD331, 0xD331 }, +{ 0xD332, 0xD332, 0xD332 }, +{ 0xD333, 0xD333, 0xD333 }, +{ 0xD334, 0xD334, 0xD334 }, +{ 0xD335, 0xD335, 0xD335 }, +{ 0xD336, 0xD336, 0xD336 }, +{ 0xD337, 0xD337, 0xD337 }, +{ 0xD338, 0xD338, 0xD338 }, +{ 0xD339, 0xD339, 0xD339 }, +{ 0xD33A, 0xD33A, 0xD33A }, +{ 0xD33B, 0xD33B, 0xD33B }, +{ 0xD33C, 0xD33C, 0xD33C }, +{ 0xD33D, 0xD33D, 0xD33D }, +{ 0xD33E, 0xD33E, 0xD33E }, +{ 0xD33F, 0xD33F, 0xD33F }, +{ 0xD340, 0xD340, 0xD340 }, +{ 0xD341, 0xD341, 0xD341 }, +{ 0xD342, 0xD342, 0xD342 }, +{ 0xD343, 0xD343, 0xD343 }, +{ 0xD344, 0xD344, 0xD344 }, +{ 0xD345, 0xD345, 0xD345 }, +{ 0xD346, 0xD346, 0xD346 }, +{ 0xD347, 0xD347, 0xD347 }, +{ 0xD348, 0xD348, 0xD348 }, +{ 0xD349, 0xD349, 0xD349 }, +{ 0xD34A, 0xD34A, 0xD34A }, +{ 0xD34B, 0xD34B, 0xD34B }, +{ 0xD34C, 0xD34C, 0xD34C }, +{ 0xD34D, 0xD34D, 0xD34D }, +{ 0xD34E, 0xD34E, 0xD34E }, +{ 0xD34F, 0xD34F, 0xD34F }, +{ 0xD350, 0xD350, 0xD350 }, +{ 0xD351, 0xD351, 0xD351 }, +{ 0xD352, 0xD352, 0xD352 }, +{ 0xD353, 0xD353, 0xD353 }, +{ 0xD354, 0xD354, 0xD354 }, +{ 0xD355, 0xD355, 0xD355 }, +{ 0xD356, 0xD356, 0xD356 }, +{ 0xD357, 0xD357, 0xD357 }, +{ 0xD358, 0xD358, 0xD358 }, +{ 0xD359, 0xD359, 0xD359 }, +{ 0xD35A, 0xD35A, 0xD35A }, +{ 0xD35B, 0xD35B, 0xD35B }, +{ 0xD35C, 0xD35C, 0xD35C }, +{ 0xD35D, 0xD35D, 0xD35D }, +{ 0xD35E, 0xD35E, 0xD35E }, +{ 0xD35F, 0xD35F, 0xD35F }, +{ 0xD360, 0xD360, 0xD360 }, +{ 0xD361, 0xD361, 0xD361 }, +{ 0xD362, 0xD362, 0xD362 }, +{ 0xD363, 0xD363, 0xD363 }, +{ 0xD364, 0xD364, 0xD364 }, +{ 0xD365, 0xD365, 0xD365 }, +{ 0xD366, 0xD366, 0xD366 }, +{ 0xD367, 0xD367, 0xD367 }, +{ 0xD368, 0xD368, 0xD368 }, +{ 0xD369, 0xD369, 0xD369 }, +{ 0xD36A, 0xD36A, 0xD36A }, +{ 0xD36B, 0xD36B, 0xD36B }, +{ 0xD36C, 0xD36C, 0xD36C }, +{ 0xD36D, 0xD36D, 0xD36D }, +{ 0xD36E, 0xD36E, 0xD36E }, +{ 0xD36F, 0xD36F, 0xD36F }, +{ 0xD370, 0xD370, 0xD370 }, +{ 0xD371, 0xD371, 0xD371 }, +{ 0xD372, 0xD372, 0xD372 }, +{ 0xD373, 0xD373, 0xD373 }, +{ 0xD374, 0xD374, 0xD374 }, +{ 0xD375, 0xD375, 0xD375 }, +{ 0xD376, 0xD376, 0xD376 }, +{ 0xD377, 0xD377, 0xD377 }, +{ 0xD378, 0xD378, 0xD378 }, +{ 0xD379, 0xD379, 0xD379 }, +{ 0xD37A, 0xD37A, 0xD37A }, +{ 0xD37B, 0xD37B, 0xD37B }, +{ 0xD37C, 0xD37C, 0xD37C }, +{ 0xD37D, 0xD37D, 0xD37D }, +{ 0xD37E, 0xD37E, 0xD37E }, +{ 0xD37F, 0xD37F, 0xD37F }, +{ 0xD380, 0xD380, 0xD380 }, +{ 0xD381, 0xD381, 0xD381 }, +{ 0xD382, 0xD382, 0xD382 }, +{ 0xD383, 0xD383, 0xD383 }, +{ 0xD384, 0xD384, 0xD384 }, +{ 0xD385, 0xD385, 0xD385 }, +{ 0xD386, 0xD386, 0xD386 }, +{ 0xD387, 0xD387, 0xD387 }, +{ 0xD388, 0xD388, 0xD388 }, +{ 0xD389, 0xD389, 0xD389 }, +{ 0xD38A, 0xD38A, 0xD38A }, +{ 0xD38B, 0xD38B, 0xD38B }, +{ 0xD38C, 0xD38C, 0xD38C }, +{ 0xD38D, 0xD38D, 0xD38D }, +{ 0xD38E, 0xD38E, 0xD38E }, +{ 0xD38F, 0xD38F, 0xD38F }, +{ 0xD390, 0xD390, 0xD390 }, +{ 0xD391, 0xD391, 0xD391 }, +{ 0xD392, 0xD392, 0xD392 }, +{ 0xD393, 0xD393, 0xD393 }, +{ 0xD394, 0xD394, 0xD394 }, +{ 0xD395, 0xD395, 0xD395 }, +{ 0xD396, 0xD396, 0xD396 }, +{ 0xD397, 0xD397, 0xD397 }, +{ 0xD398, 0xD398, 0xD398 }, +{ 0xD399, 0xD399, 0xD399 }, +{ 0xD39A, 0xD39A, 0xD39A }, +{ 0xD39B, 0xD39B, 0xD39B }, +{ 0xD39C, 0xD39C, 0xD39C }, +{ 0xD39D, 0xD39D, 0xD39D }, +{ 0xD39E, 0xD39E, 0xD39E }, +{ 0xD39F, 0xD39F, 0xD39F }, +{ 0xD3A0, 0xD3A0, 0xD3A0 }, +{ 0xD3A1, 0xD3A1, 0xD3A1 }, +{ 0xD3A2, 0xD3A2, 0xD3A2 }, +{ 0xD3A3, 0xD3A3, 0xD3A3 }, +{ 0xD3A4, 0xD3A4, 0xD3A4 }, +{ 0xD3A5, 0xD3A5, 0xD3A5 }, +{ 0xD3A6, 0xD3A6, 0xD3A6 }, +{ 0xD3A7, 0xD3A7, 0xD3A7 }, +{ 0xD3A8, 0xD3A8, 0xD3A8 }, +{ 0xD3A9, 0xD3A9, 0xD3A9 }, +{ 0xD3AA, 0xD3AA, 0xD3AA }, +{ 0xD3AB, 0xD3AB, 0xD3AB }, +{ 0xD3AC, 0xD3AC, 0xD3AC }, +{ 0xD3AD, 0xD3AD, 0xD3AD }, +{ 0xD3AE, 0xD3AE, 0xD3AE }, +{ 0xD3AF, 0xD3AF, 0xD3AF }, +{ 0xD3B0, 0xD3B0, 0xD3B0 }, +{ 0xD3B1, 0xD3B1, 0xD3B1 }, +{ 0xD3B2, 0xD3B2, 0xD3B2 }, +{ 0xD3B3, 0xD3B3, 0xD3B3 }, +{ 0xD3B4, 0xD3B4, 0xD3B4 }, +{ 0xD3B5, 0xD3B5, 0xD3B5 }, +{ 0xD3B6, 0xD3B6, 0xD3B6 }, +{ 0xD3B7, 0xD3B7, 0xD3B7 }, +{ 0xD3B8, 0xD3B8, 0xD3B8 }, +{ 0xD3B9, 0xD3B9, 0xD3B9 }, +{ 0xD3BA, 0xD3BA, 0xD3BA }, +{ 0xD3BB, 0xD3BB, 0xD3BB }, +{ 0xD3BC, 0xD3BC, 0xD3BC }, +{ 0xD3BD, 0xD3BD, 0xD3BD }, +{ 0xD3BE, 0xD3BE, 0xD3BE }, +{ 0xD3BF, 0xD3BF, 0xD3BF }, +{ 0xD3C0, 0xD3C0, 0xD3C0 }, +{ 0xD3C1, 0xD3C1, 0xD3C1 }, +{ 0xD3C2, 0xD3C2, 0xD3C2 }, +{ 0xD3C3, 0xD3C3, 0xD3C3 }, +{ 0xD3C4, 0xD3C4, 0xD3C4 }, +{ 0xD3C5, 0xD3C5, 0xD3C5 }, +{ 0xD3C6, 0xD3C6, 0xD3C6 }, +{ 0xD3C7, 0xD3C7, 0xD3C7 }, +{ 0xD3C8, 0xD3C8, 0xD3C8 }, +{ 0xD3C9, 0xD3C9, 0xD3C9 }, +{ 0xD3CA, 0xD3CA, 0xD3CA }, +{ 0xD3CB, 0xD3CB, 0xD3CB }, +{ 0xD3CC, 0xD3CC, 0xD3CC }, +{ 0xD3CD, 0xD3CD, 0xD3CD }, +{ 0xD3CE, 0xD3CE, 0xD3CE }, +{ 0xD3CF, 0xD3CF, 0xD3CF }, +{ 0xD3D0, 0xD3D0, 0xD3D0 }, +{ 0xD3D1, 0xD3D1, 0xD3D1 }, +{ 0xD3D2, 0xD3D2, 0xD3D2 }, +{ 0xD3D3, 0xD3D3, 0xD3D3 }, +{ 0xD3D4, 0xD3D4, 0xD3D4 }, +{ 0xD3D5, 0xD3D5, 0xD3D5 }, +{ 0xD3D6, 0xD3D6, 0xD3D6 }, +{ 0xD3D7, 0xD3D7, 0xD3D7 }, +{ 0xD3D8, 0xD3D8, 0xD3D8 }, +{ 0xD3D9, 0xD3D9, 0xD3D9 }, +{ 0xD3DA, 0xD3DA, 0xD3DA }, +{ 0xD3DB, 0xD3DB, 0xD3DB }, +{ 0xD3DC, 0xD3DC, 0xD3DC }, +{ 0xD3DD, 0xD3DD, 0xD3DD }, +{ 0xD3DE, 0xD3DE, 0xD3DE }, +{ 0xD3DF, 0xD3DF, 0xD3DF }, +{ 0xD3E0, 0xD3E0, 0xD3E0 }, +{ 0xD3E1, 0xD3E1, 0xD3E1 }, +{ 0xD3E2, 0xD3E2, 0xD3E2 }, +{ 0xD3E3, 0xD3E3, 0xD3E3 }, +{ 0xD3E4, 0xD3E4, 0xD3E4 }, +{ 0xD3E5, 0xD3E5, 0xD3E5 }, +{ 0xD3E6, 0xD3E6, 0xD3E6 }, +{ 0xD3E7, 0xD3E7, 0xD3E7 }, +{ 0xD3E8, 0xD3E8, 0xD3E8 }, +{ 0xD3E9, 0xD3E9, 0xD3E9 }, +{ 0xD3EA, 0xD3EA, 0xD3EA }, +{ 0xD3EB, 0xD3EB, 0xD3EB }, +{ 0xD3EC, 0xD3EC, 0xD3EC }, +{ 0xD3ED, 0xD3ED, 0xD3ED }, +{ 0xD3EE, 0xD3EE, 0xD3EE }, +{ 0xD3EF, 0xD3EF, 0xD3EF }, +{ 0xD3F0, 0xD3F0, 0xD3F0 }, +{ 0xD3F1, 0xD3F1, 0xD3F1 }, +{ 0xD3F2, 0xD3F2, 0xD3F2 }, +{ 0xD3F3, 0xD3F3, 0xD3F3 }, +{ 0xD3F4, 0xD3F4, 0xD3F4 }, +{ 0xD3F5, 0xD3F5, 0xD3F5 }, +{ 0xD3F6, 0xD3F6, 0xD3F6 }, +{ 0xD3F7, 0xD3F7, 0xD3F7 }, +{ 0xD3F8, 0xD3F8, 0xD3F8 }, +{ 0xD3F9, 0xD3F9, 0xD3F9 }, +{ 0xD3FA, 0xD3FA, 0xD3FA }, +{ 0xD3FB, 0xD3FB, 0xD3FB }, +{ 0xD3FC, 0xD3FC, 0xD3FC }, +{ 0xD3FD, 0xD3FD, 0xD3FD }, +{ 0xD3FE, 0xD3FE, 0xD3FE }, +{ 0xD3FF, 0xD3FF, 0xD3FF }, +{ 0xD400, 0xD400, 0xD400 }, +{ 0xD401, 0xD401, 0xD401 }, +{ 0xD402, 0xD402, 0xD402 }, +{ 0xD403, 0xD403, 0xD403 }, +{ 0xD404, 0xD404, 0xD404 }, +{ 0xD405, 0xD405, 0xD405 }, +{ 0xD406, 0xD406, 0xD406 }, +{ 0xD407, 0xD407, 0xD407 }, +{ 0xD408, 0xD408, 0xD408 }, +{ 0xD409, 0xD409, 0xD409 }, +{ 0xD40A, 0xD40A, 0xD40A }, +{ 0xD40B, 0xD40B, 0xD40B }, +{ 0xD40C, 0xD40C, 0xD40C }, +{ 0xD40D, 0xD40D, 0xD40D }, +{ 0xD40E, 0xD40E, 0xD40E }, +{ 0xD40F, 0xD40F, 0xD40F }, +{ 0xD410, 0xD410, 0xD410 }, +{ 0xD411, 0xD411, 0xD411 }, +{ 0xD412, 0xD412, 0xD412 }, +{ 0xD413, 0xD413, 0xD413 }, +{ 0xD414, 0xD414, 0xD414 }, +{ 0xD415, 0xD415, 0xD415 }, +{ 0xD416, 0xD416, 0xD416 }, +{ 0xD417, 0xD417, 0xD417 }, +{ 0xD418, 0xD418, 0xD418 }, +{ 0xD419, 0xD419, 0xD419 }, +{ 0xD41A, 0xD41A, 0xD41A }, +{ 0xD41B, 0xD41B, 0xD41B }, +{ 0xD41C, 0xD41C, 0xD41C }, +{ 0xD41D, 0xD41D, 0xD41D }, +{ 0xD41E, 0xD41E, 0xD41E }, +{ 0xD41F, 0xD41F, 0xD41F }, +{ 0xD420, 0xD420, 0xD420 }, +{ 0xD421, 0xD421, 0xD421 }, +{ 0xD422, 0xD422, 0xD422 }, +{ 0xD423, 0xD423, 0xD423 }, +{ 0xD424, 0xD424, 0xD424 }, +{ 0xD425, 0xD425, 0xD425 }, +{ 0xD426, 0xD426, 0xD426 }, +{ 0xD427, 0xD427, 0xD427 }, +{ 0xD428, 0xD428, 0xD428 }, +{ 0xD429, 0xD429, 0xD429 }, +{ 0xD42A, 0xD42A, 0xD42A }, +{ 0xD42B, 0xD42B, 0xD42B }, +{ 0xD42C, 0xD42C, 0xD42C }, +{ 0xD42D, 0xD42D, 0xD42D }, +{ 0xD42E, 0xD42E, 0xD42E }, +{ 0xD42F, 0xD42F, 0xD42F }, +{ 0xD430, 0xD430, 0xD430 }, +{ 0xD431, 0xD431, 0xD431 }, +{ 0xD432, 0xD432, 0xD432 }, +{ 0xD433, 0xD433, 0xD433 }, +{ 0xD434, 0xD434, 0xD434 }, +{ 0xD435, 0xD435, 0xD435 }, +{ 0xD436, 0xD436, 0xD436 }, +{ 0xD437, 0xD437, 0xD437 }, +{ 0xD438, 0xD438, 0xD438 }, +{ 0xD439, 0xD439, 0xD439 }, +{ 0xD43A, 0xD43A, 0xD43A }, +{ 0xD43B, 0xD43B, 0xD43B }, +{ 0xD43C, 0xD43C, 0xD43C }, +{ 0xD43D, 0xD43D, 0xD43D }, +{ 0xD43E, 0xD43E, 0xD43E }, +{ 0xD43F, 0xD43F, 0xD43F }, +{ 0xD440, 0xD440, 0xD440 }, +{ 0xD441, 0xD441, 0xD441 }, +{ 0xD442, 0xD442, 0xD442 }, +{ 0xD443, 0xD443, 0xD443 }, +{ 0xD444, 0xD444, 0xD444 }, +{ 0xD445, 0xD445, 0xD445 }, +{ 0xD446, 0xD446, 0xD446 }, +{ 0xD447, 0xD447, 0xD447 }, +{ 0xD448, 0xD448, 0xD448 }, +{ 0xD449, 0xD449, 0xD449 }, +{ 0xD44A, 0xD44A, 0xD44A }, +{ 0xD44B, 0xD44B, 0xD44B }, +{ 0xD44C, 0xD44C, 0xD44C }, +{ 0xD44D, 0xD44D, 0xD44D }, +{ 0xD44E, 0xD44E, 0xD44E }, +{ 0xD44F, 0xD44F, 0xD44F }, +{ 0xD450, 0xD450, 0xD450 }, +{ 0xD451, 0xD451, 0xD451 }, +{ 0xD452, 0xD452, 0xD452 }, +{ 0xD453, 0xD453, 0xD453 }, +{ 0xD454, 0xD454, 0xD454 }, +{ 0xD455, 0xD455, 0xD455 }, +{ 0xD456, 0xD456, 0xD456 }, +{ 0xD457, 0xD457, 0xD457 }, +{ 0xD458, 0xD458, 0xD458 }, +{ 0xD459, 0xD459, 0xD459 }, +{ 0xD45A, 0xD45A, 0xD45A }, +{ 0xD45B, 0xD45B, 0xD45B }, +{ 0xD45C, 0xD45C, 0xD45C }, +{ 0xD45D, 0xD45D, 0xD45D }, +{ 0xD45E, 0xD45E, 0xD45E }, +{ 0xD45F, 0xD45F, 0xD45F }, +{ 0xD460, 0xD460, 0xD460 }, +{ 0xD461, 0xD461, 0xD461 }, +{ 0xD462, 0xD462, 0xD462 }, +{ 0xD463, 0xD463, 0xD463 }, +{ 0xD464, 0xD464, 0xD464 }, +{ 0xD465, 0xD465, 0xD465 }, +{ 0xD466, 0xD466, 0xD466 }, +{ 0xD467, 0xD467, 0xD467 }, +{ 0xD468, 0xD468, 0xD468 }, +{ 0xD469, 0xD469, 0xD469 }, +{ 0xD46A, 0xD46A, 0xD46A }, +{ 0xD46B, 0xD46B, 0xD46B }, +{ 0xD46C, 0xD46C, 0xD46C }, +{ 0xD46D, 0xD46D, 0xD46D }, +{ 0xD46E, 0xD46E, 0xD46E }, +{ 0xD46F, 0xD46F, 0xD46F }, +{ 0xD470, 0xD470, 0xD470 }, +{ 0xD471, 0xD471, 0xD471 }, +{ 0xD472, 0xD472, 0xD472 }, +{ 0xD473, 0xD473, 0xD473 }, +{ 0xD474, 0xD474, 0xD474 }, +{ 0xD475, 0xD475, 0xD475 }, +{ 0xD476, 0xD476, 0xD476 }, +{ 0xD477, 0xD477, 0xD477 }, +{ 0xD478, 0xD478, 0xD478 }, +{ 0xD479, 0xD479, 0xD479 }, +{ 0xD47A, 0xD47A, 0xD47A }, +{ 0xD47B, 0xD47B, 0xD47B }, +{ 0xD47C, 0xD47C, 0xD47C }, +{ 0xD47D, 0xD47D, 0xD47D }, +{ 0xD47E, 0xD47E, 0xD47E }, +{ 0xD47F, 0xD47F, 0xD47F }, +{ 0xD480, 0xD480, 0xD480 }, +{ 0xD481, 0xD481, 0xD481 }, +{ 0xD482, 0xD482, 0xD482 }, +{ 0xD483, 0xD483, 0xD483 }, +{ 0xD484, 0xD484, 0xD484 }, +{ 0xD485, 0xD485, 0xD485 }, +{ 0xD486, 0xD486, 0xD486 }, +{ 0xD487, 0xD487, 0xD487 }, +{ 0xD488, 0xD488, 0xD488 }, +{ 0xD489, 0xD489, 0xD489 }, +{ 0xD48A, 0xD48A, 0xD48A }, +{ 0xD48B, 0xD48B, 0xD48B }, +{ 0xD48C, 0xD48C, 0xD48C }, +{ 0xD48D, 0xD48D, 0xD48D }, +{ 0xD48E, 0xD48E, 0xD48E }, +{ 0xD48F, 0xD48F, 0xD48F }, +{ 0xD490, 0xD490, 0xD490 }, +{ 0xD491, 0xD491, 0xD491 }, +{ 0xD492, 0xD492, 0xD492 }, +{ 0xD493, 0xD493, 0xD493 }, +{ 0xD494, 0xD494, 0xD494 }, +{ 0xD495, 0xD495, 0xD495 }, +{ 0xD496, 0xD496, 0xD496 }, +{ 0xD497, 0xD497, 0xD497 }, +{ 0xD498, 0xD498, 0xD498 }, +{ 0xD499, 0xD499, 0xD499 }, +{ 0xD49A, 0xD49A, 0xD49A }, +{ 0xD49B, 0xD49B, 0xD49B }, +{ 0xD49C, 0xD49C, 0xD49C }, +{ 0xD49D, 0xD49D, 0xD49D }, +{ 0xD49E, 0xD49E, 0xD49E }, +{ 0xD49F, 0xD49F, 0xD49F }, +{ 0xD4A0, 0xD4A0, 0xD4A0 }, +{ 0xD4A1, 0xD4A1, 0xD4A1 }, +{ 0xD4A2, 0xD4A2, 0xD4A2 }, +{ 0xD4A3, 0xD4A3, 0xD4A3 }, +{ 0xD4A4, 0xD4A4, 0xD4A4 }, +{ 0xD4A5, 0xD4A5, 0xD4A5 }, +{ 0xD4A6, 0xD4A6, 0xD4A6 }, +{ 0xD4A7, 0xD4A7, 0xD4A7 }, +{ 0xD4A8, 0xD4A8, 0xD4A8 }, +{ 0xD4A9, 0xD4A9, 0xD4A9 }, +{ 0xD4AA, 0xD4AA, 0xD4AA }, +{ 0xD4AB, 0xD4AB, 0xD4AB }, +{ 0xD4AC, 0xD4AC, 0xD4AC }, +{ 0xD4AD, 0xD4AD, 0xD4AD }, +{ 0xD4AE, 0xD4AE, 0xD4AE }, +{ 0xD4AF, 0xD4AF, 0xD4AF }, +{ 0xD4B0, 0xD4B0, 0xD4B0 }, +{ 0xD4B1, 0xD4B1, 0xD4B1 }, +{ 0xD4B2, 0xD4B2, 0xD4B2 }, +{ 0xD4B3, 0xD4B3, 0xD4B3 }, +{ 0xD4B4, 0xD4B4, 0xD4B4 }, +{ 0xD4B5, 0xD4B5, 0xD4B5 }, +{ 0xD4B6, 0xD4B6, 0xD4B6 }, +{ 0xD4B7, 0xD4B7, 0xD4B7 }, +{ 0xD4B8, 0xD4B8, 0xD4B8 }, +{ 0xD4B9, 0xD4B9, 0xD4B9 }, +{ 0xD4BA, 0xD4BA, 0xD4BA }, +{ 0xD4BB, 0xD4BB, 0xD4BB }, +{ 0xD4BC, 0xD4BC, 0xD4BC }, +{ 0xD4BD, 0xD4BD, 0xD4BD }, +{ 0xD4BE, 0xD4BE, 0xD4BE }, +{ 0xD4BF, 0xD4BF, 0xD4BF }, +{ 0xD4C0, 0xD4C0, 0xD4C0 }, +{ 0xD4C1, 0xD4C1, 0xD4C1 }, +{ 0xD4C2, 0xD4C2, 0xD4C2 }, +{ 0xD4C3, 0xD4C3, 0xD4C3 }, +{ 0xD4C4, 0xD4C4, 0xD4C4 }, +{ 0xD4C5, 0xD4C5, 0xD4C5 }, +{ 0xD4C6, 0xD4C6, 0xD4C6 }, +{ 0xD4C7, 0xD4C7, 0xD4C7 }, +{ 0xD4C8, 0xD4C8, 0xD4C8 }, +{ 0xD4C9, 0xD4C9, 0xD4C9 }, +{ 0xD4CA, 0xD4CA, 0xD4CA }, +{ 0xD4CB, 0xD4CB, 0xD4CB }, +{ 0xD4CC, 0xD4CC, 0xD4CC }, +{ 0xD4CD, 0xD4CD, 0xD4CD }, +{ 0xD4CE, 0xD4CE, 0xD4CE }, +{ 0xD4CF, 0xD4CF, 0xD4CF }, +{ 0xD4D0, 0xD4D0, 0xD4D0 }, +{ 0xD4D1, 0xD4D1, 0xD4D1 }, +{ 0xD4D2, 0xD4D2, 0xD4D2 }, +{ 0xD4D3, 0xD4D3, 0xD4D3 }, +{ 0xD4D4, 0xD4D4, 0xD4D4 }, +{ 0xD4D5, 0xD4D5, 0xD4D5 }, +{ 0xD4D6, 0xD4D6, 0xD4D6 }, +{ 0xD4D7, 0xD4D7, 0xD4D7 }, +{ 0xD4D8, 0xD4D8, 0xD4D8 }, +{ 0xD4D9, 0xD4D9, 0xD4D9 }, +{ 0xD4DA, 0xD4DA, 0xD4DA }, +{ 0xD4DB, 0xD4DB, 0xD4DB }, +{ 0xD4DC, 0xD4DC, 0xD4DC }, +{ 0xD4DD, 0xD4DD, 0xD4DD }, +{ 0xD4DE, 0xD4DE, 0xD4DE }, +{ 0xD4DF, 0xD4DF, 0xD4DF }, +{ 0xD4E0, 0xD4E0, 0xD4E0 }, +{ 0xD4E1, 0xD4E1, 0xD4E1 }, +{ 0xD4E2, 0xD4E2, 0xD4E2 }, +{ 0xD4E3, 0xD4E3, 0xD4E3 }, +{ 0xD4E4, 0xD4E4, 0xD4E4 }, +{ 0xD4E5, 0xD4E5, 0xD4E5 }, +{ 0xD4E6, 0xD4E6, 0xD4E6 }, +{ 0xD4E7, 0xD4E7, 0xD4E7 }, +{ 0xD4E8, 0xD4E8, 0xD4E8 }, +{ 0xD4E9, 0xD4E9, 0xD4E9 }, +{ 0xD4EA, 0xD4EA, 0xD4EA }, +{ 0xD4EB, 0xD4EB, 0xD4EB }, +{ 0xD4EC, 0xD4EC, 0xD4EC }, +{ 0xD4ED, 0xD4ED, 0xD4ED }, +{ 0xD4EE, 0xD4EE, 0xD4EE }, +{ 0xD4EF, 0xD4EF, 0xD4EF }, +{ 0xD4F0, 0xD4F0, 0xD4F0 }, +{ 0xD4F1, 0xD4F1, 0xD4F1 }, +{ 0xD4F2, 0xD4F2, 0xD4F2 }, +{ 0xD4F3, 0xD4F3, 0xD4F3 }, +{ 0xD4F4, 0xD4F4, 0xD4F4 }, +{ 0xD4F5, 0xD4F5, 0xD4F5 }, +{ 0xD4F6, 0xD4F6, 0xD4F6 }, +{ 0xD4F7, 0xD4F7, 0xD4F7 }, +{ 0xD4F8, 0xD4F8, 0xD4F8 }, +{ 0xD4F9, 0xD4F9, 0xD4F9 }, +{ 0xD4FA, 0xD4FA, 0xD4FA }, +{ 0xD4FB, 0xD4FB, 0xD4FB }, +{ 0xD4FC, 0xD4FC, 0xD4FC }, +{ 0xD4FD, 0xD4FD, 0xD4FD }, +{ 0xD4FE, 0xD4FE, 0xD4FE }, +{ 0xD4FF, 0xD4FF, 0xD4FF }, +{ 0xD500, 0xD500, 0xD500 }, +{ 0xD501, 0xD501, 0xD501 }, +{ 0xD502, 0xD502, 0xD502 }, +{ 0xD503, 0xD503, 0xD503 }, +{ 0xD504, 0xD504, 0xD504 }, +{ 0xD505, 0xD505, 0xD505 }, +{ 0xD506, 0xD506, 0xD506 }, +{ 0xD507, 0xD507, 0xD507 }, +{ 0xD508, 0xD508, 0xD508 }, +{ 0xD509, 0xD509, 0xD509 }, +{ 0xD50A, 0xD50A, 0xD50A }, +{ 0xD50B, 0xD50B, 0xD50B }, +{ 0xD50C, 0xD50C, 0xD50C }, +{ 0xD50D, 0xD50D, 0xD50D }, +{ 0xD50E, 0xD50E, 0xD50E }, +{ 0xD50F, 0xD50F, 0xD50F }, +{ 0xD510, 0xD510, 0xD510 }, +{ 0xD511, 0xD511, 0xD511 }, +{ 0xD512, 0xD512, 0xD512 }, +{ 0xD513, 0xD513, 0xD513 }, +{ 0xD514, 0xD514, 0xD514 }, +{ 0xD515, 0xD515, 0xD515 }, +{ 0xD516, 0xD516, 0xD516 }, +{ 0xD517, 0xD517, 0xD517 }, +{ 0xD518, 0xD518, 0xD518 }, +{ 0xD519, 0xD519, 0xD519 }, +{ 0xD51A, 0xD51A, 0xD51A }, +{ 0xD51B, 0xD51B, 0xD51B }, +{ 0xD51C, 0xD51C, 0xD51C }, +{ 0xD51D, 0xD51D, 0xD51D }, +{ 0xD51E, 0xD51E, 0xD51E }, +{ 0xD51F, 0xD51F, 0xD51F }, +{ 0xD520, 0xD520, 0xD520 }, +{ 0xD521, 0xD521, 0xD521 }, +{ 0xD522, 0xD522, 0xD522 }, +{ 0xD523, 0xD523, 0xD523 }, +{ 0xD524, 0xD524, 0xD524 }, +{ 0xD525, 0xD525, 0xD525 }, +{ 0xD526, 0xD526, 0xD526 }, +{ 0xD527, 0xD527, 0xD527 }, +{ 0xD528, 0xD528, 0xD528 }, +{ 0xD529, 0xD529, 0xD529 }, +{ 0xD52A, 0xD52A, 0xD52A }, +{ 0xD52B, 0xD52B, 0xD52B }, +{ 0xD52C, 0xD52C, 0xD52C }, +{ 0xD52D, 0xD52D, 0xD52D }, +{ 0xD52E, 0xD52E, 0xD52E }, +{ 0xD52F, 0xD52F, 0xD52F }, +{ 0xD530, 0xD530, 0xD530 }, +{ 0xD531, 0xD531, 0xD531 }, +{ 0xD532, 0xD532, 0xD532 }, +{ 0xD533, 0xD533, 0xD533 }, +{ 0xD534, 0xD534, 0xD534 }, +{ 0xD535, 0xD535, 0xD535 }, +{ 0xD536, 0xD536, 0xD536 }, +{ 0xD537, 0xD537, 0xD537 }, +{ 0xD538, 0xD538, 0xD538 }, +{ 0xD539, 0xD539, 0xD539 }, +{ 0xD53A, 0xD53A, 0xD53A }, +{ 0xD53B, 0xD53B, 0xD53B }, +{ 0xD53C, 0xD53C, 0xD53C }, +{ 0xD53D, 0xD53D, 0xD53D }, +{ 0xD53E, 0xD53E, 0xD53E }, +{ 0xD53F, 0xD53F, 0xD53F }, +{ 0xD540, 0xD540, 0xD540 }, +{ 0xD541, 0xD541, 0xD541 }, +{ 0xD542, 0xD542, 0xD542 }, +{ 0xD543, 0xD543, 0xD543 }, +{ 0xD544, 0xD544, 0xD544 }, +{ 0xD545, 0xD545, 0xD545 }, +{ 0xD546, 0xD546, 0xD546 }, +{ 0xD547, 0xD547, 0xD547 }, +{ 0xD548, 0xD548, 0xD548 }, +{ 0xD549, 0xD549, 0xD549 }, +{ 0xD54A, 0xD54A, 0xD54A }, +{ 0xD54B, 0xD54B, 0xD54B }, +{ 0xD54C, 0xD54C, 0xD54C }, +{ 0xD54D, 0xD54D, 0xD54D }, +{ 0xD54E, 0xD54E, 0xD54E }, +{ 0xD54F, 0xD54F, 0xD54F }, +{ 0xD550, 0xD550, 0xD550 }, +{ 0xD551, 0xD551, 0xD551 }, +{ 0xD552, 0xD552, 0xD552 }, +{ 0xD553, 0xD553, 0xD553 }, +{ 0xD554, 0xD554, 0xD554 }, +{ 0xD555, 0xD555, 0xD555 }, +{ 0xD556, 0xD556, 0xD556 }, +{ 0xD557, 0xD557, 0xD557 }, +{ 0xD558, 0xD558, 0xD558 }, +{ 0xD559, 0xD559, 0xD559 }, +{ 0xD55A, 0xD55A, 0xD55A }, +{ 0xD55B, 0xD55B, 0xD55B }, +{ 0xD55C, 0xD55C, 0xD55C }, +{ 0xD55D, 0xD55D, 0xD55D }, +{ 0xD55E, 0xD55E, 0xD55E }, +{ 0xD55F, 0xD55F, 0xD55F }, +{ 0xD560, 0xD560, 0xD560 }, +{ 0xD561, 0xD561, 0xD561 }, +{ 0xD562, 0xD562, 0xD562 }, +{ 0xD563, 0xD563, 0xD563 }, +{ 0xD564, 0xD564, 0xD564 }, +{ 0xD565, 0xD565, 0xD565 }, +{ 0xD566, 0xD566, 0xD566 }, +{ 0xD567, 0xD567, 0xD567 }, +{ 0xD568, 0xD568, 0xD568 }, +{ 0xD569, 0xD569, 0xD569 }, +{ 0xD56A, 0xD56A, 0xD56A }, +{ 0xD56B, 0xD56B, 0xD56B }, +{ 0xD56C, 0xD56C, 0xD56C }, +{ 0xD56D, 0xD56D, 0xD56D }, +{ 0xD56E, 0xD56E, 0xD56E }, +{ 0xD56F, 0xD56F, 0xD56F }, +{ 0xD570, 0xD570, 0xD570 }, +{ 0xD571, 0xD571, 0xD571 }, +{ 0xD572, 0xD572, 0xD572 }, +{ 0xD573, 0xD573, 0xD573 }, +{ 0xD574, 0xD574, 0xD574 }, +{ 0xD575, 0xD575, 0xD575 }, +{ 0xD576, 0xD576, 0xD576 }, +{ 0xD577, 0xD577, 0xD577 }, +{ 0xD578, 0xD578, 0xD578 }, +{ 0xD579, 0xD579, 0xD579 }, +{ 0xD57A, 0xD57A, 0xD57A }, +{ 0xD57B, 0xD57B, 0xD57B }, +{ 0xD57C, 0xD57C, 0xD57C }, +{ 0xD57D, 0xD57D, 0xD57D }, +{ 0xD57E, 0xD57E, 0xD57E }, +{ 0xD57F, 0xD57F, 0xD57F }, +{ 0xD580, 0xD580, 0xD580 }, +{ 0xD581, 0xD581, 0xD581 }, +{ 0xD582, 0xD582, 0xD582 }, +{ 0xD583, 0xD583, 0xD583 }, +{ 0xD584, 0xD584, 0xD584 }, +{ 0xD585, 0xD585, 0xD585 }, +{ 0xD586, 0xD586, 0xD586 }, +{ 0xD587, 0xD587, 0xD587 }, +{ 0xD588, 0xD588, 0xD588 }, +{ 0xD589, 0xD589, 0xD589 }, +{ 0xD58A, 0xD58A, 0xD58A }, +{ 0xD58B, 0xD58B, 0xD58B }, +{ 0xD58C, 0xD58C, 0xD58C }, +{ 0xD58D, 0xD58D, 0xD58D }, +{ 0xD58E, 0xD58E, 0xD58E }, +{ 0xD58F, 0xD58F, 0xD58F }, +{ 0xD590, 0xD590, 0xD590 }, +{ 0xD591, 0xD591, 0xD591 }, +{ 0xD592, 0xD592, 0xD592 }, +{ 0xD593, 0xD593, 0xD593 }, +{ 0xD594, 0xD594, 0xD594 }, +{ 0xD595, 0xD595, 0xD595 }, +{ 0xD596, 0xD596, 0xD596 }, +{ 0xD597, 0xD597, 0xD597 }, +{ 0xD598, 0xD598, 0xD598 }, +{ 0xD599, 0xD599, 0xD599 }, +{ 0xD59A, 0xD59A, 0xD59A }, +{ 0xD59B, 0xD59B, 0xD59B }, +{ 0xD59C, 0xD59C, 0xD59C }, +{ 0xD59D, 0xD59D, 0xD59D }, +{ 0xD59E, 0xD59E, 0xD59E }, +{ 0xD59F, 0xD59F, 0xD59F }, +{ 0xD5A0, 0xD5A0, 0xD5A0 }, +{ 0xD5A1, 0xD5A1, 0xD5A1 }, +{ 0xD5A2, 0xD5A2, 0xD5A2 }, +{ 0xD5A3, 0xD5A3, 0xD5A3 }, +{ 0xD5A4, 0xD5A4, 0xD5A4 }, +{ 0xD5A5, 0xD5A5, 0xD5A5 }, +{ 0xD5A6, 0xD5A6, 0xD5A6 }, +{ 0xD5A7, 0xD5A7, 0xD5A7 }, +{ 0xD5A8, 0xD5A8, 0xD5A8 }, +{ 0xD5A9, 0xD5A9, 0xD5A9 }, +{ 0xD5AA, 0xD5AA, 0xD5AA }, +{ 0xD5AB, 0xD5AB, 0xD5AB }, +{ 0xD5AC, 0xD5AC, 0xD5AC }, +{ 0xD5AD, 0xD5AD, 0xD5AD }, +{ 0xD5AE, 0xD5AE, 0xD5AE }, +{ 0xD5AF, 0xD5AF, 0xD5AF }, +{ 0xD5B0, 0xD5B0, 0xD5B0 }, +{ 0xD5B1, 0xD5B1, 0xD5B1 }, +{ 0xD5B2, 0xD5B2, 0xD5B2 }, +{ 0xD5B3, 0xD5B3, 0xD5B3 }, +{ 0xD5B4, 0xD5B4, 0xD5B4 }, +{ 0xD5B5, 0xD5B5, 0xD5B5 }, +{ 0xD5B6, 0xD5B6, 0xD5B6 }, +{ 0xD5B7, 0xD5B7, 0xD5B7 }, +{ 0xD5B8, 0xD5B8, 0xD5B8 }, +{ 0xD5B9, 0xD5B9, 0xD5B9 }, +{ 0xD5BA, 0xD5BA, 0xD5BA }, +{ 0xD5BB, 0xD5BB, 0xD5BB }, +{ 0xD5BC, 0xD5BC, 0xD5BC }, +{ 0xD5BD, 0xD5BD, 0xD5BD }, +{ 0xD5BE, 0xD5BE, 0xD5BE }, +{ 0xD5BF, 0xD5BF, 0xD5BF }, +{ 0xD5C0, 0xD5C0, 0xD5C0 }, +{ 0xD5C1, 0xD5C1, 0xD5C1 }, +{ 0xD5C2, 0xD5C2, 0xD5C2 }, +{ 0xD5C3, 0xD5C3, 0xD5C3 }, +{ 0xD5C4, 0xD5C4, 0xD5C4 }, +{ 0xD5C5, 0xD5C5, 0xD5C5 }, +{ 0xD5C6, 0xD5C6, 0xD5C6 }, +{ 0xD5C7, 0xD5C7, 0xD5C7 }, +{ 0xD5C8, 0xD5C8, 0xD5C8 }, +{ 0xD5C9, 0xD5C9, 0xD5C9 }, +{ 0xD5CA, 0xD5CA, 0xD5CA }, +{ 0xD5CB, 0xD5CB, 0xD5CB }, +{ 0xD5CC, 0xD5CC, 0xD5CC }, +{ 0xD5CD, 0xD5CD, 0xD5CD }, +{ 0xD5CE, 0xD5CE, 0xD5CE }, +{ 0xD5CF, 0xD5CF, 0xD5CF }, +{ 0xD5D0, 0xD5D0, 0xD5D0 }, +{ 0xD5D1, 0xD5D1, 0xD5D1 }, +{ 0xD5D2, 0xD5D2, 0xD5D2 }, +{ 0xD5D3, 0xD5D3, 0xD5D3 }, +{ 0xD5D4, 0xD5D4, 0xD5D4 }, +{ 0xD5D5, 0xD5D5, 0xD5D5 }, +{ 0xD5D6, 0xD5D6, 0xD5D6 }, +{ 0xD5D7, 0xD5D7, 0xD5D7 }, +{ 0xD5D8, 0xD5D8, 0xD5D8 }, +{ 0xD5D9, 0xD5D9, 0xD5D9 }, +{ 0xD5DA, 0xD5DA, 0xD5DA }, +{ 0xD5DB, 0xD5DB, 0xD5DB }, +{ 0xD5DC, 0xD5DC, 0xD5DC }, +{ 0xD5DD, 0xD5DD, 0xD5DD }, +{ 0xD5DE, 0xD5DE, 0xD5DE }, +{ 0xD5DF, 0xD5DF, 0xD5DF }, +{ 0xD5E0, 0xD5E0, 0xD5E0 }, +{ 0xD5E1, 0xD5E1, 0xD5E1 }, +{ 0xD5E2, 0xD5E2, 0xD5E2 }, +{ 0xD5E3, 0xD5E3, 0xD5E3 }, +{ 0xD5E4, 0xD5E4, 0xD5E4 }, +{ 0xD5E5, 0xD5E5, 0xD5E5 }, +{ 0xD5E6, 0xD5E6, 0xD5E6 }, +{ 0xD5E7, 0xD5E7, 0xD5E7 }, +{ 0xD5E8, 0xD5E8, 0xD5E8 }, +{ 0xD5E9, 0xD5E9, 0xD5E9 }, +{ 0xD5EA, 0xD5EA, 0xD5EA }, +{ 0xD5EB, 0xD5EB, 0xD5EB }, +{ 0xD5EC, 0xD5EC, 0xD5EC }, +{ 0xD5ED, 0xD5ED, 0xD5ED }, +{ 0xD5EE, 0xD5EE, 0xD5EE }, +{ 0xD5EF, 0xD5EF, 0xD5EF }, +{ 0xD5F0, 0xD5F0, 0xD5F0 }, +{ 0xD5F1, 0xD5F1, 0xD5F1 }, +{ 0xD5F2, 0xD5F2, 0xD5F2 }, +{ 0xD5F3, 0xD5F3, 0xD5F3 }, +{ 0xD5F4, 0xD5F4, 0xD5F4 }, +{ 0xD5F5, 0xD5F5, 0xD5F5 }, +{ 0xD5F6, 0xD5F6, 0xD5F6 }, +{ 0xD5F7, 0xD5F7, 0xD5F7 }, +{ 0xD5F8, 0xD5F8, 0xD5F8 }, +{ 0xD5F9, 0xD5F9, 0xD5F9 }, +{ 0xD5FA, 0xD5FA, 0xD5FA }, +{ 0xD5FB, 0xD5FB, 0xD5FB }, +{ 0xD5FC, 0xD5FC, 0xD5FC }, +{ 0xD5FD, 0xD5FD, 0xD5FD }, +{ 0xD5FE, 0xD5FE, 0xD5FE }, +{ 0xD5FF, 0xD5FF, 0xD5FF }, +{ 0xD600, 0xD600, 0xD600 }, +{ 0xD601, 0xD601, 0xD601 }, +{ 0xD602, 0xD602, 0xD602 }, +{ 0xD603, 0xD603, 0xD603 }, +{ 0xD604, 0xD604, 0xD604 }, +{ 0xD605, 0xD605, 0xD605 }, +{ 0xD606, 0xD606, 0xD606 }, +{ 0xD607, 0xD607, 0xD607 }, +{ 0xD608, 0xD608, 0xD608 }, +{ 0xD609, 0xD609, 0xD609 }, +{ 0xD60A, 0xD60A, 0xD60A }, +{ 0xD60B, 0xD60B, 0xD60B }, +{ 0xD60C, 0xD60C, 0xD60C }, +{ 0xD60D, 0xD60D, 0xD60D }, +{ 0xD60E, 0xD60E, 0xD60E }, +{ 0xD60F, 0xD60F, 0xD60F }, +{ 0xD610, 0xD610, 0xD610 }, +{ 0xD611, 0xD611, 0xD611 }, +{ 0xD612, 0xD612, 0xD612 }, +{ 0xD613, 0xD613, 0xD613 }, +{ 0xD614, 0xD614, 0xD614 }, +{ 0xD615, 0xD615, 0xD615 }, +{ 0xD616, 0xD616, 0xD616 }, +{ 0xD617, 0xD617, 0xD617 }, +{ 0xD618, 0xD618, 0xD618 }, +{ 0xD619, 0xD619, 0xD619 }, +{ 0xD61A, 0xD61A, 0xD61A }, +{ 0xD61B, 0xD61B, 0xD61B }, +{ 0xD61C, 0xD61C, 0xD61C }, +{ 0xD61D, 0xD61D, 0xD61D }, +{ 0xD61E, 0xD61E, 0xD61E }, +{ 0xD61F, 0xD61F, 0xD61F }, +{ 0xD620, 0xD620, 0xD620 }, +{ 0xD621, 0xD621, 0xD621 }, +{ 0xD622, 0xD622, 0xD622 }, +{ 0xD623, 0xD623, 0xD623 }, +{ 0xD624, 0xD624, 0xD624 }, +{ 0xD625, 0xD625, 0xD625 }, +{ 0xD626, 0xD626, 0xD626 }, +{ 0xD627, 0xD627, 0xD627 }, +{ 0xD628, 0xD628, 0xD628 }, +{ 0xD629, 0xD629, 0xD629 }, +{ 0xD62A, 0xD62A, 0xD62A }, +{ 0xD62B, 0xD62B, 0xD62B }, +{ 0xD62C, 0xD62C, 0xD62C }, +{ 0xD62D, 0xD62D, 0xD62D }, +{ 0xD62E, 0xD62E, 0xD62E }, +{ 0xD62F, 0xD62F, 0xD62F }, +{ 0xD630, 0xD630, 0xD630 }, +{ 0xD631, 0xD631, 0xD631 }, +{ 0xD632, 0xD632, 0xD632 }, +{ 0xD633, 0xD633, 0xD633 }, +{ 0xD634, 0xD634, 0xD634 }, +{ 0xD635, 0xD635, 0xD635 }, +{ 0xD636, 0xD636, 0xD636 }, +{ 0xD637, 0xD637, 0xD637 }, +{ 0xD638, 0xD638, 0xD638 }, +{ 0xD639, 0xD639, 0xD639 }, +{ 0xD63A, 0xD63A, 0xD63A }, +{ 0xD63B, 0xD63B, 0xD63B }, +{ 0xD63C, 0xD63C, 0xD63C }, +{ 0xD63D, 0xD63D, 0xD63D }, +{ 0xD63E, 0xD63E, 0xD63E }, +{ 0xD63F, 0xD63F, 0xD63F }, +{ 0xD640, 0xD640, 0xD640 }, +{ 0xD641, 0xD641, 0xD641 }, +{ 0xD642, 0xD642, 0xD642 }, +{ 0xD643, 0xD643, 0xD643 }, +{ 0xD644, 0xD644, 0xD644 }, +{ 0xD645, 0xD645, 0xD645 }, +{ 0xD646, 0xD646, 0xD646 }, +{ 0xD647, 0xD647, 0xD647 }, +{ 0xD648, 0xD648, 0xD648 }, +{ 0xD649, 0xD649, 0xD649 }, +{ 0xD64A, 0xD64A, 0xD64A }, +{ 0xD64B, 0xD64B, 0xD64B }, +{ 0xD64C, 0xD64C, 0xD64C }, +{ 0xD64D, 0xD64D, 0xD64D }, +{ 0xD64E, 0xD64E, 0xD64E }, +{ 0xD64F, 0xD64F, 0xD64F }, +{ 0xD650, 0xD650, 0xD650 }, +{ 0xD651, 0xD651, 0xD651 }, +{ 0xD652, 0xD652, 0xD652 }, +{ 0xD653, 0xD653, 0xD653 }, +{ 0xD654, 0xD654, 0xD654 }, +{ 0xD655, 0xD655, 0xD655 }, +{ 0xD656, 0xD656, 0xD656 }, +{ 0xD657, 0xD657, 0xD657 }, +{ 0xD658, 0xD658, 0xD658 }, +{ 0xD659, 0xD659, 0xD659 }, +{ 0xD65A, 0xD65A, 0xD65A }, +{ 0xD65B, 0xD65B, 0xD65B }, +{ 0xD65C, 0xD65C, 0xD65C }, +{ 0xD65D, 0xD65D, 0xD65D }, +{ 0xD65E, 0xD65E, 0xD65E }, +{ 0xD65F, 0xD65F, 0xD65F }, +{ 0xD660, 0xD660, 0xD660 }, +{ 0xD661, 0xD661, 0xD661 }, +{ 0xD662, 0xD662, 0xD662 }, +{ 0xD663, 0xD663, 0xD663 }, +{ 0xD664, 0xD664, 0xD664 }, +{ 0xD665, 0xD665, 0xD665 }, +{ 0xD666, 0xD666, 0xD666 }, +{ 0xD667, 0xD667, 0xD667 }, +{ 0xD668, 0xD668, 0xD668 }, +{ 0xD669, 0xD669, 0xD669 }, +{ 0xD66A, 0xD66A, 0xD66A }, +{ 0xD66B, 0xD66B, 0xD66B }, +{ 0xD66C, 0xD66C, 0xD66C }, +{ 0xD66D, 0xD66D, 0xD66D }, +{ 0xD66E, 0xD66E, 0xD66E }, +{ 0xD66F, 0xD66F, 0xD66F }, +{ 0xD670, 0xD670, 0xD670 }, +{ 0xD671, 0xD671, 0xD671 }, +{ 0xD672, 0xD672, 0xD672 }, +{ 0xD673, 0xD673, 0xD673 }, +{ 0xD674, 0xD674, 0xD674 }, +{ 0xD675, 0xD675, 0xD675 }, +{ 0xD676, 0xD676, 0xD676 }, +{ 0xD677, 0xD677, 0xD677 }, +{ 0xD678, 0xD678, 0xD678 }, +{ 0xD679, 0xD679, 0xD679 }, +{ 0xD67A, 0xD67A, 0xD67A }, +{ 0xD67B, 0xD67B, 0xD67B }, +{ 0xD67C, 0xD67C, 0xD67C }, +{ 0xD67D, 0xD67D, 0xD67D }, +{ 0xD67E, 0xD67E, 0xD67E }, +{ 0xD67F, 0xD67F, 0xD67F }, +{ 0xD680, 0xD680, 0xD680 }, +{ 0xD681, 0xD681, 0xD681 }, +{ 0xD682, 0xD682, 0xD682 }, +{ 0xD683, 0xD683, 0xD683 }, +{ 0xD684, 0xD684, 0xD684 }, +{ 0xD685, 0xD685, 0xD685 }, +{ 0xD686, 0xD686, 0xD686 }, +{ 0xD687, 0xD687, 0xD687 }, +{ 0xD688, 0xD688, 0xD688 }, +{ 0xD689, 0xD689, 0xD689 }, +{ 0xD68A, 0xD68A, 0xD68A }, +{ 0xD68B, 0xD68B, 0xD68B }, +{ 0xD68C, 0xD68C, 0xD68C }, +{ 0xD68D, 0xD68D, 0xD68D }, +{ 0xD68E, 0xD68E, 0xD68E }, +{ 0xD68F, 0xD68F, 0xD68F }, +{ 0xD690, 0xD690, 0xD690 }, +{ 0xD691, 0xD691, 0xD691 }, +{ 0xD692, 0xD692, 0xD692 }, +{ 0xD693, 0xD693, 0xD693 }, +{ 0xD694, 0xD694, 0xD694 }, +{ 0xD695, 0xD695, 0xD695 }, +{ 0xD696, 0xD696, 0xD696 }, +{ 0xD697, 0xD697, 0xD697 }, +{ 0xD698, 0xD698, 0xD698 }, +{ 0xD699, 0xD699, 0xD699 }, +{ 0xD69A, 0xD69A, 0xD69A }, +{ 0xD69B, 0xD69B, 0xD69B }, +{ 0xD69C, 0xD69C, 0xD69C }, +{ 0xD69D, 0xD69D, 0xD69D }, +{ 0xD69E, 0xD69E, 0xD69E }, +{ 0xD69F, 0xD69F, 0xD69F }, +{ 0xD6A0, 0xD6A0, 0xD6A0 }, +{ 0xD6A1, 0xD6A1, 0xD6A1 }, +{ 0xD6A2, 0xD6A2, 0xD6A2 }, +{ 0xD6A3, 0xD6A3, 0xD6A3 }, +{ 0xD6A4, 0xD6A4, 0xD6A4 }, +{ 0xD6A5, 0xD6A5, 0xD6A5 }, +{ 0xD6A6, 0xD6A6, 0xD6A6 }, +{ 0xD6A7, 0xD6A7, 0xD6A7 }, +{ 0xD6A8, 0xD6A8, 0xD6A8 }, +{ 0xD6A9, 0xD6A9, 0xD6A9 }, +{ 0xD6AA, 0xD6AA, 0xD6AA }, +{ 0xD6AB, 0xD6AB, 0xD6AB }, +{ 0xD6AC, 0xD6AC, 0xD6AC }, +{ 0xD6AD, 0xD6AD, 0xD6AD }, +{ 0xD6AE, 0xD6AE, 0xD6AE }, +{ 0xD6AF, 0xD6AF, 0xD6AF }, +{ 0xD6B0, 0xD6B0, 0xD6B0 }, +{ 0xD6B1, 0xD6B1, 0xD6B1 }, +{ 0xD6B2, 0xD6B2, 0xD6B2 }, +{ 0xD6B3, 0xD6B3, 0xD6B3 }, +{ 0xD6B4, 0xD6B4, 0xD6B4 }, +{ 0xD6B5, 0xD6B5, 0xD6B5 }, +{ 0xD6B6, 0xD6B6, 0xD6B6 }, +{ 0xD6B7, 0xD6B7, 0xD6B7 }, +{ 0xD6B8, 0xD6B8, 0xD6B8 }, +{ 0xD6B9, 0xD6B9, 0xD6B9 }, +{ 0xD6BA, 0xD6BA, 0xD6BA }, +{ 0xD6BB, 0xD6BB, 0xD6BB }, +{ 0xD6BC, 0xD6BC, 0xD6BC }, +{ 0xD6BD, 0xD6BD, 0xD6BD }, +{ 0xD6BE, 0xD6BE, 0xD6BE }, +{ 0xD6BF, 0xD6BF, 0xD6BF }, +{ 0xD6C0, 0xD6C0, 0xD6C0 }, +{ 0xD6C1, 0xD6C1, 0xD6C1 }, +{ 0xD6C2, 0xD6C2, 0xD6C2 }, +{ 0xD6C3, 0xD6C3, 0xD6C3 }, +{ 0xD6C4, 0xD6C4, 0xD6C4 }, +{ 0xD6C5, 0xD6C5, 0xD6C5 }, +{ 0xD6C6, 0xD6C6, 0xD6C6 }, +{ 0xD6C7, 0xD6C7, 0xD6C7 }, +{ 0xD6C8, 0xD6C8, 0xD6C8 }, +{ 0xD6C9, 0xD6C9, 0xD6C9 }, +{ 0xD6CA, 0xD6CA, 0xD6CA }, +{ 0xD6CB, 0xD6CB, 0xD6CB }, +{ 0xD6CC, 0xD6CC, 0xD6CC }, +{ 0xD6CD, 0xD6CD, 0xD6CD }, +{ 0xD6CE, 0xD6CE, 0xD6CE }, +{ 0xD6CF, 0xD6CF, 0xD6CF }, +{ 0xD6D0, 0xD6D0, 0xD6D0 }, +{ 0xD6D1, 0xD6D1, 0xD6D1 }, +{ 0xD6D2, 0xD6D2, 0xD6D2 }, +{ 0xD6D3, 0xD6D3, 0xD6D3 }, +{ 0xD6D4, 0xD6D4, 0xD6D4 }, +{ 0xD6D5, 0xD6D5, 0xD6D5 }, +{ 0xD6D6, 0xD6D6, 0xD6D6 }, +{ 0xD6D7, 0xD6D7, 0xD6D7 }, +{ 0xD6D8, 0xD6D8, 0xD6D8 }, +{ 0xD6D9, 0xD6D9, 0xD6D9 }, +{ 0xD6DA, 0xD6DA, 0xD6DA }, +{ 0xD6DB, 0xD6DB, 0xD6DB }, +{ 0xD6DC, 0xD6DC, 0xD6DC }, +{ 0xD6DD, 0xD6DD, 0xD6DD }, +{ 0xD6DE, 0xD6DE, 0xD6DE }, +{ 0xD6DF, 0xD6DF, 0xD6DF }, +{ 0xD6E0, 0xD6E0, 0xD6E0 }, +{ 0xD6E1, 0xD6E1, 0xD6E1 }, +{ 0xD6E2, 0xD6E2, 0xD6E2 }, +{ 0xD6E3, 0xD6E3, 0xD6E3 }, +{ 0xD6E4, 0xD6E4, 0xD6E4 }, +{ 0xD6E5, 0xD6E5, 0xD6E5 }, +{ 0xD6E6, 0xD6E6, 0xD6E6 }, +{ 0xD6E7, 0xD6E7, 0xD6E7 }, +{ 0xD6E8, 0xD6E8, 0xD6E8 }, +{ 0xD6E9, 0xD6E9, 0xD6E9 }, +{ 0xD6EA, 0xD6EA, 0xD6EA }, +{ 0xD6EB, 0xD6EB, 0xD6EB }, +{ 0xD6EC, 0xD6EC, 0xD6EC }, +{ 0xD6ED, 0xD6ED, 0xD6ED }, +{ 0xD6EE, 0xD6EE, 0xD6EE }, +{ 0xD6EF, 0xD6EF, 0xD6EF }, +{ 0xD6F0, 0xD6F0, 0xD6F0 }, +{ 0xD6F1, 0xD6F1, 0xD6F1 }, +{ 0xD6F2, 0xD6F2, 0xD6F2 }, +{ 0xD6F3, 0xD6F3, 0xD6F3 }, +{ 0xD6F4, 0xD6F4, 0xD6F4 }, +{ 0xD6F5, 0xD6F5, 0xD6F5 }, +{ 0xD6F6, 0xD6F6, 0xD6F6 }, +{ 0xD6F7, 0xD6F7, 0xD6F7 }, +{ 0xD6F8, 0xD6F8, 0xD6F8 }, +{ 0xD6F9, 0xD6F9, 0xD6F9 }, +{ 0xD6FA, 0xD6FA, 0xD6FA }, +{ 0xD6FB, 0xD6FB, 0xD6FB }, +{ 0xD6FC, 0xD6FC, 0xD6FC }, +{ 0xD6FD, 0xD6FD, 0xD6FD }, +{ 0xD6FE, 0xD6FE, 0xD6FE }, +{ 0xD6FF, 0xD6FF, 0xD6FF }, +{ 0xD700, 0xD700, 0xD700 }, +{ 0xD701, 0xD701, 0xD701 }, +{ 0xD702, 0xD702, 0xD702 }, +{ 0xD703, 0xD703, 0xD703 }, +{ 0xD704, 0xD704, 0xD704 }, +{ 0xD705, 0xD705, 0xD705 }, +{ 0xD706, 0xD706, 0xD706 }, +{ 0xD707, 0xD707, 0xD707 }, +{ 0xD708, 0xD708, 0xD708 }, +{ 0xD709, 0xD709, 0xD709 }, +{ 0xD70A, 0xD70A, 0xD70A }, +{ 0xD70B, 0xD70B, 0xD70B }, +{ 0xD70C, 0xD70C, 0xD70C }, +{ 0xD70D, 0xD70D, 0xD70D }, +{ 0xD70E, 0xD70E, 0xD70E }, +{ 0xD70F, 0xD70F, 0xD70F }, +{ 0xD710, 0xD710, 0xD710 }, +{ 0xD711, 0xD711, 0xD711 }, +{ 0xD712, 0xD712, 0xD712 }, +{ 0xD713, 0xD713, 0xD713 }, +{ 0xD714, 0xD714, 0xD714 }, +{ 0xD715, 0xD715, 0xD715 }, +{ 0xD716, 0xD716, 0xD716 }, +{ 0xD717, 0xD717, 0xD717 }, +{ 0xD718, 0xD718, 0xD718 }, +{ 0xD719, 0xD719, 0xD719 }, +{ 0xD71A, 0xD71A, 0xD71A }, +{ 0xD71B, 0xD71B, 0xD71B }, +{ 0xD71C, 0xD71C, 0xD71C }, +{ 0xD71D, 0xD71D, 0xD71D }, +{ 0xD71E, 0xD71E, 0xD71E }, +{ 0xD71F, 0xD71F, 0xD71F }, +{ 0xD720, 0xD720, 0xD720 }, +{ 0xD721, 0xD721, 0xD721 }, +{ 0xD722, 0xD722, 0xD722 }, +{ 0xD723, 0xD723, 0xD723 }, +{ 0xD724, 0xD724, 0xD724 }, +{ 0xD725, 0xD725, 0xD725 }, +{ 0xD726, 0xD726, 0xD726 }, +{ 0xD727, 0xD727, 0xD727 }, +{ 0xD728, 0xD728, 0xD728 }, +{ 0xD729, 0xD729, 0xD729 }, +{ 0xD72A, 0xD72A, 0xD72A }, +{ 0xD72B, 0xD72B, 0xD72B }, +{ 0xD72C, 0xD72C, 0xD72C }, +{ 0xD72D, 0xD72D, 0xD72D }, +{ 0xD72E, 0xD72E, 0xD72E }, +{ 0xD72F, 0xD72F, 0xD72F }, +{ 0xD730, 0xD730, 0xD730 }, +{ 0xD731, 0xD731, 0xD731 }, +{ 0xD732, 0xD732, 0xD732 }, +{ 0xD733, 0xD733, 0xD733 }, +{ 0xD734, 0xD734, 0xD734 }, +{ 0xD735, 0xD735, 0xD735 }, +{ 0xD736, 0xD736, 0xD736 }, +{ 0xD737, 0xD737, 0xD737 }, +{ 0xD738, 0xD738, 0xD738 }, +{ 0xD739, 0xD739, 0xD739 }, +{ 0xD73A, 0xD73A, 0xD73A }, +{ 0xD73B, 0xD73B, 0xD73B }, +{ 0xD73C, 0xD73C, 0xD73C }, +{ 0xD73D, 0xD73D, 0xD73D }, +{ 0xD73E, 0xD73E, 0xD73E }, +{ 0xD73F, 0xD73F, 0xD73F }, +{ 0xD740, 0xD740, 0xD740 }, +{ 0xD741, 0xD741, 0xD741 }, +{ 0xD742, 0xD742, 0xD742 }, +{ 0xD743, 0xD743, 0xD743 }, +{ 0xD744, 0xD744, 0xD744 }, +{ 0xD745, 0xD745, 0xD745 }, +{ 0xD746, 0xD746, 0xD746 }, +{ 0xD747, 0xD747, 0xD747 }, +{ 0xD748, 0xD748, 0xD748 }, +{ 0xD749, 0xD749, 0xD749 }, +{ 0xD74A, 0xD74A, 0xD74A }, +{ 0xD74B, 0xD74B, 0xD74B }, +{ 0xD74C, 0xD74C, 0xD74C }, +{ 0xD74D, 0xD74D, 0xD74D }, +{ 0xD74E, 0xD74E, 0xD74E }, +{ 0xD74F, 0xD74F, 0xD74F }, +{ 0xD750, 0xD750, 0xD750 }, +{ 0xD751, 0xD751, 0xD751 }, +{ 0xD752, 0xD752, 0xD752 }, +{ 0xD753, 0xD753, 0xD753 }, +{ 0xD754, 0xD754, 0xD754 }, +{ 0xD755, 0xD755, 0xD755 }, +{ 0xD756, 0xD756, 0xD756 }, +{ 0xD757, 0xD757, 0xD757 }, +{ 0xD758, 0xD758, 0xD758 }, +{ 0xD759, 0xD759, 0xD759 }, +{ 0xD75A, 0xD75A, 0xD75A }, +{ 0xD75B, 0xD75B, 0xD75B }, +{ 0xD75C, 0xD75C, 0xD75C }, +{ 0xD75D, 0xD75D, 0xD75D }, +{ 0xD75E, 0xD75E, 0xD75E }, +{ 0xD75F, 0xD75F, 0xD75F }, +{ 0xD760, 0xD760, 0xD760 }, +{ 0xD761, 0xD761, 0xD761 }, +{ 0xD762, 0xD762, 0xD762 }, +{ 0xD763, 0xD763, 0xD763 }, +{ 0xD764, 0xD764, 0xD764 }, +{ 0xD765, 0xD765, 0xD765 }, +{ 0xD766, 0xD766, 0xD766 }, +{ 0xD767, 0xD767, 0xD767 }, +{ 0xD768, 0xD768, 0xD768 }, +{ 0xD769, 0xD769, 0xD769 }, +{ 0xD76A, 0xD76A, 0xD76A }, +{ 0xD76B, 0xD76B, 0xD76B }, +{ 0xD76C, 0xD76C, 0xD76C }, +{ 0xD76D, 0xD76D, 0xD76D }, +{ 0xD76E, 0xD76E, 0xD76E }, +{ 0xD76F, 0xD76F, 0xD76F }, +{ 0xD770, 0xD770, 0xD770 }, +{ 0xD771, 0xD771, 0xD771 }, +{ 0xD772, 0xD772, 0xD772 }, +{ 0xD773, 0xD773, 0xD773 }, +{ 0xD774, 0xD774, 0xD774 }, +{ 0xD775, 0xD775, 0xD775 }, +{ 0xD776, 0xD776, 0xD776 }, +{ 0xD777, 0xD777, 0xD777 }, +{ 0xD778, 0xD778, 0xD778 }, +{ 0xD779, 0xD779, 0xD779 }, +{ 0xD77A, 0xD77A, 0xD77A }, +{ 0xD77B, 0xD77B, 0xD77B }, +{ 0xD77C, 0xD77C, 0xD77C }, +{ 0xD77D, 0xD77D, 0xD77D }, +{ 0xD77E, 0xD77E, 0xD77E }, +{ 0xD77F, 0xD77F, 0xD77F }, +{ 0xD780, 0xD780, 0xD780 }, +{ 0xD781, 0xD781, 0xD781 }, +{ 0xD782, 0xD782, 0xD782 }, +{ 0xD783, 0xD783, 0xD783 }, +{ 0xD784, 0xD784, 0xD784 }, +{ 0xD785, 0xD785, 0xD785 }, +{ 0xD786, 0xD786, 0xD786 }, +{ 0xD787, 0xD787, 0xD787 }, +{ 0xD788, 0xD788, 0xD788 }, +{ 0xD789, 0xD789, 0xD789 }, +{ 0xD78A, 0xD78A, 0xD78A }, +{ 0xD78B, 0xD78B, 0xD78B }, +{ 0xD78C, 0xD78C, 0xD78C }, +{ 0xD78D, 0xD78D, 0xD78D }, +{ 0xD78E, 0xD78E, 0xD78E }, +{ 0xD78F, 0xD78F, 0xD78F }, +{ 0xD790, 0xD790, 0xD790 }, +{ 0xD791, 0xD791, 0xD791 }, +{ 0xD792, 0xD792, 0xD792 }, +{ 0xD793, 0xD793, 0xD793 }, +{ 0xD794, 0xD794, 0xD794 }, +{ 0xD795, 0xD795, 0xD795 }, +{ 0xD796, 0xD796, 0xD796 }, +{ 0xD797, 0xD797, 0xD797 }, +{ 0xD798, 0xD798, 0xD798 }, +{ 0xD799, 0xD799, 0xD799 }, +{ 0xD79A, 0xD79A, 0xD79A }, +{ 0xD79B, 0xD79B, 0xD79B }, +{ 0xD79C, 0xD79C, 0xD79C }, +{ 0xD79D, 0xD79D, 0xD79D }, +{ 0xD79E, 0xD79E, 0xD79E }, +{ 0xD79F, 0xD79F, 0xD79F }, +{ 0xD7A0, 0xD7A0, 0xD7A0 }, +{ 0xD7A1, 0xD7A1, 0xD7A1 }, +{ 0xD7A2, 0xD7A2, 0xD7A2 }, +{ 0xD7A3, 0xD7A3, 0xD7A3 }, +{ 0xF900, 0xF900, 0xF900 }, +{ 0xF901, 0xF901, 0xF901 }, +{ 0xF902, 0xF902, 0xF902 }, +{ 0xF903, 0xF903, 0xF903 }, +{ 0xF904, 0xF904, 0xF904 }, +{ 0xF905, 0xF905, 0xF905 }, +{ 0xF906, 0xF906, 0xF906 }, +{ 0xF907, 0xF907, 0xF907 }, +{ 0xF908, 0xF908, 0xF908 }, +{ 0xF909, 0xF909, 0xF909 }, +{ 0xF90A, 0xF90A, 0xF90A }, +{ 0xF90B, 0xF90B, 0xF90B }, +{ 0xF90C, 0xF90C, 0xF90C }, +{ 0xF90D, 0xF90D, 0xF90D }, +{ 0xF90E, 0xF90E, 0xF90E }, +{ 0xF90F, 0xF90F, 0xF90F }, +{ 0xF910, 0xF910, 0xF910 }, +{ 0xF911, 0xF911, 0xF911 }, +{ 0xF912, 0xF912, 0xF912 }, +{ 0xF913, 0xF913, 0xF913 }, +{ 0xF914, 0xF914, 0xF914 }, +{ 0xF915, 0xF915, 0xF915 }, +{ 0xF916, 0xF916, 0xF916 }, +{ 0xF917, 0xF917, 0xF917 }, +{ 0xF918, 0xF918, 0xF918 }, +{ 0xF919, 0xF919, 0xF919 }, +{ 0xF91A, 0xF91A, 0xF91A }, +{ 0xF91B, 0xF91B, 0xF91B }, +{ 0xF91C, 0xF91C, 0xF91C }, +{ 0xF91D, 0xF91D, 0xF91D }, +{ 0xF91E, 0xF91E, 0xF91E }, +{ 0xF91F, 0xF91F, 0xF91F }, +{ 0xF920, 0xF920, 0xF920 }, +{ 0xF921, 0xF921, 0xF921 }, +{ 0xF922, 0xF922, 0xF922 }, +{ 0xF923, 0xF923, 0xF923 }, +{ 0xF924, 0xF924, 0xF924 }, +{ 0xF925, 0xF925, 0xF925 }, +{ 0xF926, 0xF926, 0xF926 }, +{ 0xF927, 0xF927, 0xF927 }, +{ 0xF928, 0xF928, 0xF928 }, +{ 0xF929, 0xF929, 0xF929 }, +{ 0xF92A, 0xF92A, 0xF92A }, +{ 0xF92B, 0xF92B, 0xF92B }, +{ 0xF92C, 0xF92C, 0xF92C }, +{ 0xF92D, 0xF92D, 0xF92D }, +{ 0xF92E, 0xF92E, 0xF92E }, +{ 0xF92F, 0xF92F, 0xF92F }, +{ 0xF930, 0xF930, 0xF930 }, +{ 0xF931, 0xF931, 0xF931 }, +{ 0xF932, 0xF932, 0xF932 }, +{ 0xF933, 0xF933, 0xF933 }, +{ 0xF934, 0xF934, 0xF934 }, +{ 0xF935, 0xF935, 0xF935 }, +{ 0xF936, 0xF936, 0xF936 }, +{ 0xF937, 0xF937, 0xF937 }, +{ 0xF938, 0xF938, 0xF938 }, +{ 0xF939, 0xF939, 0xF939 }, +{ 0xF93A, 0xF93A, 0xF93A }, +{ 0xF93B, 0xF93B, 0xF93B }, +{ 0xF93C, 0xF93C, 0xF93C }, +{ 0xF93D, 0xF93D, 0xF93D }, +{ 0xF93E, 0xF93E, 0xF93E }, +{ 0xF93F, 0xF93F, 0xF93F }, +{ 0xF940, 0xF940, 0xF940 }, +{ 0xF941, 0xF941, 0xF941 }, +{ 0xF942, 0xF942, 0xF942 }, +{ 0xF943, 0xF943, 0xF943 }, +{ 0xF944, 0xF944, 0xF944 }, +{ 0xF945, 0xF945, 0xF945 }, +{ 0xF946, 0xF946, 0xF946 }, +{ 0xF947, 0xF947, 0xF947 }, +{ 0xF948, 0xF948, 0xF948 }, +{ 0xF949, 0xF949, 0xF949 }, +{ 0xF94A, 0xF94A, 0xF94A }, +{ 0xF94B, 0xF94B, 0xF94B }, +{ 0xF94C, 0xF94C, 0xF94C }, +{ 0xF94D, 0xF94D, 0xF94D }, +{ 0xF94E, 0xF94E, 0xF94E }, +{ 0xF94F, 0xF94F, 0xF94F }, +{ 0xF950, 0xF950, 0xF950 }, +{ 0xF951, 0xF951, 0xF951 }, +{ 0xF952, 0xF952, 0xF952 }, +{ 0xF953, 0xF953, 0xF953 }, +{ 0xF954, 0xF954, 0xF954 }, +{ 0xF955, 0xF955, 0xF955 }, +{ 0xF956, 0xF956, 0xF956 }, +{ 0xF957, 0xF957, 0xF957 }, +{ 0xF958, 0xF958, 0xF958 }, +{ 0xF959, 0xF959, 0xF959 }, +{ 0xF95A, 0xF95A, 0xF95A }, +{ 0xF95B, 0xF95B, 0xF95B }, +{ 0xF95C, 0xF95C, 0xF95C }, +{ 0xF95D, 0xF95D, 0xF95D }, +{ 0xF95E, 0xF95E, 0xF95E }, +{ 0xF95F, 0xF95F, 0xF95F }, +{ 0xF960, 0xF960, 0xF960 }, +{ 0xF961, 0xF961, 0xF961 }, +{ 0xF962, 0xF962, 0xF962 }, +{ 0xF963, 0xF963, 0xF963 }, +{ 0xF964, 0xF964, 0xF964 }, +{ 0xF965, 0xF965, 0xF965 }, +{ 0xF966, 0xF966, 0xF966 }, +{ 0xF967, 0xF967, 0xF967 }, +{ 0xF968, 0xF968, 0xF968 }, +{ 0xF969, 0xF969, 0xF969 }, +{ 0xF96A, 0xF96A, 0xF96A }, +{ 0xF96B, 0xF96B, 0xF96B }, +{ 0xF96C, 0xF96C, 0xF96C }, +{ 0xF96D, 0xF96D, 0xF96D }, +{ 0xF96E, 0xF96E, 0xF96E }, +{ 0xF96F, 0xF96F, 0xF96F }, +{ 0xF970, 0xF970, 0xF970 }, +{ 0xF971, 0xF971, 0xF971 }, +{ 0xF972, 0xF972, 0xF972 }, +{ 0xF973, 0xF973, 0xF973 }, +{ 0xF974, 0xF974, 0xF974 }, +{ 0xF975, 0xF975, 0xF975 }, +{ 0xF976, 0xF976, 0xF976 }, +{ 0xF977, 0xF977, 0xF977 }, +{ 0xF978, 0xF978, 0xF978 }, +{ 0xF979, 0xF979, 0xF979 }, +{ 0xF97A, 0xF97A, 0xF97A }, +{ 0xF97B, 0xF97B, 0xF97B }, +{ 0xF97C, 0xF97C, 0xF97C }, +{ 0xF97D, 0xF97D, 0xF97D }, +{ 0xF97E, 0xF97E, 0xF97E }, +{ 0xF97F, 0xF97F, 0xF97F }, +{ 0xF980, 0xF980, 0xF980 }, +{ 0xF981, 0xF981, 0xF981 }, +{ 0xF982, 0xF982, 0xF982 }, +{ 0xF983, 0xF983, 0xF983 }, +{ 0xF984, 0xF984, 0xF984 }, +{ 0xF985, 0xF985, 0xF985 }, +{ 0xF986, 0xF986, 0xF986 }, +{ 0xF987, 0xF987, 0xF987 }, +{ 0xF988, 0xF988, 0xF988 }, +{ 0xF989, 0xF989, 0xF989 }, +{ 0xF98A, 0xF98A, 0xF98A }, +{ 0xF98B, 0xF98B, 0xF98B }, +{ 0xF98C, 0xF98C, 0xF98C }, +{ 0xF98D, 0xF98D, 0xF98D }, +{ 0xF98E, 0xF98E, 0xF98E }, +{ 0xF98F, 0xF98F, 0xF98F }, +{ 0xF990, 0xF990, 0xF990 }, +{ 0xF991, 0xF991, 0xF991 }, +{ 0xF992, 0xF992, 0xF992 }, +{ 0xF993, 0xF993, 0xF993 }, +{ 0xF994, 0xF994, 0xF994 }, +{ 0xF995, 0xF995, 0xF995 }, +{ 0xF996, 0xF996, 0xF996 }, +{ 0xF997, 0xF997, 0xF997 }, +{ 0xF998, 0xF998, 0xF998 }, +{ 0xF999, 0xF999, 0xF999 }, +{ 0xF99A, 0xF99A, 0xF99A }, +{ 0xF99B, 0xF99B, 0xF99B }, +{ 0xF99C, 0xF99C, 0xF99C }, +{ 0xF99D, 0xF99D, 0xF99D }, +{ 0xF99E, 0xF99E, 0xF99E }, +{ 0xF99F, 0xF99F, 0xF99F }, +{ 0xF9A0, 0xF9A0, 0xF9A0 }, +{ 0xF9A1, 0xF9A1, 0xF9A1 }, +{ 0xF9A2, 0xF9A2, 0xF9A2 }, +{ 0xF9A3, 0xF9A3, 0xF9A3 }, +{ 0xF9A4, 0xF9A4, 0xF9A4 }, +{ 0xF9A5, 0xF9A5, 0xF9A5 }, +{ 0xF9A6, 0xF9A6, 0xF9A6 }, +{ 0xF9A7, 0xF9A7, 0xF9A7 }, +{ 0xF9A8, 0xF9A8, 0xF9A8 }, +{ 0xF9A9, 0xF9A9, 0xF9A9 }, +{ 0xF9AA, 0xF9AA, 0xF9AA }, +{ 0xF9AB, 0xF9AB, 0xF9AB }, +{ 0xF9AC, 0xF9AC, 0xF9AC }, +{ 0xF9AD, 0xF9AD, 0xF9AD }, +{ 0xF9AE, 0xF9AE, 0xF9AE }, +{ 0xF9AF, 0xF9AF, 0xF9AF }, +{ 0xF9B0, 0xF9B0, 0xF9B0 }, +{ 0xF9B1, 0xF9B1, 0xF9B1 }, +{ 0xF9B2, 0xF9B2, 0xF9B2 }, +{ 0xF9B3, 0xF9B3, 0xF9B3 }, +{ 0xF9B4, 0xF9B4, 0xF9B4 }, +{ 0xF9B5, 0xF9B5, 0xF9B5 }, +{ 0xF9B6, 0xF9B6, 0xF9B6 }, +{ 0xF9B7, 0xF9B7, 0xF9B7 }, +{ 0xF9B8, 0xF9B8, 0xF9B8 }, +{ 0xF9B9, 0xF9B9, 0xF9B9 }, +{ 0xF9BA, 0xF9BA, 0xF9BA }, +{ 0xF9BB, 0xF9BB, 0xF9BB }, +{ 0xF9BC, 0xF9BC, 0xF9BC }, +{ 0xF9BD, 0xF9BD, 0xF9BD }, +{ 0xF9BE, 0xF9BE, 0xF9BE }, +{ 0xF9BF, 0xF9BF, 0xF9BF }, +{ 0xF9C0, 0xF9C0, 0xF9C0 }, +{ 0xF9C1, 0xF9C1, 0xF9C1 }, +{ 0xF9C2, 0xF9C2, 0xF9C2 }, +{ 0xF9C3, 0xF9C3, 0xF9C3 }, +{ 0xF9C4, 0xF9C4, 0xF9C4 }, +{ 0xF9C5, 0xF9C5, 0xF9C5 }, +{ 0xF9C6, 0xF9C6, 0xF9C6 }, +{ 0xF9C7, 0xF9C7, 0xF9C7 }, +{ 0xF9C8, 0xF9C8, 0xF9C8 }, +{ 0xF9C9, 0xF9C9, 0xF9C9 }, +{ 0xF9CA, 0xF9CA, 0xF9CA }, +{ 0xF9CB, 0xF9CB, 0xF9CB }, +{ 0xF9CC, 0xF9CC, 0xF9CC }, +{ 0xF9CD, 0xF9CD, 0xF9CD }, +{ 0xF9CE, 0xF9CE, 0xF9CE }, +{ 0xF9CF, 0xF9CF, 0xF9CF }, +{ 0xF9D0, 0xF9D0, 0xF9D0 }, +{ 0xF9D1, 0xF9D1, 0xF9D1 }, +{ 0xF9D2, 0xF9D2, 0xF9D2 }, +{ 0xF9D3, 0xF9D3, 0xF9D3 }, +{ 0xF9D4, 0xF9D4, 0xF9D4 }, +{ 0xF9D5, 0xF9D5, 0xF9D5 }, +{ 0xF9D6, 0xF9D6, 0xF9D6 }, +{ 0xF9D7, 0xF9D7, 0xF9D7 }, +{ 0xF9D8, 0xF9D8, 0xF9D8 }, +{ 0xF9D9, 0xF9D9, 0xF9D9 }, +{ 0xF9DA, 0xF9DA, 0xF9DA }, +{ 0xF9DB, 0xF9DB, 0xF9DB }, +{ 0xF9DC, 0xF9DC, 0xF9DC }, +{ 0xF9DD, 0xF9DD, 0xF9DD }, +{ 0xF9DE, 0xF9DE, 0xF9DE }, +{ 0xF9DF, 0xF9DF, 0xF9DF }, +{ 0xF9E0, 0xF9E0, 0xF9E0 }, +{ 0xF9E1, 0xF9E1, 0xF9E1 }, +{ 0xF9E2, 0xF9E2, 0xF9E2 }, +{ 0xF9E3, 0xF9E3, 0xF9E3 }, +{ 0xF9E4, 0xF9E4, 0xF9E4 }, +{ 0xF9E5, 0xF9E5, 0xF9E5 }, +{ 0xF9E6, 0xF9E6, 0xF9E6 }, +{ 0xF9E7, 0xF9E7, 0xF9E7 }, +{ 0xF9E8, 0xF9E8, 0xF9E8 }, +{ 0xF9E9, 0xF9E9, 0xF9E9 }, +{ 0xF9EA, 0xF9EA, 0xF9EA }, +{ 0xF9EB, 0xF9EB, 0xF9EB }, +{ 0xF9EC, 0xF9EC, 0xF9EC }, +{ 0xF9ED, 0xF9ED, 0xF9ED }, +{ 0xF9EE, 0xF9EE, 0xF9EE }, +{ 0xF9EF, 0xF9EF, 0xF9EF }, +{ 0xF9F0, 0xF9F0, 0xF9F0 }, +{ 0xF9F1, 0xF9F1, 0xF9F1 }, +{ 0xF9F2, 0xF9F2, 0xF9F2 }, +{ 0xF9F3, 0xF9F3, 0xF9F3 }, +{ 0xF9F4, 0xF9F4, 0xF9F4 }, +{ 0xF9F5, 0xF9F5, 0xF9F5 }, +{ 0xF9F6, 0xF9F6, 0xF9F6 }, +{ 0xF9F7, 0xF9F7, 0xF9F7 }, +{ 0xF9F8, 0xF9F8, 0xF9F8 }, +{ 0xF9F9, 0xF9F9, 0xF9F9 }, +{ 0xF9FA, 0xF9FA, 0xF9FA }, +{ 0xF9FB, 0xF9FB, 0xF9FB }, +{ 0xF9FC, 0xF9FC, 0xF9FC }, +{ 0xF9FD, 0xF9FD, 0xF9FD }, +{ 0xF9FE, 0xF9FE, 0xF9FE }, +{ 0xF9FF, 0xF9FF, 0xF9FF }, +{ 0xFA00, 0xFA00, 0xFA00 }, +{ 0xFA01, 0xFA01, 0xFA01 }, +{ 0xFA02, 0xFA02, 0xFA02 }, +{ 0xFA03, 0xFA03, 0xFA03 }, +{ 0xFA04, 0xFA04, 0xFA04 }, +{ 0xFA05, 0xFA05, 0xFA05 }, +{ 0xFA06, 0xFA06, 0xFA06 }, +{ 0xFA07, 0xFA07, 0xFA07 }, +{ 0xFA08, 0xFA08, 0xFA08 }, +{ 0xFA09, 0xFA09, 0xFA09 }, +{ 0xFA0A, 0xFA0A, 0xFA0A }, +{ 0xFA0B, 0xFA0B, 0xFA0B }, +{ 0xFA0C, 0xFA0C, 0xFA0C }, +{ 0xFA0D, 0xFA0D, 0xFA0D }, +{ 0xFA0E, 0xFA0E, 0xFA0E }, +{ 0xFA0F, 0xFA0F, 0xFA0F }, +{ 0xFA10, 0xFA10, 0xFA10 }, +{ 0xFA11, 0xFA11, 0xFA11 }, +{ 0xFA12, 0xFA12, 0xFA12 }, +{ 0xFA13, 0xFA13, 0xFA13 }, +{ 0xFA14, 0xFA14, 0xFA14 }, +{ 0xFA15, 0xFA15, 0xFA15 }, +{ 0xFA16, 0xFA16, 0xFA16 }, +{ 0xFA17, 0xFA17, 0xFA17 }, +{ 0xFA18, 0xFA18, 0xFA18 }, +{ 0xFA19, 0xFA19, 0xFA19 }, +{ 0xFA1A, 0xFA1A, 0xFA1A }, +{ 0xFA1B, 0xFA1B, 0xFA1B }, +{ 0xFA1C, 0xFA1C, 0xFA1C }, +{ 0xFA1D, 0xFA1D, 0xFA1D }, +{ 0xFA1E, 0xFA1E, 0xFA1E }, +{ 0xFA1F, 0xFA1F, 0xFA1F }, +{ 0xFA20, 0xFA20, 0xFA20 }, +{ 0xFA21, 0xFA21, 0xFA21 }, +{ 0xFA22, 0xFA22, 0xFA22 }, +{ 0xFA23, 0xFA23, 0xFA23 }, +{ 0xFA24, 0xFA24, 0xFA24 }, +{ 0xFA25, 0xFA25, 0xFA25 }, +{ 0xFA26, 0xFA26, 0xFA26 }, +{ 0xFA27, 0xFA27, 0xFA27 }, +{ 0xFA28, 0xFA28, 0xFA28 }, +{ 0xFA29, 0xFA29, 0xFA29 }, +{ 0xFA2A, 0xFA2A, 0xFA2A }, +{ 0xFA2B, 0xFA2B, 0xFA2B }, +{ 0xFA2C, 0xFA2C, 0xFA2C }, +{ 0xFA2D, 0xFA2D, 0xFA2D }, +{ 0xFA30, 0xFA30, 0xFA30 }, +{ 0xFA31, 0xFA31, 0xFA31 }, +{ 0xFA32, 0xFA32, 0xFA32 }, +{ 0xFA33, 0xFA33, 0xFA33 }, +{ 0xFA34, 0xFA34, 0xFA34 }, +{ 0xFA35, 0xFA35, 0xFA35 }, +{ 0xFA36, 0xFA36, 0xFA36 }, +{ 0xFA37, 0xFA37, 0xFA37 }, +{ 0xFA38, 0xFA38, 0xFA38 }, +{ 0xFA39, 0xFA39, 0xFA39 }, +{ 0xFA3A, 0xFA3A, 0xFA3A }, +{ 0xFA3B, 0xFA3B, 0xFA3B }, +{ 0xFA3C, 0xFA3C, 0xFA3C }, +{ 0xFA3D, 0xFA3D, 0xFA3D }, +{ 0xFA3E, 0xFA3E, 0xFA3E }, +{ 0xFA3F, 0xFA3F, 0xFA3F }, +{ 0xFA40, 0xFA40, 0xFA40 }, +{ 0xFA41, 0xFA41, 0xFA41 }, +{ 0xFA42, 0xFA42, 0xFA42 }, +{ 0xFA43, 0xFA43, 0xFA43 }, +{ 0xFA44, 0xFA44, 0xFA44 }, +{ 0xFA45, 0xFA45, 0xFA45 }, +{ 0xFA46, 0xFA46, 0xFA46 }, +{ 0xFA47, 0xFA47, 0xFA47 }, +{ 0xFA48, 0xFA48, 0xFA48 }, +{ 0xFA49, 0xFA49, 0xFA49 }, +{ 0xFA4A, 0xFA4A, 0xFA4A }, +{ 0xFA4B, 0xFA4B, 0xFA4B }, +{ 0xFA4C, 0xFA4C, 0xFA4C }, +{ 0xFA4D, 0xFA4D, 0xFA4D }, +{ 0xFA4E, 0xFA4E, 0xFA4E }, +{ 0xFA4F, 0xFA4F, 0xFA4F }, +{ 0xFA50, 0xFA50, 0xFA50 }, +{ 0xFA51, 0xFA51, 0xFA51 }, +{ 0xFA52, 0xFA52, 0xFA52 }, +{ 0xFA53, 0xFA53, 0xFA53 }, +{ 0xFA54, 0xFA54, 0xFA54 }, +{ 0xFA55, 0xFA55, 0xFA55 }, +{ 0xFA56, 0xFA56, 0xFA56 }, +{ 0xFA57, 0xFA57, 0xFA57 }, +{ 0xFA58, 0xFA58, 0xFA58 }, +{ 0xFA59, 0xFA59, 0xFA59 }, +{ 0xFA5A, 0xFA5A, 0xFA5A }, +{ 0xFA5B, 0xFA5B, 0xFA5B }, +{ 0xFA5C, 0xFA5C, 0xFA5C }, +{ 0xFA5D, 0xFA5D, 0xFA5D }, +{ 0xFA5E, 0xFA5E, 0xFA5E }, +{ 0xFA5F, 0xFA5F, 0xFA5F }, +{ 0xFA60, 0xFA60, 0xFA60 }, +{ 0xFA61, 0xFA61, 0xFA61 }, +{ 0xFA62, 0xFA62, 0xFA62 }, +{ 0xFA63, 0xFA63, 0xFA63 }, +{ 0xFA64, 0xFA64, 0xFA64 }, +{ 0xFA65, 0xFA65, 0xFA65 }, +{ 0xFA66, 0xFA66, 0xFA66 }, +{ 0xFA67, 0xFA67, 0xFA67 }, +{ 0xFA68, 0xFA68, 0xFA68 }, +{ 0xFA69, 0xFA69, 0xFA69 }, +{ 0xFA6A, 0xFA6A, 0xFA6A }, +{ 0xFA70, 0xFA70, 0xFA70 }, +{ 0xFA71, 0xFA71, 0xFA71 }, +{ 0xFA72, 0xFA72, 0xFA72 }, +{ 0xFA73, 0xFA73, 0xFA73 }, +{ 0xFA74, 0xFA74, 0xFA74 }, +{ 0xFA75, 0xFA75, 0xFA75 }, +{ 0xFA76, 0xFA76, 0xFA76 }, +{ 0xFA77, 0xFA77, 0xFA77 }, +{ 0xFA78, 0xFA78, 0xFA78 }, +{ 0xFA79, 0xFA79, 0xFA79 }, +{ 0xFA7A, 0xFA7A, 0xFA7A }, +{ 0xFA7B, 0xFA7B, 0xFA7B }, +{ 0xFA7C, 0xFA7C, 0xFA7C }, +{ 0xFA7D, 0xFA7D, 0xFA7D }, +{ 0xFA7E, 0xFA7E, 0xFA7E }, +{ 0xFA7F, 0xFA7F, 0xFA7F }, +{ 0xFA80, 0xFA80, 0xFA80 }, +{ 0xFA81, 0xFA81, 0xFA81 }, +{ 0xFA82, 0xFA82, 0xFA82 }, +{ 0xFA83, 0xFA83, 0xFA83 }, +{ 0xFA84, 0xFA84, 0xFA84 }, +{ 0xFA85, 0xFA85, 0xFA85 }, +{ 0xFA86, 0xFA86, 0xFA86 }, +{ 0xFA87, 0xFA87, 0xFA87 }, +{ 0xFA88, 0xFA88, 0xFA88 }, +{ 0xFA89, 0xFA89, 0xFA89 }, +{ 0xFA8A, 0xFA8A, 0xFA8A }, +{ 0xFA8B, 0xFA8B, 0xFA8B }, +{ 0xFA8C, 0xFA8C, 0xFA8C }, +{ 0xFA8D, 0xFA8D, 0xFA8D }, +{ 0xFA8E, 0xFA8E, 0xFA8E }, +{ 0xFA8F, 0xFA8F, 0xFA8F }, +{ 0xFA90, 0xFA90, 0xFA90 }, +{ 0xFA91, 0xFA91, 0xFA91 }, +{ 0xFA92, 0xFA92, 0xFA92 }, +{ 0xFA93, 0xFA93, 0xFA93 }, +{ 0xFA94, 0xFA94, 0xFA94 }, +{ 0xFA95, 0xFA95, 0xFA95 }, +{ 0xFA96, 0xFA96, 0xFA96 }, +{ 0xFA97, 0xFA97, 0xFA97 }, +{ 0xFA98, 0xFA98, 0xFA98 }, +{ 0xFA99, 0xFA99, 0xFA99 }, +{ 0xFA9A, 0xFA9A, 0xFA9A }, +{ 0xFA9B, 0xFA9B, 0xFA9B }, +{ 0xFA9C, 0xFA9C, 0xFA9C }, +{ 0xFA9D, 0xFA9D, 0xFA9D }, +{ 0xFA9E, 0xFA9E, 0xFA9E }, +{ 0xFA9F, 0xFA9F, 0xFA9F }, +{ 0xFAA0, 0xFAA0, 0xFAA0 }, +{ 0xFAA1, 0xFAA1, 0xFAA1 }, +{ 0xFAA2, 0xFAA2, 0xFAA2 }, +{ 0xFAA3, 0xFAA3, 0xFAA3 }, +{ 0xFAA4, 0xFAA4, 0xFAA4 }, +{ 0xFAA5, 0xFAA5, 0xFAA5 }, +{ 0xFAA6, 0xFAA6, 0xFAA6 }, +{ 0xFAA7, 0xFAA7, 0xFAA7 }, +{ 0xFAA8, 0xFAA8, 0xFAA8 }, +{ 0xFAA9, 0xFAA9, 0xFAA9 }, +{ 0xFAAA, 0xFAAA, 0xFAAA }, +{ 0xFAAB, 0xFAAB, 0xFAAB }, +{ 0xFAAC, 0xFAAC, 0xFAAC }, +{ 0xFAAD, 0xFAAD, 0xFAAD }, +{ 0xFAAE, 0xFAAE, 0xFAAE }, +{ 0xFAAF, 0xFAAF, 0xFAAF }, +{ 0xFAB0, 0xFAB0, 0xFAB0 }, +{ 0xFAB1, 0xFAB1, 0xFAB1 }, +{ 0xFAB2, 0xFAB2, 0xFAB2 }, +{ 0xFAB3, 0xFAB3, 0xFAB3 }, +{ 0xFAB4, 0xFAB4, 0xFAB4 }, +{ 0xFAB5, 0xFAB5, 0xFAB5 }, +{ 0xFAB6, 0xFAB6, 0xFAB6 }, +{ 0xFAB7, 0xFAB7, 0xFAB7 }, +{ 0xFAB8, 0xFAB8, 0xFAB8 }, +{ 0xFAB9, 0xFAB9, 0xFAB9 }, +{ 0xFABA, 0xFABA, 0xFABA }, +{ 0xFABB, 0xFABB, 0xFABB }, +{ 0xFABC, 0xFABC, 0xFABC }, +{ 0xFABD, 0xFABD, 0xFABD }, +{ 0xFABE, 0xFABE, 0xFABE }, +{ 0xFABF, 0xFABF, 0xFABF }, +{ 0xFAC0, 0xFAC0, 0xFAC0 }, +{ 0xFAC1, 0xFAC1, 0xFAC1 }, +{ 0xFAC2, 0xFAC2, 0xFAC2 }, +{ 0xFAC3, 0xFAC3, 0xFAC3 }, +{ 0xFAC4, 0xFAC4, 0xFAC4 }, +{ 0xFAC5, 0xFAC5, 0xFAC5 }, +{ 0xFAC6, 0xFAC6, 0xFAC6 }, +{ 0xFAC7, 0xFAC7, 0xFAC7 }, +{ 0xFAC8, 0xFAC8, 0xFAC8 }, +{ 0xFAC9, 0xFAC9, 0xFAC9 }, +{ 0xFACA, 0xFACA, 0xFACA }, +{ 0xFACB, 0xFACB, 0xFACB }, +{ 0xFACC, 0xFACC, 0xFACC }, +{ 0xFACD, 0xFACD, 0xFACD }, +{ 0xFACE, 0xFACE, 0xFACE }, +{ 0xFACF, 0xFACF, 0xFACF }, +{ 0xFAD0, 0xFAD0, 0xFAD0 }, +{ 0xFAD1, 0xFAD1, 0xFAD1 }, +{ 0xFAD2, 0xFAD2, 0xFAD2 }, +{ 0xFAD3, 0xFAD3, 0xFAD3 }, +{ 0xFAD4, 0xFAD4, 0xFAD4 }, +{ 0xFAD5, 0xFAD5, 0xFAD5 }, +{ 0xFAD6, 0xFAD6, 0xFAD6 }, +{ 0xFAD7, 0xFAD7, 0xFAD7 }, +{ 0xFAD8, 0xFAD8, 0xFAD8 }, +{ 0xFAD9, 0xFAD9, 0xFAD9 }, +{ 0xFB00, 0xFB00, 0xFB00 }, +{ 0xFB01, 0xFB01, 0xFB01 }, +{ 0xFB02, 0xFB02, 0xFB02 }, +{ 0xFB03, 0xFB03, 0xFB03 }, +{ 0xFB04, 0xFB04, 0xFB04 }, +{ 0xFB05, 0xFB05, 0xFB05 }, +{ 0xFB06, 0xFB06, 0xFB06 }, +{ 0xFB13, 0xFB13, 0xFB13 }, +{ 0xFB14, 0xFB14, 0xFB14 }, +{ 0xFB15, 0xFB15, 0xFB15 }, +{ 0xFB16, 0xFB16, 0xFB16 }, +{ 0xFB17, 0xFB17, 0xFB17 }, +{ 0xFB1D, 0xFB1D, 0xFB1D }, +{ 0xFB1E, 0xFB1E, 0xFB1E }, +{ 0xFB1F, 0xFB1F, 0xFB1F }, +{ 0xFB20, 0xFB20, 0xFB20 }, +{ 0xFB21, 0xFB21, 0xFB21 }, +{ 0xFB22, 0xFB22, 0xFB22 }, +{ 0xFB23, 0xFB23, 0xFB23 }, +{ 0xFB24, 0xFB24, 0xFB24 }, +{ 0xFB25, 0xFB25, 0xFB25 }, +{ 0xFB26, 0xFB26, 0xFB26 }, +{ 0xFB27, 0xFB27, 0xFB27 }, +{ 0xFB28, 0xFB28, 0xFB28 }, +{ 0xFB2A, 0xFB2A, 0xFB2A }, +{ 0xFB2B, 0xFB2B, 0xFB2B }, +{ 0xFB2C, 0xFB2C, 0xFB2C }, +{ 0xFB2D, 0xFB2D, 0xFB2D }, +{ 0xFB2E, 0xFB2E, 0xFB2E }, +{ 0xFB2F, 0xFB2F, 0xFB2F }, +{ 0xFB30, 0xFB30, 0xFB30 }, +{ 0xFB31, 0xFB31, 0xFB31 }, +{ 0xFB32, 0xFB32, 0xFB32 }, +{ 0xFB33, 0xFB33, 0xFB33 }, +{ 0xFB34, 0xFB34, 0xFB34 }, +{ 0xFB35, 0xFB35, 0xFB35 }, +{ 0xFB36, 0xFB36, 0xFB36 }, +{ 0xFB38, 0xFB38, 0xFB38 }, +{ 0xFB39, 0xFB39, 0xFB39 }, +{ 0xFB3A, 0xFB3A, 0xFB3A }, +{ 0xFB3B, 0xFB3B, 0xFB3B }, +{ 0xFB3C, 0xFB3C, 0xFB3C }, +{ 0xFB3E, 0xFB3E, 0xFB3E }, +{ 0xFB40, 0xFB40, 0xFB40 }, +{ 0xFB41, 0xFB41, 0xFB41 }, +{ 0xFB43, 0xFB43, 0xFB43 }, +{ 0xFB44, 0xFB44, 0xFB44 }, +{ 0xFB46, 0xFB46, 0xFB46 }, +{ 0xFB47, 0xFB47, 0xFB47 }, +{ 0xFB48, 0xFB48, 0xFB48 }, +{ 0xFB49, 0xFB49, 0xFB49 }, +{ 0xFB4A, 0xFB4A, 0xFB4A }, +{ 0xFB4B, 0xFB4B, 0xFB4B }, +{ 0xFB4C, 0xFB4C, 0xFB4C }, +{ 0xFB4D, 0xFB4D, 0xFB4D }, +{ 0xFB4E, 0xFB4E, 0xFB4E }, +{ 0xFB4F, 0xFB4F, 0xFB4F }, +{ 0xFB50, 0xFB50, 0xFB50 }, +{ 0xFB51, 0xFB51, 0xFB51 }, +{ 0xFB52, 0xFB52, 0xFB52 }, +{ 0xFB53, 0xFB53, 0xFB53 }, +{ 0xFB54, 0xFB54, 0xFB54 }, +{ 0xFB55, 0xFB55, 0xFB55 }, +{ 0xFB56, 0xFB56, 0xFB56 }, +{ 0xFB57, 0xFB57, 0xFB57 }, +{ 0xFB58, 0xFB58, 0xFB58 }, +{ 0xFB59, 0xFB59, 0xFB59 }, +{ 0xFB5A, 0xFB5A, 0xFB5A }, +{ 0xFB5B, 0xFB5B, 0xFB5B }, +{ 0xFB5C, 0xFB5C, 0xFB5C }, +{ 0xFB5D, 0xFB5D, 0xFB5D }, +{ 0xFB5E, 0xFB5E, 0xFB5E }, +{ 0xFB5F, 0xFB5F, 0xFB5F }, +{ 0xFB60, 0xFB60, 0xFB60 }, +{ 0xFB61, 0xFB61, 0xFB61 }, +{ 0xFB62, 0xFB62, 0xFB62 }, +{ 0xFB63, 0xFB63, 0xFB63 }, +{ 0xFB64, 0xFB64, 0xFB64 }, +{ 0xFB65, 0xFB65, 0xFB65 }, +{ 0xFB66, 0xFB66, 0xFB66 }, +{ 0xFB67, 0xFB67, 0xFB67 }, +{ 0xFB68, 0xFB68, 0xFB68 }, +{ 0xFB69, 0xFB69, 0xFB69 }, +{ 0xFB6A, 0xFB6A, 0xFB6A }, +{ 0xFB6B, 0xFB6B, 0xFB6B }, +{ 0xFB6C, 0xFB6C, 0xFB6C }, +{ 0xFB6D, 0xFB6D, 0xFB6D }, +{ 0xFB6E, 0xFB6E, 0xFB6E }, +{ 0xFB6F, 0xFB6F, 0xFB6F }, +{ 0xFB70, 0xFB70, 0xFB70 }, +{ 0xFB71, 0xFB71, 0xFB71 }, +{ 0xFB72, 0xFB72, 0xFB72 }, +{ 0xFB73, 0xFB73, 0xFB73 }, +{ 0xFB74, 0xFB74, 0xFB74 }, +{ 0xFB75, 0xFB75, 0xFB75 }, +{ 0xFB76, 0xFB76, 0xFB76 }, +{ 0xFB77, 0xFB77, 0xFB77 }, +{ 0xFB78, 0xFB78, 0xFB78 }, +{ 0xFB79, 0xFB79, 0xFB79 }, +{ 0xFB7A, 0xFB7A, 0xFB7A }, +{ 0xFB7B, 0xFB7B, 0xFB7B }, +{ 0xFB7C, 0xFB7C, 0xFB7C }, +{ 0xFB7D, 0xFB7D, 0xFB7D }, +{ 0xFB7E, 0xFB7E, 0xFB7E }, +{ 0xFB7F, 0xFB7F, 0xFB7F }, +{ 0xFB80, 0xFB80, 0xFB80 }, +{ 0xFB81, 0xFB81, 0xFB81 }, +{ 0xFB82, 0xFB82, 0xFB82 }, +{ 0xFB83, 0xFB83, 0xFB83 }, +{ 0xFB84, 0xFB84, 0xFB84 }, +{ 0xFB85, 0xFB85, 0xFB85 }, +{ 0xFB86, 0xFB86, 0xFB86 }, +{ 0xFB87, 0xFB87, 0xFB87 }, +{ 0xFB88, 0xFB88, 0xFB88 }, +{ 0xFB89, 0xFB89, 0xFB89 }, +{ 0xFB8A, 0xFB8A, 0xFB8A }, +{ 0xFB8B, 0xFB8B, 0xFB8B }, +{ 0xFB8C, 0xFB8C, 0xFB8C }, +{ 0xFB8D, 0xFB8D, 0xFB8D }, +{ 0xFB8E, 0xFB8E, 0xFB8E }, +{ 0xFB8F, 0xFB8F, 0xFB8F }, +{ 0xFB90, 0xFB90, 0xFB90 }, +{ 0xFB91, 0xFB91, 0xFB91 }, +{ 0xFB92, 0xFB92, 0xFB92 }, +{ 0xFB93, 0xFB93, 0xFB93 }, +{ 0xFB94, 0xFB94, 0xFB94 }, +{ 0xFB95, 0xFB95, 0xFB95 }, +{ 0xFB96, 0xFB96, 0xFB96 }, +{ 0xFB97, 0xFB97, 0xFB97 }, +{ 0xFB98, 0xFB98, 0xFB98 }, +{ 0xFB99, 0xFB99, 0xFB99 }, +{ 0xFB9A, 0xFB9A, 0xFB9A }, +{ 0xFB9B, 0xFB9B, 0xFB9B }, +{ 0xFB9C, 0xFB9C, 0xFB9C }, +{ 0xFB9D, 0xFB9D, 0xFB9D }, +{ 0xFB9E, 0xFB9E, 0xFB9E }, +{ 0xFB9F, 0xFB9F, 0xFB9F }, +{ 0xFBA0, 0xFBA0, 0xFBA0 }, +{ 0xFBA1, 0xFBA1, 0xFBA1 }, +{ 0xFBA2, 0xFBA2, 0xFBA2 }, +{ 0xFBA3, 0xFBA3, 0xFBA3 }, +{ 0xFBA4, 0xFBA4, 0xFBA4 }, +{ 0xFBA5, 0xFBA5, 0xFBA5 }, +{ 0xFBA6, 0xFBA6, 0xFBA6 }, +{ 0xFBA7, 0xFBA7, 0xFBA7 }, +{ 0xFBA8, 0xFBA8, 0xFBA8 }, +{ 0xFBA9, 0xFBA9, 0xFBA9 }, +{ 0xFBAA, 0xFBAA, 0xFBAA }, +{ 0xFBAB, 0xFBAB, 0xFBAB }, +{ 0xFBAC, 0xFBAC, 0xFBAC }, +{ 0xFBAD, 0xFBAD, 0xFBAD }, +{ 0xFBAE, 0xFBAE, 0xFBAE }, +{ 0xFBAF, 0xFBAF, 0xFBAF }, +{ 0xFBB0, 0xFBB0, 0xFBB0 }, +{ 0xFBB1, 0xFBB1, 0xFBB1 }, +{ 0xFBD3, 0xFBD3, 0xFBD3 }, +{ 0xFBD4, 0xFBD4, 0xFBD4 }, +{ 0xFBD5, 0xFBD5, 0xFBD5 }, +{ 0xFBD6, 0xFBD6, 0xFBD6 }, +{ 0xFBD7, 0xFBD7, 0xFBD7 }, +{ 0xFBD8, 0xFBD8, 0xFBD8 }, +{ 0xFBD9, 0xFBD9, 0xFBD9 }, +{ 0xFBDA, 0xFBDA, 0xFBDA }, +{ 0xFBDB, 0xFBDB, 0xFBDB }, +{ 0xFBDC, 0xFBDC, 0xFBDC }, +{ 0xFBDD, 0xFBDD, 0xFBDD }, +{ 0xFBDE, 0xFBDE, 0xFBDE }, +{ 0xFBDF, 0xFBDF, 0xFBDF }, +{ 0xFBE0, 0xFBE0, 0xFBE0 }, +{ 0xFBE1, 0xFBE1, 0xFBE1 }, +{ 0xFBE2, 0xFBE2, 0xFBE2 }, +{ 0xFBE3, 0xFBE3, 0xFBE3 }, +{ 0xFBE4, 0xFBE4, 0xFBE4 }, +{ 0xFBE5, 0xFBE5, 0xFBE5 }, +{ 0xFBE6, 0xFBE6, 0xFBE6 }, +{ 0xFBE7, 0xFBE7, 0xFBE7 }, +{ 0xFBE8, 0xFBE8, 0xFBE8 }, +{ 0xFBE9, 0xFBE9, 0xFBE9 }, +{ 0xFBEA, 0xFBEA, 0xFBEA }, +{ 0xFBEB, 0xFBEB, 0xFBEB }, +{ 0xFBEC, 0xFBEC, 0xFBEC }, +{ 0xFBED, 0xFBED, 0xFBED }, +{ 0xFBEE, 0xFBEE, 0xFBEE }, +{ 0xFBEF, 0xFBEF, 0xFBEF }, +{ 0xFBF0, 0xFBF0, 0xFBF0 }, +{ 0xFBF1, 0xFBF1, 0xFBF1 }, +{ 0xFBF2, 0xFBF2, 0xFBF2 }, +{ 0xFBF3, 0xFBF3, 0xFBF3 }, +{ 0xFBF4, 0xFBF4, 0xFBF4 }, +{ 0xFBF5, 0xFBF5, 0xFBF5 }, +{ 0xFBF6, 0xFBF6, 0xFBF6 }, +{ 0xFBF7, 0xFBF7, 0xFBF7 }, +{ 0xFBF8, 0xFBF8, 0xFBF8 }, +{ 0xFBF9, 0xFBF9, 0xFBF9 }, +{ 0xFBFA, 0xFBFA, 0xFBFA }, +{ 0xFBFB, 0xFBFB, 0xFBFB }, +{ 0xFBFC, 0xFBFC, 0xFBFC }, +{ 0xFBFD, 0xFBFD, 0xFBFD }, +{ 0xFBFE, 0xFBFE, 0xFBFE }, +{ 0xFBFF, 0xFBFF, 0xFBFF }, +{ 0xFC00, 0xFC00, 0xFC00 }, +{ 0xFC01, 0xFC01, 0xFC01 }, +{ 0xFC02, 0xFC02, 0xFC02 }, +{ 0xFC03, 0xFC03, 0xFC03 }, +{ 0xFC04, 0xFC04, 0xFC04 }, +{ 0xFC05, 0xFC05, 0xFC05 }, +{ 0xFC06, 0xFC06, 0xFC06 }, +{ 0xFC07, 0xFC07, 0xFC07 }, +{ 0xFC08, 0xFC08, 0xFC08 }, +{ 0xFC09, 0xFC09, 0xFC09 }, +{ 0xFC0A, 0xFC0A, 0xFC0A }, +{ 0xFC0B, 0xFC0B, 0xFC0B }, +{ 0xFC0C, 0xFC0C, 0xFC0C }, +{ 0xFC0D, 0xFC0D, 0xFC0D }, +{ 0xFC0E, 0xFC0E, 0xFC0E }, +{ 0xFC0F, 0xFC0F, 0xFC0F }, +{ 0xFC10, 0xFC10, 0xFC10 }, +{ 0xFC11, 0xFC11, 0xFC11 }, +{ 0xFC12, 0xFC12, 0xFC12 }, +{ 0xFC13, 0xFC13, 0xFC13 }, +{ 0xFC14, 0xFC14, 0xFC14 }, +{ 0xFC15, 0xFC15, 0xFC15 }, +{ 0xFC16, 0xFC16, 0xFC16 }, +{ 0xFC17, 0xFC17, 0xFC17 }, +{ 0xFC18, 0xFC18, 0xFC18 }, +{ 0xFC19, 0xFC19, 0xFC19 }, +{ 0xFC1A, 0xFC1A, 0xFC1A }, +{ 0xFC1B, 0xFC1B, 0xFC1B }, +{ 0xFC1C, 0xFC1C, 0xFC1C }, +{ 0xFC1D, 0xFC1D, 0xFC1D }, +{ 0xFC1E, 0xFC1E, 0xFC1E }, +{ 0xFC1F, 0xFC1F, 0xFC1F }, +{ 0xFC20, 0xFC20, 0xFC20 }, +{ 0xFC21, 0xFC21, 0xFC21 }, +{ 0xFC22, 0xFC22, 0xFC22 }, +{ 0xFC23, 0xFC23, 0xFC23 }, +{ 0xFC24, 0xFC24, 0xFC24 }, +{ 0xFC25, 0xFC25, 0xFC25 }, +{ 0xFC26, 0xFC26, 0xFC26 }, +{ 0xFC27, 0xFC27, 0xFC27 }, +{ 0xFC28, 0xFC28, 0xFC28 }, +{ 0xFC29, 0xFC29, 0xFC29 }, +{ 0xFC2A, 0xFC2A, 0xFC2A }, +{ 0xFC2B, 0xFC2B, 0xFC2B }, +{ 0xFC2C, 0xFC2C, 0xFC2C }, +{ 0xFC2D, 0xFC2D, 0xFC2D }, +{ 0xFC2E, 0xFC2E, 0xFC2E }, +{ 0xFC2F, 0xFC2F, 0xFC2F }, +{ 0xFC30, 0xFC30, 0xFC30 }, +{ 0xFC31, 0xFC31, 0xFC31 }, +{ 0xFC32, 0xFC32, 0xFC32 }, +{ 0xFC33, 0xFC33, 0xFC33 }, +{ 0xFC34, 0xFC34, 0xFC34 }, +{ 0xFC35, 0xFC35, 0xFC35 }, +{ 0xFC36, 0xFC36, 0xFC36 }, +{ 0xFC37, 0xFC37, 0xFC37 }, +{ 0xFC38, 0xFC38, 0xFC38 }, +{ 0xFC39, 0xFC39, 0xFC39 }, +{ 0xFC3A, 0xFC3A, 0xFC3A }, +{ 0xFC3B, 0xFC3B, 0xFC3B }, +{ 0xFC3C, 0xFC3C, 0xFC3C }, +{ 0xFC3D, 0xFC3D, 0xFC3D }, +{ 0xFC3E, 0xFC3E, 0xFC3E }, +{ 0xFC3F, 0xFC3F, 0xFC3F }, +{ 0xFC40, 0xFC40, 0xFC40 }, +{ 0xFC41, 0xFC41, 0xFC41 }, +{ 0xFC42, 0xFC42, 0xFC42 }, +{ 0xFC43, 0xFC43, 0xFC43 }, +{ 0xFC44, 0xFC44, 0xFC44 }, +{ 0xFC45, 0xFC45, 0xFC45 }, +{ 0xFC46, 0xFC46, 0xFC46 }, +{ 0xFC47, 0xFC47, 0xFC47 }, +{ 0xFC48, 0xFC48, 0xFC48 }, +{ 0xFC49, 0xFC49, 0xFC49 }, +{ 0xFC4A, 0xFC4A, 0xFC4A }, +{ 0xFC4B, 0xFC4B, 0xFC4B }, +{ 0xFC4C, 0xFC4C, 0xFC4C }, +{ 0xFC4D, 0xFC4D, 0xFC4D }, +{ 0xFC4E, 0xFC4E, 0xFC4E }, +{ 0xFC4F, 0xFC4F, 0xFC4F }, +{ 0xFC50, 0xFC50, 0xFC50 }, +{ 0xFC51, 0xFC51, 0xFC51 }, +{ 0xFC52, 0xFC52, 0xFC52 }, +{ 0xFC53, 0xFC53, 0xFC53 }, +{ 0xFC54, 0xFC54, 0xFC54 }, +{ 0xFC55, 0xFC55, 0xFC55 }, +{ 0xFC56, 0xFC56, 0xFC56 }, +{ 0xFC57, 0xFC57, 0xFC57 }, +{ 0xFC58, 0xFC58, 0xFC58 }, +{ 0xFC59, 0xFC59, 0xFC59 }, +{ 0xFC5A, 0xFC5A, 0xFC5A }, +{ 0xFC5B, 0xFC5B, 0xFC5B }, +{ 0xFC5C, 0xFC5C, 0xFC5C }, +{ 0xFC5D, 0xFC5D, 0xFC5D }, +{ 0xFC5E, 0xFC5E, 0xFC5E }, +{ 0xFC5F, 0xFC5F, 0xFC5F }, +{ 0xFC60, 0xFC60, 0xFC60 }, +{ 0xFC61, 0xFC61, 0xFC61 }, +{ 0xFC62, 0xFC62, 0xFC62 }, +{ 0xFC63, 0xFC63, 0xFC63 }, +{ 0xFC64, 0xFC64, 0xFC64 }, +{ 0xFC65, 0xFC65, 0xFC65 }, +{ 0xFC66, 0xFC66, 0xFC66 }, +{ 0xFC67, 0xFC67, 0xFC67 }, +{ 0xFC68, 0xFC68, 0xFC68 }, +{ 0xFC69, 0xFC69, 0xFC69 }, +{ 0xFC6A, 0xFC6A, 0xFC6A }, +{ 0xFC6B, 0xFC6B, 0xFC6B }, +{ 0xFC6C, 0xFC6C, 0xFC6C }, +{ 0xFC6D, 0xFC6D, 0xFC6D }, +{ 0xFC6E, 0xFC6E, 0xFC6E }, +{ 0xFC6F, 0xFC6F, 0xFC6F }, +{ 0xFC70, 0xFC70, 0xFC70 }, +{ 0xFC71, 0xFC71, 0xFC71 }, +{ 0xFC72, 0xFC72, 0xFC72 }, +{ 0xFC73, 0xFC73, 0xFC73 }, +{ 0xFC74, 0xFC74, 0xFC74 }, +{ 0xFC75, 0xFC75, 0xFC75 }, +{ 0xFC76, 0xFC76, 0xFC76 }, +{ 0xFC77, 0xFC77, 0xFC77 }, +{ 0xFC78, 0xFC78, 0xFC78 }, +{ 0xFC79, 0xFC79, 0xFC79 }, +{ 0xFC7A, 0xFC7A, 0xFC7A }, +{ 0xFC7B, 0xFC7B, 0xFC7B }, +{ 0xFC7C, 0xFC7C, 0xFC7C }, +{ 0xFC7D, 0xFC7D, 0xFC7D }, +{ 0xFC7E, 0xFC7E, 0xFC7E }, +{ 0xFC7F, 0xFC7F, 0xFC7F }, +{ 0xFC80, 0xFC80, 0xFC80 }, +{ 0xFC81, 0xFC81, 0xFC81 }, +{ 0xFC82, 0xFC82, 0xFC82 }, +{ 0xFC83, 0xFC83, 0xFC83 }, +{ 0xFC84, 0xFC84, 0xFC84 }, +{ 0xFC85, 0xFC85, 0xFC85 }, +{ 0xFC86, 0xFC86, 0xFC86 }, +{ 0xFC87, 0xFC87, 0xFC87 }, +{ 0xFC88, 0xFC88, 0xFC88 }, +{ 0xFC89, 0xFC89, 0xFC89 }, +{ 0xFC8A, 0xFC8A, 0xFC8A }, +{ 0xFC8B, 0xFC8B, 0xFC8B }, +{ 0xFC8C, 0xFC8C, 0xFC8C }, +{ 0xFC8D, 0xFC8D, 0xFC8D }, +{ 0xFC8E, 0xFC8E, 0xFC8E }, +{ 0xFC8F, 0xFC8F, 0xFC8F }, +{ 0xFC90, 0xFC90, 0xFC90 }, +{ 0xFC91, 0xFC91, 0xFC91 }, +{ 0xFC92, 0xFC92, 0xFC92 }, +{ 0xFC93, 0xFC93, 0xFC93 }, +{ 0xFC94, 0xFC94, 0xFC94 }, +{ 0xFC95, 0xFC95, 0xFC95 }, +{ 0xFC96, 0xFC96, 0xFC96 }, +{ 0xFC97, 0xFC97, 0xFC97 }, +{ 0xFC98, 0xFC98, 0xFC98 }, +{ 0xFC99, 0xFC99, 0xFC99 }, +{ 0xFC9A, 0xFC9A, 0xFC9A }, +{ 0xFC9B, 0xFC9B, 0xFC9B }, +{ 0xFC9C, 0xFC9C, 0xFC9C }, +{ 0xFC9D, 0xFC9D, 0xFC9D }, +{ 0xFC9E, 0xFC9E, 0xFC9E }, +{ 0xFC9F, 0xFC9F, 0xFC9F }, +{ 0xFCA0, 0xFCA0, 0xFCA0 }, +{ 0xFCA1, 0xFCA1, 0xFCA1 }, +{ 0xFCA2, 0xFCA2, 0xFCA2 }, +{ 0xFCA3, 0xFCA3, 0xFCA3 }, +{ 0xFCA4, 0xFCA4, 0xFCA4 }, +{ 0xFCA5, 0xFCA5, 0xFCA5 }, +{ 0xFCA6, 0xFCA6, 0xFCA6 }, +{ 0xFCA7, 0xFCA7, 0xFCA7 }, +{ 0xFCA8, 0xFCA8, 0xFCA8 }, +{ 0xFCA9, 0xFCA9, 0xFCA9 }, +{ 0xFCAA, 0xFCAA, 0xFCAA }, +{ 0xFCAB, 0xFCAB, 0xFCAB }, +{ 0xFCAC, 0xFCAC, 0xFCAC }, +{ 0xFCAD, 0xFCAD, 0xFCAD }, +{ 0xFCAE, 0xFCAE, 0xFCAE }, +{ 0xFCAF, 0xFCAF, 0xFCAF }, +{ 0xFCB0, 0xFCB0, 0xFCB0 }, +{ 0xFCB1, 0xFCB1, 0xFCB1 }, +{ 0xFCB2, 0xFCB2, 0xFCB2 }, +{ 0xFCB3, 0xFCB3, 0xFCB3 }, +{ 0xFCB4, 0xFCB4, 0xFCB4 }, +{ 0xFCB5, 0xFCB5, 0xFCB5 }, +{ 0xFCB6, 0xFCB6, 0xFCB6 }, +{ 0xFCB7, 0xFCB7, 0xFCB7 }, +{ 0xFCB8, 0xFCB8, 0xFCB8 }, +{ 0xFCB9, 0xFCB9, 0xFCB9 }, +{ 0xFCBA, 0xFCBA, 0xFCBA }, +{ 0xFCBB, 0xFCBB, 0xFCBB }, +{ 0xFCBC, 0xFCBC, 0xFCBC }, +{ 0xFCBD, 0xFCBD, 0xFCBD }, +{ 0xFCBE, 0xFCBE, 0xFCBE }, +{ 0xFCBF, 0xFCBF, 0xFCBF }, +{ 0xFCC0, 0xFCC0, 0xFCC0 }, +{ 0xFCC1, 0xFCC1, 0xFCC1 }, +{ 0xFCC2, 0xFCC2, 0xFCC2 }, +{ 0xFCC3, 0xFCC3, 0xFCC3 }, +{ 0xFCC4, 0xFCC4, 0xFCC4 }, +{ 0xFCC5, 0xFCC5, 0xFCC5 }, +{ 0xFCC6, 0xFCC6, 0xFCC6 }, +{ 0xFCC7, 0xFCC7, 0xFCC7 }, +{ 0xFCC8, 0xFCC8, 0xFCC8 }, +{ 0xFCC9, 0xFCC9, 0xFCC9 }, +{ 0xFCCA, 0xFCCA, 0xFCCA }, +{ 0xFCCB, 0xFCCB, 0xFCCB }, +{ 0xFCCC, 0xFCCC, 0xFCCC }, +{ 0xFCCD, 0xFCCD, 0xFCCD }, +{ 0xFCCE, 0xFCCE, 0xFCCE }, +{ 0xFCCF, 0xFCCF, 0xFCCF }, +{ 0xFCD0, 0xFCD0, 0xFCD0 }, +{ 0xFCD1, 0xFCD1, 0xFCD1 }, +{ 0xFCD2, 0xFCD2, 0xFCD2 }, +{ 0xFCD3, 0xFCD3, 0xFCD3 }, +{ 0xFCD4, 0xFCD4, 0xFCD4 }, +{ 0xFCD5, 0xFCD5, 0xFCD5 }, +{ 0xFCD6, 0xFCD6, 0xFCD6 }, +{ 0xFCD7, 0xFCD7, 0xFCD7 }, +{ 0xFCD8, 0xFCD8, 0xFCD8 }, +{ 0xFCD9, 0xFCD9, 0xFCD9 }, +{ 0xFCDA, 0xFCDA, 0xFCDA }, +{ 0xFCDB, 0xFCDB, 0xFCDB }, +{ 0xFCDC, 0xFCDC, 0xFCDC }, +{ 0xFCDD, 0xFCDD, 0xFCDD }, +{ 0xFCDE, 0xFCDE, 0xFCDE }, +{ 0xFCDF, 0xFCDF, 0xFCDF }, +{ 0xFCE0, 0xFCE0, 0xFCE0 }, +{ 0xFCE1, 0xFCE1, 0xFCE1 }, +{ 0xFCE2, 0xFCE2, 0xFCE2 }, +{ 0xFCE3, 0xFCE3, 0xFCE3 }, +{ 0xFCE4, 0xFCE4, 0xFCE4 }, +{ 0xFCE5, 0xFCE5, 0xFCE5 }, +{ 0xFCE6, 0xFCE6, 0xFCE6 }, +{ 0xFCE7, 0xFCE7, 0xFCE7 }, +{ 0xFCE8, 0xFCE8, 0xFCE8 }, +{ 0xFCE9, 0xFCE9, 0xFCE9 }, +{ 0xFCEA, 0xFCEA, 0xFCEA }, +{ 0xFCEB, 0xFCEB, 0xFCEB }, +{ 0xFCEC, 0xFCEC, 0xFCEC }, +{ 0xFCED, 0xFCED, 0xFCED }, +{ 0xFCEE, 0xFCEE, 0xFCEE }, +{ 0xFCEF, 0xFCEF, 0xFCEF }, +{ 0xFCF0, 0xFCF0, 0xFCF0 }, +{ 0xFCF1, 0xFCF1, 0xFCF1 }, +{ 0xFCF2, 0xFCF2, 0xFCF2 }, +{ 0xFCF3, 0xFCF3, 0xFCF3 }, +{ 0xFCF4, 0xFCF4, 0xFCF4 }, +{ 0xFCF5, 0xFCF5, 0xFCF5 }, +{ 0xFCF6, 0xFCF6, 0xFCF6 }, +{ 0xFCF7, 0xFCF7, 0xFCF7 }, +{ 0xFCF8, 0xFCF8, 0xFCF8 }, +{ 0xFCF9, 0xFCF9, 0xFCF9 }, +{ 0xFCFA, 0xFCFA, 0xFCFA }, +{ 0xFCFB, 0xFCFB, 0xFCFB }, +{ 0xFCFC, 0xFCFC, 0xFCFC }, +{ 0xFCFD, 0xFCFD, 0xFCFD }, +{ 0xFCFE, 0xFCFE, 0xFCFE }, +{ 0xFCFF, 0xFCFF, 0xFCFF }, +{ 0xFD00, 0xFD00, 0xFD00 }, +{ 0xFD01, 0xFD01, 0xFD01 }, +{ 0xFD02, 0xFD02, 0xFD02 }, +{ 0xFD03, 0xFD03, 0xFD03 }, +{ 0xFD04, 0xFD04, 0xFD04 }, +{ 0xFD05, 0xFD05, 0xFD05 }, +{ 0xFD06, 0xFD06, 0xFD06 }, +{ 0xFD07, 0xFD07, 0xFD07 }, +{ 0xFD08, 0xFD08, 0xFD08 }, +{ 0xFD09, 0xFD09, 0xFD09 }, +{ 0xFD0A, 0xFD0A, 0xFD0A }, +{ 0xFD0B, 0xFD0B, 0xFD0B }, +{ 0xFD0C, 0xFD0C, 0xFD0C }, +{ 0xFD0D, 0xFD0D, 0xFD0D }, +{ 0xFD0E, 0xFD0E, 0xFD0E }, +{ 0xFD0F, 0xFD0F, 0xFD0F }, +{ 0xFD10, 0xFD10, 0xFD10 }, +{ 0xFD11, 0xFD11, 0xFD11 }, +{ 0xFD12, 0xFD12, 0xFD12 }, +{ 0xFD13, 0xFD13, 0xFD13 }, +{ 0xFD14, 0xFD14, 0xFD14 }, +{ 0xFD15, 0xFD15, 0xFD15 }, +{ 0xFD16, 0xFD16, 0xFD16 }, +{ 0xFD17, 0xFD17, 0xFD17 }, +{ 0xFD18, 0xFD18, 0xFD18 }, +{ 0xFD19, 0xFD19, 0xFD19 }, +{ 0xFD1A, 0xFD1A, 0xFD1A }, +{ 0xFD1B, 0xFD1B, 0xFD1B }, +{ 0xFD1C, 0xFD1C, 0xFD1C }, +{ 0xFD1D, 0xFD1D, 0xFD1D }, +{ 0xFD1E, 0xFD1E, 0xFD1E }, +{ 0xFD1F, 0xFD1F, 0xFD1F }, +{ 0xFD20, 0xFD20, 0xFD20 }, +{ 0xFD21, 0xFD21, 0xFD21 }, +{ 0xFD22, 0xFD22, 0xFD22 }, +{ 0xFD23, 0xFD23, 0xFD23 }, +{ 0xFD24, 0xFD24, 0xFD24 }, +{ 0xFD25, 0xFD25, 0xFD25 }, +{ 0xFD26, 0xFD26, 0xFD26 }, +{ 0xFD27, 0xFD27, 0xFD27 }, +{ 0xFD28, 0xFD28, 0xFD28 }, +{ 0xFD29, 0xFD29, 0xFD29 }, +{ 0xFD2A, 0xFD2A, 0xFD2A }, +{ 0xFD2B, 0xFD2B, 0xFD2B }, +{ 0xFD2C, 0xFD2C, 0xFD2C }, +{ 0xFD2D, 0xFD2D, 0xFD2D }, +{ 0xFD2E, 0xFD2E, 0xFD2E }, +{ 0xFD2F, 0xFD2F, 0xFD2F }, +{ 0xFD30, 0xFD30, 0xFD30 }, +{ 0xFD31, 0xFD31, 0xFD31 }, +{ 0xFD32, 0xFD32, 0xFD32 }, +{ 0xFD33, 0xFD33, 0xFD33 }, +{ 0xFD34, 0xFD34, 0xFD34 }, +{ 0xFD35, 0xFD35, 0xFD35 }, +{ 0xFD36, 0xFD36, 0xFD36 }, +{ 0xFD37, 0xFD37, 0xFD37 }, +{ 0xFD38, 0xFD38, 0xFD38 }, +{ 0xFD39, 0xFD39, 0xFD39 }, +{ 0xFD3A, 0xFD3A, 0xFD3A }, +{ 0xFD3B, 0xFD3B, 0xFD3B }, +{ 0xFD3C, 0xFD3C, 0xFD3C }, +{ 0xFD3D, 0xFD3D, 0xFD3D }, +{ 0xFD50, 0xFD50, 0xFD50 }, +{ 0xFD51, 0xFD51, 0xFD51 }, +{ 0xFD52, 0xFD52, 0xFD52 }, +{ 0xFD53, 0xFD53, 0xFD53 }, +{ 0xFD54, 0xFD54, 0xFD54 }, +{ 0xFD55, 0xFD55, 0xFD55 }, +{ 0xFD56, 0xFD56, 0xFD56 }, +{ 0xFD57, 0xFD57, 0xFD57 }, +{ 0xFD58, 0xFD58, 0xFD58 }, +{ 0xFD59, 0xFD59, 0xFD59 }, +{ 0xFD5A, 0xFD5A, 0xFD5A }, +{ 0xFD5B, 0xFD5B, 0xFD5B }, +{ 0xFD5C, 0xFD5C, 0xFD5C }, +{ 0xFD5D, 0xFD5D, 0xFD5D }, +{ 0xFD5E, 0xFD5E, 0xFD5E }, +{ 0xFD5F, 0xFD5F, 0xFD5F }, +{ 0xFD60, 0xFD60, 0xFD60 }, +{ 0xFD61, 0xFD61, 0xFD61 }, +{ 0xFD62, 0xFD62, 0xFD62 }, +{ 0xFD63, 0xFD63, 0xFD63 }, +{ 0xFD64, 0xFD64, 0xFD64 }, +{ 0xFD65, 0xFD65, 0xFD65 }, +{ 0xFD66, 0xFD66, 0xFD66 }, +{ 0xFD67, 0xFD67, 0xFD67 }, +{ 0xFD68, 0xFD68, 0xFD68 }, +{ 0xFD69, 0xFD69, 0xFD69 }, +{ 0xFD6A, 0xFD6A, 0xFD6A }, +{ 0xFD6B, 0xFD6B, 0xFD6B }, +{ 0xFD6C, 0xFD6C, 0xFD6C }, +{ 0xFD6D, 0xFD6D, 0xFD6D }, +{ 0xFD6E, 0xFD6E, 0xFD6E }, +{ 0xFD6F, 0xFD6F, 0xFD6F }, +{ 0xFD70, 0xFD70, 0xFD70 }, +{ 0xFD71, 0xFD71, 0xFD71 }, +{ 0xFD72, 0xFD72, 0xFD72 }, +{ 0xFD73, 0xFD73, 0xFD73 }, +{ 0xFD74, 0xFD74, 0xFD74 }, +{ 0xFD75, 0xFD75, 0xFD75 }, +{ 0xFD76, 0xFD76, 0xFD76 }, +{ 0xFD77, 0xFD77, 0xFD77 }, +{ 0xFD78, 0xFD78, 0xFD78 }, +{ 0xFD79, 0xFD79, 0xFD79 }, +{ 0xFD7A, 0xFD7A, 0xFD7A }, +{ 0xFD7B, 0xFD7B, 0xFD7B }, +{ 0xFD7C, 0xFD7C, 0xFD7C }, +{ 0xFD7D, 0xFD7D, 0xFD7D }, +{ 0xFD7E, 0xFD7E, 0xFD7E }, +{ 0xFD7F, 0xFD7F, 0xFD7F }, +{ 0xFD80, 0xFD80, 0xFD80 }, +{ 0xFD81, 0xFD81, 0xFD81 }, +{ 0xFD82, 0xFD82, 0xFD82 }, +{ 0xFD83, 0xFD83, 0xFD83 }, +{ 0xFD84, 0xFD84, 0xFD84 }, +{ 0xFD85, 0xFD85, 0xFD85 }, +{ 0xFD86, 0xFD86, 0xFD86 }, +{ 0xFD87, 0xFD87, 0xFD87 }, +{ 0xFD88, 0xFD88, 0xFD88 }, +{ 0xFD89, 0xFD89, 0xFD89 }, +{ 0xFD8A, 0xFD8A, 0xFD8A }, +{ 0xFD8B, 0xFD8B, 0xFD8B }, +{ 0xFD8C, 0xFD8C, 0xFD8C }, +{ 0xFD8D, 0xFD8D, 0xFD8D }, +{ 0xFD8E, 0xFD8E, 0xFD8E }, +{ 0xFD8F, 0xFD8F, 0xFD8F }, +{ 0xFD92, 0xFD92, 0xFD92 }, +{ 0xFD93, 0xFD93, 0xFD93 }, +{ 0xFD94, 0xFD94, 0xFD94 }, +{ 0xFD95, 0xFD95, 0xFD95 }, +{ 0xFD96, 0xFD96, 0xFD96 }, +{ 0xFD97, 0xFD97, 0xFD97 }, +{ 0xFD98, 0xFD98, 0xFD98 }, +{ 0xFD99, 0xFD99, 0xFD99 }, +{ 0xFD9A, 0xFD9A, 0xFD9A }, +{ 0xFD9B, 0xFD9B, 0xFD9B }, +{ 0xFD9C, 0xFD9C, 0xFD9C }, +{ 0xFD9D, 0xFD9D, 0xFD9D }, +{ 0xFD9E, 0xFD9E, 0xFD9E }, +{ 0xFD9F, 0xFD9F, 0xFD9F }, +{ 0xFDA0, 0xFDA0, 0xFDA0 }, +{ 0xFDA1, 0xFDA1, 0xFDA1 }, +{ 0xFDA2, 0xFDA2, 0xFDA2 }, +{ 0xFDA3, 0xFDA3, 0xFDA3 }, +{ 0xFDA4, 0xFDA4, 0xFDA4 }, +{ 0xFDA5, 0xFDA5, 0xFDA5 }, +{ 0xFDA6, 0xFDA6, 0xFDA6 }, +{ 0xFDA7, 0xFDA7, 0xFDA7 }, +{ 0xFDA8, 0xFDA8, 0xFDA8 }, +{ 0xFDA9, 0xFDA9, 0xFDA9 }, +{ 0xFDAA, 0xFDAA, 0xFDAA }, +{ 0xFDAB, 0xFDAB, 0xFDAB }, +{ 0xFDAC, 0xFDAC, 0xFDAC }, +{ 0xFDAD, 0xFDAD, 0xFDAD }, +{ 0xFDAE, 0xFDAE, 0xFDAE }, +{ 0xFDAF, 0xFDAF, 0xFDAF }, +{ 0xFDB0, 0xFDB0, 0xFDB0 }, +{ 0xFDB1, 0xFDB1, 0xFDB1 }, +{ 0xFDB2, 0xFDB2, 0xFDB2 }, +{ 0xFDB3, 0xFDB3, 0xFDB3 }, +{ 0xFDB4, 0xFDB4, 0xFDB4 }, +{ 0xFDB5, 0xFDB5, 0xFDB5 }, +{ 0xFDB6, 0xFDB6, 0xFDB6 }, +{ 0xFDB7, 0xFDB7, 0xFDB7 }, +{ 0xFDB8, 0xFDB8, 0xFDB8 }, +{ 0xFDB9, 0xFDB9, 0xFDB9 }, +{ 0xFDBA, 0xFDBA, 0xFDBA }, +{ 0xFDBB, 0xFDBB, 0xFDBB }, +{ 0xFDBC, 0xFDBC, 0xFDBC }, +{ 0xFDBD, 0xFDBD, 0xFDBD }, +{ 0xFDBE, 0xFDBE, 0xFDBE }, +{ 0xFDBF, 0xFDBF, 0xFDBF }, +{ 0xFDC0, 0xFDC0, 0xFDC0 }, +{ 0xFDC1, 0xFDC1, 0xFDC1 }, +{ 0xFDC2, 0xFDC2, 0xFDC2 }, +{ 0xFDC3, 0xFDC3, 0xFDC3 }, +{ 0xFDC4, 0xFDC4, 0xFDC4 }, +{ 0xFDC5, 0xFDC5, 0xFDC5 }, +{ 0xFDC6, 0xFDC6, 0xFDC6 }, +{ 0xFDC7, 0xFDC7, 0xFDC7 }, +{ 0xFDF0, 0xFDF0, 0xFDF0 }, +{ 0xFDF1, 0xFDF1, 0xFDF1 }, +{ 0xFDF2, 0xFDF2, 0xFDF2 }, +{ 0xFDF3, 0xFDF3, 0xFDF3 }, +{ 0xFDF4, 0xFDF4, 0xFDF4 }, +{ 0xFDF5, 0xFDF5, 0xFDF5 }, +{ 0xFDF6, 0xFDF6, 0xFDF6 }, +{ 0xFDF7, 0xFDF7, 0xFDF7 }, +{ 0xFDF8, 0xFDF8, 0xFDF8 }, +{ 0xFDF9, 0xFDF9, 0xFDF9 }, +{ 0xFDFA, 0xFDFA, 0xFDFA }, +{ 0xFDFB, 0xFDFB, 0xFDFB }, +{ 0xFE00, 0xFE00, 0xFE00 }, +{ 0xFE01, 0xFE01, 0xFE01 }, +{ 0xFE02, 0xFE02, 0xFE02 }, +{ 0xFE03, 0xFE03, 0xFE03 }, +{ 0xFE04, 0xFE04, 0xFE04 }, +{ 0xFE05, 0xFE05, 0xFE05 }, +{ 0xFE06, 0xFE06, 0xFE06 }, +{ 0xFE07, 0xFE07, 0xFE07 }, +{ 0xFE08, 0xFE08, 0xFE08 }, +{ 0xFE09, 0xFE09, 0xFE09 }, +{ 0xFE0A, 0xFE0A, 0xFE0A }, +{ 0xFE0B, 0xFE0B, 0xFE0B }, +{ 0xFE0C, 0xFE0C, 0xFE0C }, +{ 0xFE0D, 0xFE0D, 0xFE0D }, +{ 0xFE0E, 0xFE0E, 0xFE0E }, +{ 0xFE0F, 0xFE0F, 0xFE0F }, +{ 0xFE20, 0xFE20, 0xFE20 }, +{ 0xFE21, 0xFE21, 0xFE21 }, +{ 0xFE22, 0xFE22, 0xFE22 }, +{ 0xFE23, 0xFE23, 0xFE23 }, +{ 0xFE70, 0xFE70, 0xFE70 }, +{ 0xFE71, 0xFE71, 0xFE71 }, +{ 0xFE72, 0xFE72, 0xFE72 }, +{ 0xFE73, 0xFE73, 0xFE73 }, +{ 0xFE74, 0xFE74, 0xFE74 }, +{ 0xFE76, 0xFE76, 0xFE76 }, +{ 0xFE77, 0xFE77, 0xFE77 }, +{ 0xFE78, 0xFE78, 0xFE78 }, +{ 0xFE79, 0xFE79, 0xFE79 }, +{ 0xFE7A, 0xFE7A, 0xFE7A }, +{ 0xFE7B, 0xFE7B, 0xFE7B }, +{ 0xFE7C, 0xFE7C, 0xFE7C }, +{ 0xFE7D, 0xFE7D, 0xFE7D }, +{ 0xFE7E, 0xFE7E, 0xFE7E }, +{ 0xFE7F, 0xFE7F, 0xFE7F }, +{ 0xFE80, 0xFE80, 0xFE80 }, +{ 0xFE81, 0xFE81, 0xFE81 }, +{ 0xFE82, 0xFE82, 0xFE82 }, +{ 0xFE83, 0xFE83, 0xFE83 }, +{ 0xFE84, 0xFE84, 0xFE84 }, +{ 0xFE85, 0xFE85, 0xFE85 }, +{ 0xFE86, 0xFE86, 0xFE86 }, +{ 0xFE87, 0xFE87, 0xFE87 }, +{ 0xFE88, 0xFE88, 0xFE88 }, +{ 0xFE89, 0xFE89, 0xFE89 }, +{ 0xFE8A, 0xFE8A, 0xFE8A }, +{ 0xFE8B, 0xFE8B, 0xFE8B }, +{ 0xFE8C, 0xFE8C, 0xFE8C }, +{ 0xFE8D, 0xFE8D, 0xFE8D }, +{ 0xFE8E, 0xFE8E, 0xFE8E }, +{ 0xFE8F, 0xFE8F, 0xFE8F }, +{ 0xFE90, 0xFE90, 0xFE90 }, +{ 0xFE91, 0xFE91, 0xFE91 }, +{ 0xFE92, 0xFE92, 0xFE92 }, +{ 0xFE93, 0xFE93, 0xFE93 }, +{ 0xFE94, 0xFE94, 0xFE94 }, +{ 0xFE95, 0xFE95, 0xFE95 }, +{ 0xFE96, 0xFE96, 0xFE96 }, +{ 0xFE97, 0xFE97, 0xFE97 }, +{ 0xFE98, 0xFE98, 0xFE98 }, +{ 0xFE99, 0xFE99, 0xFE99 }, +{ 0xFE9A, 0xFE9A, 0xFE9A }, +{ 0xFE9B, 0xFE9B, 0xFE9B }, +{ 0xFE9C, 0xFE9C, 0xFE9C }, +{ 0xFE9D, 0xFE9D, 0xFE9D }, +{ 0xFE9E, 0xFE9E, 0xFE9E }, +{ 0xFE9F, 0xFE9F, 0xFE9F }, +{ 0xFEA0, 0xFEA0, 0xFEA0 }, +{ 0xFEA1, 0xFEA1, 0xFEA1 }, +{ 0xFEA2, 0xFEA2, 0xFEA2 }, +{ 0xFEA3, 0xFEA3, 0xFEA3 }, +{ 0xFEA4, 0xFEA4, 0xFEA4 }, +{ 0xFEA5, 0xFEA5, 0xFEA5 }, +{ 0xFEA6, 0xFEA6, 0xFEA6 }, +{ 0xFEA7, 0xFEA7, 0xFEA7 }, +{ 0xFEA8, 0xFEA8, 0xFEA8 }, +{ 0xFEA9, 0xFEA9, 0xFEA9 }, +{ 0xFEAA, 0xFEAA, 0xFEAA }, +{ 0xFEAB, 0xFEAB, 0xFEAB }, +{ 0xFEAC, 0xFEAC, 0xFEAC }, +{ 0xFEAD, 0xFEAD, 0xFEAD }, +{ 0xFEAE, 0xFEAE, 0xFEAE }, +{ 0xFEAF, 0xFEAF, 0xFEAF }, +{ 0xFEB0, 0xFEB0, 0xFEB0 }, +{ 0xFEB1, 0xFEB1, 0xFEB1 }, +{ 0xFEB2, 0xFEB2, 0xFEB2 }, +{ 0xFEB3, 0xFEB3, 0xFEB3 }, +{ 0xFEB4, 0xFEB4, 0xFEB4 }, +{ 0xFEB5, 0xFEB5, 0xFEB5 }, +{ 0xFEB6, 0xFEB6, 0xFEB6 }, +{ 0xFEB7, 0xFEB7, 0xFEB7 }, +{ 0xFEB8, 0xFEB8, 0xFEB8 }, +{ 0xFEB9, 0xFEB9, 0xFEB9 }, +{ 0xFEBA, 0xFEBA, 0xFEBA }, +{ 0xFEBB, 0xFEBB, 0xFEBB }, +{ 0xFEBC, 0xFEBC, 0xFEBC }, +{ 0xFEBD, 0xFEBD, 0xFEBD }, +{ 0xFEBE, 0xFEBE, 0xFEBE }, +{ 0xFEBF, 0xFEBF, 0xFEBF }, +{ 0xFEC0, 0xFEC0, 0xFEC0 }, +{ 0xFEC1, 0xFEC1, 0xFEC1 }, +{ 0xFEC2, 0xFEC2, 0xFEC2 }, +{ 0xFEC3, 0xFEC3, 0xFEC3 }, +{ 0xFEC4, 0xFEC4, 0xFEC4 }, +{ 0xFEC5, 0xFEC5, 0xFEC5 }, +{ 0xFEC6, 0xFEC6, 0xFEC6 }, +{ 0xFEC7, 0xFEC7, 0xFEC7 }, +{ 0xFEC8, 0xFEC8, 0xFEC8 }, +{ 0xFEC9, 0xFEC9, 0xFEC9 }, +{ 0xFECA, 0xFECA, 0xFECA }, +{ 0xFECB, 0xFECB, 0xFECB }, +{ 0xFECC, 0xFECC, 0xFECC }, +{ 0xFECD, 0xFECD, 0xFECD }, +{ 0xFECE, 0xFECE, 0xFECE }, +{ 0xFECF, 0xFECF, 0xFECF }, +{ 0xFED0, 0xFED0, 0xFED0 }, +{ 0xFED1, 0xFED1, 0xFED1 }, +{ 0xFED2, 0xFED2, 0xFED2 }, +{ 0xFED3, 0xFED3, 0xFED3 }, +{ 0xFED4, 0xFED4, 0xFED4 }, +{ 0xFED5, 0xFED5, 0xFED5 }, +{ 0xFED6, 0xFED6, 0xFED6 }, +{ 0xFED7, 0xFED7, 0xFED7 }, +{ 0xFED8, 0xFED8, 0xFED8 }, +{ 0xFED9, 0xFED9, 0xFED9 }, +{ 0xFEDA, 0xFEDA, 0xFEDA }, +{ 0xFEDB, 0xFEDB, 0xFEDB }, +{ 0xFEDC, 0xFEDC, 0xFEDC }, +{ 0xFEDD, 0xFEDD, 0xFEDD }, +{ 0xFEDE, 0xFEDE, 0xFEDE }, +{ 0xFEDF, 0xFEDF, 0xFEDF }, +{ 0xFEE0, 0xFEE0, 0xFEE0 }, +{ 0xFEE1, 0xFEE1, 0xFEE1 }, +{ 0xFEE2, 0xFEE2, 0xFEE2 }, +{ 0xFEE3, 0xFEE3, 0xFEE3 }, +{ 0xFEE4, 0xFEE4, 0xFEE4 }, +{ 0xFEE5, 0xFEE5, 0xFEE5 }, +{ 0xFEE6, 0xFEE6, 0xFEE6 }, +{ 0xFEE7, 0xFEE7, 0xFEE7 }, +{ 0xFEE8, 0xFEE8, 0xFEE8 }, +{ 0xFEE9, 0xFEE9, 0xFEE9 }, +{ 0xFEEA, 0xFEEA, 0xFEEA }, +{ 0xFEEB, 0xFEEB, 0xFEEB }, +{ 0xFEEC, 0xFEEC, 0xFEEC }, +{ 0xFEED, 0xFEED, 0xFEED }, +{ 0xFEEE, 0xFEEE, 0xFEEE }, +{ 0xFEEF, 0xFEEF, 0xFEEF }, +{ 0xFEF0, 0xFEF0, 0xFEF0 }, +{ 0xFEF1, 0xFEF1, 0xFEF1 }, +{ 0xFEF2, 0xFEF2, 0xFEF2 }, +{ 0xFEF3, 0xFEF3, 0xFEF3 }, +{ 0xFEF4, 0xFEF4, 0xFEF4 }, +{ 0xFEF5, 0xFEF5, 0xFEF5 }, +{ 0xFEF6, 0xFEF6, 0xFEF6 }, +{ 0xFEF7, 0xFEF7, 0xFEF7 }, +{ 0xFEF8, 0xFEF8, 0xFEF8 }, +{ 0xFEF9, 0xFEF9, 0xFEF9 }, +{ 0xFEFA, 0xFEFA, 0xFEFA }, +{ 0xFEFB, 0xFEFB, 0xFEFB }, +{ 0xFEFC, 0xFEFC, 0xFEFC }, +{ 0xFF21, 0xFF21, 0xFF41 }, +{ 0xFF22, 0xFF22, 0xFF42 }, +{ 0xFF23, 0xFF23, 0xFF43 }, +{ 0xFF24, 0xFF24, 0xFF44 }, +{ 0xFF25, 0xFF25, 0xFF45 }, +{ 0xFF26, 0xFF26, 0xFF46 }, +{ 0xFF27, 0xFF27, 0xFF47 }, +{ 0xFF28, 0xFF28, 0xFF48 }, +{ 0xFF29, 0xFF29, 0xFF49 }, +{ 0xFF2A, 0xFF2A, 0xFF4A }, +{ 0xFF2B, 0xFF2B, 0xFF4B }, +{ 0xFF2C, 0xFF2C, 0xFF4C }, +{ 0xFF2D, 0xFF2D, 0xFF4D }, +{ 0xFF2E, 0xFF2E, 0xFF4E }, +{ 0xFF2F, 0xFF2F, 0xFF4F }, +{ 0xFF30, 0xFF30, 0xFF50 }, +{ 0xFF31, 0xFF31, 0xFF51 }, +{ 0xFF32, 0xFF32, 0xFF52 }, +{ 0xFF33, 0xFF33, 0xFF53 }, +{ 0xFF34, 0xFF34, 0xFF54 }, +{ 0xFF35, 0xFF35, 0xFF55 }, +{ 0xFF36, 0xFF36, 0xFF56 }, +{ 0xFF37, 0xFF37, 0xFF57 }, +{ 0xFF38, 0xFF38, 0xFF58 }, +{ 0xFF39, 0xFF39, 0xFF59 }, +{ 0xFF3A, 0xFF3A, 0xFF5A }, +{ 0xFF41, 0xFF21, 0xFF41 }, +{ 0xFF42, 0xFF22, 0xFF42 }, +{ 0xFF43, 0xFF23, 0xFF43 }, +{ 0xFF44, 0xFF24, 0xFF44 }, +{ 0xFF45, 0xFF25, 0xFF45 }, +{ 0xFF46, 0xFF26, 0xFF46 }, +{ 0xFF47, 0xFF27, 0xFF47 }, +{ 0xFF48, 0xFF28, 0xFF48 }, +{ 0xFF49, 0xFF29, 0xFF49 }, +{ 0xFF4A, 0xFF2A, 0xFF4A }, +{ 0xFF4B, 0xFF2B, 0xFF4B }, +{ 0xFF4C, 0xFF2C, 0xFF4C }, +{ 0xFF4D, 0xFF2D, 0xFF4D }, +{ 0xFF4E, 0xFF2E, 0xFF4E }, +{ 0xFF4F, 0xFF2F, 0xFF4F }, +{ 0xFF50, 0xFF30, 0xFF50 }, +{ 0xFF51, 0xFF31, 0xFF51 }, +{ 0xFF52, 0xFF32, 0xFF52 }, +{ 0xFF53, 0xFF33, 0xFF53 }, +{ 0xFF54, 0xFF34, 0xFF54 }, +{ 0xFF55, 0xFF35, 0xFF55 }, +{ 0xFF56, 0xFF36, 0xFF56 }, +{ 0xFF57, 0xFF37, 0xFF57 }, +{ 0xFF58, 0xFF38, 0xFF58 }, +{ 0xFF59, 0xFF39, 0xFF59 }, +{ 0xFF5A, 0xFF3A, 0xFF5A }, +{ 0xFF66, 0xFF66, 0xFF66 }, +{ 0xFF67, 0xFF67, 0xFF67 }, +{ 0xFF68, 0xFF68, 0xFF68 }, +{ 0xFF69, 0xFF69, 0xFF69 }, +{ 0xFF6A, 0xFF6A, 0xFF6A }, +{ 0xFF6B, 0xFF6B, 0xFF6B }, +{ 0xFF6C, 0xFF6C, 0xFF6C }, +{ 0xFF6D, 0xFF6D, 0xFF6D }, +{ 0xFF6E, 0xFF6E, 0xFF6E }, +{ 0xFF6F, 0xFF6F, 0xFF6F }, +{ 0xFF70, 0xFF70, 0xFF70 }, +{ 0xFF71, 0xFF71, 0xFF71 }, +{ 0xFF72, 0xFF72, 0xFF72 }, +{ 0xFF73, 0xFF73, 0xFF73 }, +{ 0xFF74, 0xFF74, 0xFF74 }, +{ 0xFF75, 0xFF75, 0xFF75 }, +{ 0xFF76, 0xFF76, 0xFF76 }, +{ 0xFF77, 0xFF77, 0xFF77 }, +{ 0xFF78, 0xFF78, 0xFF78 }, +{ 0xFF79, 0xFF79, 0xFF79 }, +{ 0xFF7A, 0xFF7A, 0xFF7A }, +{ 0xFF7B, 0xFF7B, 0xFF7B }, +{ 0xFF7C, 0xFF7C, 0xFF7C }, +{ 0xFF7D, 0xFF7D, 0xFF7D }, +{ 0xFF7E, 0xFF7E, 0xFF7E }, +{ 0xFF7F, 0xFF7F, 0xFF7F }, +{ 0xFF80, 0xFF80, 0xFF80 }, +{ 0xFF81, 0xFF81, 0xFF81 }, +{ 0xFF82, 0xFF82, 0xFF82 }, +{ 0xFF83, 0xFF83, 0xFF83 }, +{ 0xFF84, 0xFF84, 0xFF84 }, +{ 0xFF85, 0xFF85, 0xFF85 }, +{ 0xFF86, 0xFF86, 0xFF86 }, +{ 0xFF87, 0xFF87, 0xFF87 }, +{ 0xFF88, 0xFF88, 0xFF88 }, +{ 0xFF89, 0xFF89, 0xFF89 }, +{ 0xFF8A, 0xFF8A, 0xFF8A }, +{ 0xFF8B, 0xFF8B, 0xFF8B }, +{ 0xFF8C, 0xFF8C, 0xFF8C }, +{ 0xFF8D, 0xFF8D, 0xFF8D }, +{ 0xFF8E, 0xFF8E, 0xFF8E }, +{ 0xFF8F, 0xFF8F, 0xFF8F }, +{ 0xFF90, 0xFF90, 0xFF90 }, +{ 0xFF91, 0xFF91, 0xFF91 }, +{ 0xFF92, 0xFF92, 0xFF92 }, +{ 0xFF93, 0xFF93, 0xFF93 }, +{ 0xFF94, 0xFF94, 0xFF94 }, +{ 0xFF95, 0xFF95, 0xFF95 }, +{ 0xFF96, 0xFF96, 0xFF96 }, +{ 0xFF97, 0xFF97, 0xFF97 }, +{ 0xFF98, 0xFF98, 0xFF98 }, +{ 0xFF99, 0xFF99, 0xFF99 }, +{ 0xFF9A, 0xFF9A, 0xFF9A }, +{ 0xFF9B, 0xFF9B, 0xFF9B }, +{ 0xFF9C, 0xFF9C, 0xFF9C }, +{ 0xFF9D, 0xFF9D, 0xFF9D }, +{ 0xFF9E, 0xFF9E, 0xFF9E }, +{ 0xFF9F, 0xFF9F, 0xFF9F }, +{ 0xFFA0, 0xFFA0, 0xFFA0 }, +{ 0xFFA1, 0xFFA1, 0xFFA1 }, +{ 0xFFA2, 0xFFA2, 0xFFA2 }, +{ 0xFFA3, 0xFFA3, 0xFFA3 }, +{ 0xFFA4, 0xFFA4, 0xFFA4 }, +{ 0xFFA5, 0xFFA5, 0xFFA5 }, +{ 0xFFA6, 0xFFA6, 0xFFA6 }, +{ 0xFFA7, 0xFFA7, 0xFFA7 }, +{ 0xFFA8, 0xFFA8, 0xFFA8 }, +{ 0xFFA9, 0xFFA9, 0xFFA9 }, +{ 0xFFAA, 0xFFAA, 0xFFAA }, +{ 0xFFAB, 0xFFAB, 0xFFAB }, +{ 0xFFAC, 0xFFAC, 0xFFAC }, +{ 0xFFAD, 0xFFAD, 0xFFAD }, +{ 0xFFAE, 0xFFAE, 0xFFAE }, +{ 0xFFAF, 0xFFAF, 0xFFAF }, +{ 0xFFB0, 0xFFB0, 0xFFB0 }, +{ 0xFFB1, 0xFFB1, 0xFFB1 }, +{ 0xFFB2, 0xFFB2, 0xFFB2 }, +{ 0xFFB3, 0xFFB3, 0xFFB3 }, +{ 0xFFB4, 0xFFB4, 0xFFB4 }, +{ 0xFFB5, 0xFFB5, 0xFFB5 }, +{ 0xFFB6, 0xFFB6, 0xFFB6 }, +{ 0xFFB7, 0xFFB7, 0xFFB7 }, +{ 0xFFB8, 0xFFB8, 0xFFB8 }, +{ 0xFFB9, 0xFFB9, 0xFFB9 }, +{ 0xFFBA, 0xFFBA, 0xFFBA }, +{ 0xFFBB, 0xFFBB, 0xFFBB }, +{ 0xFFBC, 0xFFBC, 0xFFBC }, +{ 0xFFBD, 0xFFBD, 0xFFBD }, +{ 0xFFBE, 0xFFBE, 0xFFBE }, +{ 0xFFC2, 0xFFC2, 0xFFC2 }, +{ 0xFFC3, 0xFFC3, 0xFFC3 }, +{ 0xFFC4, 0xFFC4, 0xFFC4 }, +{ 0xFFC5, 0xFFC5, 0xFFC5 }, +{ 0xFFC6, 0xFFC6, 0xFFC6 }, +{ 0xFFC7, 0xFFC7, 0xFFC7 }, +{ 0xFFCA, 0xFFCA, 0xFFCA }, +{ 0xFFCB, 0xFFCB, 0xFFCB }, +{ 0xFFCC, 0xFFCC, 0xFFCC }, +{ 0xFFCD, 0xFFCD, 0xFFCD }, +{ 0xFFCE, 0xFFCE, 0xFFCE }, +{ 0xFFCF, 0xFFCF, 0xFFCF }, +{ 0xFFD2, 0xFFD2, 0xFFD2 }, +{ 0xFFD3, 0xFFD3, 0xFFD3 }, +{ 0xFFD4, 0xFFD4, 0xFFD4 }, +{ 0xFFD5, 0xFFD5, 0xFFD5 }, +{ 0xFFD6, 0xFFD6, 0xFFD6 }, +{ 0xFFD7, 0xFFD7, 0xFFD7 }, +{ 0xFFDA, 0xFFDA, 0xFFDA }, +{ 0xFFDB, 0xFFDB, 0xFFDB }, +{ 0xFFDC, 0xFFDC, 0xFFDC } +}; diff --git a/src/lib/hunspell/src/w_char.hxx b/src/lib/hunspell/src/w_char.hxx new file mode 100644 index 0000000..3719dd3 --- /dev/null +++ b/src/lib/hunspell/src/w_char.hxx @@ -0,0 +1,21 @@ +#ifndef __WCHARHXX__ +#define __WCHARHXX__ + +#ifndef GCC +typedef struct { +#else +typedef struct __attribute__ ((packed)) { +#endif + unsigned char l; + unsigned char h; +} w_char; + +// two character arrays +struct replentry { + char * pattern; + char * pattern2; + bool start; + bool end; +}; + +#endif diff --git a/src/lib/lib.pro b/src/lib/lib.pro new file mode 100644 index 0000000..469dfcc --- /dev/null +++ b/src/lib/lib.pro @@ -0,0 +1,15 @@ +TEMPLATE = subdirs + +CONFIG += ordered + +SUBDIRS += crashdump +#SUBDIRS += markdown +SUBDIRS += hunspell +SUBDIRS += multimarkdown/peg/leg.pro +SUBDIRS += pcre +SUBDIRS += core.pro + +#SUBDIRS += zlib +#SUBDIRS += quazip +#SUBDIRS += libpng +#SUBDIRS += libharu diff --git a/src/lib/markdown/examples/smartypants.c b/src/lib/markdown/examples/smartypants.c new file mode 100644 index 0000000..4840703 --- /dev/null +++ b/src/lib/markdown/examples/smartypants.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "markdown.h" +#include "html.h" +#include "buffer.h" + +#include +#include +#include +#include +#include + +#define READ_UNIT 1024 +#define OUTPUT_UNIT 64 + +int +main(int argc, char **argv) +{ + struct buf *ib, *ob; + size_t ret; + FILE *in = stdin; + + /* opening the file if given from the command line */ + if (argc > 1) { + in = fopen(argv[1], "r"); + if (!in) { + fprintf(stderr, "Unable to open input file \"%s\": %s\n", argv[0], strerror(errno)); + return 1; + } + } + + /* reading everything */ + ib = bufnew(READ_UNIT); + bufgrow(ib, READ_UNIT); + while ((ret = fread(ib->data + ib->size, 1, ib->asize - ib->size, in)) > 0) { + ib->size += ret; + bufgrow(ib, ib->size + READ_UNIT); + } + + if (in != stdin) + fclose(in); + + /* performing markdown parsing */ + ob = bufnew(OUTPUT_UNIT); + + sdhtml_smartypants(ob, ib->data, ib->size); + + /* writing the result to stdout */ + (void)fwrite(ob->data, 1, ob->size, stdout); + + /* cleanup */ + bufrelease(ib); + bufrelease(ob); + + return 0; +} + +/* vim: set filetype=c: */ diff --git a/src/lib/markdown/examples/sundown.c b/src/lib/markdown/examples/sundown.c new file mode 100644 index 0000000..8a475dc --- /dev/null +++ b/src/lib/markdown/examples/sundown.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "markdown.h" +#include "html.h" +#include "buffer.h" + +#include +#include +#include +#include + +#define READ_UNIT 1024 +#define OUTPUT_UNIT 64 + +/* main • main function, interfacing STDIO with the parser */ +int +main(int argc, char **argv) +{ + struct buf *ib, *ob; + int ret; + FILE *in = stdin; + + struct sd_callbacks callbacks; + struct html_renderopt options; + struct sd_markdown *markdown; + + /* opening the file if given from the command line */ + if (argc > 1) { + in = fopen(argv[1], "r"); + if (!in) { + fprintf(stderr,"Unable to open input file \"%s\": %s\n", argv[1], strerror(errno)); + return 1; + } + } + + /* reading everything */ + ib = bufnew(READ_UNIT); + bufgrow(ib, READ_UNIT); + while ((ret = fread(ib->data + ib->size, 1, ib->asize - ib->size, in)) > 0) { + ib->size += ret; + bufgrow(ib, ib->size + READ_UNIT); + } + + if (in != stdin) + fclose(in); + + /* performing markdown parsing */ + ob = bufnew(OUTPUT_UNIT); + + sdhtml_renderer(&callbacks, &options, 0); + markdown = sd_markdown_new(0, 16, &callbacks, &options); + + sd_markdown_render(ob, ib->data, ib->size, markdown); + sd_markdown_free(markdown); + + /* writing the result to stdout */ + ret = fwrite(ob->data, 1, ob->size, stdout); + + /* cleanup */ + bufrelease(ib); + bufrelease(ob); + + return (ret < 0) ? -1 : 0; +} + +/* vim: set filetype=c: */ diff --git a/src/lib/markdown/html/houdini.h b/src/lib/markdown/html/houdini.h new file mode 100644 index 0000000..b4954c0 --- /dev/null +++ b/src/lib/markdown/html/houdini.h @@ -0,0 +1,37 @@ +#ifndef HOUDINI_H__ +#define HOUDINI_H__ + +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HOUDINI_USE_LOCALE +# define _isxdigit(c) isxdigit(c) +# define _isdigit(c) isdigit(c) +#else +/* + * Helper _isdigit methods -- do not trust the current locale + * */ +# define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL) +# define _isdigit(c) ((c) >= '0' && (c) <= '9') +#endif + +extern void houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure); +extern void houdini_unescape_html(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_escape_xml(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_escape_uri(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_escape_url(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_unescape_uri(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_unescape_url(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_escape_js(struct buf *ob, const uint8_t *src, size_t size); +extern void houdini_unescape_js(struct buf *ob, const uint8_t *src, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/markdown/html/houdini_href_e.c b/src/lib/markdown/html/houdini_href_e.c new file mode 100644 index 0000000..981b3b1 --- /dev/null +++ b/src/lib/markdown/html/houdini_href_e.c @@ -0,0 +1,108 @@ +#include +#include +#include + +#include "houdini.h" + +#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) + +/* + * The following characters will not be escaped: + * + * -_.+!*'(),%#@?=;:/,+&$ alphanum + * + * Note that this character set is the addition of: + * + * - The characters which are safe to be in an URL + * - The characters which are *not* safe to be in + * an URL because they are RESERVED characters. + * + * We asume (lazily) that any RESERVED char that + * appears inside an URL is actually meant to + * have its native function (i.e. as an URL + * component/separator) and hence needs no escaping. + * + * There are two exceptions: the chacters & (amp) + * and ' (single quote) do not appear in the table. + * They are meant to appear in the URL as components, + * yet they require special HTML-entity escaping + * to generate valid HTML markup. + * + * All other characters will be escaped to %XX. + * + */ +static const char HREF_SAFE[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 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, 0, 0, 0, 0, 1, + 0, 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, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void +houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size) +{ + static const char hex_chars[] = "0123456789ABCDEF"; + size_t i = 0, org; + char hex_str[3]; + + bufgrow(ob, ESCAPE_GROW_FACTOR(size)); + hex_str[0] = '%'; + + while (i < size) { + org = i; + while (i < size && HREF_SAFE[src[i]] != 0) + i++; + + if (i > org) + bufput(ob, src + org, i - org); + + /* escaping */ + if (i >= size) + break; + + switch (src[i]) { + /* amp appears all the time in URLs, but needs + * HTML-entity escaping to be inside an href */ + case '&': + BUFPUTSL(ob, "&"); + break; + + /* the single quote is a valid URL character + * according to the standard; it needs HTML + * entity escaping too */ + case '\'': + BUFPUTSL(ob, "'"); + break; + + /* the space can be escaped to %20 or a plus + * sign. we're going with the generic escape + * for now. the plus thing is more commonly seen + * when building GET strings */ +#if 0 + case ' ': + bufputc(ob, '+'); + break; +#endif + + /* every other character goes with a %XX escaping */ + default: + hex_str[1] = hex_chars[(src[i] >> 4) & 0xF]; + hex_str[2] = hex_chars[src[i] & 0xF]; + bufput(ob, hex_str, 3); + } + + i++; + } +} diff --git a/src/lib/markdown/html/houdini_html_e.c b/src/lib/markdown/html/houdini_html_e.c new file mode 100644 index 0000000..d9bbf18 --- /dev/null +++ b/src/lib/markdown/html/houdini_html_e.c @@ -0,0 +1,84 @@ +#include +#include +#include + +#include "houdini.h" + +#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */ + +/** + * According to the OWASP rules: + * + * & --> & + * < --> < + * > --> > + * " --> " + * ' --> ' ' is not recommended + * / --> / forward slash is included as it helps end an HTML entity + * + */ +static const char HTML_ESCAPE_TABLE[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const char *HTML_ESCAPES[] = { + "", + """, + "&", + "'", + "/", + "<", + ">" +}; + +void +houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure) +{ + size_t i = 0, org, esc = 0; + + bufgrow(ob, ESCAPE_GROW_FACTOR(size)); + + while (i < size) { + org = i; + while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0) + i++; + + if (i > org) + bufput(ob, src + org, i - org); + + /* escaping */ + if (i >= size) + break; + + /* The forward slash is only escaped in secure mode */ + if (src[i] == '/' && !secure) { + bufputc(ob, '/'); + } else { + bufputs(ob, HTML_ESCAPES[esc]); + } + + i++; + } +} + +void +houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size) +{ + houdini_escape_html0(ob, src, size, 1); +} + diff --git a/src/lib/markdown/html/html.c b/src/lib/markdown/html/html.c new file mode 100644 index 0000000..11ecdfe --- /dev/null +++ b/src/lib/markdown/html/html.c @@ -0,0 +1,671 @@ +/* + * Copyright (c) 2009, Natacha Porté + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "markdown.h" +#include "html.h" + +#include +#include +#include +#include + +#include "houdini.h" + +#include "highlighter.h" + +#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML) + +//extern void highlighter(struct buf *ob, const char *name, int len, const char *code, int codeLen); + +int +sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname) +{ + size_t i; + int closed = 0; + + if (tag_size < 3 || tag_data[0] != '<') + return HTML_TAG_NONE; + + i = 1; + + if (tag_data[i] == '/') { + closed = 1; + i++; + } + + for (; i < tag_size; ++i, ++tagname) { + if (*tagname == 0) + break; + + if (tag_data[i] != *tagname) + return HTML_TAG_NONE; + } + + if (i == tag_size) + return HTML_TAG_NONE; + + if (isspace(tag_data[i]) || tag_data[i] == '>') + return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN; + + return HTML_TAG_NONE; +} + +static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length) +{ + houdini_escape_html0(ob, source, length, 0); +} + +static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length) +{ + houdini_escape_href(ob, source, length); +} + +/******************** + * GENERIC RENDERER * + ********************/ +static int +rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque) +{ + struct html_renderopt *options = opaque; + + if (!link || !link->size) + return 0; + + if ((options->flags & HTML_SAFELINK) != 0 && + !sd_autolink_issafe(link->data, link->size) && + type != MKDA_EMAIL) + return 0; + + BUFPUTSL(ob, "data, link->size); + + if (options->link_attributes) { + bufputc(ob, '\"'); + options->link_attributes(ob, link, opaque); + bufputc(ob, '>'); + } else { + BUFPUTSL(ob, "\">"); + } + + /* + * Pretty printing: if we get an email address as + * an actual URI, e.g. `mailto:foo@bar.com`, we don't + * want to print the `mailto:` prefix + */ + if (bufprefix(link, "mailto:") == 0) { + escape_html(ob, link->data + 7, link->size - 7); + } else { + escape_html(ob, link->data, link->size); + } + + BUFPUTSL(ob, ""); + + return 1; +} + +static void +rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque) +{ + if (ob->size) bufputc(ob, '\n'); + + if (lang && lang->size) { + size_t i, cls, lanStart=0, lanEnd=0; + BUFPUTSL(ob, "
    size;
    +		for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {
    +			while (i < lang->size && isspace(lang->data[i]))
    +				i++;
    +
    +			if (i < lang->size) {
    +				size_t org = i;
    +				while (i < lang->size && !isspace(lang->data[i]))
    +					i++;
    +
    +				if (lang->data[org] == '.')
    +					org++;
    +
    +                if (cls) bufputc(ob, ' ');
    +				escape_html(ob, lang->data + org, i - org);
    +			}
    +		}
    +        lanEnd = ob->size;
    +		BUFPUTSL(ob, "\">");
    +        highlighter(ob, ob->data+lanStart, lanEnd-lanStart, text->data, text->size);
    +    } else {
    +		BUFPUTSL(ob, "
    ");
    +        if(text)
    +            escape_html(ob, text->data, text->size);
    +    }
    +//    if (text){
    +//        CodeSyntaxHighlighter highlighter;
    +//    }
    +//		escape_html(ob, text->data, text->size);
    +
    +	BUFPUTSL(ob, "
    \n"); +} + +static void +rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque) +{ + if (ob->size) bufputc(ob, '\n'); + BUFPUTSL(ob, "
    \n"); + if (text) bufput(ob, text->data, text->size); + BUFPUTSL(ob, "
    \n"); +} + +static int +rndr_codespan(struct buf *ob, const struct buf *text, void *opaque) +{ + BUFPUTSL(ob, ""); + if (text) escape_html(ob, text->data, text->size); + BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque) +{ + if (!text || !text->size) + return 0; + + BUFPUTSL(ob, ""); + bufput(ob, text->data, text->size); + BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque) +{ + if (!text || !text->size) + return 0; + + BUFPUTSL(ob, ""); + bufput(ob, text->data, text->size); + BUFPUTSL(ob, ""); + + return 1; +} + +static int +rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque) +{ + if (!text || !text->size) return 0; + BUFPUTSL(ob, ""); + if (text) bufput(ob, text->data, text->size); + BUFPUTSL(ob, ""); + return 1; +} + +static int +rndr_linebreak(struct buf *ob, void *opaque) +{ + struct html_renderopt *options = opaque; + bufputs(ob, USE_XHTML(options) ? "
    \n" : "
    \n"); + return 1; +} + +static void +rndr_header(struct buf *ob, const struct buf *text, const struct buf *id, int level, void *opaque) +{ + struct html_renderopt *options = opaque; + + if (ob->size) + bufputc(ob, '\n'); + + if (options->flags & HTML_TOC) + bufprintf(ob, "", level, options->toc_data.header_count++); + else if (id){ + bufprintf(ob, "data, id->size); + bufput(ob, "\">", 2); + } + else + bufprintf(ob, "", level); + + if (text) bufput(ob, text->data, text->size); + bufprintf(ob, "\n", level); +} + +static int +rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque) +{ + struct html_renderopt *options = opaque; + + if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size)) + return 0; + + BUFPUTSL(ob, "size) + escape_href(ob, link->data, link->size); + + if (title && title->size) { + BUFPUTSL(ob, "\" title=\""); + escape_html(ob, title->data, title->size); + } + + if (options->link_attributes) { + bufputc(ob, '\"'); + options->link_attributes(ob, link, opaque); + bufputc(ob, '>'); + } else { + BUFPUTSL(ob, "\">"); + } + + if (content && content->size) bufput(ob, content->data, content->size); + BUFPUTSL(ob, ""); + return 1; +} +//int (*footnote_link)(unsigned int id, const struct buf* linkContent); +static int +rndr_footnote_link(struct buf *ob, unsigned int id) +{ + bufprintf(ob, "%d", id, id, id); + return 1; +} + +static void +rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque) +{ + if (ob->size) bufputc(ob, '\n'); + bufput(ob, flags & MKD_LIST_ORDERED ? "
      \n" : "
        \n", 5); + if (text) bufput(ob, text->data, text->size); + bufput(ob, flags & MKD_LIST_ORDERED ? "
    \n" : "\n", 6); +} + +static void +rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque) +{ + BUFPUTSL(ob, "
  1. "); + if (text) { + size_t size = text->size; + while (size && text->data[size - 1] == '\n') + size--; + + bufput(ob, text->data, size); + } + BUFPUTSL(ob, "
  2. \n"); +} + +static void +rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque) +{ + struct html_renderopt *options = opaque; + size_t i = 0; + + if (ob->size) bufputc(ob, '\n'); + + if (!text || !text->size) + return; + + while (i < text->size && isspace(text->data[i])) i++; + + if (i == text->size) + return; + + BUFPUTSL(ob, "

    "); + if (options->flags & HTML_HARD_WRAP) { + size_t org; + while (i < text->size) { + org = i; + while (i < text->size && text->data[i] != '\n') + i++; + + if (i > org) + bufput(ob, text->data + org, i - org); + + /* + * do not insert a line break if this newline + * is the last character on the paragraph + */ + if (i >= text->size - 1) + break; + + rndr_linebreak(ob, opaque); + i++; + } + } else { + bufput(ob, &text->data[i], text->size - i); + } + BUFPUTSL(ob, "

    \n"); +} + +static void +rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque) +{ + size_t org, sz; + if (!text) return; + sz = text->size; + while (sz > 0 && text->data[sz - 1] == '\n') sz--; + org = 0; + while (org < sz && text->data[org] == '\n') org++; + if (org >= sz) return; + if (ob->size) bufputc(ob, '\n'); + bufput(ob, text->data + org, sz - org); + bufputc(ob, '\n'); +} + +static int +rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque) +{ + if (!text || !text->size) return 0; + BUFPUTSL(ob, ""); + bufput(ob, text->data, text->size); + BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_hrule(struct buf *ob, void *opaque) +{ + struct html_renderopt *options = opaque; + if (ob->size) bufputc(ob, '\n'); + bufputs(ob, USE_XHTML(options) ? "
    \n" : "
    \n"); +} + +static int +rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque) +{ + struct html_renderopt *options = opaque; + if (!link || !link->size) return 0; + + BUFPUTSL(ob, "data, link->size); + BUFPUTSL(ob, "\" alt=\""); + + if (alt && alt->size) + escape_html(ob, alt->data, alt->size); + + if (title && title->size) { + BUFPUTSL(ob, "\" title=\""); + escape_html(ob, title->data, title->size); } + + bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">"); + return 1; +} + +static int +rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque) +{ + struct html_renderopt *options = opaque; + + /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES + * It doens't see if there are any valid tags, just escape all of them. */ + if((options->flags & HTML_ESCAPE) != 0) { + escape_html(ob, text->data, text->size); + return 1; + } + + if ((options->flags & HTML_SKIP_HTML) != 0) + return 1; + + if ((options->flags & HTML_SKIP_STYLE) != 0 && + sdhtml_is_tag(text->data, text->size, "style")) + return 1; + + if ((options->flags & HTML_SKIP_LINKS) != 0 && + sdhtml_is_tag(text->data, text->size, "a")) + return 1; + + if ((options->flags & HTML_SKIP_IMAGES) != 0 && + sdhtml_is_tag(text->data, text->size, "img")) + return 1; + + bufput(ob, text->data, text->size); + return 1; +} + +static void +rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque) +{ + if (ob->size) bufputc(ob, '\n'); + BUFPUTSL(ob, "\n"); + if (header) + bufput(ob, header->data, header->size); + BUFPUTSL(ob, "\n"); + if (body) + bufput(ob, body->data, body->size); + BUFPUTSL(ob, "
    \n"); +} + +static void +rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque) +{ + BUFPUTSL(ob, "\n"); + if (text) + bufput(ob, text->data, text->size); + BUFPUTSL(ob, "\n"); +} + +static void +rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque) +{ + if (flags & MKD_TABLE_HEADER) { + BUFPUTSL(ob, ""); + break; + + case MKD_TABLE_ALIGN_L: + BUFPUTSL(ob, " align=\"left\">"); + break; + + case MKD_TABLE_ALIGN_R: + BUFPUTSL(ob, " align=\"right\">"); + break; + + default: + BUFPUTSL(ob, ">"); + } + + if (text) + bufput(ob, text->data, text->size); + + if (flags & MKD_TABLE_HEADER) { + BUFPUTSL(ob, "\n"); + } else { + BUFPUTSL(ob, "\n"); + } +} + +static int +rndr_superscript(struct buf *ob, const struct buf *text, void *opaque) +{ + if (!text || !text->size) return 0; + BUFPUTSL(ob, ""); + bufput(ob, text->data, text->size); + BUFPUTSL(ob, ""); + return 1; +} + +static void +rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque) +{ + if (text) + escape_html(ob, text->data, text->size); +} + +//void (*footnote_bottom_content_list_item)(struct buf *ob, unsigned int id, const struct buf *content); +static void +rndr_footnote_list_item(struct buf *ob, unsigned int id, const struct buf * content) +{ + bufprintf(ob, "
  3. \n", id); + bufput(ob, content->data, content->size); + bufprintf(ob, "
  4. ", id); +} + +static void +toc_header(struct buf *ob, const struct buf *text, const struct buf *id, int level, void *opaque) +{ + struct html_renderopt *options = opaque; + + /* set the level offset if this is the first header + * we're parsing for the document */ + if (options->toc_data.current_level == 0) { + options->toc_data.level_offset = level - 1; + } + level -= options->toc_data.level_offset; + + if (level > options->toc_data.current_level) { + while (level > options->toc_data.current_level) { + BUFPUTSL(ob, "
      \n
    • \n"); + options->toc_data.current_level++; + } + } else if (level < options->toc_data.current_level) { + BUFPUTSL(ob, "
    • \n"); + while (level < options->toc_data.current_level) { + BUFPUTSL(ob, "
    \n\n"); + options->toc_data.current_level--; + } + BUFPUTSL(ob,"
  5. \n"); + } else { + BUFPUTSL(ob,"
  6. \n
  7. \n"); + } + + bufprintf(ob, "", options->toc_data.header_count++); + if (text) + escape_html(ob, text->data, text->size); + BUFPUTSL(ob, "\n"); +} + +static int +toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque) +{ + if (content && content->size) + bufput(ob, content->data, content->size); + return 1; +} + +static void +toc_finalize(struct buf *ob, void *opaque) +{ + struct html_renderopt *options = opaque; + + while (options->toc_data.current_level > 0) { + BUFPUTSL(ob, "
  8. \n\n"); + options->toc_data.current_level--; + } +} + +void +sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options) +{ + static const struct sd_callbacks cb_default = { + NULL, + NULL, + NULL, + toc_header, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + + NULL, + rndr_codespan, + rndr_double_emphasis, + rndr_emphasis, + NULL, + NULL, + toc_link, + NULL,//footnote_link callback + NULL, + rndr_triple_emphasis, + rndr_strikethrough, + rndr_superscript, + + NULL, + NULL, + + NULL, //footnote bottom content list item callback + + NULL, + toc_finalize, + }; + + memset(options, 0x0, sizeof(struct html_renderopt)); + options->flags = HTML_TOC; + + memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks)); +} + +void +sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags) +{ + static const struct sd_callbacks cb_default = { + rndr_blockcode, + rndr_blockquote, + rndr_raw_block, + rndr_header, + rndr_hrule, + rndr_list, + rndr_listitem, + rndr_paragraph, + rndr_table, + rndr_tablerow, + rndr_tablecell, + + rndr_autolink, + rndr_codespan, + rndr_double_emphasis, + rndr_emphasis, + rndr_image, + rndr_linebreak, + rndr_link, + rndr_footnote_link,//footnote_link + rndr_raw_html, + rndr_triple_emphasis, + rndr_strikethrough, + rndr_superscript, + + NULL, + rndr_normal_text, + + rndr_footnote_list_item,//TODO footnote bottom list item content + + NULL, + NULL, + }; + + /* Prepare the options pointer */ + memset(options, 0x0, sizeof(struct html_renderopt)); + options->flags = render_flags; + + /* Prepare the callbacks */ + memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks)); + + if (render_flags & HTML_SKIP_IMAGES) + callbacks->image = NULL; + + if (render_flags & HTML_SKIP_LINKS) { + callbacks->link = NULL; + callbacks->autolink = NULL; + } + + if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE) + callbacks->blockhtml = NULL; +} diff --git a/src/lib/markdown/html/html.h b/src/lib/markdown/html/html.h new file mode 100644 index 0000000..4c8810d --- /dev/null +++ b/src/lib/markdown/html/html.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UPSKIRT_HTML_H +#define UPSKIRT_HTML_H + +#include "markdown.h" +#include "buffer.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct html_renderopt { + struct { + int header_count; + int current_level; + int level_offset; + } toc_data; + + unsigned int flags; + + /* extra callbacks */ + void (*link_attributes)(struct buf *ob, const struct buf *url, void *self); +}; + +typedef enum { + HTML_SKIP_HTML = (1 << 0), + HTML_SKIP_STYLE = (1 << 1), + HTML_SKIP_IMAGES = (1 << 2), + HTML_SKIP_LINKS = (1 << 3), + HTML_EXPAND_TABS = (1 << 4), + HTML_SAFELINK = (1 << 5), + HTML_TOC = (1 << 6), + HTML_HARD_WRAP = (1 << 7), + HTML_USE_XHTML = (1 << 8), + HTML_ESCAPE = (1 << 9), +} html_render_mode; + +typedef enum { + HTML_TAG_NONE = 0, + HTML_TAG_OPEN, + HTML_TAG_CLOSE, +} html_tag; + +int +sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname); + +extern void +sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags); + +extern void +sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr); + +extern void +sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/lib/markdown/html/html_smartypants.c b/src/lib/markdown/html/html_smartypants.c new file mode 100644 index 0000000..367c26a --- /dev/null +++ b/src/lib/markdown/html/html_smartypants.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "buffer.h" +#include "html.h" + +#include +#include +#include +#include + +#if defined(_WIN32) +#define snprintf _snprintf +#endif + +struct smartypants_data { + int in_squote; + int in_dquote; +}; + +static size_t smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); +static size_t smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size); + +static size_t (*smartypants_cb_ptrs[]) + (struct buf *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) = +{ + NULL, /* 0 */ + smartypants_cb__dash, /* 1 */ + smartypants_cb__parens, /* 2 */ + smartypants_cb__squote, /* 3 */ + smartypants_cb__dquote, /* 4 */ + smartypants_cb__amp, /* 5 */ + smartypants_cb__period, /* 6 */ + smartypants_cb__number, /* 7 */ + smartypants_cb__ltag, /* 8 */ + smartypants_cb__backtick, /* 9 */ + smartypants_cb__escape, /* 10 */ +}; + +static const uint8_t smartypants_cb_chars[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0, + 0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static inline int +word_boundary(uint8_t c) +{ + return c == 0 || isspace(c) || ispunct(c); +} + +static int +smartypants_quotes(struct buf *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open) +{ + char ent[8]; + + if (*is_open && !word_boundary(next_char)) + return 0; + + if (!(*is_open) && !word_boundary(previous_char)) + return 0; + + snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote); + *is_open = !(*is_open); + bufputs(ob, ent); + return 1; +} + +static size_t +smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 2) { + uint8_t t1 = tolower(text[1]); + + if (t1 == '\'') { + if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) + return 1; + } + + if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && + (size == 3 || word_boundary(text[2]))) { + BUFPUTSL(ob, "’"); + return 0; + } + + if (size >= 3) { + uint8_t t2 = tolower(text[2]); + + if (((t1 == 'r' && t2 == 'e') || + (t1 == 'l' && t2 == 'l') || + (t1 == 'v' && t2 == 'e')) && + (size == 4 || word_boundary(text[3]))) { + BUFPUTSL(ob, "’"); + return 0; + } + } + } + + if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote)) + return 0; + + bufputc(ob, text[0]); + return 0; +} + +static size_t +smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3) { + uint8_t t1 = tolower(text[1]); + uint8_t t2 = tolower(text[2]); + + if (t1 == 'c' && t2 == ')') { + BUFPUTSL(ob, "©"); + return 2; + } + + if (t1 == 'r' && t2 == ')') { + BUFPUTSL(ob, "®"); + return 2; + } + + if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') { + BUFPUTSL(ob, "™"); + return 3; + } + } + + bufputc(ob, text[0]); + return 0; +} + +static size_t +smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3 && text[1] == '-' && text[2] == '-') { + BUFPUTSL(ob, "—"); + return 2; + } + + if (size >= 2 && text[1] == '-') { + BUFPUTSL(ob, "–"); + return 1; + } + + bufputc(ob, text[0]); + return 0; +} + +static size_t +smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 6 && memcmp(text, """, 6) == 0) { + if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote)) + return 5; + } + + if (size >= 4 && memcmp(text, "�", 4) == 0) + return 3; + + bufputc(ob, '&'); + return 0; +} + +static size_t +smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 3 && text[1] == '.' && text[2] == '.') { + BUFPUTSL(ob, "…"); + return 2; + } + + if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') { + BUFPUTSL(ob, "…"); + return 4; + } + + bufputc(ob, text[0]); + return 0; +} + +static size_t +smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size >= 2 && text[1] == '`') { + if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) + return 1; + } + + return 0; +} + +static size_t +smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (word_boundary(previous_char) && size >= 3) { + if (text[0] == '1' && text[1] == '/' && text[2] == '2') { + if (size == 3 || word_boundary(text[3])) { + BUFPUTSL(ob, "½"); + return 2; + } + } + + if (text[0] == '1' && text[1] == '/' && text[2] == '4') { + if (size == 3 || word_boundary(text[3]) || + (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) { + BUFPUTSL(ob, "¼"); + return 2; + } + } + + if (text[0] == '3' && text[1] == '/' && text[2] == '4') { + if (size == 3 || word_boundary(text[3]) || + (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) { + BUFPUTSL(ob, "¾"); + return 2; + } + } + } + + bufputc(ob, text[0]); + return 0; +} + +static size_t +smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote)) + BUFPUTSL(ob, """); + + return 0; +} + +static size_t +smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + static const char *skip_tags[] = { + "pre", "code", "var", "samp", "kbd", "math", "script", "style" + }; + static const size_t skip_tags_count = 8; + + size_t tag, i = 0; + + while (i < size && text[i] != '>') + i++; + + for (tag = 0; tag < skip_tags_count; ++tag) { + if (sdhtml_is_tag(text, size, skip_tags[tag]) == HTML_TAG_OPEN) + break; + } + + if (tag < skip_tags_count) { + for (;;) { + while (i < size && text[i] != '<') + i++; + + if (i == size) + break; + + if (sdhtml_is_tag(text + i, size - i, skip_tags[tag]) == HTML_TAG_CLOSE) + break; + + i++; + } + + while (i < size && text[i] != '>') + i++; + } + + bufput(ob, text, i + 1); + return i; +} + +static size_t +smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) +{ + if (size < 2) + return 0; + + switch (text[1]) { + case '\\': + case '"': + case '\'': + case '.': + case '-': + case '`': + bufputc(ob, text[1]); + return 1; + + default: + bufputc(ob, '\\'); + return 0; + } +} + +#if 0 +static struct { + uint8_t c0; + const uint8_t *pattern; + const uint8_t *entity; + int skip; +} smartypants_subs[] = { + { '\'', "'s>", "’", 0 }, + { '\'', "'t>", "’", 0 }, + { '\'', "'re>", "’", 0 }, + { '\'', "'ll>", "’", 0 }, + { '\'', "'ve>", "’", 0 }, + { '\'', "'m>", "’", 0 }, + { '\'', "'d>", "’", 0 }, + { '-', "--", "—", 1 }, + { '-', "<->", "–", 0 }, + { '.', "...", "…", 2 }, + { '.', ". . .", "…", 4 }, + { '(', "(c)", "©", 2 }, + { '(', "(r)", "®", 2 }, + { '(', "(tm)", "™", 3 }, + { '3', "<3/4>", "¾", 2 }, + { '3', "<3/4ths>", "¾", 2 }, + { '1', "<1/2>", "½", 2 }, + { '1', "<1/4>", "¼", 2 }, + { '1', "<1/4th>", "¼", 2 }, + { '&', "�", 0, 3 }, +}; +#endif + +void +sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size) +{ + size_t i; + struct smartypants_data smrt = {0, 0}; + + if (!text) + return; + + bufgrow(ob, size); + + for (i = 0; i < size; ++i) { + size_t org; + uint8_t action = 0; + + org = i; + while (i < size && (action = smartypants_cb_chars[text[i]]) == 0) + i++; + + if (i > org) + bufput(ob, text + org, i - org); + + if (i < size) { + i += smartypants_cb_ptrs[(int)action] + (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i); + } + } +} + + diff --git a/src/lib/markdown/html_block_names.txt b/src/lib/markdown/html_block_names.txt new file mode 100644 index 0000000..a41d7d1 --- /dev/null +++ b/src/lib/markdown/html_block_names.txt @@ -0,0 +1,25 @@ +## +p +dl +h1 +h2 +h3 +h4 +h5 +h6 +ol +ul +del +div +ins +pre +form +math +table +figure +iframe +script +style +fieldset +noscript +blockquote diff --git a/src/lib/markdown/markdown.pro b/src/lib/markdown/markdown.pro new file mode 100644 index 0000000..b776e20 --- /dev/null +++ b/src/lib/markdown/markdown.pro @@ -0,0 +1,46 @@ +# use sundown +CONFIG += warn_off #stable + +#QT += core + +#for dynamic library +win32: DEFINES += MARKDOWN_LIB +#revision b6b58da + +#CONFIG += static + +#CONFIG(debug, debug|release){ #debug +# TARGET = markdown_d +# DESTDIR = ../../debug +# LIBS += -L ../../debug -lrapidxml +#} else { #release +# TARGET = markdown +# DESTDIR = ../../release +# LIBS += -L ../../release -lrapidxml +#} + +include(../rapidxml/rapidxml.pro) + +#TEMPLATE = lib + +INCLUDEPATH += $$PWD/html \ + $$PWD/src \ + $$PWD/../core \ + $$PWD/../pcre + +SOURCES += $$PWD/src/autolink.c \ + $$PWD/src/buffer.c \ + $$PWD/src/markdown.c \ + $$PWD/src/stack.c \ + $$PWD/html/houdini_href_e.c \ + $$PWD/html/houdini_html_e.c \ + $$PWD/html/html.c \ + $$PWD/html/html_smartypants.c \ + +HEADERS += $$PWD/src/autolink.h \ + $$PWD/src/buffer.h \ + $$PWD/src/html_blocks.h \ + $$PWD/src/markdown.h \ + $$PWD/src/stack.h \ + $$PWD/html/houdini.h \ + $$PWD/html/html.h \ diff --git a/src/lib/markdown/src/autolink.c b/src/lib/markdown/src/autolink.c new file mode 100644 index 0000000..1a47a8d --- /dev/null +++ b/src/lib/markdown/src/autolink.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "buffer.h" +#include "autolink.h" + +#include +#include +#include +#include + +#if defined(_WIN32) +#define strncasecmp _strnicmp +#endif + +int +sd_autolink_issafe(const uint8_t *link, size_t link_len) +{ + static const size_t valid_uris_count = 5; + static const char *valid_uris[] = { + "/", "http://", "https://", "ftp://", "mailto:" + }; + + size_t i; + + for (i = 0; i < valid_uris_count; ++i) { + size_t len = strlen(valid_uris[i]); + + if (link_len > len && + strncasecmp((char *)link, valid_uris[i], len) == 0 && + isalnum(link[len])) + return 1; + } + + return 0; +} + +static size_t +autolink_delim(uint8_t *data, size_t link_end, size_t offset, size_t size) +{ + uint8_t cclose, copen = 0; + size_t i; + + for (i = 0; i < link_end; ++i) + if (data[i] == '<') { + link_end = i; + break; + } + + while (link_end > 0) { + if (strchr("?!.,", data[link_end - 1]) != NULL) + link_end--; + + else if (data[link_end - 1] == ';') { + size_t new_end = link_end - 2; + + while (new_end > 0 && isalpha(data[new_end])) + new_end--; + + if (new_end < link_end - 2 && data[new_end] == '&') + link_end = new_end; + else + link_end--; + } + else break; + } + + if (link_end == 0) + return 0; + + cclose = data[link_end - 1]; + + switch (cclose) { + case '"': copen = '"'; break; + case '\'': copen = '\''; break; + case ')': copen = '('; break; + case ']': copen = '['; break; + case '}': copen = '{'; break; + } + + if (copen != 0) { + size_t closing = 0; + size_t opening = 0; + size_t i = 0; + + /* Try to close the final punctuation sign in this same line; + * if we managed to close it outside of the URL, that means that it's + * not part of the URL. If it closes inside the URL, that means it + * is part of the URL. + * + * Examples: + * + * foo http://www.pokemon.com/Pikachu_(Electric) bar + * => http://www.pokemon.com/Pikachu_(Electric) + * + * foo (http://www.pokemon.com/Pikachu_(Electric)) bar + * => http://www.pokemon.com/Pikachu_(Electric) + * + * foo http://www.pokemon.com/Pikachu_(Electric)) bar + * => http://www.pokemon.com/Pikachu_(Electric)) + * + * (foo http://www.pokemon.com/Pikachu_(Electric)) bar + * => foo http://www.pokemon.com/Pikachu_(Electric) + */ + + while (i < link_end) { + if (data[i] == copen) + opening++; + else if (data[i] == cclose) + closing++; + + i++; + } + + if (closing != opening) + link_end--; + } + + return link_end; +} + +static size_t +check_domain(uint8_t *data, size_t size, int allow_short) +{ + size_t i, np = 0; + + if (!isalnum(data[0])) + return 0; + + for (i = 1; i < size - 1; ++i) { + if (data[i] == '.') np++; + else if (!isalnum(data[i]) && data[i] != '-') break; + } + + if (allow_short) { + /* We don't need a valid domain in the strict sense (with + * least one dot; so just make sure it's composed of valid + * domain characters and return the length of the the valid + * sequence. */ + return i; + } else { + /* a valid domain needs to have at least a dot. + * that's as far as we get */ + return np ? i : 0; + } +} + +size_t +sd_autolink__www( + size_t *rewind_p, + struct buf *link, + uint8_t *data, + size_t offset, + size_t size, + unsigned int flags) +{ + size_t link_end; + + if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1])) + return 0; + + if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0) + return 0; + + link_end = check_domain(data, size, 0); + + if (link_end == 0) + return 0; + + while (link_end < size && !isspace(data[link_end])) + link_end++; + + link_end = autolink_delim(data, link_end, offset, size); + + if (link_end == 0) + return 0; + + bufput(link, data, link_end); + *rewind_p = 0; + + return (int)link_end; +} + +size_t +sd_autolink__email( + size_t *rewind_p, + struct buf *link, + uint8_t *data, + size_t offset, + size_t size, + unsigned int flags) +{ + size_t link_end, rewind; + int nb = 0, np = 0; + + for (rewind = 0; rewind < offset; ++rewind) { + uint8_t c = data[-rewind - 1]; + + if (isalnum(c)) + continue; + + if (strchr(".+-_", c) != NULL) + continue; + + break; + } + + if (rewind == 0) + return 0; + + for (link_end = 0; link_end < size; ++link_end) { + uint8_t c = data[link_end]; + + if (isalnum(c)) + continue; + + if (c == '@') + nb++; + else if (c == '.' && link_end < size - 1) + np++; + else if (c != '-' && c != '_') + break; + } + + if (link_end < 2 || nb != 1 || np == 0) + return 0; + + link_end = autolink_delim(data, link_end, offset, size); + + if (link_end == 0) + return 0; + + bufput(link, data - rewind, link_end + rewind); + *rewind_p = rewind; + + return link_end; +} + +size_t +sd_autolink__url( + size_t *rewind_p, + struct buf *link, + uint8_t *data, + size_t offset, + size_t size, + unsigned int flags) +{ + size_t link_end, rewind = 0, domain_len; + + if (size < 4 || data[1] != '/' || data[2] != '/') + return 0; + + while (rewind < offset && isalpha(data[-rewind - 1])) + rewind++; + + if (!sd_autolink_issafe(data - rewind, size + rewind)) + return 0; + + link_end = strlen("://"); + + domain_len = check_domain( + data + link_end, + size - link_end, + flags & SD_AUTOLINK_SHORT_DOMAINS); + + if (domain_len == 0) + return 0; + + link_end += domain_len; + while (link_end < size && !isspace(data[link_end])) + link_end++; + + link_end = autolink_delim(data, link_end, offset, size); + + if (link_end == 0) + return 0; + + bufput(link, data - rewind, link_end + rewind); + *rewind_p = rewind; + + return link_end; +} + diff --git a/src/lib/markdown/src/autolink.h b/src/lib/markdown/src/autolink.h new file mode 100644 index 0000000..65e0fe6 --- /dev/null +++ b/src/lib/markdown/src/autolink.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UPSKIRT_AUTOLINK_H +#define UPSKIRT_AUTOLINK_H + +#include "buffer.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SD_AUTOLINK_SHORT_DOMAINS = (1 << 0), +}; + +int +sd_autolink_issafe(const uint8_t *link, size_t link_len); + +size_t +sd_autolink__www(size_t *rewind_p, struct buf *link, + uint8_t *data, size_t offset, size_t size, unsigned int flags); + +size_t +sd_autolink__email(size_t *rewind_p, struct buf *link, + uint8_t *data, size_t offset, size_t size, unsigned int flags); + +size_t +sd_autolink__url(size_t *rewind_p, struct buf *link, + uint8_t *data, size_t offset, size_t size, unsigned int flags); + +#ifdef __cplusplus +} +#endif + +#endif + +/* vim: set filetype=c: */ diff --git a/src/lib/markdown/src/buffer.c b/src/lib/markdown/src/buffer.c new file mode 100644 index 0000000..47b40ce --- /dev/null +++ b/src/lib/markdown/src/buffer.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2008, Natacha Porté + * Copyright (c) 2011, Vicent Martí + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb + +#include "buffer.h" + +#include +#include +#include +#include + +/* MSVC compat */ +#if defined(_MSC_VER) +# define _buf_vsnprintf _vsnprintf +#else +# define _buf_vsnprintf vsnprintf +#endif + +int +bufprefix(const struct buf *buf, const char *prefix) +{ + size_t i; + assert(buf && buf->unit); + + for (i = 0; i < buf->size; ++i) { + if (prefix[i] == 0) + return 0; + + if (buf->data[i] != prefix[i]) + return buf->data[i] - prefix[i]; + } + + return 0; +} + +/* bufgrow: increasing the allocated size to the given value */ +int +bufgrow(struct buf *buf, size_t neosz) +{ + size_t neoasz; + void *neodata; + + assert(buf && buf->unit); + + if (neosz > BUFFER_MAX_ALLOC_SIZE) + return BUF_ENOMEM; + + if (buf->asize >= neosz) + return BUF_OK; + + neoasz = buf->asize + buf->unit; + while (neoasz < neosz) + neoasz += buf->unit; + + neodata = realloc(buf->data, neoasz); + if (!neodata) + return BUF_ENOMEM; + + buf->data = neodata; + buf->asize = neoasz; + return BUF_OK; +} + + +/* bufnew: allocation of a new buffer */ +struct buf * +bufnew(size_t unit) +{ + struct buf *ret; + ret = malloc(sizeof (struct buf)); + + if (ret) { + ret->data = 0; + ret->size = ret->asize = 0; + ret->unit = unit; + } + return ret; +} + +/* bufnullterm: NULL-termination of the string array */ +const char * +bufcstr(struct buf *buf) +{ + assert(buf && buf->unit); + + if (buf->size < buf->asize && buf->data[buf->size] == 0) + return (char *)buf->data; + + if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) { + buf->data[buf->size] = 0; + return (char *)buf->data; + } + + return NULL; +} + +/* bufprintf: formatted printing to a buffer */ +void +bufprintf(struct buf *buf, const char *fmt, ...) +{ + va_list ap; + int n; + + assert(buf && buf->unit); + + if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0) + return; + + va_start(ap, fmt); + n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); + va_end(ap); + + if (n < 0) { +#ifdef _MSC_VER + va_start(ap, fmt); + n = _vscprintf(fmt, ap); + va_end(ap); +#else + return; +#endif + } + + if ((size_t)n >= buf->asize - buf->size) { + if (bufgrow(buf, buf->size + n + 1) < 0) + return; + + va_start(ap, fmt); + n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap); + va_end(ap); + } + + if (n < 0) + return; + + buf->size += n; +} + +/* bufput: appends raw data to a buffer */ +void +bufput(struct buf *buf, const void *data, size_t len) +{ + assert(buf && buf->unit); + + if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0) + return; + + memcpy(buf->data + buf->size, data, len); + buf->size += len; +} + +/* bufputs: appends a NUL-terminated string to a buffer */ +void +bufputs(struct buf *buf, const char *str) +{ + bufput(buf, str, strlen(str)); +} + + +/* bufputc: appends a single uint8_t to a buffer */ +void +bufputc(struct buf *buf, int c) +{ + assert(buf && buf->unit); + + if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0) + return; + + buf->data[buf->size] = c; + buf->size += 1; +} + +/* bufrelease: decrease the reference count and free the buffer if needed */ +void +bufrelease(struct buf *buf) +{ + if (!buf) + return; + + free(buf->data); + free(buf); +} + + +/* bufreset: frees internal data of the buffer */ +void +bufreset(struct buf *buf) +{ + if (!buf) + return; + + free(buf->data); + buf->data = NULL; + buf->size = buf->asize = 0; +} + +/* bufslurp: removes a given number of bytes from the head of the array */ +void +bufslurp(struct buf *buf, size_t len) +{ + assert(buf && buf->unit); + + if (len >= buf->size) { + buf->size = 0; + return; + } + + buf->size -= len; + memmove(buf->data, buf->data + len, buf->size); +} + diff --git a/src/lib/markdown/src/buffer.h b/src/lib/markdown/src/buffer.h new file mode 100644 index 0000000..221d142 --- /dev/null +++ b/src/lib/markdown/src/buffer.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2008, Natacha Porté + * Copyright (c) 2011, Vicent Martí + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BUFFER_H__ +#define BUFFER_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) +#define __attribute__(x) +#define inline +#endif + +typedef enum { + BUF_OK = 0, + BUF_ENOMEM = -1, +} buferror_t; + +/* struct buf: character array buffer */ +struct buf { + uint8_t *data; /* actual character data */ + size_t size; /* size of the string */ + size_t asize; /* allocated size (0 = volatile buffer) */ + size_t unit; /* reallocation unit size (0 = read-only buffer) */ +}; + +/* CONST_BUF: global buffer from a string litteral */ +#define BUF_STATIC(string) \ + { (uint8_t *)string, sizeof string -1, sizeof string, 0, 0 } + +/* VOLATILE_BUF: macro for creating a volatile buffer on the stack */ +#define BUF_VOLATILE(strname) \ + { (uint8_t *)strname, strlen(strname), 0, 0, 0 } + +/* BUFPUTSL: optimized bufputs of a string litteral */ +#define BUFPUTSL(output, literal) \ + bufput(output, literal, sizeof literal - 1) + +/* bufgrow: increasing the allocated size to the given value */ +int bufgrow(struct buf *, size_t); + +/* bufnew: allocation of a new buffer */ +struct buf *bufnew(size_t) __attribute__ ((malloc)); + +/* bufnullterm: NUL-termination of the string array (making a C-string) */ +const char *bufcstr(struct buf *); + +/* bufprefix: compare the beginning of a buffer with a string */ +int bufprefix(const struct buf *buf, const char *prefix); + +/* bufput: appends raw data to a buffer */ +void bufput(struct buf *, const void *, size_t); + +/* bufputs: appends a NUL-terminated string to a buffer */ +void bufputs(struct buf *, const char *); + +/* bufputc: appends a single char to a buffer */ +void bufputc(struct buf *, int); + +/* bufrelease: decrease the reference count and free the buffer if needed */ +void bufrelease(struct buf *); + +/* bufreset: frees internal data of the buffer */ +void bufreset(struct buf *); + +/* bufslurp: removes a given number of bytes from the head of the array */ +void bufslurp(struct buf *, size_t); + +/* bufprintf: formatted printing to a buffer */ +void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3))); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/markdown/src/html_blocks.h b/src/lib/markdown/src/html_blocks.h new file mode 100644 index 0000000..09a758f --- /dev/null +++ b/src/lib/markdown/src/html_blocks.h @@ -0,0 +1,206 @@ +/* C code produced by gperf version 3.0.3 */ +/* Command-line: gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case html_block_names.txt */ +/* Computed positions: -k'1-2' */ + +#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \ + && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \ + && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \ + && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \ + && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \ + && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \ + && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \ + && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \ + && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \ + && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \ + && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \ + && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \ + && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \ + && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \ + && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \ + && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \ + && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \ + && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \ + && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \ + && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \ + && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \ + && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \ + && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126)) +/* The character set is not based on ISO-646. */ +error "gperf generated tables don't work with this execution character set. Please report a bug to ." +#endif + +/* maximum key range = 37, duplicates = 0 */ + +#ifndef GPERF_DOWNCASE +#define GPERF_DOWNCASE 1 +static unsigned char gperf_downcase[256] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, + 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, + 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, + 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, + 255 + }; +#endif + +#ifndef GPERF_CASE_STRNCMP +#define GPERF_CASE_STRNCMP 1 +static int +gperf_case_strncmp (s1, s2, n) + register const char *s1; + register const char *s2; + register unsigned int n; +{ + for (; n > 0;) + { + unsigned char c1 = gperf_downcase[(unsigned char)*s1++]; + unsigned char c2 = gperf_downcase[(unsigned char)*s2++]; + if (c1 != 0 && c1 == c2) + { + n--; + continue; + } + return (int)c1 - (int)c2; + } + return 0; +} +#endif + +#ifdef __GNUC__ +__inline +#else +#ifdef __cplusplus +inline +#endif +#endif +static unsigned int +hash_block_tag (str, len) + register const char *str; + register unsigned int len; +{ + static const unsigned char asso_values[] = + { + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 8, 30, 25, 20, 15, 10, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 0, 38, 0, 38, + 5, 5, 5, 15, 0, 38, 38, 0, 15, 10, + 0, 38, 38, 15, 0, 5, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 0, 38, + 0, 38, 5, 5, 5, 15, 0, 38, 38, 0, + 15, 10, 0, 38, 38, 15, 0, 5, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38 + }; + register int hval = len; + + switch (hval) + { + default: + hval += asso_values[(unsigned char)str[1]+1]; + /*FALLTHROUGH*/ + case 1: + hval += asso_values[(unsigned char)str[0]]; + break; + } + return hval; +} + +#ifdef __GNUC__ +__inline +#ifdef __GNUC_STDC_INLINE__ +__attribute__ ((__gnu_inline__)) +#endif +#endif +const char * +find_block_tag (str, len) + register const char *str; + register unsigned int len; +{ + enum + { + TOTAL_KEYWORDS = 24, + MIN_WORD_LENGTH = 1, + MAX_WORD_LENGTH = 10, + MIN_HASH_VALUE = 1, + MAX_HASH_VALUE = 37 + }; + + static const char * const wordlist[] = + { + "", + "p", + "dl", + "div", + "math", + "table", + "", + "ul", + "del", + "form", + "blockquote", + "figure", + "ol", + "fieldset", + "", + "h1", + "", + "h6", + "pre", + "", "", + "script", + "h5", + "noscript", + "", + "style", + "iframe", + "h4", + "ins", + "", "", "", + "h3", + "", "", "", "", + "h2" + }; + + if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) + { + register int key = hash_block_tag (str, len); + + if (key <= MAX_HASH_VALUE && key >= 0) + { + register const char *s = wordlist[key]; + + if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strncmp (str, s, len) && s[len] == '\0') + return s; + } + } + return 0; +} diff --git a/src/lib/markdown/src/markdown.c b/src/lib/markdown/src/markdown.c new file mode 100644 index 0000000..cf78f60 --- /dev/null +++ b/src/lib/markdown/src/markdown.c @@ -0,0 +1,2853 @@ +/* markdown.c - generic markdown parser */ + +/* + * Copyright (c) 2009, Natacha Porté + * Copyright (c) 2011, Vicent Marti + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "markdown.h" +#include "stack.h" + +#include +#include +#include +#include + +#if defined(_WIN32) +#define strncasecmp _strnicmp +#endif + +#define REF_TABLE_SIZE 8 + +#define BUFFER_BLOCK 0 +#define BUFFER_SPAN 1 + +#define MKD_LI_END 8 /* internal list flag */ + +#define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n) +#define GPERF_DOWNCASE 1 +#define GPERF_CASE_STRNCMP 1 +#include "html_blocks.h" + +/*************** + * LOCAL TYPES * + ***************/ + +/* link_ref: reference to a link */ +struct link_ref { + unsigned int id; + + struct buf *link; + struct buf *title; + + struct link_ref *next; +}; +/* footnote */ +struct footnote { + unsigned int hashId; + unsigned int id; + unsigned int is_used; + + struct buf *content; + + struct footnote *next; +}; +struct footnote_info{ + struct footnote* firstOne; + struct footnote* lastOne; + unsigned int count; +}; + +/* char_trigger: function pointer to render active chars */ +/* returns the number of chars taken care of */ +/* data is the pointer of the beginning of the span */ +/* offset is the number of valid chars before data */ +struct sd_markdown; +typedef size_t +(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); + +static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); +static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size); + +enum markdown_char_t { + MD_CHAR_NONE = 0, + MD_CHAR_EMPHASIS, + MD_CHAR_CODESPAN, + MD_CHAR_LINEBREAK, + MD_CHAR_LINK, + MD_CHAR_LANGLE, + MD_CHAR_ESCAPE, + MD_CHAR_ENTITITY, + MD_CHAR_AUTOLINK_URL, + MD_CHAR_AUTOLINK_EMAIL, + MD_CHAR_AUTOLINK_WWW, + MD_CHAR_SUPERSCRIPT, +}; + +static char_trigger markdown_char_ptrs[] = { + NULL, + &char_emphasis, + &char_codespan, + &char_linebreak, + &char_link, + &char_langle_tag, + &char_escape, + &char_entity, + &char_autolink_url, + &char_autolink_email, + &char_autolink_www, + &char_superscript, +}; + +/* render • structure containing one particular render */ +struct sd_markdown { + struct sd_callbacks cb; + void *opaque; + + struct link_ref *refs[REF_TABLE_SIZE]; + struct footnote_info fnInfo; + uint8_t active_char[256]; + struct stack work_bufs[2]; + unsigned int ext_flags; + size_t max_nesting; + int in_link_body; +}; + +/*************************** + * HELPER FUNCTIONS * + ***************************/ + +static inline struct buf * +rndr_newbuf(struct sd_markdown *rndr, int type) +{ + static const size_t buf_size[2] = {256, 64}; + struct buf *work = NULL; + struct stack *pool = &rndr->work_bufs[type]; + + if (pool->size < pool->asize && + pool->item[pool->size] != NULL) { + work = pool->item[pool->size++]; + work->size = 0; + } else { + work = bufnew(buf_size[type]); + stack_push(pool, work); + } + + return work; +} + +static inline void +rndr_popbuf(struct sd_markdown *rndr, int type) +{ + rndr->work_bufs[type].size--; +} + +static void +unscape_text(struct buf *ob, struct buf *src) +{ + size_t i = 0, org; + while (i < src->size) { + org = i; + while (i < src->size && src->data[i] != '\\') + i++; + + if (i > org) + bufput(ob, src->data + org, i - org); + + if (i + 1 >= src->size) + break; + + bufputc(ob, src->data[i + 1]); + i += 2; + } +} + +static unsigned int +hash_link_ref(const uint8_t *link_ref, size_t length) +{ + size_t i; + unsigned int hash = 0; + + for (i = 0; i < length; ++i) + hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash; + + return hash; +} + +static struct link_ref * +add_link_ref( + struct link_ref **references, + const uint8_t *name, size_t name_size) +{ + struct link_ref *ref = calloc(1, sizeof(struct link_ref)); + + if (!ref) + return NULL; + + ref->id = hash_link_ref(name, name_size); + ref->next = references[ref->id % REF_TABLE_SIZE]; + + references[ref->id % REF_TABLE_SIZE] = ref; + return ref; +} + +static struct link_ref * +find_link_ref(struct link_ref **references, uint8_t *name, size_t length) +{ + unsigned int hash = hash_link_ref(name, length); + struct link_ref *ref = NULL; + + ref = references[hash % REF_TABLE_SIZE]; + + while (ref != NULL) { + if (ref->id == hash) + return ref; + + ref = ref->next; + } + + return NULL; +} + +static void +free_link_refs(struct link_ref **references) +{ + size_t i; + + for (i = 0; i < REF_TABLE_SIZE; ++i) { + struct link_ref *r = references[i]; + struct link_ref *next; + + while (r) { + next = r->next; + bufrelease(r->link); + bufrelease(r->title); + free(r); + r = next; + } + } +} + +static struct footnote * +add_footnote( + struct footnote_info *footnoteInfo, + const uint8_t *name, size_t name_size){ + struct footnote *fn = calloc(1, sizeof(struct footnote)); + if(!fn) + return NULL; + fn->hashId = hash_link_ref(name, name_size); + fn->id = 0; + fn->is_used = 0; + fn->next = NULL; + if(!footnoteInfo->firstOne){ + footnoteInfo->firstOne = fn; + } else { + footnoteInfo->lastOne->next = fn; + } + footnoteInfo->lastOne = fn; + return fn; +} + +static struct footnote * +find_footnote(struct footnote_info *footnoteHead, uint8_t *name, size_t length){ + unsigned int hash = hash_link_ref(name, length); + struct footnote *fn = footnoteHead->firstOne; + while(fn){ + if(fn->hashId==hash) + return fn; + fn = fn->next; + } + return NULL; +} + +static void +free_footnotes(struct footnote_info *footnoteHead){ + struct footnote *start = footnoteHead->firstOne; + struct footnote *next; + while(start){ + next = start->next; + bufrelease(start->content); + free(start); + start = next; + } + footnoteHead->firstOne = NULL; + footnoteHead->lastOne = NULL; + footnoteHead->count = 0; +} + +static void +free_single_footnote(struct footnote *fn){ + bufrelease(fn->content); + free(fn); + fn = NULL; +} + + +/* + * Check whether a char is a Markdown space. + + * Right now we only consider spaces the actual + * space and a newline: tabs and carriage returns + * are filtered out during the preprocessing phase. + * + * If we wanted to actually be UTF-8 compliant, we + * should instead extract an Unicode codepoint from + * this character and check for space properties. + */ +static inline int +_isspace(int c) +{ + return c == ' ' || c == '\n'; +} + +/**************************** + * INLINE PARSING FUNCTIONS * + ****************************/ + +/* is_mail_autolink • looks for the address part of a mail autolink and '>' */ +/* this is less strict than the original markdown e-mail address matching */ +static size_t +is_mail_autolink(uint8_t *data, size_t size) +{ + size_t i = 0, nb = 0; + + /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */ + for (i = 0; i < size; ++i) { + if (isalnum(data[i])) + continue; + + switch (data[i]) { + case '@': + nb++; + + case '-': + case '.': + case '_': + break; + + case '>': + return (nb == 1) ? i + 1 : 0; + + default: + return 0; + } + } + + return 0; +} + +/* tag_length • returns the length of the given tag, or 0 is it's not valid */ +static size_t +tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink) +{ + size_t i, j; + + /* a valid tag can't be shorter than 3 chars */ + if (size < 3) return 0; + + /* begins with a '<' optionally followed by '/', followed by letter or number */ + if (data[0] != '<') return 0; + i = (data[1] == '/') ? 2 : 1; + + if (!isalnum(data[i])) + return 0; + + /* scheme test */ + *autolink = MKDA_NOT_AUTOLINK; + + /* try to find the beginning of an URI */ + while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-')) + i++; + + if (i > 1 && data[i] == '@') { + if ((j = is_mail_autolink(data + i, size - i)) != 0) { + *autolink = MKDA_EMAIL; + return i + j; + } + } + + if (i > 2 && data[i] == ':') { + *autolink = MKDA_NORMAL; + i++; + } + + /* completing autolink test: no whitespace or ' or " */ + if (i >= size) + *autolink = MKDA_NOT_AUTOLINK; + + else if (*autolink) { + j = i; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == '>' || data[i] == '\'' || + data[i] == '"' || data[i] == ' ' || data[i] == '\n') + break; + else i++; + } + + if (i >= size) return 0; + if (i > j && data[i] == '>') return i + 1; + /* one of the forbidden chars has been found */ + *autolink = MKDA_NOT_AUTOLINK; + } + + /* looking for sometinhg looking like a tag end */ + while (i < size && data[i] != '>') i++; + if (i >= size) return 0; + return i + 1; +} + +/* parse_inline • parses inline markdown elements */ +static void +parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t i = 0, end = 0; + uint8_t action = 0; + struct buf work = { 0, 0, 0, 0 }; + + if (rndr->work_bufs[BUFFER_SPAN].size + + rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting) + return; + + while (i < size) { + /* copying inactive chars into the output */ + while (end < size && (action = rndr->active_char[data[end]]) == 0) { + end++; + } + + if (rndr->cb.normal_text) { + work.data = data + i; + work.size = end - i; + rndr->cb.normal_text(ob, &work, rndr->opaque); + } + else + bufput(ob, data + i, end - i); + + if (end >= size) break; + i = end; + + end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i); + if (!end) /* no action from the callback */ + end = i + 1; + else { + i += end; + end = i; + } + } +} + +/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */ +static size_t +find_emph_char(uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 1; + + while (i < size) { + while (i < size && data[i] != c && data[i] != '`' && data[i] != '[') + i++; + + if (i == size) + return 0; + + if (data[i] == c) + return i; + + /* not counting escaped chars */ + if (i && data[i - 1] == '\\') { + i++; continue; + } + + if (data[i] == '`') { + size_t span_nb = 0, bt; + size_t tmp_i = 0; + + /* counting the number of opening backticks */ + while (i < size && data[i] == '`') { + i++; span_nb++; + } + + if (i >= size) return 0; + + /* finding the matching closing sequence */ + bt = 0; + while (i < size && bt < span_nb) { + if (!tmp_i && data[i] == c) tmp_i = i; + if (data[i] == '`') bt++; + else bt = 0; + i++; + } + + if (i >= size) return tmp_i; + } + /* skipping a link */ + else if (data[i] == '[') { + size_t tmp_i = 0; + uint8_t cc; + + i++; + while (i < size && data[i] != ']') { + if (!tmp_i && data[i] == c) tmp_i = i; + i++; + } + + i++; + while (i < size && (data[i] == ' ' || data[i] == '\n')) + i++; + + if (i >= size) + return tmp_i; + + switch (data[i]) { + case '[': + cc = ']'; break; + + case '(': + cc = ')'; break; + + default: + if (tmp_i) + return tmp_i; + else + continue; + } + + i++; + while (i < size && data[i] != cc) { + if (!tmp_i && data[i] == c) tmp_i = i; + i++; + } + + if (i >= size) + return tmp_i; + + i++; + } + } + + return 0; +} + +/* parse_emph1 • parsing single emphase */ +/* closed by a symbol not preceded by whitespace and not followed by symbol */ +static size_t +parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + struct buf *work = 0; + int r; + + if (!rndr->cb.emphasis) return 0; + + /* skipping one symbol if coming from emph3 */ + if (size > 1 && data[0] == c && data[1] == c) i = 1; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + if (i >= size) return 0; + + if (data[i] == c && !_isspace(data[i - 1])) { + + if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) { + if (!(i + 1 == size || _isspace(data[i + 1]) || ispunct(data[i + 1]))) + continue; + } + + work = rndr_newbuf(rndr, BUFFER_SPAN); + parse_inline(work, rndr, data, i); + r = rndr->cb.emphasis(ob, work, rndr->opaque); + rndr_popbuf(rndr, BUFFER_SPAN); + return r ? i + 1 : 0; + } + } + + return 0; +} + +/* parse_emph2 • parsing single emphase */ +static size_t +parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c) +{ + int (*render_method)(struct buf *ob, const struct buf *text, void *opaque); + size_t i = 0, len; + struct buf *work = 0; + int r; + + render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis; + + if (!render_method) + return 0; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + + if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) { + work = rndr_newbuf(rndr, BUFFER_SPAN); + parse_inline(work, rndr, data, i); + r = render_method(ob, work, rndr->opaque); + rndr_popbuf(rndr, BUFFER_SPAN); + return r ? i + 2 : 0; + } + i++; + } + return 0; +} + +/* parse_emph3 • parsing single emphase */ +/* finds the first closing tag, and delegates to the other emph */ +static size_t +parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c) +{ + size_t i = 0, len; + int r; + + while (i < size) { + len = find_emph_char(data + i, size - i, c); + if (!len) return 0; + i += len; + + /* skip whitespace preceded symbols */ + if (data[i] != c || _isspace(data[i - 1])) + continue; + + if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) { + /* triple symbol found */ + struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN); + + parse_inline(work, rndr, data, i); + r = rndr->cb.triple_emphasis(ob, work, rndr->opaque); + rndr_popbuf(rndr, BUFFER_SPAN); + return r ? i + 3 : 0; + + } else if (i + 1 < size && data[i + 1] == c) { + /* double symbol found, handing over to emph1 */ + len = parse_emph1(ob, rndr, data - 2, size + 2, c); + if (!len) return 0; + else return len - 2; + + } else { + /* single symbol found, handing over to emph2 */ + len = parse_emph2(ob, rndr, data - 1, size + 1, c); + if (!len) return 0; + else return len - 1; + } + } + return 0; +} + +/* char_emphasis • single and double emphasis parsing */ +static size_t +char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + uint8_t c = data[0]; + size_t ret; + + if (size > 2 && data[1] != c) { + /* whitespace cannot follow an opening emphasis; + * strikethrough only takes two characters '~~' */ + if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0) + return 0; + + return ret + 1; + } + + if (size > 3 && data[1] == c && data[2] != c) { + if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0) + return 0; + + return ret + 2; + } + + if (size > 4 && data[1] == c && data[2] == c && data[3] != c) { + if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0) + return 0; + + return ret + 3; + } + + return 0; +} + + +/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */ +static size_t +char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + if (offset < 2 || data[-1] != ' ' || data[-2] != ' ') + return 0; + + /* removing the last space from ob and rendering */ + while (ob->size && ob->data[ob->size - 1] == ' ') + ob->size--; + + return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0; +} + + +/* char_codespan • '`' parsing a code span (assuming codespan != 0) */ +static size_t +char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + size_t end, nb = 0, i, f_begin, f_end; + + /* counting the number of backticks in the delimiter */ + while (nb < size && data[nb] == '`') + nb++; + + /* finding the next delimiter */ + i = 0; + for (end = nb; end < size && i < nb; end++) { + if (data[end] == '`') i++; + else i = 0; + } + + if (i < nb && end >= size) + return 0; /* no matching delimiter */ + + /* trimming outside whitespaces */ + f_begin = nb; + while (f_begin < end && data[f_begin] == ' ') + f_begin++; + + f_end = end - nb; + while (f_end > nb && data[f_end-1] == ' ') + f_end--; + + /* real code span */ + if (f_begin < f_end) { + struct buf work = { data + f_begin, f_end - f_begin, 0, 0 }; + if (!rndr->cb.codespan(ob, &work, rndr->opaque)) + end = 0; + } else { + if (!rndr->cb.codespan(ob, 0, rndr->opaque)) + end = 0; + } + + return end; +} + + +/* char_escape • '\\' backslash escape */ +static size_t +char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~"; + struct buf work = { 0, 0, 0, 0 }; + + if (size > 1) { + if (strchr(escape_chars, data[1]) == NULL) + return 0; + + if (rndr->cb.normal_text) { + work.data = data + 1; + work.size = 1; + rndr->cb.normal_text(ob, &work, rndr->opaque); + } + else bufputc(ob, data[1]); + } else if (size == 1) { + bufputc(ob, data[0]); + } + + return 2; +} + +/* char_entity • '&' escaped when it doesn't belong to an entity */ +/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */ +static size_t +char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + size_t end = 1; + struct buf work = { 0, 0, 0, 0 }; + + if (end < size && data[end] == '#') + end++; + + while (end < size && isalnum(data[end])) + end++; + + if (end < size && data[end] == ';') + end++; /* real entity */ + else + return 0; /* lone '&' */ + + if (rndr->cb.entity) { + work.data = data; + work.size = end; + rndr->cb.entity(ob, &work, rndr->opaque); + } + else bufput(ob, data, end); + + return end; +} + +/* char_langle_tag • '<' when tags or autolinks are allowed */ +static size_t +char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + enum mkd_autolink altype = MKDA_NOT_AUTOLINK; + size_t end = tag_length(data, size, &altype); + struct buf work = { data, end, 0, 0 }; + int ret = 0; + + if (end > 2) { + if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) { + struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN); + work.data = data + 1; + work.size = end - 2; + unscape_text(u_link, &work); + ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque); + rndr_popbuf(rndr, BUFFER_SPAN); + } + else if (rndr->cb.raw_html_tag) + ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque); + } + + if (!ret) return 0; + else return end; +} + +static size_t +char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + struct buf *link, *link_url, *link_text; + size_t link_len, rewind; + + if (!rndr->cb.link || rndr->in_link_body) + return 0; + + link = rndr_newbuf(rndr, BUFFER_SPAN); + + if ((link_len = sd_autolink__www(&rewind, link, data, offset, size, 0)) > 0) { + link_url = rndr_newbuf(rndr, BUFFER_SPAN); + BUFPUTSL(link_url, "http://"); + bufput(link_url, link->data, link->size); + + ob->size -= rewind; + if (rndr->cb.normal_text) { + link_text = rndr_newbuf(rndr, BUFFER_SPAN); + rndr->cb.normal_text(link_text, link, rndr->opaque); + rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque); + rndr_popbuf(rndr, BUFFER_SPAN); + } else { + rndr->cb.link(ob, link_url, NULL, link, rndr->opaque); + } + rndr_popbuf(rndr, BUFFER_SPAN); + } + + rndr_popbuf(rndr, BUFFER_SPAN); + return link_len; +} + +static size_t +char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + struct buf *link; + size_t link_len, rewind; + + if (!rndr->cb.autolink || rndr->in_link_body) + return 0; + + link = rndr_newbuf(rndr, BUFFER_SPAN); + + if ((link_len = sd_autolink__email(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque); + } + + rndr_popbuf(rndr, BUFFER_SPAN); + return link_len; +} + +static size_t +char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + struct buf *link; + size_t link_len, rewind; + + if (!rndr->cb.autolink || rndr->in_link_body) + return 0; + + link = rndr_newbuf(rndr, BUFFER_SPAN); + + if ((link_len = sd_autolink__url(&rewind, link, data, offset, size, 0)) > 0) { + ob->size -= rewind; + rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque); + } + + rndr_popbuf(rndr, BUFFER_SPAN); + return link_len; +} + +/* char_link • '[': parsing a link or an image */ +static size_t +char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + int is_img = (offset && data[-1] == '!'), level; + size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0; + struct buf *content = 0; + struct buf *link = 0; + struct buf *title = 0; + struct buf *u_link = 0; + size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size; + int text_has_nl = 0, ret = 0; + int in_title = 0, qtype = 0; + /* checking whether the correct renderer exists */ + if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link)) + goto cleanup; + + /* looking for the matching closing bracket */ + for (level = 1; i < size; i++) { + if (data[i] == '\n') + text_has_nl = 1; + + else if (data[i - 1] == '\\') + continue; + + else if (data[i] == '[') + level++; + + else if (data[i] == ']') { + level--; + if (level <= 0) + break; + } + } + + if (i >= size) + goto cleanup; + + txt_e = i; + i++; + + /* skip any amount of whitespace or newline */ + /* (this is much more laxist than original markdown syntax) */ + while (i < size && _isspace(data[i])) + i++; + /* footnote link style */ + if ((rndr->ext_flags & MKDEXT_FOOTNOTE) && rndr->cb.footnote_link && + i <= size && size>3 && data[1]=='^'){ + struct footnote *fn=NULL; + struct buf id = { 0, 0, 0, 0}; + id.data = data+1; + id.size = txt_e - 1; + fn = find_footnote(&rndr->fnInfo, id.data, id.size); + if(fn && !fn->is_used){ + fn->id = ++rndr->fnInfo.count; + fn->is_used = 1; + ret = rndr->cb.footnote_link(ob, fn->id); + goto cleanup; + } + } + /* inline style link */ + if (i < size && data[i] == '(') { + /* skipping initial whitespace */ + i++; + + while (i < size && _isspace(data[i])) + i++; + + link_b = i; + + /* looking for link end: ' " ) */ + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == ')') break; + else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break; + else i++; + } + + if (i >= size) goto cleanup; + link_e = i; + + /* looking for title end if present */ + if (data[i] == '\'' || data[i] == '"') { + qtype = data[i]; + in_title = 1; + i++; + title_b = i; + + while (i < size) { + if (data[i] == '\\') i += 2; + else if (data[i] == qtype) {in_title = 0; i++;} + else if ((data[i] == ')') && !in_title) break; + else i++; + } + + if (i >= size) goto cleanup; + + /* skipping whitespaces after title */ + title_e = i - 1; + while (title_e > title_b && _isspace(data[title_e])) + title_e--; + + /* checking for closing quote presence */ + if (data[title_e] != '\'' && data[title_e] != '"') { + title_b = title_e = 0; + link_e = i; + } + } + + /* remove whitespace at the end of the link */ + while (link_e > link_b && _isspace(data[link_e - 1])) + link_e--; + + /* remove optional angle brackets around the link */ + if (data[link_b] == '<') link_b++; + if (data[link_e - 1] == '>') link_e--; + + /* building escaped link and title */ + if (link_e > link_b) { + link = rndr_newbuf(rndr, BUFFER_SPAN); + bufput(link, data + link_b, link_e - link_b); + } + + if (title_e > title_b) { + title = rndr_newbuf(rndr, BUFFER_SPAN); + bufput(title, data + title_b, title_e - title_b); + } + + i++; + } + + /* reference style link */ + else if (i < size && data[i] == '[') { + struct buf id = { 0, 0, 0, 0 }; + struct link_ref *lr; + + /* looking for the id */ + i++; + link_b = i; + while (i < size && data[i] != ']') i++; + if (i >= size) goto cleanup; + link_e = i; + + /* finding the link_ref */ + if (link_b == link_e) { + if (text_has_nl) { + struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN); + size_t j; + + for (j = 1; j < txt_e; j++) { + if (data[j] != '\n') + bufputc(b, data[j]); + else if (data[j - 1] != ' ') + bufputc(b, ' '); + } + + id.data = b->data; + id.size = b->size; + } else { + id.data = data + 1; + id.size = txt_e - 1; + } + } else { + id.data = data + link_b; + id.size = link_e - link_b; + } + + lr = find_link_ref(rndr->refs, id.data, id.size); + if (!lr) + goto cleanup; + + /* keeping link and title from link_ref */ + link = lr->link; + title = lr->title; + i++; + } + + /* shortcut reference style link */ + else { + struct buf id = { 0, 0, 0, 0 }; + struct link_ref *lr; + + /* crafting the id */ + if (text_has_nl) { + struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN); + size_t j; + + for (j = 1; j < txt_e; j++) { + if (data[j] != '\n') + bufputc(b, data[j]); + else if (data[j - 1] != ' ') + bufputc(b, ' '); + } + + id.data = b->data; + id.size = b->size; + } else { + id.data = data + 1; + id.size = txt_e - 1; + } + + /* finding the link_ref */ + lr = find_link_ref(rndr->refs, id.data, id.size); + if (!lr) + goto cleanup; + + /* keeping link and title from link_ref */ + link = lr->link; + title = lr->title; + + /* rewinding the whitespace */ + i = txt_e + 1; + } + + /* building content: img alt is escaped, link content is parsed */ + if (txt_e > 1) { + content = rndr_newbuf(rndr, BUFFER_SPAN); + if (is_img) { + bufput(content, data + 1, txt_e - 1); + } else { + /* disable autolinking when parsing inline the + * content of a link */ + rndr->in_link_body = 1; + parse_inline(content, rndr, data + 1, txt_e - 1); + rndr->in_link_body = 0; + } + } + + if (link) { + u_link = rndr_newbuf(rndr, BUFFER_SPAN); + unscape_text(u_link, link); + } + + /* calling the relevant rendering function */ + if (is_img) { + if (ob->size && ob->data[ob->size - 1] == '!') + ob->size -= 1; + + ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque); + } else { + ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque); + } + + /* cleanup */ +cleanup: + rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size; + return ret ? i : 0; +} + +static size_t +char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size) +{ + size_t sup_start, sup_len; + struct buf *sup; + if (!rndr->cb.superscript) + return 0; + + if (size < 2) + return 0; + + if (data[1] == '(') { + sup_start = sup_len = 2; + + while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\') + sup_len++; + + if (sup_len == size) + return 0; + } else { + sup_start = sup_len = 1; + + while (sup_len < size && !_isspace(data[sup_len])) + sup_len++; + } + + if (sup_len - sup_start == 0) + return (sup_start == 2) ? 3 : 0; + + sup = rndr_newbuf(rndr, BUFFER_SPAN); + parse_inline(sup, rndr, data + sup_start, sup_len - sup_start); + rndr->cb.superscript(ob, sup, rndr->opaque); + rndr_popbuf(rndr, BUFFER_SPAN); + + return (sup_start == 2) ? sup_len + 1 : sup_len; +} + +/********************************* + * BLOCK-LEVEL PARSING FUNCTIONS * + *********************************/ + +/* is_empty • returns the line length when it is empty, 0 otherwise */ +static size_t +is_empty(uint8_t *data, size_t size) +{ + size_t i; + + for (i = 0; i < size && data[i] != '\n'; i++) + if (data[i] != ' ') + return 0; + + return i + 1; +} + +/* is_hrule • returns whether a line is a horizontal rule */ +static int +is_hrule(uint8_t *data, size_t size) +{ + size_t i = 0, n = 0; + uint8_t c; + + /* skipping initial spaces */ + if (size < 3) return 0; + if (data[0] == ' ') { i++; + if (data[1] == ' ') { i++; + if (data[2] == ' ') { i++; } } } + + /* looking at the hrule uint8_t */ + if (i + 2 >= size + || (data[i] != '*' && data[i] != '-' && data[i] != '_')) + return 0; + c = data[i]; + + /* the whole line must be the char or whitespace */ + while (i < size && data[i] != '\n') { + if (data[i] == c) n++; + else if (data[i] != ' ') + return 0; + + i++; + } + + return n >= 3; +} + +/* check if a line begins with a code fence; return the + * width of the code fence */ +static size_t +prefix_codefence(uint8_t *data, size_t size) +{ + size_t i = 0, n = 0; + uint8_t c; + + /* skipping initial spaces */ + if (size < 3) return 0; + if (data[0] == ' ') { i++; + if (data[1] == ' ') { i++; + if (data[2] == ' ') { i++; } } } + + /* looking at the hrule uint8_t */ + if (i + 2 >= size || !(data[i] == '~' || data[i] == '`')) + return 0; + + c = data[i]; + + /* the whole line must be the uint8_t or whitespace */ + while (i < size && data[i] == c) { + n++; i++; + } + + if (n < 3) + return 0; + + return i; +} + +/* check if a line is a code fence; return its size if it is */ +static size_t +is_codefence(uint8_t *data, size_t size, struct buf *syntax) +{ + size_t i = 0, syn_len = 0; + uint8_t *syn_start; + + i = prefix_codefence(data, size); + if (i == 0) + return 0; + + while (i < size && data[i] == ' ') + i++; + + syn_start = data + i; + + if (i < size && data[i] == '{') { + i++; syn_start++; + + while (i < size && data[i] != '}' && data[i] != '\n') { + syn_len++; i++; + } + + if (i == size || data[i] != '}') + return 0; + + /* strip all whitespace at the beginning and the end + * of the {} block */ + while (syn_len > 0 && _isspace(syn_start[0])) { + syn_start++; syn_len--; + } + + while (syn_len > 0 && _isspace(syn_start[syn_len - 1])) + syn_len--; + + i++; + } else { + while (i < size && !_isspace(data[i])) { + syn_len++; i++; + } + } + + if (syntax) { + syntax->data = syn_start; + syntax->size = syn_len; + } + + while (i < size && data[i] != '\n') { + if (!_isspace(data[i])) + return 0; + + i++; + } + + return i + 1; +} + +/* is_atxheader • returns whether the line is a hash-prefixed header */ +static int +is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + if (data[0] != '#') + return 0; + + if (rndr->ext_flags & MKDEXT_SPACE_HEADERS) { + size_t level = 0; + + while (level < size && level < 6 && data[level] == '#') + level++; + + if (level < size && data[level] != ' ') + return 0; + } + + return 1; +} + +/* is_headerline • returns whether the line is a setext-style hdr underline */ +static int +is_headerline(uint8_t *data, size_t size) +{ + size_t i = 0; + + /* test of level 1 header */ + if (data[i] == '=') { + for (i = 1; i < size && data[i] == '='; i++); + while (i < size && data[i] == ' ') i++; + return (i >= size || data[i] == '\n') ? 1 : 0; } + + /* test of level 2 header */ + if (data[i] == '-') { + for (i = 1; i < size && data[i] == '-'; i++); + while (i < size && data[i] == ' ') i++; + return (i >= size || data[i] == '\n') ? 2 : 0; } + + return 0; +} + +static int +is_next_headerline(uint8_t *data, size_t size) +{ + size_t i = 0; + + while (i < size && data[i] != '\n') + i++; + + if (++i >= size) + return 0; + + return is_headerline(data + i, size - i); +} + +/* prefix_quote • returns blockquote prefix length */ +static size_t +prefix_quote(uint8_t *data, size_t size) +{ + size_t i = 0; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i < size && data[i] == '>') { + if (i + 1 < size && data[i + 1] == ' ') + return i + 2; + + return i + 1; + } + + return 0; +} + +/* prefix_code • returns prefix length for block code*/ +static size_t +prefix_code(uint8_t *data, size_t size) +{ + if (size > 3 && data[0] == ' ' && data[1] == ' ' + && data[2] == ' ' && data[3] == ' ') return 4; + + return 0; +} + +/* prefix_oli • returns ordered list item prefix */ +static size_t +prefix_oli(uint8_t *data, size_t size) +{ + size_t i = 0; + + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i >= size || data[i] < '0' || data[i] > '9') + return 0; + + while (i < size && data[i] >= '0' && data[i] <= '9') + i++; + + if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ') + return 0; + + if (is_next_headerline(data + i, size - i)) + return 0; + + return i + 2; +} + +/* prefix_uli • returns ordered list item prefix */ +static size_t +prefix_uli(uint8_t *data, size_t size) +{ + size_t i = 0; + + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + if (i < size && data[i] == ' ') i++; + + if (i + 1 >= size || + (data[i] != '*' && data[i] != '+' && data[i] != '-') || + data[i + 1] != ' ') + return 0; + + if (is_next_headerline(data + i, size - i)) + return 0; + + return i + 2; +} + + +/* parse_block • parsing of one block, returning next uint8_t to parse */ +static void parse_block(struct buf *ob, struct sd_markdown *rndr, + uint8_t *data, size_t size); + + +/* parse_blockquote • handles parsing of a blockquote fragment */ +static size_t +parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t beg, end = 0, pre, work_size = 0; + uint8_t *work_data = 0; + struct buf *out = 0; + + out = rndr_newbuf(rndr, BUFFER_BLOCK); + beg = 0; + while (beg < size) { + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); + + pre = prefix_quote(data + beg, end - beg); + + if (pre) + beg += pre; /* skipping prefix */ + + /* empty line followed by non-quote line */ + else if (is_empty(data + beg, end - beg) && + (end >= size || (prefix_quote(data + end, size - end) == 0 && + !is_empty(data + end, size - end)))) + break; + + if (beg < end) { /* copy into the in-place working buffer */ + /* bufput(work, data + beg, end - beg); */ + if (!work_data) + work_data = data + beg; + else if (data + beg != work_data + work_size) + memmove(work_data + work_size, data + beg, end - beg); + work_size += end - beg; + } + beg = end; + } + + parse_block(out, rndr, work_data, work_size); + if (rndr->cb.blockquote) + rndr->cb.blockquote(ob, out, rndr->opaque); + rndr_popbuf(rndr, BUFFER_BLOCK); + return end; +} + +static size_t +parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render); + +/* parse_blockquote • handles parsing of a regular paragraph */ +static size_t +parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t i = 0, end = 0; + int level = 0; + struct buf work = { data, 0, 0, 0 }; + + while (i < size) { + for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */; + + if (is_empty(data + i, size - i)) + break; + + if ((level = is_headerline(data + i, size - i)) != 0) + break; + + if (is_atxheader(rndr, data + i, size - i) || + is_hrule(data + i, size - i) || + prefix_quote(data + i, size - i)) { + end = i; + break; + } + + /* + * Early termination of a paragraph with the same logic + * as Markdown 1.0.0. If this logic is applied, the + * Markdown 1.0.3 test suite won't pass cleanly + * + * :: If the first character in a new line is not a letter, + * let's check to see if there's some kind of block starting + * here + */ + if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalnum(data[i])) { + if (prefix_oli(data + i, size - i) || + prefix_uli(data + i, size - i)) { + end = i; + break; + } + + /* see if an html block starts here */ + if (data[i] == '<' && rndr->cb.blockhtml && + parse_htmlblock(ob, rndr, data + i, size - i, 0)) { + end = i; + break; + } + + /* see if a code fence starts here */ + if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 && + is_codefence(data + i, size - i, NULL) != 0) { + end = i; + break; + } + } + + i = end; + } + + work.size = i; + while (work.size && data[work.size - 1] == '\n') + work.size--; + + if (!level) { + struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK); + parse_inline(tmp, rndr, work.data, work.size); + if (rndr->cb.paragraph) + rndr->cb.paragraph(ob, tmp, rndr->opaque); + rndr_popbuf(rndr, BUFFER_BLOCK); + } else { + struct buf *id=NULL; + struct buf *header_work; + + if (work.size) { + size_t beg; + i = work.size; + work.size -= 1; + + while (work.size && data[work.size] != '\n') + work.size -= 1; + + beg = work.size + 1; + while (work.size && data[work.size - 1] == '\n') + work.size -= 1; + + if (work.size > 0) { + struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK); + parse_inline(tmp, rndr, work.data, work.size); + + if (rndr->cb.paragraph) + rndr->cb.paragraph(ob, tmp, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_BLOCK); + work.data += beg; + work.size = i - beg; + } + else work.size = i; + } + if((rndr->ext_flags & MKDEXT_HEADER_ID_ATTRIBUTE) && work.size>3){ + size_t idStart=0, idEnd; + for(; (idStart+3)cb.header) + rndr->cb.header(ob, header_work, id, (int)level, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_SPAN); + if(id) + rndr_popbuf(rndr, BUFFER_SPAN); + } + + return end; +} + +/* parse_fencedcode • handles parsing of a block-level code fragment */ +static size_t +parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t beg, end; + struct buf *work = 0; + struct buf lang = { 0, 0, 0, 0 }; + + beg = is_codefence(data, size, &lang); + if (beg == 0) return 0; + + work = rndr_newbuf(rndr, BUFFER_BLOCK); + + while (beg < size) { + size_t fence_end; + struct buf fence_trail = { 0, 0, 0, 0 }; + + fence_end = is_codefence(data + beg, size - beg, &fence_trail); + if (fence_end != 0 && fence_trail.size == 0) { + beg += fence_end; + break; + } + + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++); + + if (beg < end) { + /* verbatim copy to the working buffer, + escaping entities */ + if (is_empty(data + beg, end - beg)) + bufputc(work, '\n'); + else bufput(work, data + beg, end - beg); + } + beg = end; + } + + if (work->size && work->data[work->size - 1] != '\n') + bufputc(work, '\n'); + + if (rndr->cb.blockcode) + rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_BLOCK); + return beg; +} + +static size_t +parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t beg, end, pre; + struct buf *work = 0; + + work = rndr_newbuf(rndr, BUFFER_BLOCK); + + beg = 0; + while (beg < size) { + for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {}; + pre = prefix_code(data + beg, end - beg); + + if (pre) + beg += pre; /* skipping prefix */ + else if (!is_empty(data + beg, end - beg)) + /* non-empty non-prefixed line breaks the pre */ + break; + + if (beg < end) { + /* verbatim copy to the working buffer, + escaping entities */ + if (is_empty(data + beg, end - beg)) + bufputc(work, '\n'); + else bufput(work, data + beg, end - beg); + } + beg = end; + } + + while (work->size && work->data[work->size - 1] == '\n') + work->size -= 1; + + bufputc(work, '\n'); + + if (rndr->cb.blockcode) + rndr->cb.blockcode(ob, work, NULL, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_BLOCK); + return beg; +} + +/* parse_listitem • parsing of a single list item */ +/* assuming initial prefix is already removed */ +static size_t +parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags) +{ + struct buf *work = 0, *inter = 0; + size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i; + int in_empty = 0, has_inside_empty = 0, in_fence = 0; + + /* keeping track of the first indentation prefix */ + while (orgpre < 3 && orgpre < size && data[orgpre] == ' ') + orgpre++; + + beg = prefix_uli(data, size); + if (!beg) + beg = prefix_oli(data, size); + + if (!beg) + return 0; + + /* skipping to the beginning of the following line */ + end = beg; + while (end < size && data[end - 1] != '\n') + end++; + + /* getting working buffers */ + work = rndr_newbuf(rndr, BUFFER_SPAN); + inter = rndr_newbuf(rndr, BUFFER_SPAN); + + /* putting the first line into the working buffer */ + bufput(work, data + beg, end - beg); + beg = end; + + /* process the following lines */ + while (beg < size) { + size_t has_next_uli = 0, has_next_oli = 0; + + end++; + + while (end < size && data[end - 1] != '\n') + end++; + + /* process an empty line */ + if (is_empty(data + beg, end - beg)) { + in_empty = 1; + beg = end; + continue; + } + + /* calculating the indentation */ + i = 0; + while (i < 4 && beg + i < end && data[beg + i] == ' ') + i++; + + pre = i; + + if (rndr->ext_flags & MKDEXT_FENCED_CODE) { + if (is_codefence(data + beg + i, end - beg - i, NULL) != 0) + in_fence = !in_fence; + } + + /* Only check for new list items if we are **not** inside + * a fenced code block */ + if (!in_fence) { + has_next_uli = prefix_uli(data + beg + i, end - beg - i); + has_next_oli = prefix_oli(data + beg + i, end - beg - i); + } + + /* checking for ul/ol switch */ + if (in_empty && ( + ((*flags & MKD_LIST_ORDERED) && has_next_uli) || + (!(*flags & MKD_LIST_ORDERED) && has_next_oli))){ + *flags |= MKD_LI_END; + break; /* the following item must have same list type */ + } + + /* checking for a new item */ + if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) { + if (in_empty) + has_inside_empty = 1; + + if (pre == orgpre) /* the following item must have */ + break; /* the same indentation */ + + if (!sublist) + sublist = work->size; + } + /* joining only indented stuff after empty lines; + * note that now we only require 1 space of indentation + * to continue a list */ + else if (in_empty && pre == 0) { + *flags |= MKD_LI_END; + break; + } + else if (in_empty) { + bufputc(work, '\n'); + has_inside_empty = 1; + } + + in_empty = 0; + + /* adding the line without prefix into the working buffer */ + bufput(work, data + beg + i, end - beg - i); + beg = end; + } + + /* render of li contents */ + if (has_inside_empty) + *flags |= MKD_LI_BLOCK; + + if (*flags & MKD_LI_BLOCK) { + /* intermediate render of block li */ + if (sublist && sublist < work->size) { + parse_block(inter, rndr, work->data, sublist); + parse_block(inter, rndr, work->data + sublist, work->size - sublist); + } + else + parse_block(inter, rndr, work->data, work->size); + } else { + /* intermediate render of inline li */ + if (sublist && sublist < work->size) { + parse_inline(inter, rndr, work->data, sublist); + parse_block(inter, rndr, work->data + sublist, work->size - sublist); + } + else + parse_inline(inter, rndr, work->data, work->size); + } + + /* render of li itself */ + if (rndr->cb.listitem) + rndr->cb.listitem(ob, inter, *flags, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_SPAN); + rndr_popbuf(rndr, BUFFER_SPAN); + return beg; +} + + +/* parse_list • parsing ordered or unordered list block */ +static size_t +parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags) +{ + struct buf *work = 0; + size_t i = 0, j; + + work = rndr_newbuf(rndr, BUFFER_BLOCK); + + while (i < size) { + j = parse_listitem(work, rndr, data + i, size - i, &flags); + i += j; + + if (!j || (flags & MKD_LI_END)) + break; + } + + if (rndr->cb.list) + rndr->cb.list(ob, work, flags, rndr->opaque); + rndr_popbuf(rndr, BUFFER_BLOCK); + return i; +} + +/* parse_atxheader • parsing of atx-style headers */ +static size_t +parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t level = 0; + size_t i, end, skip; + struct buf *id = NULL;//for header id attribute + + while (level < size && level < 6 && data[level] == '#') + level++; + + for (i = level; i < size && data[i] == ' '; i++); + + for (end = i; end < size && data[end] != '\n'; end++); + skip = end; + + //header id attribute + if( (rndr->ext_flags & MKDEXT_HEADER_ID_ATTRIBUTE) + && ((i+3) i) { + struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN); + + parse_inline(work, rndr, data + i, end - i); + + if (rndr->cb.header) + rndr->cb.header(ob, work, id, (int)level, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_SPAN); + } + if(id) + rndr_popbuf(rndr, BUFFER_SPAN); + return skip; +} + + +/* htmlblock_end • checking end of HTML block : [ \t]*\n[ \t*]\n */ +/* returns the length on match, 0 otherwise */ +static size_t +htmlblock_end_tag( + const char *tag, + size_t tag_len, + struct sd_markdown *rndr, + uint8_t *data, + size_t size) +{ + size_t i, w; + + /* checking if tag is a match */ + if (tag_len + 3 >= size || + strncasecmp((char *)data + 2, tag, tag_len) != 0 || + data[tag_len + 2] != '>') + return 0; + + /* checking white lines */ + i = tag_len + 3; + w = 0; + if (i < size && (w = is_empty(data + i, size - i)) == 0) + return 0; /* non-blank after tag */ + i += w; + w = 0; + + if (i < size) + w = is_empty(data + i, size - i); + + return i + w; +} + +static size_t +htmlblock_end(const char *curtag, + struct sd_markdown *rndr, + uint8_t *data, + size_t size, + int start_of_line) +{ + size_t tag_size = strlen(curtag); + size_t i = 1, end_tag; + int block_lines = 0; + + while (i < size) { + i++; + while (i < size && !(data[i - 1] == '<' && data[i] == '/')) { + if (data[i] == '\n') + block_lines++; + + i++; + } + + /* If we are only looking for unindented tags, skip the tag + * if it doesn't follow a newline. + * + * The only exception to this is if the tag is still on the + * initial line; in that case it still counts as a closing + * tag + */ + if (start_of_line && block_lines > 0 && data[i - 2] != '\n') + continue; + + if (i + 2 + tag_size >= size) + break; + + end_tag = htmlblock_end_tag(curtag, tag_size, rndr, data + i - 1, size - i + 1); + if (end_tag) + return i + end_tag - 1; + } + + return 0; +} + + +/* parse_htmlblock • parsing of inline HTML block */ +static size_t +parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render) +{ + size_t i, j = 0, tag_end; + const char *curtag = NULL; + struct buf work = { data, 0, 0, 0 }; + + /* identification of the opening tag */ + if (size < 2 || data[0] != '<') + return 0; + + i = 1; + while (i < size && data[i] != '>' && data[i] != ' ') + i++; + + if (i < size) + curtag = find_block_tag((char *)data + 1, (int)i - 1); + + /* handling of special cases */ + if (!curtag) { + + /* HTML comment, laxist form */ + if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') { + i = 5; + + while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>')) + i++; + + i++; + + if (i < size) + j = is_empty(data + i, size - i); + + if (j) { + work.size = i + j; + if (do_render && rndr->cb.blockhtml) + rndr->cb.blockhtml(ob, &work, rndr->opaque); + return work.size; + } + } + + /* HR, which is the only self-closing block tag considered */ + if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) { + i = 3; + while (i < size && data[i] != '>') + i++; + + if (i + 1 < size) { + i++; + j = is_empty(data + i, size - i); + if (j) { + work.size = i + j; + if (do_render && rndr->cb.blockhtml) + rndr->cb.blockhtml(ob, &work, rndr->opaque); + return work.size; + } + } + } + + /* no special case recognised */ + return 0; + } + + /* looking for an unindented matching closing tag */ + /* followed by a blank line */ + tag_end = htmlblock_end(curtag, rndr, data, size, 1); + + /* if not found, trying a second pass looking for indented match */ + /* but not if tag is "ins" or "del" (following original Markdown.pl) */ + if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) { + tag_end = htmlblock_end(curtag, rndr, data, size, 0); + } + + if (!tag_end) + return 0; + + /* the end of the block has been found */ + work.size = tag_end; + if (do_render && rndr->cb.blockhtml) + rndr->cb.blockhtml(ob, &work, rndr->opaque); + + return tag_end; +} + +static void +parse_table_row( + struct buf *ob, + struct sd_markdown *rndr, + uint8_t *data, + size_t size, + size_t columns, + int *col_data, + int header_flag) +{ + size_t i = 0, col; + struct buf *row_work = 0; + + if (!rndr->cb.table_cell || !rndr->cb.table_row) + return; + + row_work = rndr_newbuf(rndr, BUFFER_SPAN); + + if (i < size && data[i] == '|') + i++; + + for (col = 0; col < columns && i < size; ++col) { + size_t cell_start, cell_end; + struct buf *cell_work; + + cell_work = rndr_newbuf(rndr, BUFFER_SPAN); + + while (i < size && _isspace(data[i])) + i++; + + cell_start = i; + + while (i < size && data[i] != '|') + i++; + + cell_end = i - 1; + + while (cell_end > cell_start && _isspace(data[cell_end])) + cell_end--; + + parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start); + rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_SPAN); + i++; + } + + for (; col < columns; ++col) { + struct buf empty_cell = { 0, 0, 0, 0 }; + rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque); + } + + rndr->cb.table_row(ob, row_work, rndr->opaque); + + rndr_popbuf(rndr, BUFFER_SPAN); +} + +static size_t +parse_table_header( + struct buf *ob, + struct sd_markdown *rndr, + uint8_t *data, + size_t size, + size_t *columns, + int **column_data) +{ + int pipes; + size_t i = 0, col, header_end, under_end; + + pipes = 0; + while (i < size && data[i] != '\n') + if (data[i++] == '|') + pipes++; + + if (i == size || pipes == 0) + return 0; + + header_end = i; + + while (header_end > 0 && _isspace(data[header_end - 1])) + header_end--; + + if (data[0] == '|') + pipes--; + + if (header_end && data[header_end - 1] == '|') + pipes--; + + *columns = pipes + 1; + *column_data = calloc(*columns, sizeof(int)); + + /* Parse the header underline */ + i++; + if (i < size && data[i] == '|') + i++; + + under_end = i; + while (under_end < size && data[under_end] != '\n') + under_end++; + + for (col = 0; col < *columns && i < under_end; ++col) { + size_t dashes = 0; + + while (i < under_end && data[i] == ' ') + i++; + + if (data[i] == ':') { + i++; (*column_data)[col] |= MKD_TABLE_ALIGN_L; + dashes++; + } + + while (i < under_end && data[i] == '-') { + i++; dashes++; + } + + if (i < under_end && data[i] == ':') { + i++; (*column_data)[col] |= MKD_TABLE_ALIGN_R; + dashes++; + } + + while (i < under_end && data[i] == ' ') + i++; + + if (i < under_end && data[i] != '|') + break; + + if (dashes < 3) + break; + + i++; + } + + if (col < *columns) + return 0; + + parse_table_row( + ob, rndr, data, + header_end, + *columns, + *column_data, + MKD_TABLE_HEADER + ); + + return under_end + 1; +} + +static size_t +parse_table( + struct buf *ob, + struct sd_markdown *rndr, + uint8_t *data, + size_t size) +{ + size_t i; + + struct buf *header_work = 0; + struct buf *body_work = 0; + + size_t columns; + int *col_data = NULL; + + header_work = rndr_newbuf(rndr, BUFFER_SPAN); + body_work = rndr_newbuf(rndr, BUFFER_BLOCK); + + i = parse_table_header(header_work, rndr, data, size, &columns, &col_data); + if (i > 0) { + + while (i < size) { + size_t row_start; + int pipes = 0; + + row_start = i; + + while (i < size && data[i] != '\n') + if (data[i++] == '|') + pipes++; + + if (pipes == 0 || i == size) { + i = row_start; + break; + } + + parse_table_row( + body_work, + rndr, + data + row_start, + i - row_start, + columns, + col_data, 0 + ); + + i++; + } + + if (rndr->cb.table) + rndr->cb.table(ob, header_work, body_work, rndr->opaque); + } + + free(col_data); + rndr_popbuf(rndr, BUFFER_SPAN); + rndr_popbuf(rndr, BUFFER_BLOCK); + return i; +} + +/* parse_block • parsing of one block, returning next uint8_t to parse */ +static void +parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size) +{ + size_t beg, end, i; + uint8_t *txt_data; + beg = 0; + + if (rndr->work_bufs[BUFFER_SPAN].size + + rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting) + return; + + while (beg < size) { + txt_data = data + beg; + end = size - beg; + + if (is_atxheader(rndr, txt_data, end)) + beg += parse_atxheader(ob, rndr, txt_data, end); + + else if (data[beg] == '<' && rndr->cb.blockhtml && + (i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0) + beg += i; + + else if ((i = is_empty(txt_data, end)) != 0) + beg += i; + + else if (is_hrule(txt_data, end)) { + if (rndr->cb.hrule) + rndr->cb.hrule(ob, rndr->opaque); + + while (beg < size && data[beg] != '\n') + beg++; + + beg++; + } + + else if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 && + (i = parse_fencedcode(ob, rndr, txt_data, end)) != 0) + beg += i; + + else if ((rndr->ext_flags & MKDEXT_TABLES) != 0 && + (i = parse_table(ob, rndr, txt_data, end)) != 0) + beg += i; + + else if (prefix_quote(txt_data, end)) + beg += parse_blockquote(ob, rndr, txt_data, end); + + else if (prefix_code(txt_data, end)) + beg += parse_blockcode(ob, rndr, txt_data, end); + + else if (prefix_uli(txt_data, end)) + beg += parse_list(ob, rndr, txt_data, end, 0); + + else if (prefix_oli(txt_data, end)) + beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED); + + else + beg += parse_paragraph(ob, rndr, txt_data, end); + } +} + +/******************** + * FOOTNOTE PARSING * + ********************/ +static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size); + +/* is_footnote -> returns whether a line is a footnote or not, similar with is_ref */ +static int +is_footnote(const uint8_t *data, size_t beg, size_t end, size_t *last, + struct sd_markdown *md){ + size_t i = 0; + size_t idStart, idEnd; + size_t contentStart, contentEnd; + size_t line_end = 0; + + if (beg + 4 >= end) return 0; + if (data[beg] == ' '){ + i = 1; + if (data[beg + 1] == ' '){ + i = 2; + if (data[beg+2] == ' '){ + i = 3; + if (data[beg+3] == ' ') + return 0; + } + } + } + i += beg;/* offset of the whole document */ + + /* id part: anything prefix with `^` but a newline between brackets */ + if (data[i] != '[') return 0; + i++; + idStart = i; + if (idStart>=end || data[idStart]!='^') return 0; // footnote flag + while( i= end || data[i] != ']') return 0; + idEnd = i; + if( (idEnd-idStart)<2 )//at last two char, one is '^', the other is name + return 0; + + /* spacer: colon (space) * newline? (space){4} */ + i++; + if (i >= end || data[i] != ':') return 0; + i++; + while (i=end) return 0; + if(data[i]=='\t'){ + i++; + } else { + if(i+4>=end) return 0; + if(data[i]!=' '||data[i+1]!=' '||data[i+2]!=' '||data[i+3]!=' ') return 0; + i += 4; + } + contentStart = i; + } else { // footenote following `:`, i>=end or i is the start of content + if (i>=end) return 0; + contentStart = i; + } + i++; + while(i=end||(i+1)>=end){ + line_end = i; + contentEnd = i; + break; + }//else (i+1)=end||(i+4)>=end){ + line_end = i; + contentEnd = i; + break; + }//else (i+4)=end && !line_end){ + line_end = end; + contentEnd = end; + } + if(contentEnd-contentStart<1) + return 0; + if(last) + *last = line_end; + if(md){ + size_t size = contentEnd; + size_t begin = contentStart, cend; + struct buf *mdContent = bufnew(idEnd-idStart); + struct footnote *fn = add_footnote(&md->fnInfo, data + idStart, idEnd - idStart); + if (!fn) + return 0; + + while(beginbegin) + expand_tabs(mdContent, data+begin, cend-begin); + while(cendcontent = mdContent; + } + return 1; +} + + +/********************* + * REFERENCE PARSING * + *********************/ + +/* is_ref • returns whether a line is a reference or not */ +static int +is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs) +{ +/* int n; */ + size_t i = 0; + size_t id_offset, id_end; + size_t link_offset, link_end; + size_t title_offset, title_end; + size_t line_end; + + /* up to 3 optional leading spaces */ + if (beg + 3 >= end) return 0; + if (data[beg] == ' ') { i = 1; + if (data[beg + 1] == ' ') { i = 2; + if (data[beg + 2] == ' ') { i = 3; + if (data[beg + 3] == ' ') return 0; } } } + i += beg; + + /* id part: anything but a newline between brackets */ + if (data[i] != '[') return 0; + i++; + id_offset = i; + while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']') + i++; + if (i >= end || data[i] != ']') return 0; + id_end = i; + + /* spacer: colon (space | tab)* newline? (space | tab)* */ + i++; + if (i >= end || data[i] != ':') return 0; + i++; + while (i < end && (data[i] == ' ' || data[i] == '\t') ) i++; + if (i < end && (data[i] == '\n' || data[i] == '\r')) { + i++; + if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; } + while (i < end && (data[i] == ' ' || data[i] == '\t') ) i++; + if (i >= end) return 0; + + /* link: whitespace-free sequence, optionally between angle brackets */ + if (data[i] == '<') + i++; + + link_offset = i; + + while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r') + i++; + + if (data[i - 1] == '>') link_end = i - 1; + else link_end = i; + + /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */ + while (i < end && data[i] == ' ') i++; + if (i < end && data[i] != '\n' && data[i] != '\r' + && data[i] != '\'' && data[i] != '"' && data[i] != '(') + return 0; + line_end = 0; + /* computing end-of-line */ + if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i; + if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') + line_end = i + 1; + + /* optional (space|tab)* spacer after a newline */ + if (line_end) { + i = line_end + 1; + while (i < end && data[i] == ' ') i++; } + + /* optional title: any non-newline sequence enclosed in '"() + alone on its line */ + title_offset = title_end = 0; + if (i + 1 < end + && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) { + i++; + title_offset = i; + /* looking for EOL */ + while (i < end && data[i] != '\n' && data[i] != '\r') i++; + if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r') + title_end = i + 1; + else title_end = i; + /* stepping back */ + i -= 1; + while (i > title_offset && data[i] == ' ') + i -= 1; + if (i > title_offset + && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) { + line_end = title_end; + title_end = i; + } + } + + if (!line_end || link_end == link_offset) + return 0; /* garbage after the link empty link */ + + /* a valid ref has been found, filling-in return structures */ + if (last) + *last = line_end; + + if (refs) { + struct link_ref *ref; + + ref = add_link_ref(refs, data + id_offset, id_end - id_offset); + if (!ref) + return 0; + + ref->link = bufnew(link_end - link_offset); + bufput(ref->link, data + link_offset, link_end - link_offset); + + if (title_end > title_offset) { + ref->title = bufnew(title_end - title_offset); + bufput(ref->title, data + title_offset, title_end - title_offset); + } + } + + return 1; +} + +static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size) +{ + size_t i = 0, tab = 0; + + while (i < size) { + size_t org = i; + + while (i < size && line[i] != '\t') { + i++; tab++; + } + + if (i > org) + bufput(ob, line + org, i - org); + + if (i >= size) + break; + + do { + bufputc(ob, ' '); tab++; + } while (tab % 4); + + i++; + } +} + +/********************** + * EXPORTED FUNCTIONS * + **********************/ + +struct sd_markdown * +sd_markdown_new( + unsigned int extensions, + size_t max_nesting, + const struct sd_callbacks *callbacks, + void *opaque) +{ + struct sd_markdown *md = NULL; + + assert(max_nesting > 0 && callbacks); + + md = malloc(sizeof(struct sd_markdown)); + if (!md) + return NULL; + + memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks)); + + stack_init(&md->work_bufs[BUFFER_BLOCK], 4); + stack_init(&md->work_bufs[BUFFER_SPAN], 8); + + memset(md->active_char, 0x0, 256); + + if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) { + md->active_char['*'] = MD_CHAR_EMPHASIS; + md->active_char['_'] = MD_CHAR_EMPHASIS; + if (extensions & MKDEXT_STRIKETHROUGH) + md->active_char['~'] = MD_CHAR_EMPHASIS; + } + + if (md->cb.codespan) + md->active_char['`'] = MD_CHAR_CODESPAN; + + if (md->cb.linebreak) + md->active_char['\n'] = MD_CHAR_LINEBREAK; + + if (md->cb.image || md->cb.link) + md->active_char['['] = MD_CHAR_LINK; + + md->active_char['<'] = MD_CHAR_LANGLE; + md->active_char['\\'] = MD_CHAR_ESCAPE; + md->active_char['&'] = MD_CHAR_ENTITITY; + + if (extensions & MKDEXT_AUTOLINK) { + md->active_char[':'] = MD_CHAR_AUTOLINK_URL; + md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL; + md->active_char['w'] = MD_CHAR_AUTOLINK_WWW; + } + + if (extensions & MKDEXT_SUPERSCRIPT) + md->active_char['^'] = MD_CHAR_SUPERSCRIPT; + + /* Extension data */ + md->ext_flags = extensions; + md->opaque = opaque; + md->max_nesting = max_nesting; + md->in_link_body = 0; + + return md; +} + +void +sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md) +{ +#define MARKDOWN_GROW(x) ((x) + ((x) >> 1)) + static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF}; + + struct buf *text; + size_t beg, end; + size_t fence_code_area=0; + + text = bufnew(64); + if (!text) + return; + + /* Preallocate enough space for our buffer to avoid expanding while copying */ + bufgrow(text, doc_size); + + /* reset the references table */ + memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *)); + /* reset the footnote list */ + md->fnInfo.firstOne = NULL; + md->fnInfo.lastOne = NULL; + md->fnInfo.count = 0; + + /* first pass: looking for references, copying everything else */ + beg = 0; + + /* Skip a possible UTF-8 BOM, even though the Unicode standard + * discourages having these in UTF-8 documents */ + if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0) + beg += 3; + + while (beg < doc_size) /* iterating over lines */ + if ((md->ext_flags & MKDEXT_FOOTNOTE) &&(!fence_code_area)&& is_footnote(document, beg, doc_size, &end, md)) + beg = end; + else if ((!fence_code_area)&&is_ref(document, beg, doc_size, &end, md->refs)) + beg = end; + else { /* skipping to the next line */ + if((md->ext_flags & MKDEXT_FOOTNOTE) && (is_codefence(document+beg, doc_size-beg, NULL)!=0)){ + fence_code_area ^= 1; + } + end = beg; + while (end < doc_size && document[end] != '\n' && document[end] != '\r') + end++; + + /* adding the line body if present */ + if (end > beg) + expand_tabs(text, document + beg, end - beg); + + while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) { + /* add one \n per newline */ + if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n')) + bufputc(text, '\n'); + end++; + } + + beg = end; + } + + /* pre-grow the output buffer to minimize allocations */ + bufgrow(ob, MARKDOWN_GROW(text->size)); + + /* second pass: actual rendering */ + if (md->cb.doc_header) + md->cb.doc_header(ob, md->opaque); + + if (text->size) { + /* adding a final newline if not already present */ + if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r') + bufputc(text, '\n'); + + parse_block(ob, md, text->data, text->size); + } + //footnote bottom content + if (md->cb.footnote_bottom_content_list_item && md->cb.list && (md->ext_flags&MKDEXT_FOOTNOTE) && md->fnInfo.firstOne){ + struct buf *footnoteListItems = rndr_newbuf(md, BUFFER_BLOCK); + unsigned int i=0; + for(i=1; i<=md->fnInfo.count; i++){ + struct footnote *prev = NULL; + struct footnote *f = md->fnInfo.firstOne; + while(f){ + if(f->id == i){ + if(f->content->size){ + struct buf *mdHtml = rndr_newbuf(md, BUFFER_BLOCK); + if(mdHtml){ + bufprintf(f->content, "\n", f->id); + parse_block(mdHtml, md, f->content->data, f->content->size); + md->cb.footnote_bottom_content_list_item(footnoteListItems, f->id, mdHtml); + rndr_popbuf(md, BUFFER_BLOCK); + } + } + if(f==md->fnInfo.firstOne){ + md->fnInfo.firstOne = f->next; + } else { + assert(prev); + prev->next = f->next; + } + free_single_footnote(f); + break; + } + prev = f; + f = f->next; + } + } + bufputs(ob, "

    "); + md->cb.list(ob, footnoteListItems, MKD_LIST_ORDERED, NULL); + rndr_popbuf(md, BUFFER_BLOCK); + bufputs(ob, "
    "); + } + + if (md->cb.doc_footer) + md->cb.doc_footer(ob, md->opaque); + + /* clean-up */ + bufrelease(text); + free_link_refs(md->refs); + free_footnotes(&md->fnInfo); + + assert(md->work_bufs[BUFFER_SPAN].size == 0); + assert(md->work_bufs[BUFFER_BLOCK].size == 0); +} + +void +sd_markdown_free(struct sd_markdown *md) +{ + size_t i; + + for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i) + bufrelease(md->work_bufs[BUFFER_SPAN].item[i]); + + for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i) + bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]); + + stack_free(&md->work_bufs[BUFFER_SPAN]); + stack_free(&md->work_bufs[BUFFER_BLOCK]); + + free(md); +} + +void +sd_version(int *ver_major, int *ver_minor, int *ver_revision) +{ + *ver_major = SUNDOWN_VER_MAJOR; + *ver_minor = SUNDOWN_VER_MINOR; + *ver_revision = SUNDOWN_VER_REVISION; +} + +/* vim: set filetype=c: */ diff --git a/src/lib/markdown/src/markdown.h b/src/lib/markdown/src/markdown.h new file mode 100644 index 0000000..44d09d9 --- /dev/null +++ b/src/lib/markdown/src/markdown.h @@ -0,0 +1,148 @@ +/* markdown.h - generic markdown parser */ + +/* + * Copyright (c) 2009, Natacha Porté + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UPSKIRT_MARKDOWN_H +#define UPSKIRT_MARKDOWN_H + +#include "buffer.h" +#include "autolink.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUNDOWN_VERSION "1.16.0" +#define SUNDOWN_VER_MAJOR 1 +#define SUNDOWN_VER_MINOR 16 +#define SUNDOWN_VER_REVISION 0 + +/******************** + * TYPE DEFINITIONS * + ********************/ + +/* mkd_autolink - type of autolink */ +enum mkd_autolink { + MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/ + MKDA_NORMAL, /* normal http/http/ftp/mailto/etc link */ + MKDA_EMAIL, /* e-mail link without explit mailto: */ +}; + +enum mkd_tableflags { + MKD_TABLE_ALIGN_L = 1, + MKD_TABLE_ALIGN_R = 2, + MKD_TABLE_ALIGN_CENTER = 3, + MKD_TABLE_ALIGNMASK = 3, + MKD_TABLE_HEADER = 4 +}; + +enum mkd_extensions { + MKDEXT_NO_INTRA_EMPHASIS = (1 << 0), + MKDEXT_TABLES = (1 << 1), + MKDEXT_FENCED_CODE = (1 << 2), + MKDEXT_AUTOLINK = (1 << 3), + MKDEXT_STRIKETHROUGH = (1 << 4), + MKDEXT_SPACE_HEADERS = (1 << 6), + MKDEXT_SUPERSCRIPT = (1 << 7), + MKDEXT_LAX_SPACING = (1 << 8), // less limatation inline html + + MKDEXT_MKD_IN_HTML = (1 <<9), + MKDEXT_HEADER_ID_ATTRIBUTE = (1 << 10), //ok + MKDEXT_DEFINITION_LISTS = (1 << 11), + MKDEXT_ABBREVIATIONS = (1 << 12), + MKDEXT_FOOTNOTE = (1 << 13)//ok +}; + +/* sd_callbacks - functions for rendering parsed data */ +struct sd_callbacks { + /* block level callbacks - NULL skips the block */ + void (*blockcode)(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque); + void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque); + void (*blockhtml)(struct buf *ob,const struct buf *text, void *opaque); + void (*header)(struct buf *ob, const struct buf *text, const struct buf *id, int level, void *opaque); + void (*hrule)(struct buf *ob, void *opaque); + void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque); + void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque); + void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque); + void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque); + void (*table_row)(struct buf *ob, const struct buf *text, void *opaque); + void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque); + + + /* span level callbacks - NULL or return 0 prints the span verbatim */ + int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque); + int (*codespan)(struct buf *ob, const struct buf *text, void *opaque); + int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque); + int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque); + int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque); + int (*linebreak)(struct buf *ob, void *opaque); + int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque); + int (*footnote_link)(struct buf *ob, unsigned int id); + int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque); + int (*triple_emphasis)(struct buf *ob, const struct buf *text, void *opaque); + int (*strikethrough)(struct buf *ob, const struct buf *text, void *opaque); + int (*superscript)(struct buf *ob, const struct buf *text, void *opaque); + + /* low level callbacks - NULL copies input directly into the output */ + void (*entity)(struct buf *ob, const struct buf *entity, void *opaque); + void (*normal_text)(struct buf *ob, const struct buf *text, void *opaque); + + /* footnote bottom content list */ + void (*footnote_bottom_content_list_item)(struct buf *ob, unsigned int id, const struct buf *content); + + /* header and footer */ + void (*doc_header)(struct buf *ob, void *opaque); + void (*doc_footer)(struct buf *ob, void *opaque); +}; + +struct sd_markdown; + +/********* + * FLAGS * + *********/ + +/* list/listitem flags */ +#define MKD_LIST_ORDERED 1 +#define MKD_LI_BLOCK 2 /*
  9. containing block data */ + +/********************** + * EXPORTED FUNCTIONS * + **********************/ + +extern struct sd_markdown * +sd_markdown_new( + unsigned int extensions, + size_t max_nesting, + const struct sd_callbacks *callbacks, + void *opaque); + +extern void +sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md); + +extern void +sd_markdown_free(struct sd_markdown *md); + +extern void +sd_version(int *major, int *minor, int *revision); + +#ifdef __cplusplus +} +#endif + +#endif + +/* vim: set filetype=c: */ diff --git a/src/lib/markdown/src/stack.c b/src/lib/markdown/src/stack.c new file mode 100644 index 0000000..ce069ff --- /dev/null +++ b/src/lib/markdown/src/stack.c @@ -0,0 +1,81 @@ +#include "stack.h" +#include + +int +stack_grow(struct stack *st, size_t new_size) +{ + void **new_st; + + if (st->asize >= new_size) + return 0; + + new_st = realloc(st->item, new_size * sizeof(void *)); + if (new_st == NULL) + return -1; + + memset(new_st + st->asize, 0x0, + (new_size - st->asize) * sizeof(void *)); + + st->item = new_st; + st->asize = new_size; + + if (st->size > new_size) + st->size = new_size; + + return 0; +} + +void +stack_free(struct stack *st) +{ + if (!st) + return; + + free(st->item); + + st->item = NULL; + st->size = 0; + st->asize = 0; +} + +int +stack_init(struct stack *st, size_t initial_size) +{ + st->item = NULL; + st->size = 0; + st->asize = 0; + + if (!initial_size) + initial_size = 8; + + return stack_grow(st, initial_size); +} + +void * +stack_pop(struct stack *st) +{ + if (!st->size) + return NULL; + + return st->item[--st->size]; +} + +int +stack_push(struct stack *st, void *item) +{ + if (stack_grow(st, st->size * 2) < 0) + return -1; + + st->item[st->size++] = item; + return 0; +} + +void * +stack_top(struct stack *st) +{ + if (!st->size) + return NULL; + + return st->item[st->size - 1]; +} + diff --git a/src/lib/markdown/src/stack.h b/src/lib/markdown/src/stack.h new file mode 100644 index 0000000..08ff030 --- /dev/null +++ b/src/lib/markdown/src/stack.h @@ -0,0 +1,29 @@ +#ifndef STACK_H__ +#define STACK_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct stack { + void **item; + size_t size; + size_t asize; +}; + +void stack_free(struct stack *); +int stack_grow(struct stack *, size_t); +int stack_init(struct stack *, size_t); + +int stack_push(struct stack *, void *); + +void *stack_pop(struct stack *); +void *stack_top(struct stack *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lib/multimarkdown/.gitignore b/src/lib/multimarkdown/.gitignore new file mode 100644 index 0000000..f3a5b7a --- /dev/null +++ b/src/lib/multimarkdown/.gitignore @@ -0,0 +1,40 @@ +.DS_Store +project.xcworkspace +xcuserdata + +build/* +speed/* + +test.* +testing/* + +*.o +markdown_parser.c +/markdown +/multimarkdown +/multimarkdown.exe + +/mac_installer/Resources/*.html +/mac_installer/Package_Root/usr/local/bin/multimarkdown + +/windows_installer/README.txt +/windows_installer/*.zip +/windows_installer/*.exe +/windows_installer/*.xml.backup +/windows_installer/License.html + +/old_zips/ + +/drag/ + +/manual/ + +*.pbxuser +*.perspectivev3 + +*Makefile* +*.user +*.1 +*.0 +*.so +multimarkdown diff --git a/src/lib/multimarkdown/.gitmodules b/src/lib/multimarkdown/.gitmodules new file mode 100644 index 0000000..e516b7a --- /dev/null +++ b/src/lib/multimarkdown/.gitmodules @@ -0,0 +1,6 @@ +[submodule "MarkdownTest"] + path = MarkdownTest + url = https://github.com/fletcher/MMD-Test-Suite.git +[submodule "samples"] + path = samples + url = https://github.com/fletcher/MultiMarkdown-Gallery.git diff --git a/src/lib/multimarkdown/.travis.yml b/src/lib/multimarkdown/.travis.yml new file mode 100644 index 0000000..e0ec549 --- /dev/null +++ b/src/lib/multimarkdown/.travis.yml @@ -0,0 +1,15 @@ +language: cpp +compiler: + - gcc + - clang +install: + - sudo apt-get install libqt4-dev qt4-qmake +script: + - qmake peg-multimarkdown.pro + - make +branches: + only: + - master + - dev +notifications: + email: false diff --git a/src/lib/multimarkdown/LICENSE b/src/lib/multimarkdown/LICENSE new file mode 100644 index 0000000..b2f8be5 --- /dev/null +++ b/src/lib/multimarkdown/LICENSE @@ -0,0 +1,75 @@ +Title: License Agreement + +peg-multimarkdown +portions Copyright (c) 2010-2013 Fletcher T. Penney + +based on: +markdown in c, implemented using PEG grammar +Copyright (c) 2008-2011 John MacFarlane +ODF output code (c) 2011-2013 Fletcher T. Penney + +peg-markdown is released under both the GPL and MIT licenses. +You may pick the license that best fits your needs. + +Additional MultiMarkdown files +Copyright (c) 2005-2013 Fletcher T. Penney +Copyright (c) 2011 Daniel Jalkut, licensed explicitly MIT. + +Modifications to remove reliance on Glib2 +Copyright (c) 2011 Daniel Jalkut, licensed explicitly MIT. + + +The GPL + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +The MIT License + +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. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + +peg-0.1.4 (included for convenience - http://piumarta.com/software/peg/) + +Copyright (c) 2007 by Ian Piumarta +All rights reserved. + +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, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, provided that the above copyright notice(s) and this +permission notice appear in all copies of the Software. Acknowledgement +of the use of this Software in supporting documentation would be +appreciated but is not required. + +THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. diff --git a/src/lib/multimarkdown/README.markdown b/src/lib/multimarkdown/README.markdown new file mode 100755 index 0000000..29f55e8 --- /dev/null +++ b/src/lib/multimarkdown/README.markdown @@ -0,0 +1,11 @@ +peg-multimarkdown for MdCharm +============================= + +Branch Status +------------- +https://travis-ci.org/ + +Branch | Status +-------|------- +master| ![](https://travis-ci.org/MdCharm/peg-multimarkdown.png?branch=master "") +dev|![](https://travis-ci.org/MdCharm/peg-multimarkdown.png?branch=dev "") diff --git a/src/lib/multimarkdown/TODO.txt b/src/lib/multimarkdown/TODO.txt new file mode 100644 index 0000000..068aa41 --- /dev/null +++ b/src/lib/multimarkdown/TODO.txt @@ -0,0 +1,16 @@ +Notes for myself while pulling together the remaining lose ends in MultiMarkdown standalone for Mac. + +CURRENT: + +? + +HISTORY: + +- Fix short options parsing in markdown.c + +- Performance tests - compare with a very large markdown file vs. e.g. multimarkdown based on GLib. + - Performance now seems approximately on par with the GLib version for massive files +- Garbage output e.g. when processing unicode within tags:

    + - This went away with removal of NSString from the GString printf-append function. + + \ No newline at end of file diff --git a/src/lib/multimarkdown/clean_dfx.sh b/src/lib/multimarkdown/clean_dfx.sh new file mode 100755 index 0000000..6ac8c34 --- /dev/null +++ b/src/lib/multimarkdown/clean_dfx.sh @@ -0,0 +1,2 @@ +git clean -dfx +git submodule foreach --recursive git clean -dfx diff --git a/src/lib/multimarkdown/peg-multimarkdown.pro b/src/lib/multimarkdown/peg-multimarkdown.pro new file mode 100644 index 0000000..42ed8f3 --- /dev/null +++ b/src/lib/multimarkdown/peg-multimarkdown.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +CONFIG += ordered + +SUBDIRS += peg/leg.pro +SUBDIRS += src/multimarkdown_lib.pro +SUBDIRS += src/multimarkdown.pro diff --git a/src/lib/multimarkdown/peg/.gitignore b/src/lib/multimarkdown/peg/.gitignore new file mode 100644 index 0000000..64e4452 --- /dev/null +++ b/src/lib/multimarkdown/peg/.gitignore @@ -0,0 +1,17 @@ +ipch/ +*.opensdf +*.suo +*.sdf +Debug/ +Release/ +*.xcworkspace/ +xcuserdata/ +*.user +*.exe +leg +peg +*Makefile* +Makefile.Debug +Makefile.Release +*.manifest +*.o diff --git a/src/lib/multimarkdown/peg/README.md b/src/lib/multimarkdown/peg/README.md new file mode 100644 index 0000000..b684a1d --- /dev/null +++ b/src/lib/multimarkdown/peg/README.md @@ -0,0 +1,17 @@ +peg/leg +======= + +This a port of Ian Piumarta's peg/leg implementation that can be compiled on +both Windows and Mac OS X. + +Requirements +------------ + +On Windows you would need Visual Studio 2010, on Mac OS X you would need Xcode +4.3.2. You can try compiling with different versions of compiler – it might +work. + +Building +-------- + +Simply run `build.cmd` on Windows or `build.sh` on Mac OS X. diff --git a/src/lib/multimarkdown/peg/compile.c b/src/lib/multimarkdown/peg/compile.c new file mode 100644 index 0000000..109eccb --- /dev/null +++ b/src/lib/multimarkdown/peg/compile.c @@ -0,0 +1,724 @@ +/* Copyright (c) 2007, 2012 by Ian Piumarta + * All rights reserved. + * + * 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, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, provided that the above copyright notice(s) and this + * permission notice appear in all copies of the Software. Acknowledgement + * of the use of this Software in supporting documentation would be + * appreciated but is not required. + * + * THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. + * + * Last edited: 2012-04-29 16:09:36 by piumarta on emilia + */ + +#include +#include +#include +#include + +#ifdef WIN32 +#undef inline +#define inline __inline +#endif + +#include "version.h" +#include "tree.h" + +static int yyl(void) +{ + static int prev= 0; + return ++prev; +} + +static void charClassSet (unsigned char bits[], int c) { bits[c >> 3] |= (1 << (c & 7)); } +static void charClassClear(unsigned char bits[], int c) { bits[c >> 3] &= ~(1 << (c & 7)); } + +typedef void (*setter)(unsigned char bits[], int c); + +static inline int oigit(int c) { return '0' <= c && c <= '7'; } + +static int cnext(unsigned char **ccp) +{ + unsigned char *cclass= *ccp; + int c= *cclass++; + if (c) + { + if ('\\' == c && *cclass) + { + switch (c= *cclass++) + { + case 'a': c= '\a'; break; /* bel */ + case 'b': c= '\b'; break; /* bs */ + case 'e': c= '\033'; break; /* esc */ + case 'f': c= '\f'; break; /* ff */ + case 'n': c= '\n'; break; /* nl */ + case 'r': c= '\r'; break; /* cr */ + case 't': c= '\t'; break; /* ht */ + case 'v': c= '\v'; break; /* vt */ + default: + if (oigit(c)) + { + c -= '0'; + if (oigit(*cclass)) c= (c << 3) + *cclass++ - '0'; + if (oigit(*cclass)) c= (c << 3) + *cclass++ - '0'; + } + break; + } + } + *ccp= cclass; + } + return c; +} + +static char *makeCharClass(unsigned char *cclass) +{ + unsigned char bits[32]; + setter set; + int c, prev= -1; + static char string[256]; + char *ptr; + + if ('^' == *cclass) + { + memset(bits, 255, 32); + set= charClassClear; + ++cclass; + } + else + { + memset(bits, 0, 32); + set= charClassSet; + } + + while (*cclass) + { + if ('-' == *cclass && cclass[1] && prev >= 0) + { + ++cclass; + for (c= cnext(&cclass); prev <= c; ++prev) + set(bits, prev); + prev= -1; + } + else + { + c= cnext(&cclass); + set(bits, prev= c); + } + } + + ptr= string; + for (c= 0; c < 32; ++c) + ptr += sprintf(ptr, "\\%03o", bits[c]); + + return string; +} + +static void begin(void) { fprintf(output, "\n {"); } +static void end(void) { fprintf(output, "\n }"); } +static void label(int n) { fprintf(output, "\n l%d:;\t", n); } +static void jump(int n) { fprintf(output, " goto l%d;", n); } +static void save(int n) { fprintf(output, " int yypos%d= ctx->pos, yythunkpos%d= ctx->thunkpos;", n, n); } +static void restore(int n) { fprintf(output, " ctx->pos= yypos%d; ctx->thunkpos= yythunkpos%d;", n, n); } + +static void Node_compile_c_ko(Node *node, int ko) +{ + assert(node); + switch (node->type) + { + case Rule: + fprintf(stderr, "\ninternal error #1 (%s)\n", node->rule.name); + exit(1); + break; + + case Dot: + fprintf(output, " if (!yymatchDot(ctx)) goto l%d;", ko); + break; + + case Name: + fprintf(output, " if (!yy_%s(ctx)) goto l%d;", node->name.rule->rule.name, ko); + if (node->name.variable) + fprintf(output, " yyDo(ctx, yySet, %d, 0);", node->name.variable->variable.offset); + break; + + case Character: + case String: + { + int len= strlen(node->string.value); + if (1 == len) + { + if ('\'' == node->string.value[0]) + fprintf(output, " if (!yymatchChar(ctx, '\\'')) goto l%d;", ko); + else + fprintf(output, " if (!yymatchChar(ctx, '%s')) goto l%d;", node->string.value, ko); + } + else + if (2 == len && '\\' == node->string.value[0]) + fprintf(output, " if (!yymatchChar(ctx, '%s')) goto l%d;", node->string.value, ko); + else + fprintf(output, " if (!yymatchString(ctx, \"%s\")) goto l%d;", node->string.value, ko); + } + break; + + case Class: + fprintf(output, " if (!yymatchClass(ctx, (unsigned char *)\"%s\")) goto l%d;", makeCharClass(node->cclass.value), ko); + break; + + case Action: + fprintf(output, " yyDo(ctx, yy%s, ctx->begin, ctx->end);", node->action.name); + break; + + case Predicate: + fprintf(output, " yyText(ctx, ctx->begin, ctx->end); if (!(%s)) goto l%d;", node->action.text, ko); + break; + + case Alternate: + { + int ok= yyl(); + begin(); + save(ok); + for (node= node->alternate.first; node; node= node->alternate.next) + if (node->alternate.next) + { + int next= yyl(); + Node_compile_c_ko(node, next); + jump(ok); + label(next); + restore(ok); + } + else + Node_compile_c_ko(node, ko); + end(); + label(ok); + } + break; + + case Sequence: + for (node= node->sequence.first; node; node= node->sequence.next) + Node_compile_c_ko(node, ko); + break; + + case PeekFor: + { + int ok= yyl(); + begin(); + save(ok); + Node_compile_c_ko(node->peekFor.element, ko); + restore(ok); + end(); + } + break; + + case PeekNot: + { + int ok= yyl(); + begin(); + save(ok); + Node_compile_c_ko(node->peekFor.element, ok); + jump(ko); + label(ok); + restore(ok); + end(); + } + break; + + case Query: + { + int qko= yyl(), qok= yyl(); + begin(); + save(qko); + Node_compile_c_ko(node->query.element, qko); + jump(qok); + label(qko); + restore(qko); + end(); + label(qok); + } + break; + + case Star: + { + int again= yyl(), out= yyl(); + label(again); + begin(); + save(out); + Node_compile_c_ko(node->star.element, out); + jump(again); + label(out); + restore(out); + end(); + } + break; + + case Plus: + { + int again= yyl(), out= yyl(); + Node_compile_c_ko(node->plus.element, ko); + label(again); + begin(); + save(out); + Node_compile_c_ko(node->plus.element, out); + jump(again); + label(out); + restore(out); + end(); + } + break; + + default: + fprintf(stderr, "\nNode_compile_c_ko: illegal node type %d\n", node->type); + exit(1); + } +} + + +static int countVariables(Node *node) +{ + int count= 0; + while (node) + { + ++count; + node= node->variable.next; + } + return count; +} + +static void defineVariables(Node *node) +{ + int count= 0; + while (node) + { + fprintf(output, "#define %s ctx->val[%d]\n", node->variable.name, --count); + node->variable.offset= count; + node= node->variable.next; + } + fprintf(output, "#define yy ctx->yy\n"); + fprintf(output, "#define yypos ctx->pos\n"); + fprintf(output, "#define yythunkpos ctx->thunkpos\n"); +} + +static void undefineVariables(Node *node) +{ + fprintf(output, "#undef yythunkpos\n"); + fprintf(output, "#undef yypos\n"); + fprintf(output, "#undef yy\n"); + while (node) + { + fprintf(output, "#undef %s\n", node->variable.name); + node= node->variable.next; + } +} + + +static void Rule_compile_c2(Node *node) +{ + assert(node); + assert(Rule == node->type); + + if (!node->rule.expression) + fprintf(stderr, "rule '%s' used but not defined\n", node->rule.name); + else + { + int ko= yyl(), safe; + + if ((!(RuleUsed & node->rule.flags)) && (node != start)) + fprintf(stderr, "rule '%s' defined but not used\n", node->rule.name); + + safe= ((Query == node->rule.expression->type) || (Star == node->rule.expression->type)); + + fprintf(output, "\nYY_RULE(int) yy_%s(yycontext *ctx)\n{", node->rule.name); + if (!safe) save(0); + if (node->rule.variables) + fprintf(output, " yyDo(ctx, yyPush, %d, 0);", countVariables(node->rule.variables)); + fprintf(output, "\n yyprintf((stderr, \"%%s\\n\", \"%s\"));", node->rule.name); + Node_compile_c_ko(node->rule.expression, ko); + fprintf(output, "\n yyprintf((stderr, \" ok %%s @ %%s\\n\", \"%s\", ctx->buf+ctx->pos));", node->rule.name); + if (node->rule.variables) + fprintf(output, " yyDo(ctx, yyPop, %d, 0);", countVariables(node->rule.variables)); + fprintf(output, "\n return 1;"); + if (!safe) + { + label(ko); + restore(0); + fprintf(output, "\n yyprintf((stderr, \" fail %%s @ %%s\\n\", \"%s\", ctx->buf+ctx->pos));", node->rule.name); + fprintf(output, "\n return 0;"); + } + fprintf(output, "\n}"); + } + + if (node->rule.next) + Rule_compile_c2(node->rule.next); +} + +static char *header= "\ +#include \n\ +#include \n\ +#include \n\ +"; + +static char *preamble= "\ +#ifndef YY_LOCAL\n\ +#define YY_LOCAL(T) static T\n\ +#endif\n\ +#ifndef YY_ACTION\n\ +#define YY_ACTION(T) static T\n\ +#endif\n\ +#ifndef YY_RULE\n\ +#define YY_RULE(T) static T\n\ +#endif\n\ +#ifndef YY_PARSE\n\ +#define YY_PARSE(T) T\n\ +#endif\n\ +#ifndef YYPARSE\n\ +#define YYPARSE yyparse\n\ +#endif\n\ +#ifndef YYPARSEFROM\n\ +#define YYPARSEFROM yyparsefrom\n\ +#endif\n\ +#ifndef YY_INPUT\n\ +#define YY_INPUT(buf, result, max_size) \\\n\ + { \\\n\ + int yyc= getchar(); \\\n\ + result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \\\n\ + yyprintf((stderr, \"<%c>\", yyc)); \\\n\ + }\n\ +#endif\n\ +#ifndef YY_BEGIN\n\ +#define YY_BEGIN ( ctx->begin= ctx->pos, 1)\n\ +#endif\n\ +#ifndef YY_END\n\ +#define YY_END ( ctx->end= ctx->pos, 1)\n\ +#endif\n\ +#ifdef YY_DEBUG\n\ +# define yyprintf(args) fprintf args\n\ +#else\n\ +# define yyprintf(args)\n\ +#endif\n\ +#ifndef YYSTYPE\n\ +#define YYSTYPE int\n\ +#endif\n\ +\n\ +#ifndef YY_PART\n\ +\n\ +typedef struct _yycontext yycontext;\n\ +typedef void (*yyaction)(yycontext *ctx, char *yytext, int yyleng);\n\ +typedef struct _yythunk { int begin, end; yyaction action; struct _yythunk *next; } yythunk;\n\ +\n\ +struct _yycontext {\n\ + char *buf;\n\ + int buflen;\n\ + int pos;\n\ + int limit;\n\ + char *text;\n\ + int textlen;\n\ + int begin;\n\ + int end;\n\ + int textmax;\n\ + yythunk *thunks;\n\ + int thunkslen;\n\ + int thunkpos;\n\ + YYSTYPE yy;\n\ + YYSTYPE *val;\n\ + YYSTYPE *vals;\n\ + int valslen;\n\ +#ifdef YY_CTX_MEMBERS\n\ + YY_CTX_MEMBERS\n\ +#endif\n\ +};\n\ +\n\ +#ifdef YY_CTX_LOCAL\n\ +#define YY_CTX_PARAM_ yycontext *yyctx,\n\ +#define YY_CTX_PARAM yycontext *yyctx\n\ +#define YY_CTX_ARG_ yyctx,\n\ +#define YY_CTX_ARG yyctx\n\ +#else\n\ +#define YY_CTX_PARAM_\n\ +#define YY_CTX_PARAM\n\ +#define YY_CTX_ARG_\n\ +#define YY_CTX_ARG\n\ +yycontext yyctx0;\n\ +yycontext *yyctx= &yyctx0;\n\ +#endif\n\ +\n\ +YY_LOCAL(int) yyrefill(yycontext *ctx)\n\ +{\n\ + int yyn;\n\ + while (ctx->buflen - ctx->pos < 512)\n\ + {\n\ + ctx->buflen *= 2;\n\ + ctx->buf= (char *)realloc(ctx->buf, ctx->buflen);\n\ + }\n\ + YY_INPUT((ctx->buf + ctx->pos), yyn, (ctx->buflen - ctx->pos));\n\ + if (!yyn) return 0;\n\ + ctx->limit += yyn;\n\ + return 1;\n\ +}\n\ +\n\ +YY_LOCAL(int) yymatchDot(yycontext *ctx)\n\ +{\n\ + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;\n\ + ++ctx->pos;\n\ + return 1;\n\ +}\n\ +\n\ +YY_LOCAL(int) yymatchChar(yycontext *ctx, int c)\n\ +{\n\ + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;\n\ + if ((unsigned char)ctx->buf[ctx->pos] == c)\n\ + {\n\ + ++ctx->pos;\n\ + yyprintf((stderr, \" ok yymatchChar(ctx, %c) @ %s\\n\", c, ctx->buf+ctx->pos));\n\ + return 1;\n\ + }\n\ + yyprintf((stderr, \" fail yymatchChar(ctx, %c) @ %s\\n\", c, ctx->buf+ctx->pos));\n\ + return 0;\n\ +}\n\ +\n\ +YY_LOCAL(int) yymatchString(yycontext *ctx, char *s)\n\ +{\n\ + int yysav= ctx->pos;\n\ + while (*s)\n\ + {\n\ + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;\n\ + if (ctx->buf[ctx->pos] != *s)\n\ + {\n\ + ctx->pos= yysav;\n\ + return 0;\n\ + }\n\ + ++s;\n\ + ++ctx->pos;\n\ + }\n\ + return 1;\n\ +}\n\ +\n\ +YY_LOCAL(int) yymatchClass(yycontext *ctx, unsigned char *bits)\n\ +{\n\ + int c;\n\ + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0;\n\ + c= (unsigned char)ctx->buf[ctx->pos];\n\ + if (bits[c >> 3] & (1 << (c & 7)))\n\ + {\n\ + ++ctx->pos;\n\ + yyprintf((stderr, \" ok yymatchClass @ %s\\n\", ctx->buf+ctx->pos));\n\ + return 1;\n\ + }\n\ + yyprintf((stderr, \" fail yymatchClass @ %s\\n\", ctx->buf+ctx->pos));\n\ + return 0;\n\ +}\n\ +\n\ +YY_LOCAL(void) yyDo(yycontext *ctx, yyaction action, int begin, int end)\n\ +{\n\ + while (ctx->thunkpos >= ctx->thunkslen)\n\ + {\n\ + ctx->thunkslen *= 2;\n\ + ctx->thunks= (yythunk *)realloc(ctx->thunks, sizeof(yythunk) * ctx->thunkslen);\n\ + }\n\ + ctx->thunks[ctx->thunkpos].begin= begin;\n\ + ctx->thunks[ctx->thunkpos].end= end;\n\ + ctx->thunks[ctx->thunkpos].action= action;\n\ + ++ctx->thunkpos;\n\ +}\n\ +\n\ +YY_LOCAL(int) yyText(yycontext *ctx, int begin, int end)\n\ +{\n\ + int yyleng= end - begin;\n\ + if (yyleng <= 0)\n\ + yyleng= 0;\n\ + else\n\ + {\n\ + while (ctx->textlen < (yyleng + 1))\n\ + {\n\ + ctx->textlen *= 2;\n\ + ctx->text= (char *)realloc(ctx->text, ctx->textlen);\n\ + }\n\ + memcpy(ctx->text, ctx->buf + begin, yyleng);\n\ + }\n\ + ctx->text[yyleng]= '\\0';\n\ + return yyleng;\n\ +}\n\ +\n\ +YY_LOCAL(void) yyDone(yycontext *ctx)\n\ +{\n\ + int pos;\n\ + for (pos= 0; pos < ctx->thunkpos; ++pos)\n\ + {\n\ + yythunk *thunk= &ctx->thunks[pos];\n\ + int yyleng= thunk->end ? yyText(ctx, thunk->begin, thunk->end) : thunk->begin;\n\ + yyprintf((stderr, \"DO [%d] %p %s\\n\", pos, thunk->action, ctx->text));\n\ + thunk->action(ctx, ctx->text, yyleng);\n\ + }\n\ + ctx->thunkpos= 0;\n\ +}\n\ +\n\ +YY_LOCAL(void) yyCommit(yycontext *ctx)\n\ +{\n\ + if ((ctx->limit -= ctx->pos))\n\ + {\n\ + memmove(ctx->buf, ctx->buf + ctx->pos, ctx->limit);\n\ + }\n\ + ctx->begin -= ctx->pos;\n\ + ctx->end -= ctx->pos;\n\ + ctx->pos= ctx->thunkpos= 0;\n\ +}\n\ +\n\ +YY_LOCAL(int) yyAccept(yycontext *ctx, int tp0)\n\ +{\n\ + if (tp0)\n\ + {\n\ + fprintf(stderr, \"accept denied at %d\\n\", tp0);\n\ + return 0;\n\ + }\n\ + else\n\ + {\n\ + yyDone(ctx);\n\ + yyCommit(ctx);\n\ + }\n\ + return 1;\n\ +}\n\ +\n\ +YY_LOCAL(void) yyPush(yycontext *ctx, char *text, int count) { ctx->val += count; }\n\ +YY_LOCAL(void) yyPop(yycontext *ctx, char *text, int count) { ctx->val -= count; }\n\ +YY_LOCAL(void) yySet(yycontext *ctx, char *text, int count) { ctx->val[count]= ctx->yy; }\n\ +\n\ +#endif /* YY_PART */\n\ +\n\ +#define YYACCEPT yyAccept(ctx, yythunkpos0)\n\ +\n\ +"; + +static char *footer= "\n\ +\n\ +#ifndef YY_PART\n\ +\n\ +typedef int (*yyrule)(yycontext *ctx);\n\ +\n\ +YY_PARSE(int) YYPARSEFROM(YY_CTX_PARAM_ yyrule yystart)\n\ +{\n\ + int yyok;\n\ + if (!yyctx->buflen)\n\ + {\n\ + yyctx->buflen= 1024;\n\ + yyctx->buf= (char *)malloc(yyctx->buflen);\n\ + yyctx->textlen= 1024;\n\ + yyctx->text= (char *)malloc(yyctx->textlen);\n\ + yyctx->thunkslen= 32;\n\ + yyctx->thunks= (yythunk *)malloc(sizeof(yythunk) * yyctx->thunkslen);\n\ + yyctx->valslen= 32;\n\ + yyctx->vals= (YYSTYPE *)malloc(sizeof(YYSTYPE) * yyctx->valslen);\n\ + yyctx->begin= yyctx->end= yyctx->pos= yyctx->limit= yyctx->thunkpos= 0;\n\ + }\n\ + yyctx->begin= yyctx->end= yyctx->pos;\n\ + yyctx->thunkpos= 0;\n\ + yyctx->val= yyctx->vals;\n\ + yyok= yystart(yyctx);\n\ + if (yyok) yyDone(yyctx);\n\ + yyCommit(yyctx);\n\ + return yyok;\n\ +}\n\ +\n\ +YY_PARSE(int) YYPARSE(YY_CTX_PARAM)\n\ +{\n\ + return YYPARSEFROM(YY_CTX_ARG_ yy_%s);\n\ +}\n\ +\n\ +#endif\n\ +"; + +void Rule_compile_c_header(void) +{ + fprintf(output, "/* A recursive-descent parser generated by peg %d.%d.%d */\n", PEG_MAJOR, PEG_MINOR, PEG_LEVEL); + fprintf(output, "\n"); + fprintf(output, "%s", header); + fprintf(output, "#define YYRULECOUNT %d\n", ruleCount); +} + +int consumesInput(Node *node) +{ + if (!node) return 0; + + switch (node->type) + { + case Rule: + { + int result= 0; + if (RuleReached & node->rule.flags) + fprintf(stderr, "possible infinite left recursion in rule '%s'\n", node->rule.name); + else + { + node->rule.flags |= RuleReached; + result= consumesInput(node->rule.expression); + node->rule.flags &= ~RuleReached; + } + return result; + } + break; + + case Dot: return 1; + case Name: return consumesInput(node->name.rule); + case Character: + case String: return strlen(node->string.value) > 0; + case Class: return 1; + case Action: return 0; + case Predicate: return 0; + + case Alternate: + { + Node *n; + for (n= node->alternate.first; n; n= n->alternate.next) + if (!consumesInput(n)) + return 0; + } + return 1; + + case Sequence: + { + Node *n; + for (n= node->alternate.first; n; n= n->alternate.next) + if (consumesInput(n)) + return 1; + } + return 0; + + case PeekFor: return 0; + case PeekNot: return 0; + case Query: return 0; + case Star: return 0; + case Plus: return consumesInput(node->plus.element); + + default: + fprintf(stderr, "\nconsumesInput: illegal node type %d\n", node->type); + exit(1); + } + return 0; +} + + +void Rule_compile_c(Node *node) +{ + Node *n; + + for (n= rules; n; n= n->rule.next) + consumesInput(n); + + fprintf(output, "%s", preamble); + for (n= node; n; n= n->rule.next) + fprintf(output, "YY_RULE(int) yy_%s(yycontext *ctx); /* %d */\n", n->rule.name, n->rule.id); + fprintf(output, "\n"); + for (n= actions; n; n= n->action.list) + { + fprintf(output, "YY_ACTION(void) yy%s(yycontext *ctx, char *yytext, int yyleng)\n{\n", n->action.name); + defineVariables(n->action.rule->rule.variables); + fprintf(output, " yyprintf((stderr, \"do yy%s\\n\"));\n", n->action.name); + fprintf(output, " {\n"); + fprintf(output, " %s;\n", n->action.text); + fprintf(output, " }\n"); + undefineVariables(n->action.rule->rule.variables); + fprintf(output, "}\n"); + } + Rule_compile_c2(node); + fprintf(output, footer, start->rule.name); +} diff --git a/src/lib/multimarkdown/peg/examples/accept.c b/src/lib/multimarkdown/peg/examples/accept.c new file mode 100644 index 0000000..781e3b1 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/accept.c @@ -0,0 +1,11 @@ +#include +#include + +#include "accept.peg.c" + +int main() +{ + while (yyparse()); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/accept.peg b/src/lib/multimarkdown/peg/examples/accept.peg new file mode 100644 index 0000000..9b28e40 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/accept.peg @@ -0,0 +1,8 @@ +start <- abcd+ + +abcd <- 'a' { printf("A %d\n", yypos); } bc { printf("ABC %d\n", yypos); } &{YYACCEPT} + / 'b' { printf("B %d\n", yypos); } cd { printf("BCD %d\n", yypos); } &{YYACCEPT} + +bc <- 'b' { printf("B %d\n", yypos); } 'c' { printf("C %d\n", yypos); } + +cd <- 'c' { printf("C %d\n", yypos); } 'd' { printf("D %d\n", yypos); } diff --git a/src/lib/multimarkdown/peg/examples/accept.ref b/src/lib/multimarkdown/peg/examples/accept.ref new file mode 100644 index 0000000..789f528 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/accept.ref @@ -0,0 +1,32 @@ +A 3 +B 3 +C 3 +ABC 3 +B 3 +C 3 +D 3 +BCD 3 +A 3 +B 3 +C 3 +ABC 3 +B 3 +C 3 +D 3 +BCD 3 +A 3 +B 3 +C 3 +ABC 3 +B 3 +C 3 +D 3 +BCD 3 +A 3 +B 3 +C 3 +ABC 3 +B 3 +C 3 +D 3 +BCD 3 diff --git a/src/lib/multimarkdown/peg/examples/basic.leg b/src/lib/multimarkdown/peg/examples/basic.leg new file mode 100644 index 0000000..ed38a8d --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/basic.leg @@ -0,0 +1,361 @@ +# A 'syntax-directed interpreter' (all execution is a side-effect of parsing). +# Inspired by Dennis Allison's original Tiny BASIC grammar, circa 1975. +# +# Copyright (c) 2007 by Ian Piumarta +# All rights reserved. +# +# 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, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, provided that the above copyright notice(s) and this +# permission notice appear in all copies of the Software. Acknowledgement +# of the use of this Software in supporting documentation would be +# appreciated but is not required. +# +# THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK. +# +# Last edited: 2012-04-29 15:14:06 by piumarta on emilia + +%{ +# include + + typedef struct line line; + + struct line + { + int number; + int length; + char *text; + }; + + line *lines= 0; + int numLines= 0; + int pc= -1, epc= -1; + int batch= 0; + + int nextline(char *buf, int max); + +# define min(x, y) ((x) < (y) ? (x) : (y)) + +# define YY_INPUT(buf, result, max_size) \ + { \ + if ((pc >= 0) && (pc < numLines)) \ + { \ + line *linep= lines+pc++; \ + result= min(max_size, linep->length); \ + memcpy(buf, linep->text, result); \ + } \ + else \ + result= nextline(buf, max_size); \ + } + + union value { + int number; + char *string; + int (*binop)(int lhs, int rhs); + }; + +# define YYSTYPE union value + + int variables[26]; + + void accept(int number, char *line); + + void save(char *name); + void load(char *name); + void type(char *name); + + int lessThan(int lhs, int rhs) { return lhs < rhs; } + int lessEqual(int lhs, int rhs) { return lhs <= rhs; } + int notEqual(int lhs, int rhs) { return lhs != rhs; } + int equalTo(int lhs, int rhs) { return lhs == rhs; } + int greaterEqual(int lhs, int rhs) { return lhs >= rhs; } + int greaterThan(int lhs, int rhs) { return lhs > rhs; } + + int input(void); + + int stack[1024], sp= 0; + + char *help; + + void error(char *fmt, ...); + int findLine(int n, int create); +%} + +line = - s:statement CR +| - n:number < ( !CR . )* CR > { accept(n.number, yytext); } +| - CR +| - < ( !CR . )* CR > { epc= pc; error("syntax error"); } +| - !. { exit(0); } + +statement = 'print'- expr-list +| 'if'- e1:expression r:relop e2:expression { if (!r.binop(e1.number, e2.number)) yythunkpos= 0; } + 'then'- statement +| 'goto'- e:expression { epc= pc; if ((pc= findLine(e.number, 0)) < 0) error("no such line"); } +| 'input'- var-list +| 'let'- v:var EQUAL e:expression { variables[v.number]= e.number; } +| 'gosub'- e:expression { epc= pc; if (sp < 1024) stack[sp++]= pc, pc= findLine(e.number, 0); else error("too many gosubs"); + if (pc < 0) error("no such line"); } +| 'return'- { epc= pc; if ((pc= sp ? stack[--sp] : -1) < 0) error("no gosub"); } +| 'clear'- { while (numLines) accept(lines->number, "\n"); } +| 'list'- { int i; for (i= 0; i < numLines; ++i) printf("%5d %s", lines[i].number, lines[i].text); } +| 'run'- s:string { load(s.string); pc= 0; } +| 'run'- { pc= 0; } +| 'end'- { pc= -1; if (batch) exit(0); } +| 'rem'- ( !CR . )* +| ('bye'|'quit'|'exit')- { exit(0); } +| 'save'- s:string { save(s.string); } +| 'load'- s:string { load(s.string); } +| 'type'- s:string { type(s.string); } +| 'dir'- { system("ls *.bas"); } +| 'help'- { fprintf(stderr, "%s", help); } + +expr-list = ( e:string { printf("%s", e.string); } + | e:expression { printf("%d", e.number); } + )? ( COMMA ( e:string { printf("%s", e.string); } + | e:expression { printf("%d", e.number); } + ) + )* ( COMMA + | !COMMA { printf("\n"); } + ) + +var-list = v:var { variables[v.number]= input(); } + ( COMMA v:var { variables[v.number]= input(); } + )* + +expression = ( PLUS? l:term + | MINUS l:term { l.number = -l.number } + ) ( PLUS r:term { l.number += r.number } + | MINUS r:term { l.number -= r.number } + )* { $$.number = l.number } + +term = l:factor ( STAR r:factor { l.number *= r.number } + | SLASH r:factor { l.number /= r.number } + )* { $$.number = l.number } + +factor = v:var { $$.number = variables[v.number] } +| n:number +| OPEN expression CLOSE + +var = < [a-z] > - { $$.number = yytext[0] - 'a' } + +number = < digit+ > - { $$.number = atoi(yytext); } + +digit = [0-9] + +string = '"' < [^\"]* > '"' - { $$.string = yytext; } + +relop = '<=' - { $$.binop= lessEqual; } +| '<>' - { $$.binop= notEqual; } +| '<' - { $$.binop= lessThan; } +| '>=' - { $$.binop= greaterEqual; } +| '>' - { $$.binop= greaterThan; } +| '=' - { $$.binop= equalTo; } + +EQUAL = '=' - CLOSE = ')' - OPEN = '(' - +SLASH = '/' - STAR = '*' - MINUS = '-' - +PLUS = '+' - COMMA = ',' - + +- = [ \t]* + +CR = '\n' | '\r' | '\r\n' + +%% + +#include +#include + +char *help= + "print | [, | ...] [,]\n" + "if <|<=|<>|=|>=|> then \n" + "input [, ...] let = \n" + "goto gosub \n" + "end return\n" + "list clear\n" + "run [\"filename\"] rem \n" + "dir type \"filename\"\n" + "save \"filename\" load \"filename\"\n" + "bye|quit|exit help\n" + ; + +void error(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + if (epc > 0) + fprintf(stderr, "\nline %d: %s", lines[epc-1].number, lines[epc-1].text); + else + fprintf(stderr, "\n"); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + epc= pc= -1; +} + +#ifdef USE_READLINE +# include +# include +#endif + +int nextline(char *buf, int max) +{ + pc= -1; + if (batch) exit(0); + if (isatty(fileno(stdin))) + { +# ifdef USE_READLINE + char *line= readline(">"); + if (line) + { + int len= strlen(line); + if (len >= max) len= max - 1; + strncpy(buf, line, len); + (buf)[len]= '\n'; + add_history(line); + free(line); + return len + 1; + } + else + { + printf("\n"); + return 0; + } +# endif + putchar('>'); + fflush(stdout); + } + return fgets(buf, max, stdin) ? strlen(buf) : 0; +} + +int maxLines= 0; + +int findLine(int n, int create) +{ + int lo= 0, hi= numLines - 1; + while (lo <= hi) + { + int mid= (lo + hi) / 2, lno= lines[mid].number; + if (lno > n) + hi= mid - 1; + else if (lno < n) + lo= mid + 1; + else + return mid; + } + if (create) + { + if (numLines == maxLines) + { + maxLines *= 2; + lines= realloc(lines, sizeof(line) * maxLines); + } + if (lo < numLines) + memmove(lines + lo + 1, lines + lo, sizeof(line) * (numLines - lo)); + ++numLines; + lines[lo].number= n; + lines[lo].text= 0; + return lo; + } + return -1; +} + +void accept(int n, char *s) +{ + if (s[0] < 32) /* delete */ + { + int lno= findLine(n, 0); + if (lno >= 0) + { + if (lno < numLines - 1) + memmove(lines + lno, lines + lno + 1, sizeof(line) * (numLines - lno - 1)); + --numLines; + } + } + else /* insert */ + { + int lno= findLine(n, 1); + if (lines[lno].text) free(lines[lno].text); + lines[lno].length= strlen(s); + lines[lno].text= strdup(s); + } +} + +char *extend(char *name) +{ + static char path[1024]; + int len= strlen(name); + sprintf(path, "%s%s", name, (((len > 4) && !strcasecmp(".bas", name + len - 4)) ? "" : ".bas")); + return path; +} + +void save(char *name) +{ + FILE *f= fopen(name= extend(name), "w"); + if (!f) + perror(name); + else + { + int i; + for (i= 0; i < numLines; ++i) + fprintf(f, "%d %s", lines[i].number, lines[i].text); + fclose(f); + } +} + +void load(char *name) +{ + FILE *f= fopen(name= extend(name), "r"); + if (!f) + perror(name); + else + { + int lineNumber; + char lineText[1024]; + while ((1 == fscanf(f, " %d ", &lineNumber)) && fgets(lineText, sizeof(lineText), f)) + accept(lineNumber, lineText); + fclose(f); + } +} + +void type(char *name) +{ + FILE *f= fopen(name= extend(name), "r"); + if (!f) + perror(name); + else + { + int c, d; + while ((c= getc(f)) >= 0) + putchar(d= c); + fclose(f); + if ('\n' != d && '\r' != d) putchar('\n'); + } +} + +int input(void) +{ + char line[32]; + fgets(line, sizeof(line), stdin); + return atoi(line); +} + +int main(int argc, char **argv) +{ + lines= malloc(sizeof(line) * (maxLines= 32)); + numLines= 0; + + if (argc > 1) + { + batch= 1; + while (argc-- > 1) + load(*++argv); + pc= 0; + } + + while (!feof(stdin)) + yyparse(); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/basic.ref b/src/lib/multimarkdown/peg/examples/basic.ref new file mode 100644 index 0000000..90d916c --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/basic.ref @@ -0,0 +1,10 @@ + 1 + 2 4 + 3 6 9 + 4 8 12 16 + 5 10 15 20 25 + 6 12 18 24 30 36 + 7 14 21 28 35 42 49 + 8 16 24 32 40 48 56 64 + 9 18 27 36 45 54 63 72 81 + 10 20 30 40 50 60 70 80 90 100 diff --git a/src/lib/multimarkdown/peg/examples/bench.bas b/src/lib/multimarkdown/peg/examples/bench.bas new file mode 100644 index 0000000..ffdbd44 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/bench.bas @@ -0,0 +1,8 @@ +100 let n=100000 +120 let m=0 +110 let s=0 +130 let m=m+1 +140 let s=s+m +150 if m +int vars[26]; +%} + +Stmt = - e:Expr EOL { printf("%d\n", e); } + | ( !EOL . )* EOL { printf("error\n"); } + +Expr = i:ID ASSIGN s:Sum { $$= vars[i]= s; } + | s:Sum { $$= s; } + +Sum = l:Product + ( PLUS r:Product { l += r; } + | MINUS r:Product { l -= r; } + )* { $$= l; } + +Product = l:Value + ( TIMES r:Value { l *= r; } + | DIVIDE r:Value { l /= r; } + )* { $$= l; } + +Value = i:NUMBER { $$= atoi(yytext); } + | i:ID !ASSIGN { $$= vars[i]; } + | OPEN i:Expr CLOSE { $$= i; } + +NUMBER = < [0-9]+ > - { $$= atoi(yytext); } +ID = < [a-z] > - { $$= yytext[0] - 'a'; } +ASSIGN = '=' - +PLUS = '+' - +MINUS = '-' - +TIMES = '*' - +DIVIDE = '/' - +OPEN = '(' - +CLOSE = ')' - + +- = [ \t]* +EOL = '\n' | '\r\n' | '\r' | ';' + +%% + +int main() +{ + while (yyparse()); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/calc.ref b/src/lib/multimarkdown/peg/examples/calc.ref new file mode 100644 index 0000000..dbd7d59 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/calc.ref @@ -0,0 +1,3 @@ +6 +7 +42 diff --git a/src/lib/multimarkdown/peg/examples/dc.c b/src/lib/multimarkdown/peg/examples/dc.c new file mode 100644 index 0000000..32bf1a5 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/dc.c @@ -0,0 +1,17 @@ +#include +#include + +int stack[1024]; +int stackp= -1; + +int push(int n) { return stack[++stackp]= n; } +int pop(void) { return stack[stackp--]; } + +#include "dc.peg.c" + +int main() +{ + while (yyparse()); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/dc.peg b/src/lib/multimarkdown/peg/examples/dc.peg new file mode 100644 index 0000000..75dcb67 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/dc.peg @@ -0,0 +1,27 @@ +# Grammar + +Expr <- SPACE Sum EOL { printf("%d\n", pop()); } + / (!EOL .)* EOL { printf("error\n"); } + +Sum <- Product ( PLUS Product { int r= pop(), l= pop(); push(l + r); } + / MINUS Product { int r= pop(), l= pop(); push(l - r); } + )* + +Product <- Value ( TIMES Value { int r= pop(), l= pop(); push(l * r); } + / DIVIDE Value { int r= pop(), l= pop(); push(l / r); } + )* + +Value <- NUMBER { push(atoi(yytext)); } + / OPEN Sum CLOSE + +# Lexemes + +NUMBER <- < [0-9]+ > SPACE +PLUS <- '+' SPACE +MINUS <- '-' SPACE +TIMES <- '*' SPACE +DIVIDE <- '/' SPACE +OPEN <- '(' SPACE +CLOSE <- ')' SPACE +SPACE <- [ \t]* +EOL <- '\n' / '\r\n' / '\r' diff --git a/src/lib/multimarkdown/peg/examples/dc.ref b/src/lib/multimarkdown/peg/examples/dc.ref new file mode 100644 index 0000000..d81cc07 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/dc.ref @@ -0,0 +1 @@ +42 diff --git a/src/lib/multimarkdown/peg/examples/dcv.c b/src/lib/multimarkdown/peg/examples/dcv.c new file mode 100644 index 0000000..0c5c46d --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/dcv.c @@ -0,0 +1,20 @@ +#include +#include + +int stack[1024]; +int stackp= -1; +int var= 0; +int vars[26]; + +int push(int n) { return stack[++stackp]= n; } +int pop(void) { return stack[stackp--]; } +int top(void) { return stack[stackp]; } + +#include "dcv.peg.c" + +int main() +{ + while (yyparse()); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/dcv.peg b/src/lib/multimarkdown/peg/examples/dcv.peg new file mode 100644 index 0000000..2ae3a8c --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/dcv.peg @@ -0,0 +1,34 @@ +# Grammar + +Stmt <- SPACE Expr EOL { printf("%d\n", pop()); } + / (!EOL .)* EOL { printf("error\n"); } + +Expr <- ID { var= yytext[0] } ASSIGN Sum { vars[var - 'a']= top(); } + / Sum + +Sum <- Product ( PLUS Product { int r= pop(), l= pop(); push(l + r); } + / MINUS Product { int r= pop(), l= pop(); push(l - r); } + )* + +Product <- Value ( TIMES Value { int r= pop(), l= pop(); push(l * r); } + / DIVIDE Value { int r= pop(), l= pop(); push(l / r); } + )* + +Value <- NUMBER { push(atoi(yytext)); } + / < ID > !ASSIGN { push(vars[yytext[0] - 'a']); } + / OPEN Expr CLOSE + +# Lexemes + +NUMBER <- < [0-9]+ > SPACE +ID <- < [a-z] > SPACE +ASSIGN <- '=' SPACE +PLUS <- '+' SPACE +MINUS <- '-' SPACE +TIMES <- '*' SPACE +DIVIDE <- '/' SPACE +OPEN <- '(' SPACE +CLOSE <- ')' SPACE + +SPACE <- [ \t]* +EOL <- '\n' / '\r\n' / '\r' / ';' diff --git a/src/lib/multimarkdown/peg/examples/dcv.ref b/src/lib/multimarkdown/peg/examples/dcv.ref new file mode 100644 index 0000000..dbd7d59 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/dcv.ref @@ -0,0 +1,3 @@ +6 +7 +42 diff --git a/src/lib/multimarkdown/peg/examples/fibonacci.bas b/src/lib/multimarkdown/peg/examples/fibonacci.bas new file mode 100644 index 0000000..1872bd3 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/fibonacci.bas @@ -0,0 +1,17 @@ +100 let n=32 +110 gosub 200 +120 print "fibonacci(",n,") = ", m +130 end + +200 let c=n +210 let b=1 +220 if c<2 then goto 400 +230 let c=c-1 +240 let a=1 +300 let c=c-1 +310 let d=a+b +320 let a=b +330 let b=d+1 +340 if c<>0 then goto 300 +400 let m=b +410 return diff --git a/src/lib/multimarkdown/peg/examples/left.c b/src/lib/multimarkdown/peg/examples/left.c new file mode 100644 index 0000000..ac8cd0b --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/left.c @@ -0,0 +1,17 @@ +#include + +#define YY_INPUT(buf, result, max) \ +{ \ + int c= getchar(); \ + result= (EOF == c) ? 0 : (*(buf)= c, 1); \ + if (EOF != c) printf("<%c>\n", c); \ +} + +#include "left.peg.c" + +int main() +{ + printf(yyparse() ? "success\n" : "failure\n"); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/left.peg b/src/lib/multimarkdown/peg/examples/left.peg new file mode 100644 index 0000000..f282227 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/left.peg @@ -0,0 +1,3 @@ +# Grammar + +S <- (S 'a' / 'a') !'a' diff --git a/src/lib/multimarkdown/peg/examples/localctx.c b/src/lib/multimarkdown/peg/examples/localctx.c new file mode 100644 index 0000000..837ebc8 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/localctx.c @@ -0,0 +1,13 @@ +#include + +#define YY_CTX_LOCAL + +#include "test.peg.c" + +int main() +{ + yycontext ctx; + memset(&ctx, 0, sizeof(yycontext)); + while (yyparse(&ctx)); + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/localctx.ref b/src/lib/multimarkdown/peg/examples/localctx.ref new file mode 100644 index 0000000..2d18109 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/localctx.ref @@ -0,0 +1,10 @@ +a1 ab1 . +a2 ac2 . +a3 ad3 . +a3 ae3 . +a4 af4 afg4 . +a4 af5 afh5 . +a4 af4 afg4 . +a4 af5 afh5 . +af6 afi6 a6 . +af6 af7 afj7 a6 . diff --git a/src/lib/multimarkdown/peg/examples/rule.c b/src/lib/multimarkdown/peg/examples/rule.c new file mode 100644 index 0000000..15eb0c6 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/rule.c @@ -0,0 +1,11 @@ +#include +#include + +#include "rule.peg.c" + +int main() +{ + while (yyparse()); + + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/rule.peg b/src/lib/multimarkdown/peg/examples/rule.peg new file mode 100644 index 0000000..60a32fa --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/rule.peg @@ -0,0 +1,8 @@ +start <- abcd+ + +abcd <- 'a' { printf("A %d\n", yypos); } bc { printf("ABC %d\n", yypos); } + / 'b' { printf("B %d\n", yypos); } cd { printf("BCD %d\n", yypos); } + +bc <- 'b' { printf("B %d\n", yypos); } 'c' { printf("C %d\n", yypos); } + +cd <- 'c' { printf("C %d\n", yypos); } 'd' { printf("D %d\n", yypos); } diff --git a/src/lib/multimarkdown/peg/examples/rule.ref b/src/lib/multimarkdown/peg/examples/rule.ref new file mode 100644 index 0000000..4249ebe --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/rule.ref @@ -0,0 +1,32 @@ +A 24 +B 24 +C 24 +ABC 24 +B 24 +C 24 +D 24 +BCD 24 +A 24 +B 24 +C 24 +ABC 24 +B 24 +C 24 +D 24 +BCD 24 +A 24 +B 24 +C 24 +ABC 24 +B 24 +C 24 +D 24 +BCD 24 +A 24 +B 24 +C 24 +ABC 24 +B 24 +C 24 +D 24 +BCD 24 diff --git a/src/lib/multimarkdown/peg/examples/username.leg b/src/lib/multimarkdown/peg/examples/username.leg new file mode 100644 index 0000000..2170052 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/username.leg @@ -0,0 +1,14 @@ +%{ +#include +%} + +start = "username" { printf("%s", getlogin()); } +| < . > { putchar(yytext[0]); } + +%% + +int main() +{ + while (yyparse()); + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/wc.leg b/src/lib/multimarkdown/peg/examples/wc.leg new file mode 100644 index 0000000..59199c8 --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/wc.leg @@ -0,0 +1,22 @@ +%{ +#include +int lines= 0, words= 0, chars= 0; +%} + +start = (line | word | char) + +line = < (( '\n' '\r'* ) | ( '\r' '\n'* )) > { lines++; chars += yyleng; } +word = < [a-zA-Z]+ > { words++; chars += yyleng; printf("<%s>\n", yytext); } +char = . { chars++; } + +%% + +int main() +{ + while (yyparse()) + ; + printf("%d lines\n", lines); + printf("%d chars\n", chars); + printf("%d words\n", words); + return 0; +} diff --git a/src/lib/multimarkdown/peg/examples/wc.ref b/src/lib/multimarkdown/peg/examples/wc.ref new file mode 100644 index 0000000..083a46e --- /dev/null +++ b/src/lib/multimarkdown/peg/examples/wc.ref @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + +22 lines +425 chars +52 words diff --git a/src/lib/multimarkdown/peg/leg.c b/src/lib/multimarkdown/peg/leg.c new file mode 100644 index 0000000..4651f73 --- /dev/null +++ b/src/lib/multimarkdown/peg/leg.c @@ -0,0 +1,1213 @@ +/* A recursive-descent parser generated by peg 0.1.9 */ + +#include +#include +#include +#define YYRULECOUNT 36 + +# include "tree.h" +# include "version.h" + +# include +# include +# include +# include +# include +# include + + typedef struct Header Header; + + struct Header { + char *text; + Header *next; + }; + + FILE *input= 0; + + int verboseFlag= 0; + + static int lineNumber= 0; + static char *fileName= 0; + static char *trailer= 0; + static Header *headers= 0; + + void makeHeader(char *text); + void makeTrailer(char *text); + + void yyerror(char *message); + +# define YY_INPUT(buf, result, max) \ + { \ + int c= getc(input); \ + if ('\n' == c || '\r' == c) ++lineNumber; \ + result= (EOF == c) ? 0 : (*(buf)= c, 1); \ + } + +# define YY_LOCAL(T) static T +# define YY_RULE(T) static T + +#ifndef YY_LOCAL +#define YY_LOCAL(T) static T +#endif +#ifndef YY_ACTION +#define YY_ACTION(T) static T +#endif +#ifndef YY_RULE +#define YY_RULE(T) static T +#endif +#ifndef YY_PARSE +#define YY_PARSE(T) T +#endif +#ifndef YYPARSE +#define YYPARSE yyparse +#endif +#ifndef YYPARSEFROM +#define YYPARSEFROM yyparsefrom +#endif +#ifndef YY_INPUT +#define YY_INPUT(buf, result, max_size) \ + { \ + int yyc= getchar(); \ + result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \ + yyprintf((stderr, "<%c>", yyc)); \ + } +#endif +#ifndef YY_BEGIN +#define YY_BEGIN ( ctx->begin= ctx->pos, 1) +#endif +#ifndef YY_END +#define YY_END ( ctx->end= ctx->pos, 1) +#endif +#ifdef YY_DEBUG +# define yyprintf(args) fprintf args +#else +# define yyprintf(args) +#endif +#ifndef YYSTYPE +#define YYSTYPE int +#endif + +#ifndef YY_PART + +typedef struct _yycontext yycontext; +typedef void (*yyaction)(yycontext *ctx, char *yytext, int yyleng); +typedef struct _yythunk { int begin, end; yyaction action; struct _yythunk *next; } yythunk; + +struct _yycontext { + char *buf; + int buflen; + int pos; + int limit; + char *text; + int textlen; + int begin; + int end; + int textmax; + yythunk *thunks; + int thunkslen; + int thunkpos; + YYSTYPE yy; + YYSTYPE *val; + YYSTYPE *vals; + int valslen; +#ifdef YY_CTX_MEMBERS + YY_CTX_MEMBERS +#endif +}; + +#ifdef YY_CTX_LOCAL +#define YY_CTX_PARAM_ yycontext *yyctx, +#define YY_CTX_PARAM yycontext *yyctx +#define YY_CTX_ARG_ yyctx, +#define YY_CTX_ARG yyctx +#else +#define YY_CTX_PARAM_ +#define YY_CTX_PARAM +#define YY_CTX_ARG_ +#define YY_CTX_ARG +yycontext yyctx0; +yycontext *yyctx= &yyctx0; +#endif + +YY_LOCAL(int) yyrefill(yycontext *ctx) +{ + int yyn; + while (ctx->buflen - ctx->pos < 512) + { + ctx->buflen *= 2; + ctx->buf= (char *)realloc(ctx->buf, ctx->buflen); + } + YY_INPUT((ctx->buf + ctx->pos), yyn, (ctx->buflen - ctx->pos)); + if (!yyn) return 0; + ctx->limit += yyn; + return 1; +} + +YY_LOCAL(int) yymatchDot(yycontext *ctx) +{ + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0; + ++ctx->pos; + return 1; +} + +YY_LOCAL(int) yymatchChar(yycontext *ctx, int c) +{ + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0; + if ((unsigned char)ctx->buf[ctx->pos] == c) + { + ++ctx->pos; + yyprintf((stderr, " ok yymatchChar(ctx, %c) @ %s\n", c, ctx->buf+ctx->pos)); + return 1; + } + yyprintf((stderr, " fail yymatchChar(ctx, %c) @ %s\n", c, ctx->buf+ctx->pos)); + return 0; +} + +YY_LOCAL(int) yymatchString(yycontext *ctx, char *s) +{ + int yysav= ctx->pos; + while (*s) + { + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0; + if (ctx->buf[ctx->pos] != *s) + { + ctx->pos= yysav; + return 0; + } + ++s; + ++ctx->pos; + } + return 1; +} + +YY_LOCAL(int) yymatchClass(yycontext *ctx, unsigned char *bits) +{ + int c; + if (ctx->pos >= ctx->limit && !yyrefill(ctx)) return 0; + c= (unsigned char)ctx->buf[ctx->pos]; + if (bits[c >> 3] & (1 << (c & 7))) + { + ++ctx->pos; + yyprintf((stderr, " ok yymatchClass @ %s\n", ctx->buf+ctx->pos)); + return 1; + } + yyprintf((stderr, " fail yymatchClass @ %s\n", ctx->buf+ctx->pos)); + return 0; +} + +YY_LOCAL(void) yyDo(yycontext *ctx, yyaction action, int begin, int end) +{ + while (ctx->thunkpos >= ctx->thunkslen) + { + ctx->thunkslen *= 2; + ctx->thunks= (yythunk *)realloc(ctx->thunks, sizeof(yythunk) * ctx->thunkslen); + } + ctx->thunks[ctx->thunkpos].begin= begin; + ctx->thunks[ctx->thunkpos].end= end; + ctx->thunks[ctx->thunkpos].action= action; + ++ctx->thunkpos; +} + +YY_LOCAL(int) yyText(yycontext *ctx, int begin, int end) +{ + int yyleng= end - begin; + if (yyleng <= 0) + yyleng= 0; + else + { + while (ctx->textlen < (yyleng + 1)) + { + ctx->textlen *= 2; + ctx->text= (char *)realloc(ctx->text, ctx->textlen); + } + memcpy(ctx->text, ctx->buf + begin, yyleng); + } + ctx->text[yyleng]= '\0'; + return yyleng; +} + +YY_LOCAL(void) yyDone(yycontext *ctx) +{ + int pos; + for (pos= 0; pos < ctx->thunkpos; ++pos) + { + yythunk *thunk= &ctx->thunks[pos]; + int yyleng= thunk->end ? yyText(ctx, thunk->begin, thunk->end) : thunk->begin; + yyprintf((stderr, "DO [%d] %p %s\n", pos, thunk->action, ctx->text)); + thunk->action(ctx, ctx->text, yyleng); + } + ctx->thunkpos= 0; +} + +YY_LOCAL(void) yyCommit(yycontext *ctx) +{ + if ((ctx->limit -= ctx->pos)) + { + memmove(ctx->buf, ctx->buf + ctx->pos, ctx->limit); + } + ctx->begin -= ctx->pos; + ctx->end -= ctx->pos; + ctx->pos= ctx->thunkpos= 0; +} + +YY_LOCAL(int) yyAccept(yycontext *ctx, int tp0) +{ + if (tp0) + { + fprintf(stderr, "accept denied at %d\n", tp0); + return 0; + } + else + { + yyDone(ctx); + yyCommit(ctx); + } + return 1; +} + +YY_LOCAL(void) yyPush(yycontext *ctx, char *text, int count) { ctx->val += count; } +YY_LOCAL(void) yyPop(yycontext *ctx, char *text, int count) { ctx->val -= count; } +YY_LOCAL(void) yySet(yycontext *ctx, char *text, int count) { ctx->val[count]= ctx->yy; } + +#endif /* YY_PART */ + +#define YYACCEPT yyAccept(ctx, yythunkpos0) + +YY_RULE(int) yy_end_of_line(yycontext *ctx); /* 36 */ +YY_RULE(int) yy_comment(yycontext *ctx); /* 35 */ +YY_RULE(int) yy_space(yycontext *ctx); /* 34 */ +YY_RULE(int) yy_braces(yycontext *ctx); /* 33 */ +YY_RULE(int) yy_range(yycontext *ctx); /* 32 */ +YY_RULE(int) yy_char(yycontext *ctx); /* 31 */ +YY_RULE(int) yy_END(yycontext *ctx); /* 30 */ +YY_RULE(int) yy_BEGIN(yycontext *ctx); /* 29 */ +YY_RULE(int) yy_DOT(yycontext *ctx); /* 28 */ +YY_RULE(int) yy_class(yycontext *ctx); /* 27 */ +YY_RULE(int) yy_literal(yycontext *ctx); /* 26 */ +YY_RULE(int) yy_CLOSE(yycontext *ctx); /* 25 */ +YY_RULE(int) yy_OPEN(yycontext *ctx); /* 24 */ +YY_RULE(int) yy_COLON(yycontext *ctx); /* 23 */ +YY_RULE(int) yy_PLUS(yycontext *ctx); /* 22 */ +YY_RULE(int) yy_STAR(yycontext *ctx); /* 21 */ +YY_RULE(int) yy_QUESTION(yycontext *ctx); /* 20 */ +YY_RULE(int) yy_primary(yycontext *ctx); /* 19 */ +YY_RULE(int) yy_NOT(yycontext *ctx); /* 18 */ +YY_RULE(int) yy_suffix(yycontext *ctx); /* 17 */ +YY_RULE(int) yy_action(yycontext *ctx); /* 16 */ +YY_RULE(int) yy_AND(yycontext *ctx); /* 15 */ +YY_RULE(int) yy_prefix(yycontext *ctx); /* 14 */ +YY_RULE(int) yy_BAR(yycontext *ctx); /* 13 */ +YY_RULE(int) yy_sequence(yycontext *ctx); /* 12 */ +YY_RULE(int) yy_SEMICOLON(yycontext *ctx); /* 11 */ +YY_RULE(int) yy_expression(yycontext *ctx); /* 10 */ +YY_RULE(int) yy_EQUAL(yycontext *ctx); /* 9 */ +YY_RULE(int) yy_identifier(yycontext *ctx); /* 8 */ +YY_RULE(int) yy_RPERCENT(yycontext *ctx); /* 7 */ +YY_RULE(int) yy_end_of_file(yycontext *ctx); /* 6 */ +YY_RULE(int) yy_trailer(yycontext *ctx); /* 5 */ +YY_RULE(int) yy_definition(yycontext *ctx); /* 4 */ +YY_RULE(int) yy_declaration(yycontext *ctx); /* 3 */ +YY_RULE(int) yy__(yycontext *ctx); /* 2 */ +YY_RULE(int) yy_grammar(yycontext *ctx); /* 1 */ + +YY_ACTION(void) yy_9_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_9_primary\n")); + push(makePredicate("YY_END")); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_8_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_8_primary\n")); + push(makePredicate("YY_BEGIN")); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_7_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_7_primary\n")); + push(makeAction(yytext)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_6_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_6_primary\n")); + push(makeDot()); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_5_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_5_primary\n")); + push(makeClass(yytext)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_4_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_4_primary\n")); + push(makeString(yytext)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_3_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_3_primary\n")); + push(makeName(findRule(yytext))); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_2_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + Node *name; + yyprintf((stderr, "do yy_2_primary\n")); + name= makeName(findRule(yytext)); name->name.variable= pop(); push(name); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_primary(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_1_primary\n")); + push(makeVariable(yytext)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_3_suffix(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_3_suffix\n")); + push(makePlus (pop())); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_2_suffix(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_2_suffix\n")); + push(makeStar (pop())); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_suffix(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_1_suffix\n")); + push(makeQuery(pop())); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_3_prefix(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_3_prefix\n")); + push(makePeekNot(pop())); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_2_prefix(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_2_prefix\n")); + push(makePeekFor(pop())); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_prefix(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_1_prefix\n")); + push(makePredicate(yytext)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_sequence(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + Node *f; + yyprintf((stderr, "do yy_1_sequence\n")); + f= pop(); push(Sequence_append(pop(), f)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_expression(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + Node *f; + yyprintf((stderr, "do yy_1_expression\n")); + f= pop(); push(Alternate_append(pop(), f)); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_2_definition(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + Node *e; + yyprintf((stderr, "do yy_2_definition\n")); + e= pop(); Rule_setExpression(pop(), e); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_definition(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_1_definition\n")); + if (push(beginRule(findRule(yytext)))->rule.expression) + fprintf(stderr, "rule '%s' redefined\n", yytext); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_trailer(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_1_trailer\n")); + makeTrailer(yytext); ; +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_declaration(yycontext *ctx, char *yytext, int yyleng) +{ +#define yy ctx->yy +#define yypos ctx->pos +#define yythunkpos ctx->thunkpos + yyprintf((stderr, "do yy_1_declaration\n")); + makeHeader(yytext); ; +#undef yythunkpos +#undef yypos +#undef yy +} + +YY_RULE(int) yy_end_of_line(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "end_of_line")); + { int yypos2= ctx->pos, yythunkpos2= ctx->thunkpos; if (!yymatchString(ctx, "\r\n")) goto l3; goto l2; + l3:; ctx->pos= yypos2; ctx->thunkpos= yythunkpos2; if (!yymatchChar(ctx, '\n')) goto l4; goto l2; + l4:; ctx->pos= yypos2; ctx->thunkpos= yythunkpos2; if (!yymatchChar(ctx, '\r')) goto l1; + } + l2:; + yyprintf((stderr, " ok %s @ %s\n", "end_of_line", ctx->buf+ctx->pos)); + return 1; + l1:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "end_of_line", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_comment(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "comment")); if (!yymatchChar(ctx, '#')) goto l5; + l6:; + { int yypos7= ctx->pos, yythunkpos7= ctx->thunkpos; + { int yypos8= ctx->pos, yythunkpos8= ctx->thunkpos; if (!yy_end_of_line(ctx)) goto l8; goto l7; + l8:; ctx->pos= yypos8; ctx->thunkpos= yythunkpos8; + } if (!yymatchDot(ctx)) goto l7; goto l6; + l7:; ctx->pos= yypos7; ctx->thunkpos= yythunkpos7; + } if (!yy_end_of_line(ctx)) goto l5; + yyprintf((stderr, " ok %s @ %s\n", "comment", ctx->buf+ctx->pos)); + return 1; + l5:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "comment", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_space(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "space")); + { int yypos10= ctx->pos, yythunkpos10= ctx->thunkpos; if (!yymatchChar(ctx, ' ')) goto l11; goto l10; + l11:; ctx->pos= yypos10; ctx->thunkpos= yythunkpos10; if (!yymatchChar(ctx, '\t')) goto l12; goto l10; + l12:; ctx->pos= yypos10; ctx->thunkpos= yythunkpos10; if (!yy_end_of_line(ctx)) goto l9; + } + l10:; + yyprintf((stderr, " ok %s @ %s\n", "space", ctx->buf+ctx->pos)); + return 1; + l9:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "space", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_braces(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "braces")); + { int yypos14= ctx->pos, yythunkpos14= ctx->thunkpos; if (!yymatchChar(ctx, '{')) goto l15; + l16:; + { int yypos17= ctx->pos, yythunkpos17= ctx->thunkpos; if (!yy_braces(ctx)) goto l17; goto l16; + l17:; ctx->pos= yypos17; ctx->thunkpos= yythunkpos17; + } if (!yymatchChar(ctx, '}')) goto l15; goto l14; + l15:; ctx->pos= yypos14; ctx->thunkpos= yythunkpos14; + { int yypos18= ctx->pos, yythunkpos18= ctx->thunkpos; if (!yymatchChar(ctx, '}')) goto l18; goto l13; + l18:; ctx->pos= yypos18; ctx->thunkpos= yythunkpos18; + } if (!yymatchDot(ctx)) goto l13; + } + l14:; + yyprintf((stderr, " ok %s @ %s\n", "braces", ctx->buf+ctx->pos)); + return 1; + l13:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "braces", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_range(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "range")); + { int yypos20= ctx->pos, yythunkpos20= ctx->thunkpos; if (!yy_char(ctx)) goto l21; if (!yymatchChar(ctx, '-')) goto l21; if (!yy_char(ctx)) goto l21; goto l20; + l21:; ctx->pos= yypos20; ctx->thunkpos= yythunkpos20; if (!yy_char(ctx)) goto l19; + } + l20:; + yyprintf((stderr, " ok %s @ %s\n", "range", ctx->buf+ctx->pos)); + return 1; + l19:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "range", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_char(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "char")); + { int yypos23= ctx->pos, yythunkpos23= ctx->thunkpos; if (!yymatchChar(ctx, '\\')) goto l24; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\204\040\000\000\000\000\000\070\146\100\124\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l24; goto l23; + l24:; ctx->pos= yypos23; ctx->thunkpos= yythunkpos23; if (!yymatchChar(ctx, '\\')) goto l25; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\017\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l25; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l25; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l25; goto l23; + l25:; ctx->pos= yypos23; ctx->thunkpos= yythunkpos23; if (!yymatchChar(ctx, '\\')) goto l26; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l26; + { int yypos27= ctx->pos, yythunkpos27= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\000\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l27; goto l28; + l27:; ctx->pos= yypos27; ctx->thunkpos= yythunkpos27; + } + l28:; goto l23; + l26:; ctx->pos= yypos23; ctx->thunkpos= yythunkpos23; + { int yypos29= ctx->pos, yythunkpos29= ctx->thunkpos; if (!yymatchChar(ctx, '\\')) goto l29; goto l22; + l29:; ctx->pos= yypos29; ctx->thunkpos= yythunkpos29; + } if (!yymatchDot(ctx)) goto l22; + } + l23:; + yyprintf((stderr, " ok %s @ %s\n", "char", ctx->buf+ctx->pos)); + return 1; + l22:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "char", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_END(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "END")); if (!yymatchChar(ctx, '>')) goto l30; if (!yy__(ctx)) goto l30; + yyprintf((stderr, " ok %s @ %s\n", "END", ctx->buf+ctx->pos)); + return 1; + l30:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "END", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_BEGIN(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "BEGIN")); if (!yymatchChar(ctx, '<')) goto l31; if (!yy__(ctx)) goto l31; + yyprintf((stderr, " ok %s @ %s\n", "BEGIN", ctx->buf+ctx->pos)); + return 1; + l31:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "BEGIN", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_DOT(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "DOT")); if (!yymatchChar(ctx, '.')) goto l32; if (!yy__(ctx)) goto l32; + yyprintf((stderr, " ok %s @ %s\n", "DOT", ctx->buf+ctx->pos)); + return 1; + l32:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "DOT", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_class(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "class")); if (!yymatchChar(ctx, '[')) goto l33; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l33; + l34:; + { int yypos35= ctx->pos, yythunkpos35= ctx->thunkpos; + { int yypos36= ctx->pos, yythunkpos36= ctx->thunkpos; if (!yymatchChar(ctx, ']')) goto l36; goto l35; + l36:; ctx->pos= yypos36; ctx->thunkpos= yythunkpos36; + } if (!yy_range(ctx)) goto l35; goto l34; + l35:; ctx->pos= yypos35; ctx->thunkpos= yythunkpos35; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l33; if (!yymatchChar(ctx, ']')) goto l33; if (!yy__(ctx)) goto l33; + yyprintf((stderr, " ok %s @ %s\n", "class", ctx->buf+ctx->pos)); + return 1; + l33:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "class", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_literal(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "literal")); + { int yypos38= ctx->pos, yythunkpos38= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l39; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l39; + l40:; + { int yypos41= ctx->pos, yythunkpos41= ctx->thunkpos; + { int yypos42= ctx->pos, yythunkpos42= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l42; goto l41; + l42:; ctx->pos= yypos42; ctx->thunkpos= yythunkpos42; + } if (!yy_char(ctx)) goto l41; goto l40; + l41:; ctx->pos= yypos41; ctx->thunkpos= yythunkpos41; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l39; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\200\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l39; if (!yy__(ctx)) goto l39; goto l38; + l39:; ctx->pos= yypos38; ctx->thunkpos= yythunkpos38; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l37; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l37; + l43:; + { int yypos44= ctx->pos, yythunkpos44= ctx->thunkpos; + { int yypos45= ctx->pos, yythunkpos45= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l45; goto l44; + l45:; ctx->pos= yypos45; ctx->thunkpos= yythunkpos45; + } if (!yy_char(ctx)) goto l44; goto l43; + l44:; ctx->pos= yypos44; ctx->thunkpos= yythunkpos44; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l37; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l37; if (!yy__(ctx)) goto l37; + } + l38:; + yyprintf((stderr, " ok %s @ %s\n", "literal", ctx->buf+ctx->pos)); + return 1; + l37:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "literal", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_CLOSE(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "CLOSE")); if (!yymatchChar(ctx, ')')) goto l46; if (!yy__(ctx)) goto l46; + yyprintf((stderr, " ok %s @ %s\n", "CLOSE", ctx->buf+ctx->pos)); + return 1; + l46:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "CLOSE", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_OPEN(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "OPEN")); if (!yymatchChar(ctx, '(')) goto l47; if (!yy__(ctx)) goto l47; + yyprintf((stderr, " ok %s @ %s\n", "OPEN", ctx->buf+ctx->pos)); + return 1; + l47:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "OPEN", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_COLON(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "COLON")); if (!yymatchChar(ctx, ':')) goto l48; if (!yy__(ctx)) goto l48; + yyprintf((stderr, " ok %s @ %s\n", "COLON", ctx->buf+ctx->pos)); + return 1; + l48:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "COLON", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_PLUS(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "PLUS")); if (!yymatchChar(ctx, '+')) goto l49; if (!yy__(ctx)) goto l49; + yyprintf((stderr, " ok %s @ %s\n", "PLUS", ctx->buf+ctx->pos)); + return 1; + l49:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "PLUS", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_STAR(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "STAR")); if (!yymatchChar(ctx, '*')) goto l50; if (!yy__(ctx)) goto l50; + yyprintf((stderr, " ok %s @ %s\n", "STAR", ctx->buf+ctx->pos)); + return 1; + l50:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "STAR", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_QUESTION(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "QUESTION")); if (!yymatchChar(ctx, '?')) goto l51; if (!yy__(ctx)) goto l51; + yyprintf((stderr, " ok %s @ %s\n", "QUESTION", ctx->buf+ctx->pos)); + return 1; + l51:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "QUESTION", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_primary(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "primary")); + { int yypos53= ctx->pos, yythunkpos53= ctx->thunkpos; if (!yy_identifier(ctx)) goto l54; yyDo(ctx, yy_1_primary, ctx->begin, ctx->end); if (!yy_COLON(ctx)) goto l54; if (!yy_identifier(ctx)) goto l54; + { int yypos55= ctx->pos, yythunkpos55= ctx->thunkpos; if (!yy_EQUAL(ctx)) goto l55; goto l54; + l55:; ctx->pos= yypos55; ctx->thunkpos= yythunkpos55; + } yyDo(ctx, yy_2_primary, ctx->begin, ctx->end); goto l53; + l54:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_identifier(ctx)) goto l56; + { int yypos57= ctx->pos, yythunkpos57= ctx->thunkpos; if (!yy_EQUAL(ctx)) goto l57; goto l56; + l57:; ctx->pos= yypos57; ctx->thunkpos= yythunkpos57; + } yyDo(ctx, yy_3_primary, ctx->begin, ctx->end); goto l53; + l56:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_OPEN(ctx)) goto l58; if (!yy_expression(ctx)) goto l58; if (!yy_CLOSE(ctx)) goto l58; goto l53; + l58:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_literal(ctx)) goto l59; yyDo(ctx, yy_4_primary, ctx->begin, ctx->end); goto l53; + l59:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_class(ctx)) goto l60; yyDo(ctx, yy_5_primary, ctx->begin, ctx->end); goto l53; + l60:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_DOT(ctx)) goto l61; yyDo(ctx, yy_6_primary, ctx->begin, ctx->end); goto l53; + l61:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_action(ctx)) goto l62; yyDo(ctx, yy_7_primary, ctx->begin, ctx->end); goto l53; + l62:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_BEGIN(ctx)) goto l63; yyDo(ctx, yy_8_primary, ctx->begin, ctx->end); goto l53; + l63:; ctx->pos= yypos53; ctx->thunkpos= yythunkpos53; if (!yy_END(ctx)) goto l52; yyDo(ctx, yy_9_primary, ctx->begin, ctx->end); + } + l53:; + yyprintf((stderr, " ok %s @ %s\n", "primary", ctx->buf+ctx->pos)); + return 1; + l52:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "primary", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_NOT(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "NOT")); if (!yymatchChar(ctx, '!')) goto l64; if (!yy__(ctx)) goto l64; + yyprintf((stderr, " ok %s @ %s\n", "NOT", ctx->buf+ctx->pos)); + return 1; + l64:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "NOT", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_suffix(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "suffix")); if (!yy_primary(ctx)) goto l65; + { int yypos66= ctx->pos, yythunkpos66= ctx->thunkpos; + { int yypos68= ctx->pos, yythunkpos68= ctx->thunkpos; if (!yy_QUESTION(ctx)) goto l69; yyDo(ctx, yy_1_suffix, ctx->begin, ctx->end); goto l68; + l69:; ctx->pos= yypos68; ctx->thunkpos= yythunkpos68; if (!yy_STAR(ctx)) goto l70; yyDo(ctx, yy_2_suffix, ctx->begin, ctx->end); goto l68; + l70:; ctx->pos= yypos68; ctx->thunkpos= yythunkpos68; if (!yy_PLUS(ctx)) goto l66; yyDo(ctx, yy_3_suffix, ctx->begin, ctx->end); + } + l68:; goto l67; + l66:; ctx->pos= yypos66; ctx->thunkpos= yythunkpos66; + } + l67:; + yyprintf((stderr, " ok %s @ %s\n", "suffix", ctx->buf+ctx->pos)); + return 1; + l65:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "suffix", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_action(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "action")); if (!yymatchChar(ctx, '{')) goto l71; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l71; + l72:; + { int yypos73= ctx->pos, yythunkpos73= ctx->thunkpos; if (!yy_braces(ctx)) goto l73; goto l72; + l73:; ctx->pos= yypos73; ctx->thunkpos= yythunkpos73; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l71; if (!yymatchChar(ctx, '}')) goto l71; if (!yy__(ctx)) goto l71; + yyprintf((stderr, " ok %s @ %s\n", "action", ctx->buf+ctx->pos)); + return 1; + l71:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "action", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_AND(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "AND")); if (!yymatchChar(ctx, '&')) goto l74; if (!yy__(ctx)) goto l74; + yyprintf((stderr, " ok %s @ %s\n", "AND", ctx->buf+ctx->pos)); + return 1; + l74:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "AND", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_prefix(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "prefix")); + { int yypos76= ctx->pos, yythunkpos76= ctx->thunkpos; if (!yy_AND(ctx)) goto l77; if (!yy_action(ctx)) goto l77; yyDo(ctx, yy_1_prefix, ctx->begin, ctx->end); goto l76; + l77:; ctx->pos= yypos76; ctx->thunkpos= yythunkpos76; if (!yy_AND(ctx)) goto l78; if (!yy_suffix(ctx)) goto l78; yyDo(ctx, yy_2_prefix, ctx->begin, ctx->end); goto l76; + l78:; ctx->pos= yypos76; ctx->thunkpos= yythunkpos76; if (!yy_NOT(ctx)) goto l79; if (!yy_suffix(ctx)) goto l79; yyDo(ctx, yy_3_prefix, ctx->begin, ctx->end); goto l76; + l79:; ctx->pos= yypos76; ctx->thunkpos= yythunkpos76; if (!yy_suffix(ctx)) goto l75; + } + l76:; + yyprintf((stderr, " ok %s @ %s\n", "prefix", ctx->buf+ctx->pos)); + return 1; + l75:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "prefix", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_BAR(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "BAR")); if (!yymatchChar(ctx, '|')) goto l80; if (!yy__(ctx)) goto l80; + yyprintf((stderr, " ok %s @ %s\n", "BAR", ctx->buf+ctx->pos)); + return 1; + l80:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "BAR", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_sequence(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "sequence")); if (!yy_prefix(ctx)) goto l81; + l82:; + { int yypos83= ctx->pos, yythunkpos83= ctx->thunkpos; if (!yy_prefix(ctx)) goto l83; yyDo(ctx, yy_1_sequence, ctx->begin, ctx->end); goto l82; + l83:; ctx->pos= yypos83; ctx->thunkpos= yythunkpos83; + } + yyprintf((stderr, " ok %s @ %s\n", "sequence", ctx->buf+ctx->pos)); + return 1; + l81:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "sequence", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_SEMICOLON(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "SEMICOLON")); if (!yymatchChar(ctx, ';')) goto l84; if (!yy__(ctx)) goto l84; + yyprintf((stderr, " ok %s @ %s\n", "SEMICOLON", ctx->buf+ctx->pos)); + return 1; + l84:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "SEMICOLON", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_expression(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "expression")); if (!yy_sequence(ctx)) goto l85; + l86:; + { int yypos87= ctx->pos, yythunkpos87= ctx->thunkpos; if (!yy_BAR(ctx)) goto l87; if (!yy_sequence(ctx)) goto l87; yyDo(ctx, yy_1_expression, ctx->begin, ctx->end); goto l86; + l87:; ctx->pos= yypos87; ctx->thunkpos= yythunkpos87; + } + yyprintf((stderr, " ok %s @ %s\n", "expression", ctx->buf+ctx->pos)); + return 1; + l85:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "expression", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_EQUAL(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "EQUAL")); if (!yymatchChar(ctx, '=')) goto l88; if (!yy__(ctx)) goto l88; + yyprintf((stderr, " ok %s @ %s\n", "EQUAL", ctx->buf+ctx->pos)); + return 1; + l88:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "EQUAL", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_identifier(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "identifier")); yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l89; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\040\000\000\376\377\377\207\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l89; + l90:; + { int yypos91= ctx->pos, yythunkpos91= ctx->thunkpos; if (!yymatchClass(ctx, (unsigned char *)"\000\000\000\000\000\040\377\003\376\377\377\207\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l91; goto l90; + l91:; ctx->pos= yypos91; ctx->thunkpos= yythunkpos91; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l89; if (!yy__(ctx)) goto l89; + yyprintf((stderr, " ok %s @ %s\n", "identifier", ctx->buf+ctx->pos)); + return 1; + l89:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "identifier", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_RPERCENT(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "RPERCENT")); if (!yymatchString(ctx, "%}")) goto l92; if (!yy__(ctx)) goto l92; + yyprintf((stderr, " ok %s @ %s\n", "RPERCENT", ctx->buf+ctx->pos)); + return 1; + l92:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "RPERCENT", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_end_of_file(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "end_of_file")); + { int yypos94= ctx->pos, yythunkpos94= ctx->thunkpos; if (!yymatchDot(ctx)) goto l94; goto l93; + l94:; ctx->pos= yypos94; ctx->thunkpos= yythunkpos94; + } + yyprintf((stderr, " ok %s @ %s\n", "end_of_file", ctx->buf+ctx->pos)); + return 1; + l93:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "end_of_file", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_trailer(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "trailer")); if (!yymatchString(ctx, "%%")) goto l95; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l95; + l96:; + { int yypos97= ctx->pos, yythunkpos97= ctx->thunkpos; if (!yymatchDot(ctx)) goto l97; goto l96; + l97:; ctx->pos= yypos97; ctx->thunkpos= yythunkpos97; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l95; yyDo(ctx, yy_1_trailer, ctx->begin, ctx->end); + yyprintf((stderr, " ok %s @ %s\n", "trailer", ctx->buf+ctx->pos)); + return 1; + l95:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "trailer", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_definition(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "definition")); if (!yy_identifier(ctx)) goto l98; yyDo(ctx, yy_1_definition, ctx->begin, ctx->end); if (!yy_EQUAL(ctx)) goto l98; if (!yy_expression(ctx)) goto l98; yyDo(ctx, yy_2_definition, ctx->begin, ctx->end); + { int yypos99= ctx->pos, yythunkpos99= ctx->thunkpos; if (!yy_SEMICOLON(ctx)) goto l99; goto l100; + l99:; ctx->pos= yypos99; ctx->thunkpos= yythunkpos99; + } + l100:; + yyprintf((stderr, " ok %s @ %s\n", "definition", ctx->buf+ctx->pos)); + return 1; + l98:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "definition", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy_declaration(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "declaration")); if (!yymatchString(ctx, "%{")) goto l101; yyText(ctx, ctx->begin, ctx->end); if (!(YY_BEGIN)) goto l101; + l102:; + { int yypos103= ctx->pos, yythunkpos103= ctx->thunkpos; + { int yypos104= ctx->pos, yythunkpos104= ctx->thunkpos; if (!yymatchString(ctx, "%}")) goto l104; goto l103; + l104:; ctx->pos= yypos104; ctx->thunkpos= yythunkpos104; + } if (!yymatchDot(ctx)) goto l103; goto l102; + l103:; ctx->pos= yypos103; ctx->thunkpos= yythunkpos103; + } yyText(ctx, ctx->begin, ctx->end); if (!(YY_END)) goto l101; if (!yy_RPERCENT(ctx)) goto l101; yyDo(ctx, yy_1_declaration, ctx->begin, ctx->end); + yyprintf((stderr, " ok %s @ %s\n", "declaration", ctx->buf+ctx->pos)); + return 1; + l101:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "declaration", ctx->buf+ctx->pos)); + return 0; +} +YY_RULE(int) yy__(yycontext *ctx) +{ + yyprintf((stderr, "%s\n", "_")); + l106:; + { int yypos107= ctx->pos, yythunkpos107= ctx->thunkpos; + { int yypos108= ctx->pos, yythunkpos108= ctx->thunkpos; if (!yy_space(ctx)) goto l109; goto l108; + l109:; ctx->pos= yypos108; ctx->thunkpos= yythunkpos108; if (!yy_comment(ctx)) goto l107; + } + l108:; goto l106; + l107:; ctx->pos= yypos107; ctx->thunkpos= yythunkpos107; + } + yyprintf((stderr, " ok %s @ %s\n", "_", ctx->buf+ctx->pos)); + return 1; +} +YY_RULE(int) yy_grammar(yycontext *ctx) +{ int yypos0= ctx->pos, yythunkpos0= ctx->thunkpos; + yyprintf((stderr, "%s\n", "grammar")); if (!yy__(ctx)) goto l110; + { int yypos113= ctx->pos, yythunkpos113= ctx->thunkpos; if (!yy_declaration(ctx)) goto l114; goto l113; + l114:; ctx->pos= yypos113; ctx->thunkpos= yythunkpos113; if (!yy_definition(ctx)) goto l110; + } + l113:; + l111:; + { int yypos112= ctx->pos, yythunkpos112= ctx->thunkpos; + { int yypos115= ctx->pos, yythunkpos115= ctx->thunkpos; if (!yy_declaration(ctx)) goto l116; goto l115; + l116:; ctx->pos= yypos115; ctx->thunkpos= yythunkpos115; if (!yy_definition(ctx)) goto l112; + } + l115:; goto l111; + l112:; ctx->pos= yypos112; ctx->thunkpos= yythunkpos112; + } + { int yypos117= ctx->pos, yythunkpos117= ctx->thunkpos; if (!yy_trailer(ctx)) goto l117; goto l118; + l117:; ctx->pos= yypos117; ctx->thunkpos= yythunkpos117; + } + l118:; if (!yy_end_of_file(ctx)) goto l110; + yyprintf((stderr, " ok %s @ %s\n", "grammar", ctx->buf+ctx->pos)); + return 1; + l110:; ctx->pos= yypos0; ctx->thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "grammar", ctx->buf+ctx->pos)); + return 0; +} + +#ifndef YY_PART + +typedef int (*yyrule)(yycontext *ctx); + +YY_PARSE(int) YYPARSEFROM(YY_CTX_PARAM_ yyrule yystart) +{ + int yyok; + if (!yyctx->buflen) + { + yyctx->buflen= 1024; + yyctx->buf= (char *)malloc(yyctx->buflen); + yyctx->textlen= 1024; + yyctx->text= (char *)malloc(yyctx->textlen); + yyctx->thunkslen= 32; + yyctx->thunks= (yythunk *)malloc(sizeof(yythunk) * yyctx->thunkslen); + yyctx->valslen= 32; + yyctx->vals= (YYSTYPE *)malloc(sizeof(YYSTYPE) * yyctx->valslen); + yyctx->begin= yyctx->end= yyctx->pos= yyctx->limit= yyctx->thunkpos= 0; + } + yyctx->begin= yyctx->end= yyctx->pos; + yyctx->thunkpos= 0; + yyctx->val= yyctx->vals; + yyok= yystart(yyctx); + if (yyok) yyDone(yyctx); + yyCommit(yyctx); + return yyok; +} + +YY_PARSE(int) YYPARSE(YY_CTX_PARAM) +{ + return YYPARSEFROM(YY_CTX_ARG_ yy_grammar); +} + +#endif + + +void yyerror(char *message) +{ + fprintf(stderr, "%s:%d: %s", fileName, lineNumber, message); + if (yyctx->text[0]) fprintf(stderr, " near token '%s'", yyctx->text); + if (yyctx->pos < yyctx->limit || !feof(input)) + { + yyctx->buf[yyctx->limit]= '\0'; + fprintf(stderr, " before text \""); + while (yyctx->pos < yyctx->limit) + { + if ('\n' == yyctx->buf[yyctx->pos] || '\r' == yyctx->buf[yyctx->pos]) break; + fputc(yyctx->buf[yyctx->pos++], stderr); + } + if (yyctx->pos == yyctx->limit) + { + int c; + while (EOF != (c= fgetc(input)) && '\n' != c && '\r' != c) + fputc(c, stderr); + } + fputc('\"', stderr); + } + fprintf(stderr, "\n"); + exit(1); +} + +void makeHeader(char *text) +{ + Header *header= (Header *)malloc(sizeof(Header)); + header->text= strdup(text); + header->next= headers; + headers= header; +} + +void makeTrailer(char *text) +{ + trailer= strdup(text); +} + +static void version(char *name) +{ + printf("%s version %d.%d.%d\n", name, PEG_MAJOR, PEG_MINOR, PEG_LEVEL); +} + +static void usage(char *name) +{ + version(name); + fprintf(stderr, "usage: %s [contents.link->url, obfuscate); + g_string_append_printf(out, "\""); + if (strlen(elt->contents.link->title) > 0) { + g_string_append_printf(out, " title=\""); + print_html_string(out, elt->contents.link->title, obfuscate); + g_string_append_printf(out, "\""); + } + print_html_element_list(out, elt->contents.link->attr, obfuscate); + g_string_append_printf(out, ">"); + print_html_element_list(out, elt->contents.link->label, obfuscate); + g_string_append_printf(out, ""); + break; + case IMAGEBLOCK: + pad(out, 2); + case IMAGE: + if (elt->key == IMAGEBLOCK) { + g_string_append_printf(out, "
    \n"); + } + g_string_append_printf(out, "contents.link->url, obfuscate); + g_string_append_printf(out, "\" alt=\""); + print_raw_element_list(out,elt->contents.link->label); + if ( (extension(EXT_COMPATIBILITY)) || + (strcmp(elt->contents.link->identifier, "") == 0) ) { + g_string_append_printf(out, "\""); + } else { + if (!(extension(EXT_COMPATIBILITY))) { + g_string_append_printf(out, "\" id=\"%s\"",elt->contents.link->identifier); + } + } + if (strlen(elt->contents.link->title) > 0) { + g_string_append_printf(out, " title=\""); + print_html_string(out, elt->contents.link->title, obfuscate); + g_string_append_printf(out, "\""); + } + width = NULL; + height = NULL; + attribute = element_for_attribute("height", elt->contents.link->attr); + if (attribute != NULL) { + height = strdup(attribute->children->contents.str); + } + attribute = element_for_attribute("width", elt->contents.link->attr); + if (attribute != NULL) { + width = strdup(attribute->children->contents.str); + } + if ((height != NULL) || (width != NULL)) { + g_string_append_printf(out, " style=\""); + if (height != NULL) + g_string_append_printf(out, "height:%s;", height); + if (width != NULL) + g_string_append_printf(out, "width:%s;", width); + g_string_append_printf(out, "\""); + } + print_html_element_list(out, elt->contents.link->attr, obfuscate); + g_string_append_printf(out, " />"); + if (elt->key == IMAGEBLOCK) { + if (elt->contents.link->label != NULL) { + g_string_append_printf(out, "\n
    "); + print_html_element_list(out, elt->contents.link->label, obfuscate); + g_string_append_printf(out, "
    "); + } + g_string_append_printf(out, "
    \n"); + } + free(height); + free(width); + break; + case EMPH: + g_string_append_printf(out, ""); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, ""); + break; + case STRONG: + g_string_append_printf(out, ""); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, ""); + break; + case LIST: + print_html_element_list(out, elt->children, obfuscate); + break; + case RAW: + /* Shouldn't occur - these are handled by process_raw_blocks() */ + assert(elt->key != RAW); + break; + case H1: case H2: case H3: case H4: case H5: case H6: + lev = elt->key - H1 + base_header_level; /* assumes H1 ... H6 are in order */ + if (lev > 6) + lev = 6; + pad(out, 2); + if ( extension(EXT_COMPATIBILITY)) { + /* Use regular Markdown header format */ + g_string_append_printf(out, "", lev); + print_html_element_list(out, elt->children, obfuscate); + } else if (elt->children->key == AUTOLABEL) { + /* use label for header since one was specified (MMD)*/ + g_string_append_printf(out, "", lev,elt->children->contents.str); + print_html_element_list(out, elt->children->next, obfuscate); + } else if ( extension(EXT_NO_LABELS)) { + /* Don't generate a label */ + g_string_append_printf(out, "", lev); + print_html_element_list(out, elt->children, obfuscate); + } else { + /* generate a label by default for MMD */ + label = label_from_element_list(elt->children, obfuscate); + g_string_append_printf(out, "", lev, label); + print_html_element_list(out, elt->children, obfuscate); + free(label); + } + g_string_append_printf(out, "", lev); + padded = 0; + break; + case PLAIN: + pad(out, 1); + print_html_element_list(out, elt->children, obfuscate); + padded = 0; + break; + case PARA: + pad(out, 2); + g_string_append_printf(out, "

    "); + print_html_element_list(out, elt->children, obfuscate); + if (am_printing_html_footnote && ( elt->next == NULL)) { + g_string_append_printf(out, "  ↩", footnote_counter_to_print); + /* Only print once. For now, it's the first paragraph, until + I can figure out to make it the last paragraph */ + am_printing_html_footnote = FALSE; + } + g_string_append_printf(out, "

    "); + padded = 0; + break; + case HRULE: + pad(out, 2); + g_string_append_printf(out, "
    "); + padded = 0; + break; + case HTMLBLOCK: + pad(out, 2); + g_string_append_printf(out, "%s", elt->contents.str); + padded = 0; + break; + case VERBATIM: + pad(out, 2); + g_string_append_printf(out, "%s", "
    ");
    +        print_html_string(out, elt->contents.str, obfuscate);
    +        g_string_append_printf(out, "%s", "
    "); + padded = 0; + break; + case BULLETLIST: + pad(out, 2); + g_string_append_printf(out, "%s", "
      "); + padded = 0; + print_html_element_list(out, elt->children, obfuscate); + pad(out, 1); + g_string_append_printf(out, "%s", "
    "); + padded = 0; + break; + case ORDEREDLIST: + pad(out, 2); + g_string_append_printf(out, "%s", "
      "); + padded = 0; + print_html_element_list(out, elt->children, obfuscate); + pad(out, 1); + g_string_append_printf(out, "
    "); + padded = 0; + break; + case LISTITEM: + pad(out, 1); + g_string_append_printf(out, "
  10. "); + padded = 2; + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "
  11. "); + padded = 0; + break; + case BLOCKQUOTE: + pad(out, 2); + g_string_append_printf(out, "
    \n"); + padded = 2; + print_html_element_list(out, elt->children, obfuscate); + pad(out, 1); + g_string_append_printf(out, "
    "); + padded = 0; + break; + case REFERENCE: + /* Nonprinting */ + break; + case NOTELABEL: + /* Nonprinting */ + break; + case NOTE: + /* if contents.str == 0, then print note; else ignore, since this + * is a note block that has been incorporated into the notes list */ + if (elt->contents.str == 0) { + if (elt->children->contents.str == 0) { + /* The referenced note has not been used before */ + add_endnote(elt->children); + ++notenumber; + sprintf(buf,"%d",notenumber); + /* Assign footnote number for future use */ + elt->children->contents.str = strdup(buf); + if (elt->children->key == GLOSSARYTERM) { + g_string_append_printf(out, "[%d]", + notenumber, notenumber, notenumber); + } else { + g_string_append_printf(out, "[%d]", + notenumber, notenumber, notenumber); + } + } else { + /* The referenced note has already been used */ + g_string_append_printf(out, "[%s]", + elt->children->contents.str, elt->children->contents.str); + } + } + elt->children = NULL; + break; + case GLOSSARY: + /* Shouldn't do anything */ + break; + case GLOSSARYTERM: + g_string_append_printf(out,""); + print_html_string(out, elt->children->contents.str, obfuscate); + g_string_append_printf(out, ""); + if ((elt->next != NULL) && (elt->next->key == GLOSSARYSORTKEY) ) { + g_string_append_printf(out, ""); + print_html_string(out, elt->next->contents.str, obfuscate); + g_string_append_printf(out, ""); + } + g_string_append_printf(out, ": "); + break; + case GLOSSARYSORTKEY: + break; + case NOCITATION: + case CITATION: + /* Get locator, if present */ + locator = locator_for_citation(elt); + + if (strncmp(elt->contents.str,"[#",2) == 0) { + /* reference specified externally */ + if ( elt->key == NOCITATION ) { + /* work not cited, but used in bibliography for LaTeX */ + g_string_append_printf(out, "", elt->contents.str); + } else { + /* work was cited, so output normally */ + g_string_append_printf(out, ""); + if (locator != NULL) { + g_string_append_printf(out, "["); + print_html_element(out,locator,obfuscate); + g_string_append_printf(out, "]"); + } + g_string_append_printf(out, "%s",elt->contents.str); + g_string_append_printf(out, ""); + } + } else { + /* reference specified within the MMD document, + so will output as footnote */ + if (elt->children->contents.str == NULL) { + /* Work not previously cited in this document, + so create "endnote" */ + elt->children->key = CITATION; + add_endnote(elt->children); + ++notenumber; + sprintf(buf,"%d",notenumber); + /* Store the number for future reference */ + elt->children->contents.str = strdup(buf); + } + if (locator != NULL) { + if ( elt->key == NOCITATION ) { + g_string_append_printf(out, "", + elt->children->contents.str); + } else { + g_string_append_printf(out, "[", elt->children->contents.str); + print_html_element(out,locator,obfuscate); + g_string_append_printf(out,", %s]", + elt->children->contents.str); + } + } else { + g_string_append_printf(out, "[%s]", + elt->children->contents.str, elt->children->contents.str); + } + /* Now prune children since will likely be shared elsewhere */ + elt->children = NULL; + + g_string_append_printf(out, "%s", elt->contents.str); + if ((locator != NULL) && (elt->key == NOCITATION)) { + g_string_append_printf(out,""); + } else { + g_string_append_printf(out,""); + } + } + break; + case LOCATOR: + print_html_element_list(out, elt->children, obfuscate); + break; + case DEFLIST: + pad(out,1); + padded = 1; + g_string_append_printf(out, "
    \n"); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "
    \n"); + padded = 0; + break; + case TERM: + pad(out,1); + g_string_append_printf(out, "
    "); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "
    \n"); + padded = 1; + break; + case DEFINITION: + pad(out,1); + padded = 1; + g_string_append_printf(out, "
    "); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "
    \n"); + padded = 0; + break; + case METADATA: + /* Metadata is present, so this should be a "complete" document */ + html_footer = is_html_complete_doc(elt); + if (html_footer) { + print_html_header(out, elt, obfuscate); + } else { + print_html_element_list(out, elt->children, obfuscate); + } + break; + case METAKEY: + if (strcmp(elt->contents.str, "title") == 0) { + g_string_append_printf(out, "\t"); + print_html_element(out, elt->children, obfuscate); + g_string_append_printf(out, "\n"); + } else if (strcmp(elt->contents.str, "css") == 0) { + g_string_append_printf(out, "\tchildren, obfuscate); + g_string_append_printf(out, "\"/>\n"); + } else if (strcmp(elt->contents.str, "xhtmlheader") == 0) { + print_raw_element(out, elt->children); + g_string_append_printf(out, "\n"); + } else if (strcmp(elt->contents.str, "htmlheader") == 0) { + print_raw_element(out, elt->children); + g_string_append_printf(out, "\n"); + } else if (strcmp(elt->contents.str, "baseheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "xhtmlheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "htmlheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "quoteslanguage") == 0) { + label = label_from_element_list(elt->children, 0); + if (strcmp(label, "dutch") == 0) { language = DUTCH; } else + if (strcmp(label, "german") == 0) { language = GERMAN; } else + if (strcmp(label, "germanguillemets") == 0) { language = GERMANGUILL; } else + if (strcmp(label, "french") == 0) { language = FRENCH; } else + if (strcmp(label, "swedish") == 0) { language = SWEDISH; } + free(label); + } else { + g_string_append_printf(out, "\tcontents.str, obfuscate); + g_string_append_printf(out, "\" content=\""); + print_html_element(out, elt->children, obfuscate); + g_string_append_printf(out, "\"/>\n"); + } + break; + case METAVALUE: + print_html_string(out, elt->contents.str, obfuscate); + break; + case FOOTER: + break; + case HEADINGSECTION: + print_html_element_list(out, elt->children, obfuscate); + break; + case TABLE: + g_string_append_printf(out, "\n\n\n"); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "
    \n"); + break; + case TABLESEPARATOR: + table_alignment = elt->contents.str; + break; + case TABLECAPTION: + if (elt->children->key == TABLELABEL) { + label = label_from_element_list(elt->children->children,obfuscate); + } else { + label = label_from_element_list(elt->children,obfuscate); + } + g_string_append_printf(out, "", label); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "\n"); + free(label); + break; + case TABLELABEL: + break; + case TABLEHEAD: + /* print column alignment for XSLT processing if needed */ + g_string_append_printf(out, "\n"); + for (table_column=0;table_column\n"); + } else if ( strncmp(&table_alignment[table_column],"R",1) == 0) { + g_string_append_printf(out, "\n"); + } else if ( strncmp(&table_alignment[table_column],"c",1) == 0) { + g_string_append_printf(out, "\n"); + } else if ( strncmp(&table_alignment[table_column],"C",1) == 0) { + g_string_append_printf(out, "\n"); + } else if ( strncmp(&table_alignment[table_column],"L",1) == 0) { + g_string_append_printf(out, "\n"); + } else { + g_string_append_printf(out, "\n"); + } + } + g_string_append_printf(out, "\n"); + cell_type = 'h'; + g_string_append_printf(out, "\n\n"); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "\n"); + cell_type = 'd'; + break; + case TABLEBODY: + g_string_append_printf(out, "\n\n"); + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "\n"); + break; + case TABLEROW: + g_string_append_printf(out, "\n"); + table_column = 0; + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "\n"); + break; + case TABLECELL: + if ( strncmp(&table_alignment[table_column],"r",1) == 0) { + g_string_append_printf(out, "\tchildren != NULL) && (elt->children->key == CELLSPAN)) { + g_string_append_printf(out, " colspan=\"%d\"",(int)strlen(elt->children->contents.str)+1); + } + g_string_append_printf(out, ">"); + padded = 2; + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "\n", cell_type); + table_column++; + break; + case CELLSPAN: + break; + case ATTRKEY: + if ( (strcmp(elt->contents.str,"height") == 0) || + (strcmp(elt->contents.str, "width") == 0)) { + } else { + g_string_append_printf(out, " %s=\"%s\"", elt->contents.str, + elt->children->contents.str); + } + break; + case MATHSPAN: + if ( elt->contents.str[strlen(elt->contents.str)-1] == ']') { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s\\]", elt->contents.str); + } else { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s\\)", elt->contents.str); + } + break; + default: + fprintf(stderr, "print_html_element encountered unknown element key = %d\n", elt->key); + exit(EXIT_FAILURE); + } +} + +static void print_html_endnotes(GString *out) { + int counter = 0; + GSList *note; + element *note_elt; + element *temp; + if (endnotes == NULL) + return; + note = g_slist_reverse(endnotes); + g_string_append_printf(out, "
    \n
    \n
      "); + while (note != NULL) { + note_elt = note->data; + counter++; + pad(out, 1); + if (note_elt->key == CITATION) { + g_string_append_printf(out, "
    1. ", note_elt->contents.str); + temp = note_elt; + while ( temp != NULL ) { + if (temp->key == NOTELABEL) + print_html_string(out, temp->contents.str, 0); + temp = temp->next; + } + g_string_append_printf(out, ""); + padded = 2; + print_html_element_list(out, note_elt->children, false); + pad(out, 1); + g_string_append_printf(out, "
    2. "); + } else { + g_string_append_printf(out, "
    3. \n", counter); + padded = 2; + am_printing_html_footnote = TRUE; + footnote_counter_to_print = counter; + print_html_element_list(out, note_elt, false); + am_printing_html_footnote = FALSE; + footnote_counter_to_print = 0; + pad(out, 1); + g_string_append_printf(out, "
    4. "); + } + note = note->next; + } + pad(out, 1); + g_string_append_printf(out, "
    \n
    \n"); + + g_slist_free(endnotes); +} + +/********************************************************************** + + Functions for printing Elements as LaTeX + + ***********************************************************************/ + +/* print_latex_string - print string, escaping for LaTeX */ +static void print_latex_string(GString *out, char *str) { + char *tmp; + while (*str != '\0') { + switch (*str) { + case '{': case '}': case '$': case '%': + case '&': case '_': case '#': + g_string_append_printf(out, "\\%c", *str); + break; + case '^': + g_string_append_printf(out, "\\^{}"); + break; + case '\\': + g_string_append_printf(out, "\\textbackslash{}"); + break; + case '~': + g_string_append_printf(out, "\\ensuremath{\\sim}"); + break; + case '|': + g_string_append_printf(out, "\\textbar{}"); + break; + case '<': + g_string_append_printf(out, "$<$"); + break; + case '>': + g_string_append_printf(out, "$>$"); + break; + case '/': + str++; + while (*str == '/') { + g_string_append_printf(out, "/"); + str++; + } + g_string_append_printf(out, "\\slash "); + str--; + break; + case '\n': + tmp = str; + tmp--; + if (*tmp == ' ') { + tmp--; + if (*tmp == ' ') { + g_string_append_printf(out, "\\\\\n"); + } else { + g_string_append_printf(out, "\n"); + } + } else { + g_string_append_printf(out, "\n"); + } + break; + default: + g_string_append_c(out, *str); + } + str++; + } +} + +static void print_latex_endnotes(GString *out) { + GSList *note; + element *note_elt; + if (endnotes == NULL) + return; + note = g_slist_reverse(endnotes); + pad(out,2); + g_string_append_printf(out, "\\begin{thebibliography}{0}"); + while (note != NULL) { + note_elt = note->data; + pad(out, 1); + g_string_append_printf(out, "\\bibitem{%s}\n", note_elt->contents.str); + padded=2; + print_latex_element_list(out, note_elt); + pad(out, 1); + note = note->next; + } + pad(out, 1); + g_string_append_printf(out, "\\end{thebibliography}\n"); + padded = 1; + g_slist_free(endnotes); +} + +/* print_latex_element_list - print a list of elements as LaTeX */ +static void print_latex_element_list(GString *out, element *list) { + while (list != NULL) { + print_latex_element(out, list); + list = list->next; + } +} + +/* print_latex_element - print an element as LaTeX */ +static void print_latex_element(GString *out, element *elt) { + int lev; + char *label; + char *height; + char *width; + char *upper; + int i; + double floatnum; + element *temp; + switch (elt->key) { + case SPACE: + g_string_append_printf(out, "%s", elt->contents.str); + break; + case LINEBREAK: + g_string_append_printf(out, "\\\\\n"); + break; + case STR: + print_latex_string(out, elt->contents.str); + break; + case ELLIPSIS: + localize_typography(out, ELLIP, language, LATEXOUT); + break; + case EMDASH: + localize_typography(out, MDASH, language, LATEXOUT); + break; + case ENDASH: + localize_typography(out, NDASH, language, LATEXOUT); + break; + case APOSTROPHE: + localize_typography(out, APOS, language, LATEXOUT); + break; + case SINGLEQUOTED: + localize_typography(out, LSQUOTE, language, LATEXOUT); + print_latex_element_list(out, elt->children); + localize_typography(out, RSQUOTE, language, LATEXOUT); + break; + case DOUBLEQUOTED: + localize_typography(out, LDQUOTE, language, LATEXOUT); + print_latex_element_list(out, elt->children); + localize_typography(out, RDQUOTE, language, LATEXOUT); + break; + case CODE: + g_string_append_printf(out, "\\texttt{"); + print_latex_string(out, elt->contents.str); + g_string_append_printf(out, "}"); + break; + case HTML: + /* don't print HTML */ + /* but do print HTML comments for raw LaTeX */ + if (strncmp(elt->contents.str,"" from end */ + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s", &elt->contents.str[4]); + } + break; + case LINK: + if (elt->contents.link->url[0] == '#') { + /* This is a link to anchor within document */ + label = label_from_string(elt->contents.link->url,0); + if (elt->contents.link->label != NULL) { + print_latex_element_list(out, elt->contents.link->label); + g_string_append_printf(out, " (\\autoref{%s})", label); + } else { + g_string_append_printf(out, "\\autoref{%s}", label); + } + free(label); + } else if ( (elt->contents.link->label != NULL) && + ( elt->contents.link->label->contents.str != NULL) && + ( strcmp(elt->contents.link->label->contents.str, + elt->contents.link->url) == 0 )) { + /* This is a */ + g_string_append_printf(out, "\\href{%s}{", elt->contents.link->url); + print_latex_string(out, elt->contents.link->url); + g_string_append_printf(out, "}"); + } else if ( (elt->contents.link->label != NULL) && + ( elt->contents.link->label->contents.str != NULL) && + ( strcmp(&elt->contents.link->url[7], + elt->contents.link->label->contents.str) == 0 )) { + /* This is a */ + g_string_append_printf(out, "\\href{%s}{%s}", elt->contents.link->url, &elt->contents.link->url[7]); + } else { + /* This is a [text](link) */ + g_string_append_printf(out, "\\href{%s}{", elt->contents.link->url); + print_latex_element_list(out, elt->contents.link->label); + g_string_append_printf(out, "}"); + if ( no_latex_footnote == FALSE ) { + g_string_append_printf(out, "\\footnote{\\href{%s}{", elt->contents.link->url); + print_latex_string(out, elt->contents.link->url); + g_string_append_printf(out, "}}"); + } + } + break; + case IMAGEBLOCK: + pad(out, 2); + case IMAGE: + /* Figure if we have height, width, neither */ + height = dimension_for_attribute("height", elt->contents.link->attr); + width = dimension_for_attribute("width", elt->contents.link->attr); + if (elt->key == IMAGEBLOCK) { + g_string_append_printf(out, "\\begin{figure}[htbp]\n\\centering\n"); + } + g_string_append_printf(out, "\\includegraphics["); + if ((height == NULL) && (width == NULL)) { + /* No dimensions given */ + g_string_append_printf(out,"keepaspectratio,width=\\textwidth,height=0.75\\textheight"); + } else { + /* at least one dimension given */ + if ((height != NULL) && (width != NULL)) { + + } else { + g_string_append_printf(out, "keepaspectratio,"); + } + if (width != NULL) { + if (width[strlen(width)-1] == '%') { + width[strlen(width)-1] = '\0'; + floatnum = strtod(width, NULL); + floatnum = floatnum/100; + g_string_append_printf(out,"width=%.4f\\textwidth,", floatnum); + } else { + g_string_append_printf(out,"width=%s,", width); + } + } else { + g_string_append_printf(out, "width=\\textwidth,"); + } + if (height != NULL) { + if (height[strlen(height)-1] == '%') { + height[strlen(height)-1] = '\0'; + floatnum = strtod(height, NULL); + floatnum = floatnum/100; + g_string_append_printf(out,"height=%.4f\\textheight,", floatnum); + } else { + g_string_append_printf(out,"height=%s",height); + } + } else { + g_string_append_printf(out, "height=0.75\\textheight"); + } + } + + g_string_append_printf(out, "]{%s}\n", elt->contents.link->url); + if (elt->key == IMAGEBLOCK) { + if (elt->contents.link->label != NULL) { + g_string_append_printf(out, "\\caption{"); + print_latex_element_list(out, elt->contents.link->label); + g_string_append_printf(out, "}\n"); + } + g_string_append_printf(out, "\\label{%s}\n", elt->contents.link->identifier); + g_string_append_printf(out,"\\end{figure}\n"); + } + free(height); + free(width); + break; + case EMPH: + g_string_append_printf(out, "\\emph{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}"); + break; + case STRONG: + g_string_append_printf(out, "\\textbf{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}"); + break; + case LIST: + print_latex_element_list(out, elt->children); + break; + case RAW: + /* Shouldn't occur - these are handled by process_raw_blocks() */ + assert(elt->key != RAW); + break; + case H1: case H2: case H3: case H4: case H5: case H6: + pad(out, 2); + lev = elt->key - H1 + base_header_level; /* assumes H1 ... H6 are in order */ + switch (lev) { + case 1: + g_string_append_printf(out, "\\part{"); + break; + case 2: + g_string_append_printf(out, "\\chapter{"); + break; + case 3: + g_string_append_printf(out, "\\section{"); + break; + case 4: + g_string_append_printf(out, "\\subsection{"); + break; + case 5: + g_string_append_printf(out, "\\subsubsection{"); + break; + case 6: + g_string_append_printf(out, "\\paragraph{"); + break; + case 7: + g_string_append_printf(out, "\\subparagraph{"); + break; + default: + g_string_append_printf(out, "\\noindent\\textbf{"); + break; + } + /* generate a label for each header (MMD); + don't allow footnotes since invalid here */ + no_latex_footnote = TRUE; + if (elt->children->key == AUTOLABEL) { + label = label_from_string(elt->children->contents.str,0); + print_latex_element_list(out, elt->children->next); + } else { + label = label_from_element_list(elt->children,0); + print_latex_element_list(out, elt->children); + } + no_latex_footnote = FALSE; + g_string_append_printf(out, "}\n\\label{"); + g_string_append_printf(out, "%s", label); + g_string_append_printf(out, "}\n"); + free(label); + padded = 1; + break; + case PLAIN: + pad(out, 1); + print_latex_element_list(out, elt->children); + padded = 0; + break; + case PARA: + pad(out, 2); + print_latex_element_list(out, elt->children); + padded = 0; + break; + case HRULE: + pad(out, 2); + g_string_append_printf(out, "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n"); + padded = 0; + break; + case HTMLBLOCK: + /* don't print HTML block */ + /* but do print HTML comments for raw LaTeX */ + if (strncmp(elt->contents.str,"" from end */ + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s", &elt->contents.str[4]); + padded = 0; + } + break; + case VERBATIM: + pad(out, 1); + g_string_append_printf(out, "\n\\begin{verbatim}\n"); + print_raw_element(out, elt); + g_string_append_printf(out, "\\end{verbatim}\n"); + padded = 0; + break; + case BULLETLIST: + pad(out, 1); + g_string_append_printf(out, "\n\\begin{itemize}"); + padded = 0; + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "\n\\end{itemize}"); + padded = 0; + break; + case ORDEREDLIST: + pad(out, 2); + g_string_append_printf(out, "\\begin{enumerate}"); + padded = 0; + print_latex_element_list(out, elt->children); + pad(out, 1); + g_string_append_printf(out, "\\end{enumerate}"); + padded = 0; + break; + case LISTITEM: + pad(out, 1); + g_string_append_printf(out, "\\item "); + padded = 2; + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "\n"); + break; + case BLOCKQUOTE: + pad(out, 2); + g_string_append_printf(out, "\\begin{quote}"); + padded = 0; + print_latex_element_list(out, elt->children); + pad(out, 1); + g_string_append_printf(out, "\\end{quote}"); + padded = 0; + break; + case NOTELABEL: + /* Nonprinting */ + break; + case NOTE: + /* if contents.str == 0, then print note; else ignore, since this + * is a note block that has been incorporated into the notes list */ + if (elt->contents.str == 0) { + if (elt->children->key == GLOSSARYTERM) { + g_string_append_printf(out, "\\newglossaryentry{%s}{", elt->children->children->contents.str); + padded = 2; + if (elt->children->next->key == GLOSSARYSORTKEY) { + g_string_append_printf(out, "sort={"); + print_latex_string(out, elt->children->next->contents.str); + g_string_append_printf(out, "},"); + } + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}}\\glsadd{%s}", elt->children->children->contents.str); + padded = 0; + } else { + g_string_append_printf(out, "\\footnote{"); + padded = 2; + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}"); + padded = 0; + } + elt->children = NULL; + } + break; + case GLOSSARY: + /* This shouldn't do anything */ + break; + case GLOSSARYTERM: + g_string_append_printf(out, "name={"); + print_latex_string(out, elt->children->contents.str); + g_string_append_printf(out, "},description={"); + break; + case GLOSSARYSORTKEY: + break; + case REFERENCE: + /* Nonprinting */ + break; + case NOCITATION: + case CITATION: + if (strncmp(elt->contents.str,"[#",2) == 0) { + /* This should be used as a bibtex citation key after trimming */ + elt->contents.str[strlen(elt->contents.str)-1] = '\0'; + if (elt->key == NOCITATION ) { + g_string_append_printf(out, "~\\nocite{%s}", &elt->contents.str[2]); + } else { + if ((elt->children != NULL) && (elt->children->key == LOCATOR)) { + if (strcmp(&elt->contents.str[strlen(elt->contents.str) - 1],";") == 0) { + g_string_append_printf(out, " \\citet["); + elt->contents.str[strlen(elt->contents.str) - 1] = '\0'; + } else { + g_string_append_printf(out, "~\\citep["); + } + print_latex_element(out,elt->children); + g_string_append_printf(out, "]{%s}",&elt->contents.str[2]); + } else { + if (strcmp(&elt->contents.str[strlen(elt->contents.str) - 1],";") == 0) { + elt->contents.str[strlen(elt->contents.str) - 1] = '\0'; + g_string_append_printf(out, " \\citet{%s}", &elt->contents.str[2]); + } else { + g_string_append_printf(out, "~\\citep{%s}", &elt->contents.str[2]); + } + } + } + } else { + /* This citation was specified in the document itself */ + if (elt->key == NOCITATION ) { + g_string_append_printf(out, "~\\nocite{%s}", elt->contents.str); + temp = elt->children; + elt->children = temp->next; + free_element(temp); + } else { + if ((elt->children != NULL) && (elt->children->key == LOCATOR)){ + if (strcmp(&elt->contents.str[strlen(elt->contents.str) - 1],";") == 0) { + g_string_append_printf(out, " \\citet["); + elt->contents.str[strlen(elt->contents.str) - 1] = '\0'; + } else { + g_string_append_printf(out, "~\\citep["); + } + print_latex_element(out,elt->children); + g_string_append_printf(out, "]{%s}",elt->contents.str); +// element *temp; + temp = elt->children; + elt->children = temp->next; + free_element(temp); + } else { + if (strcmp(&elt->contents.str[strlen(elt->contents.str) - 1],";") == 0) { + elt->contents.str[strlen(elt->contents.str) - 1] = '\0'; + g_string_append_printf(out, " \\citet{%s}", elt->contents.str); + } else { + g_string_append_printf(out, "~\\citep{%s}", elt->contents.str); + } + } + } + if ((elt->children != NULL) && (elt->children->contents.str == NULL)) { + elt->children->contents.str = strdup(elt->contents.str); + add_endnote(elt->children); + } + elt->children = NULL; + } + break; + case LOCATOR: + print_latex_element_list(out, elt->children); + break; + case DEFLIST: + g_string_append_printf(out, "\\begin{description}"); + padded = 0; + print_latex_element_list(out, elt->children); + pad(out,1); + g_string_append_printf(out, "\\end{description}"); + padded = 0; + break; + case TERM: + pad(out,2); + g_string_append_printf(out, "\\item["); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "]"); + padded = 0; + break; + case DEFINITION: + pad(out,2); + padded = 2; + print_latex_element_list(out, elt->children); + padded = 0; + break; + case METADATA: + /* Metadata is present, so this should be a "complete" document */ + print_latex_header(out, elt); + html_footer = is_html_complete_doc(elt); + break; + case METAKEY: + if (strcmp(elt->contents.str, "title") == 0) { + g_string_append_printf(out, "\\def\\mytitle{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}\n"); + } else if (strcmp(elt->contents.str, "author") == 0) { + g_string_append_printf(out, "\\def\\myauthor{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}\n"); + } else if (strcmp(elt->contents.str, "date") == 0) { + g_string_append_printf(out, "\\def\\mydate{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}\n"); + } else if (strcmp(elt->contents.str, "copyright") == 0) { + g_string_append_printf(out, "\\def\\mycopyright{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}\n"); + } else if (strcmp(elt->contents.str, "baseheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "latexheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "latexinput") == 0) { + g_string_append_printf(out, "\\input{%s}\n", elt->children->contents.str); + } else if (strcmp(elt->contents.str, "latexfooter") == 0) { + latex_footer = elt->children->contents.str; + } else if (strcmp(elt->contents.str, "bibtex") == 0) { + g_string_append_printf(out, "\\def\\bibliocommand{\\bibliography{%s}}\n",elt->children->contents.str); + } else if (strcmp(elt->contents.str, "xhtmlheader") == 0) { + } else if (strcmp(elt->contents.str, "htmlheader") == 0) { + } else if (strcmp(elt->contents.str, "css") == 0) { + } else if (strcmp(elt->contents.str, "quoteslanguage") == 0) { + label = label_from_element_list(elt->children, 0); + if (strcmp(label, "dutch") == 0) { language = DUTCH; } else + if (strcmp(label, "german") == 0) { language = GERMAN; } else + if (strcmp(label, "germanguillemets") == 0) { language = GERMANGUILL; } else + if (strcmp(label, "french") == 0) { language = FRENCH; } else + if (strcmp(label, "swedish") == 0) { language = SWEDISH; } + free(label); + } else { + g_string_append_printf(out, "\\def\\"); + print_latex_string(out, elt->contents.str); + g_string_append_printf(out, "{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}\n"); + } + break; + case METAVALUE: + print_latex_string(out, elt->contents.str); + break; + case FOOTER: + print_latex_endnotes(out); + print_latex_footer(out); + break; + case HEADINGSECTION: + print_latex_element_list(out, elt->children); + break; + case TABLE: + pad(out, 2); + g_string_append_printf(out, "\\begin{table}[htbp]\n\\begin{minipage}{\\linewidth}\n\\setlength{\\tymax}{0.5\\linewidth}\n\\centering\n\\small\n"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "\n\\end{tabulary}\n\\end{minipage}\n\\end{table}\n"); + padded = 0; + break; + case TABLESEPARATOR: + upper = strdup(elt->contents.str); + + for(i = 0; upper[ i ]; i++) + upper[i] = toupper(upper[ i ]); + + g_string_append_printf(out, "\\begin{tabulary}{\\textwidth}{@{}%s@{}} \\toprule\n", upper); + free(upper); + break; + case TABLECAPTION: + if (elt->children->key == TABLELABEL) { + label = label_from_element_list(elt->children->children,0); + } else { + label = label_from_element_list(elt->children,0); + } + g_string_append_printf(out, "\\caption{"); + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "}\n\\label{%s}\n",label); + free(label); + break; + case TABLELABEL: + break; + case TABLEHEAD: + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "\\midrule\n"); + break; + case TABLEBODY: + print_latex_element_list(out, elt->children); + if ( ( elt->next != NULL ) && (elt->next->key == TABLEBODY) ) { + g_string_append_printf(out, "\n\\midrule\n"); + } else { + g_string_append_printf(out, "\n\\bottomrule\n"); + } + break; + case TABLEROW: + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "\\\\\n"); + break; + case TABLECELL: + padded = 2; + if ((elt->children != NULL) && (elt->children->key == CELLSPAN)) { + g_string_append_printf(out, "\\multicolumn{%d}{c}{", (int)strlen(elt->children->contents.str)+1); + } + print_latex_element_list(out, elt->children); + if ((elt->children != NULL) && (elt->children->key == CELLSPAN)) { + g_string_append_printf(out, "}"); + } + if (elt->next != NULL) { + g_string_append_printf(out, "&"); + } + break; + case CELLSPAN: + break; + case ATTRKEY: + g_string_append_printf(out, " %s=\"%s\"", elt->contents.str, + elt->children->contents.str); + break; + case MATHSPAN: + if (strncmp(&elt->contents.str[2],"\\begin",5) == 0) { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s",&elt->contents.str[2]); + } else { + if ( elt->contents.str[strlen(elt->contents.str)-1] == ']') { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s\\]", elt->contents.str); + } else { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "$%s$", &elt->contents.str[2]); + } + } + break; + default: + fprintf(stderr, "print_latex_element encountered unknown element key = %d\n", elt->key); + exit(EXIT_FAILURE); + } +} + +/********************************************************************** + + Functions for printing Elements as groff (mm macros) + + ***********************************************************************/ + +static bool in_list_item = false; /* True if we're parsing contents of a list item. */ + +/* print_groff_string - print string, escaping for groff */ +static void print_groff_string(GString *out, char *str) { + while (*str != '\0') { + switch (*str) { + case '\\': + g_string_append_printf(out, "\\e"); + break; + default: + g_string_append_c(out, *str); + } + str++; + } +} + +/* print_groff_mm_element_list - print a list of elements as groff ms */ +static void print_groff_mm_element_list(GString *out, element *list) { + int count = 1; + while (list != NULL) { + print_groff_mm_element(out, list, count); + list = list->next; + count++; + } +} + +/* print_groff_mm_element - print an element as groff ms */ +static void print_groff_mm_element(GString *out, element *elt, int count) { + int lev; + switch (elt->key) { + case SPACE: + g_string_append_printf(out, "%s", elt->contents.str); + padded = 0; + break; + case LINEBREAK: + pad(out, 1); + g_string_append_printf(out, ".br\n"); + padded = 0; + break; + case STR: + print_groff_string(out, elt->contents.str); + padded = 0; + break; + case ELLIPSIS: + g_string_append_printf(out, "..."); + break; + case EMDASH: + g_string_append_printf(out, "\\[em]"); + break; + case ENDASH: + g_string_append_printf(out, "\\[en]"); + break; + case APOSTROPHE: + g_string_append_printf(out, "'"); + break; + case SINGLEQUOTED: + g_string_append_printf(out, "`"); + print_groff_mm_element_list(out, elt->children); + g_string_append_printf(out, "'"); + break; + case DOUBLEQUOTED: + g_string_append_printf(out, "\\[lq]"); + print_groff_mm_element_list(out, elt->children); + g_string_append_printf(out, "\\[rq]"); + break; + case CODE: + g_string_append_printf(out, "\\fC"); + print_groff_string(out, elt->contents.str); + g_string_append_printf(out, "\\fR"); + padded = 0; + break; + case HTML: + /* don't print HTML */ + break; + case LINK: + print_groff_mm_element_list(out, elt->contents.link->label); + g_string_append_printf(out, " (%s)", elt->contents.link->url); + padded = 0; + break; + case IMAGE: + g_string_append_printf(out, "[IMAGE: "); + print_groff_mm_element_list(out, elt->contents.link->label); + g_string_append_printf(out, "]"); + padded = 0; + /* not supported */ + break; + case EMPH: + g_string_append_printf(out, "\\fI"); + print_groff_mm_element_list(out, elt->children); + g_string_append_printf(out, "\\fR"); + padded = 0; + break; + case STRONG: + g_string_append_printf(out, "\\fB"); + print_groff_mm_element_list(out, elt->children); + g_string_append_printf(out, "\\fR"); + padded = 0; + break; + case LIST: + print_groff_mm_element_list(out, elt->children); + padded = 0; + break; + case RAW: + /* Shouldn't occur - these are handled by process_raw_blocks() */ + assert(elt->key != RAW); + break; + case H1: case H2: case H3: case H4: case H5: case H6: + lev = elt->key - H1 + 1; + pad(out, 1); + g_string_append_printf(out, ".H %d \"", lev); + print_groff_mm_element_list(out, elt->children); + g_string_append_printf(out, "\""); + padded = 0; + break; + case PLAIN: + pad(out, 1); + print_groff_mm_element_list(out, elt->children); + padded = 0; + break; + case PARA: + pad(out, 1); + if (!in_list_item || count != 1) + g_string_append_printf(out, ".P\n"); + print_groff_mm_element_list(out, elt->children); + padded = 0; + break; + case HRULE: + pad(out, 1); + g_string_append_printf(out, "\\l'\\n(.lu*8u/10u'"); + padded = 0; + break; + case HTMLBLOCK: + /* don't print HTML block */ + break; + case VERBATIM: + pad(out, 1); + g_string_append_printf(out, ".VERBON 2\n"); + print_groff_string(out, elt->contents.str); + g_string_append_printf(out, ".VERBOFF"); + padded = 0; + break; + case BULLETLIST: + pad(out, 1); + g_string_append_printf(out, ".BL"); + padded = 0; + print_groff_mm_element_list(out, elt->children); + pad(out, 1); + g_string_append_printf(out, ".LE 1"); + padded = 0; + break; + case ORDEREDLIST: + pad(out, 1); + g_string_append_printf(out, ".AL"); + padded = 0; + print_groff_mm_element_list(out, elt->children); + pad(out, 1); + g_string_append_printf(out, ".LE 1"); + padded = 0; + break; + case LISTITEM: + pad(out, 1); + g_string_append_printf(out, ".LI\n"); + in_list_item = true; + padded = 2; + print_groff_mm_element_list(out, elt->children); + in_list_item = false; + break; + case BLOCKQUOTE: + pad(out, 1); + g_string_append_printf(out, ".DS I\n"); + padded = 2; + print_groff_mm_element_list(out, elt->children); + pad(out, 1); + g_string_append_printf(out, ".DE"); + padded = 0; + break; + case NOTELABEL: + /* Nonprinting */ + break; + case NOTE: + /* if contents.str == 0, then print note; else ignore, since this + * is a note block that has been incorporated into the notes list */ + if (elt->contents.str == 0) { + g_string_append_printf(out, "\\*F\n"); + g_string_append_printf(out, ".FS\n"); + padded = 2; + print_groff_mm_element_list(out, elt->children); + pad(out, 1); + g_string_append_printf(out, ".FE\n"); + padded = 1; + } + break; + case REFERENCE: + /* Nonprinting */ + break; + default: + fprintf(stderr, "print_groff_mm_element encountered unknown element key = %d\n", elt->key); + exit(EXIT_FAILURE); + } +} + +/********************************************************************** + + Functions for printing Elements as ODF + + ***********************************************************************/ + +/* print_odf_code_string - print string, escaping for HTML and saving newlines +*/ +static void print_odf_code_string(GString *out, char *str) { + char *tmp; + while (*str != '\0') { + switch (*str) { + case '&': + g_string_append_printf(out, "&"); + break; + case '<': + g_string_append_printf(out, "<"); + break; + case '>': + g_string_append_printf(out, ">"); + break; + case '"': + g_string_append_printf(out, """); + break; + case '\n': + g_string_append_printf(out, ""); + break; + case ' ': + tmp = str; + tmp++; + if (*tmp == ' ') { + tmp++; + if (*tmp == ' ') { + tmp++; + if (*tmp == ' ') { + g_string_append_printf(out, ""); + str = tmp; + } else { + g_string_append_printf(out, " "); + } + } else { + g_string_append_printf(out, " "); + } + } else { + g_string_append_printf(out, " "); + } + break; + default: + g_string_append_c(out, *str); + } + str++; + } +} + +/* print_odf_string - print string, escaping for HTML and saving newlines */ +static void print_odf_string(GString *out, char *str) { + char *tmp; + while (*str != '\0') { + switch (*str) { + case '&': + g_string_append_printf(out, "&"); + break; + case '<': + g_string_append_printf(out, "<"); + break; + case '>': + g_string_append_printf(out, ">"); + break; + case '"': + g_string_append_printf(out, """); + break; + case '\n': + tmp = str; + tmp--; + if (*tmp == ' ') { + tmp--; + if (*tmp == ' ') { + g_string_append_printf(out, ""); + } else { + g_string_append_printf(out, "\n"); + } + } else { + g_string_append_printf(out, "\n"); + } + break; + case ' ': + tmp = str; + tmp++; + if (*tmp == ' ') { + tmp++; + if (*tmp == ' ') { + tmp++; + if (*tmp == ' ') { + g_string_append_printf(out, ""); + str = tmp; + } else { + g_string_append_printf(out, " "); + } + } else { + g_string_append_printf(out, " "); + } + } else { + g_string_append_printf(out, " "); + } + break; + default: + g_string_append_c(out, *str); + } + str++; + } +} + +/* print_odf_element_list - print an element list as ODF */ +static void print_odf_element_list(GString *out, element *list) { + while (list != NULL) { + print_odf_element(out, list); + list = list->next; + } +} + +/* print_odf_element - print an element as ODF */ +static void print_odf_element(GString *out, element *elt) { + int lev; + char *label; + char *height; + char *width; + /* element *locator = NULL; */ + int old_type = 0; + char buf[5]; + element *header; + switch (elt->key) { + case SPACE: + g_string_append_printf(out, "%s", elt->contents.str); + break; + case LINEBREAK: + g_string_append_printf(out, ""); + break; + case STR: + print_html_string(out, elt->contents.str, 0); + break; + case ELLIPSIS: + localize_typography(out, ELLIP, language, HTMLOUT); + break; + case EMDASH: + localize_typography(out, MDASH, language, HTMLOUT); + break; + case ENDASH: + localize_typography(out, NDASH, language, HTMLOUT); + break; + case APOSTROPHE: + localize_typography(out, APOS, language, HTMLOUT); + break; + case SINGLEQUOTED: + localize_typography(out, LSQUOTE, language, HTMLOUT); + print_odf_element_list(out, elt->children); + localize_typography(out, RSQUOTE, language, HTMLOUT); + break; + case DOUBLEQUOTED: + localize_typography(out, LDQUOTE, language, HTMLOUT); + print_odf_element_list(out, elt->children); + localize_typography(out, RDQUOTE, language, HTMLOUT); + break; + case CODE: + g_string_append_printf(out, ""); + print_html_string(out, elt->contents.str, 0); + g_string_append_printf(out, ""); + break; + case HTML: + /* don't print HTML */ + /* but do print HTML comments for raw ODF */ + if (strncmp(elt->contents.str,"" from end */ + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s", &elt->contents.str[4]); + } + break; + case LINK: + if (elt->contents.link->url[0] == '#') { + /* This is a cross-reference */ + label = label_from_string(elt->contents.link->url,0); + if (elt->contents.link->label != NULL) { + g_string_append_printf(out, "",label); + print_latex_element_list(out, elt->contents.link->label); + g_string_append_printf(out,""); + } else { + + } + } else { + g_string_append_printf(out, "contents.link->url, 0); + g_string_append_printf(out, "\""); + if (strlen(elt->contents.link->title) > 0) { + g_string_append_printf(out, " office:name=\""); + print_html_string(out, elt->contents.link->title, 0); + g_string_append_printf(out, "\""); + } + /* print_html_element_list(out, elt->contents.link->attr, obfuscate);*/ + g_string_append_printf(out, ">"); + print_odf_element_list(out, elt->contents.link->label); + g_string_append_printf(out, ""); + } + break; + case IMAGEBLOCK: + g_string_append_printf(out, "\n"); + case IMAGE: + height = dimension_for_attribute("height", elt->contents.link->attr); + width = dimension_for_attribute("width", elt->contents.link->attr); + g_string_append_printf(out, "\ncontents.link->url); + g_string_append_printf(out,"\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\" draw:filter-name=\"<All formats>\"/>\n"); + if (elt->key == IMAGEBLOCK) { + g_string_append_printf(out, ""); + if (elt->contents.link->label != NULL) { + g_string_append_printf(out, "Figure Update Fields to calculate numbers: "); + print_odf_element_list(out, elt->contents.link->label); + } + g_string_append_printf(out, "\n\n"); + } else { + g_string_append_printf(out, "\n"); + } + break; + case EMPH: + g_string_append_printf(out, + ""); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, ""); + break; + case STRONG: + g_string_append_printf(out, + ""); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, ""); + break; + case LIST: + print_odf_element_list(out, elt->children); + break; + case RAW: + /* Shouldn't occur - these are handled by process_raw_blocks() */ + assert(elt->key != RAW); + break; + case H1: case H2: case H3: case H4: case H5: case H6: + lev = elt->key - H1 + base_header_level; /* assumes H1 ... H6 are in order */ + g_string_append_printf(out, "", lev); + if (elt->children->key == AUTOLABEL) { + /* generate a label for each header (MMD)*/ + g_string_append_printf(out,"", elt->children->contents.str); + print_odf_element_list(out, elt->children->next); + g_string_append_printf(out,"", elt->children->contents.str); + } else { + label = label_from_element_list(elt->children, 0); + g_string_append_printf(out,"", label); + print_odf_element_list(out, elt->children); + g_string_append_printf(out,"", label); + free(label); + } + g_string_append_printf(out, "\n"); + padded = 0; + break; + case PLAIN: + print_odf_element_list(out, elt->children); + padded = 0; + break; + case PARA: + g_string_append_printf(out, ""); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "\n"); + break; + case HRULE: + g_string_append_printf(out,"\n"); + break; + case HTMLBLOCK: + /* don't print HTML block */ + /* but do print HTML comments for raw ODF */ + if (strncmp(elt->contents.str,"" from end */ + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s", &elt->contents.str[4]); + } + break; + case VERBATIM: + old_type = odf_type; + odf_type = VERBATIM; + g_string_append_printf(out, ""); + print_odf_code_string(out, elt->contents.str); + g_string_append_printf(out, "\n"); + odf_type = old_type; + break; + case BULLETLIST: + if ((odf_type == BULLETLIST) || + (odf_type == ORDEREDLIST)) { + /* I think this was made unnecessary by another change. + Same for ORDEREDLIST below */ + /* g_string_append_printf(out, ""); */ + } + old_type = odf_type; + odf_type = BULLETLIST; + if (odf_list_needs_end_p) { + g_string_append_printf(out, "%s", ""); + odf_list_needs_end_p = 0; + } + g_string_append_printf(out, "%s", ""); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "%s", ""); + odf_type = old_type; + break; + case ORDEREDLIST: + if ((odf_type == BULLETLIST) || + (odf_type == ORDEREDLIST)) { + /* g_string_append_printf(out, ""); */ + } + old_type = odf_type; + odf_type = ORDEREDLIST; + if (odf_list_needs_end_p) { + g_string_append_printf(out, "%s", ""); + odf_list_needs_end_p = 0; + } + g_string_append_printf(out, "%s", "\n"); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "%s", "\n"); + odf_type = old_type; + break; + case LISTITEM: + g_string_append_printf(out, "\n"); + if (elt->children->children->key != PARA) { + g_string_append_printf(out, ""); + odf_list_needs_end_p = 1; + } + print_odf_element_list(out, elt->children); + + odf_list_needs_end_p = 0; + if ((list_contains_key(elt->children,BULLETLIST) || + (list_contains_key(elt->children,ORDEREDLIST)))) { + } else { + if (elt->children->children->key != PARA) { + g_string_append_printf(out, ""); + } + } + g_string_append_printf(out, "\n"); + break; + case BLOCKQUOTE: + old_type = odf_type; + odf_type = BLOCKQUOTE; + print_odf_element_list(out, elt->children); + odf_type = old_type; + break; + case REFERENCE: + break; + case NOTELABEL: + break; + case NOTE: + old_type = odf_type; + odf_type = NOTE; + /* if contents.str == 0 then print; else ignore - like above */ + if (elt->contents.str == 0) { + if (elt->children->key == GLOSSARYTERM) { + g_string_append_printf(out, "\n"); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "\n\n"); + } else { + g_string_append_printf(out, "\n"); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "\n\n"); + } + } + elt->children = NULL; + odf_type = old_type; + break; + case GLOSSARY: + break; + case GLOSSARYTERM: + g_string_append_printf(out, ""); + print_odf_string(out, elt->children->contents.str); + g_string_append_printf(out, ":"); + g_string_append_printf(out, ""); + break; + case GLOSSARYSORTKEY: + break; + case NOCITATION: + case CITATION: + /* Get locator, if present */ + /* locator = locator_for_citation(elt); */ + + if (strncmp(elt->contents.str,"[#",2) == 0) { + /* reference specified externally, so just display it */ + g_string_append_printf(out, "%s", elt->contents.str); + } else { + /* reference specified within the MMD document, + so will output as footnote */ + if (elt->children->contents.str == NULL) { + /* First use of this citation */ + ++notenumber; +// char buf[5]; + sprintf(buf, "%d",notenumber); + /* Store the number for future reference */ + elt->children->contents.str = strdup(buf); + + /* Insert the footnote here */ + old_type = odf_type; + odf_type = NOTE; + g_string_append_printf(out, "\n", buf); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "\n\n"); + odf_type = old_type; + + elt->children->key = CITATION; + } else { + /* Additional reference to prior citation, + and therefore must link to another footnote */ + g_string_append_printf(out, "%s", elt->children->contents.str, elt->children->contents.str); + } + elt->children = NULL; + } + break; + case LOCATOR: + print_odf_element_list(out, elt->children); + break; + case DEFLIST: + print_odf_element_list(out, elt->children); + break; + case TERM: + g_string_append_printf(out, ""); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, ""); + break; + case DEFINITION: + old_type = odf_type; + odf_type = DEFINITION; + g_string_append_printf(out, ""); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, ""); + odf_type = old_type; + break; + case METADATA: + g_string_append_printf(out, "\n"); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, "\n"); +// element *header; + header = metadata_for_key("odfheader",elt); + if (header != NULL) { + print_raw_element(out,header->children); + } + break; + case METAKEY: + if (strcmp(elt->contents.str, "title") == 0) { + g_string_append_printf(out, ""); + print_odf_element(out, elt->children); + g_string_append_printf(out,"\n"); + } else if (strcmp(elt->contents.str, "css") == 0) { + } else if (strcmp(elt->contents.str, "baseheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "odfheaderlevel") == 0) { + base_header_level = atoi(elt->children->contents.str); + } else if (strcmp(elt->contents.str, "xhtmlheader") == 0) { + } else if (strcmp(elt->contents.str, "htmlheader") == 0) { + } else if (strcmp(elt->contents.str, "odfheader") == 0) { + } else if (strcmp(elt->contents.str, "latexfooter") == 0) { + } else if (strcmp(elt->contents.str, "latexinput") == 0) { + } else if (strcmp(elt->contents.str, "latexmode") == 0) { + } else if (strcmp(elt->contents.str, "keywords") == 0) { + g_string_append_printf(out, ""); + print_odf_element(out,elt->children); + g_string_append_printf(out, "\n"); + } else if (strcmp(elt->contents.str, "quoteslanguage") == 0) { + label = label_from_element_list(elt->children, 0); + if (strcmp(label, "dutch") == 0) { language = DUTCH; } else + if (strcmp(label, "german") == 0) { language = GERMAN; } else + if (strcmp(label, "germanguillemets") == 0) { language = GERMANGUILL; } else + if (strcmp(label, "french") == 0) { language = FRENCH; } else + if (strcmp(label, "swedish") == 0) { language = SWEDISH; } + free(label); + } else { + g_string_append_printf(out, "contents.str); + g_string_append_printf(out, "\">"); + print_odf_element(out, elt->children); + g_string_append_printf(out,"\n"); + } + break; + case METAVALUE: + print_odf_string(out, elt->contents.str); + break; + case FOOTER: + break; + case HEADINGSECTION: + print_odf_element_list(out, elt->children); + break; + case TABLE: + g_string_append_printf(out,"\n\n"); + print_odf_element_list(out, elt->children); + g_string_append_printf(out, ""); + /* print caption if present */ + if (elt->children->key == TABLECAPTION) { + if (elt->children->children->key == TABLELABEL) { + label = label_from_element_list(elt->children->children->children,0); + } else { + label = label_from_element_list(elt->children->children,0); + } + g_string_append_printf(out,"Table Update Fields to calculate numbers:", label); + print_odf_element_list(out,elt->children->children); + g_string_append_printf(out, "\n",label); + free(label); + } + break; + case TABLESEPARATOR: + table_alignment = elt->contents.str; + break; + case TABLECAPTION: + break; + case TABLELABEL: + break; + case TABLEHEAD: + for (table_column=0;table_column\n"); + } + cell_type = 'h'; + print_odf_element_list(out, elt->children); + cell_type = 'd'; + break; + case TABLEBODY: + print_odf_element_list(out,elt->children); + break; + case TABLEROW: + g_string_append_printf(out, "\n"); + table_column = 0; + print_odf_element_list(out,elt->children); + g_string_append_printf(out,"\n"); + break; + case TABLECELL: + g_string_append_printf(out, "children != NULL) && (elt->children->key == CELLSPAN)) { + g_string_append_printf(out, " table:number-columns-spanned=\"%d\"",(int)strlen(elt->children->contents.str)+1); + } + g_string_append_printf(out,">\n"); + print_odf_element_list(out,elt->children); + g_string_append_printf(out, "\n\n"); + table_column++; + break; + case CELLSPAN: + break; + case MATHSPAN: + if ( elt->contents.str[strlen(elt->contents.str)-1] == ']') { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s\\]", elt->contents.str); + } else { + elt->contents.str[strlen(elt->contents.str)-3] = '\0'; + g_string_append_printf(out, "%s\\)", elt->contents.str); + } + break; default: + fprintf(stderr, "print_html_element encountered unknown element key = %d\n", elt->key); + exit(EXIT_FAILURE); + } +} + +/********************************************************************** + + Parameterized function for printing an Element. + + ***********************************************************************/ + +void print_element_list(GString *out, element *elt, int format, int exts) { + /* And MultiMarkdown globals */ + element *title; + base_header_level = 1; + language = ENGLISH; + html_footer = FALSE; + no_latex_footnote = FALSE; + footnote_counter_to_print = 0; + odf_list_needs_end_p = 0; + + /* Initialize globals */ + endnotes = NULL; + notenumber = 0; + + + + + extensions = exts; + padded = 2; /* set padding to 2, so no extra blank lines at beginning */ + + format = find_latex_mode(format, elt); + switch (format) { + case HTML_FORMAT: + print_html_element_list(out, elt, false); + if (endnotes != NULL) { + pad(out, 2); + print_html_endnotes(out); + } + if (html_footer == TRUE) print_html_footer(out, false); + break; + case LATEX_FORMAT: + print_latex_element_list(out, elt); + break; + case MEMOIR_FORMAT: + print_memoir_element_list(out, elt); + break; + case BEAMER_FORMAT: + print_beamer_element_list(out, elt); + break; + case OPML_FORMAT: + g_string_append_printf(out, "\n\n"); + if (list_contains_key(elt,METAKEY)) { + title = metadata_for_key("title",elt); + if (title != NULL) { + g_string_append_printf(out,""); + print_raw_element(out,title->children); + g_string_append_printf(out,""); + } + } + g_string_append_printf(out, "\n"); + print_opml_element_list(out, elt); + if (html_footer == TRUE) print_opml_metadata(out, elt); + g_string_append_printf(out, "\n"); + break; + case ODF_FORMAT: + print_odf_header(out); + if (elt->key == METADATA) { + /* print metadata */ + print_odf_element(out,elt); + elt = elt->next; + } + g_string_append_printf(out, "\n\n"); + if (elt != NULL) print_odf_element_list(out,elt); + print_odf_footer(out); + break; + case ODF_BODY_FORMAT: + if (elt != NULL) print_odf_body_element_list(out, elt); + break; + case GROFF_MM_FORMAT: + print_groff_mm_element_list(out, elt); + break; + default: + fprintf(stderr, "print_element - unknown format = %d\n", format); + exit(EXIT_FAILURE); + } +} + + +/********************************************************************** + + MultiMarkdown Routines - Used for generating "complete" documents + + ***********************************************************************/ + + +void print_html_header(GString *out, element *elt, bool obfuscate) { + g_string_append_printf(out, +"\n\n\n\t\n"); + + print_html_element_list(out, elt->children, obfuscate); + g_string_append_printf(out, "\n\n\n"); +} + + +void print_html_footer(GString *out, bool obfuscate) { + g_string_append_printf(out, "\n\n\n"); +} + + +void print_latex_header(GString *out, element *elt) { + print_latex_element_list(out, elt->children); +} + + +void print_latex_footer(GString *out) { + if (latex_footer != NULL) { + pad(out,2); + g_string_append_printf(out, "\\input{%s}\n", latex_footer); + } + if (html_footer) { + g_string_append_printf(out, "\n\\end{document}"); + } +} + + +/* print_memoir_element_list - print an element as LaTeX for memoir class */ +void print_memoir_element_list(GString *out, element *list) { + while (list != NULL) { + print_memoir_element(out, list); + list = list->next; + } +} + + +/* print_memoir_element - print an element as LaTeX for memoir class */ +static void print_memoir_element(GString *out, element *elt) { + switch (elt->key) { + case VERBATIM: + pad(out, 1); + g_string_append_printf(out, "\n\\begin{adjustwidth}{2.5em}{2.5em}\n\\begin{verbatim}\n\n"); + print_raw_element(out, elt); + g_string_append_printf(out, "\n\\end{verbatim}\n\\end{adjustwidth}"); + padded = 0; + break; + case HEADINGSECTION: + print_memoir_element_list(out, elt->children); + break; + case DEFLIST: + g_string_append_printf(out, "\\begin{description}"); + padded = 0; + print_memoir_element_list(out, elt->children); + pad(out,1); + g_string_append_printf(out, "\\end{description}"); + padded = 0; + break; + case DEFINITION: + pad(out,2); + padded = 2; + print_memoir_element_list(out, elt->children); + padded = 0; + break; + default: + /* most things are not changed for memoir output */ + print_latex_element(out, elt); + } +} + + +/* print_beamer_element_list - print an element as LaTeX for beamer class */ +void print_beamer_element_list(GString *out, element *list) { + while (list != NULL) { + print_beamer_element(out, list); + list = list->next; + } +} + +static void print_beamer_endnotes(GString *out) { + GSList *note; + element *note_elt; + if (endnotes == NULL) + return; + note = g_slist_reverse(endnotes); + pad(out,2); + g_string_append_printf(out, "\\part{Bibliography}\n\\begin{frame}[allowframebreaks]\n\\frametitle{Bibliography}\n\\def\\newblock{}\n\\begin{thebibliography}{0}\n"); + while (note != NULL) { + note_elt = note->data; + pad(out, 1); + g_string_append_printf(out, "\\bibitem{%s}\n", note_elt->contents.str); + padded=2; + print_latex_element_list(out, note_elt); + pad(out, 1); + note = note->next; + } + pad(out, 1); + g_string_append_printf(out, "\\end{thebibliography}\n\\end{frame}\n\n"); + padded = 2; + g_slist_free(endnotes); +} + +/* print_beamer_element - print an element as LaTeX for beamer class */ +static void print_beamer_element(GString *out, element *elt) { + int lev; + char *label; + switch (elt->key) { + case FOOTER: + print_beamer_endnotes(out); + g_string_append_printf(out, "\\mode\n"); + print_latex_footer(out); + g_string_append_printf(out, "\\mode*\n"); + break; + case LISTITEM: + pad(out, 1); + g_string_append_printf(out, "\\item<+-> "); + padded = 2; + print_latex_element_list(out, elt->children); + g_string_append_printf(out, "\n"); + break; + case HEADINGSECTION: + if (elt->children->key -H1 + base_header_level == 3) { + pad(out,2); + g_string_append_printf(out, "\\begin{frame}"); + if (list_contains_key(elt->children,VERBATIM)) { + g_string_append_printf(out, "[fragile]"); + } + padded = 0; + print_beamer_element_list(out, elt->children); + g_string_append_printf(out, "\n\n\\end{frame}\n\n"); + padded = 2; + } else if (elt->children->key -H1 + base_header_level == 4) { + pad(out, 1); + g_string_append_printf(out, "\\mode
    {\n"); + padded = 0; + print_beamer_element_list(out, elt->children->next); + g_string_append_printf(out, "\n\n}\n\n"); + padded = 2; + } else { + print_beamer_element_list(out, elt->children); + } + break; + case H1: case H2: case H3: case H4: case H5: case H6: + pad(out, 2); + lev = elt->key - H1 + base_header_level; /* assumes H1 ... H6 are in order */ + switch (lev) { + case 1: + g_string_append_printf(out, "\\part{"); + break; + case 2: + g_string_append_printf(out, "\\section{"); + break; + case 3: + g_string_append_printf(out, "\\frametitle{"); + break; + default: + g_string_append_printf(out, "\\emph{"); + break; + } + /* generate a label for each header (MMD); + don't allow footnotes since invalid here */ + no_latex_footnote = TRUE; + if (elt->children->key == AUTOLABEL) { + label = label_from_string(elt->children->contents.str,0); + print_latex_element_list(out, elt->children->next); + } else { + label = label_from_element_list(elt->children,0); + print_latex_element_list(out, elt->children); + } + no_latex_footnote = FALSE; + g_string_append_printf(out, "}\n\\label{"); + g_string_append_printf(out, "%s", label); + g_string_append_printf(out, "}\n"); + free(label); + padded = 1; + break; + default: + print_latex_element(out, elt); + } +} + + +element * print_html_headingsection(GString *out, element *list, bool obfuscate) { + element *base = list; + print_html_element_list(out, list->children, obfuscate); + + list = list->next; + while ( (list != NULL) && (list->key == HEADINGSECTION) && (list->children->key > base->children->key) && (list->children->key <= H6)) { + list = print_html_headingsection(out, list, obfuscate); + } + + return list; +} + +/* look for "LaTeX Mode" metadata and change format to match */ +static int find_latex_mode(int format, element *list) { + element *latex_mode; + char *label; + + if (format != LATEX_FORMAT) return format; + + if (list_contains_key(list,METAKEY)) { + latex_mode = metadata_for_key("latexmode", list); + if ( latex_mode != NULL) { + label = label_from_element_list(latex_mode->children, 0); + if (strcmp(label, "beamer") == 0) { format = BEAMER_FORMAT; } else + if (strcmp(label, "memoir") == 0) { format = MEMOIR_FORMAT; } + free(label); + } + return format; + } else { + return format; + } +} + + +/* find specified metadata key, if present */ +element * metadata_for_key(char *key, element *list) { + char *label; + element *step = NULL; + step = list; + + label = label_from_string(key,0); + + while (step != NULL) { + if (step->key == METADATA) { + /* search METAKEY children */ + step = step->children; + while ( step != NULL) { + if (strcmp(step->contents.str, label) == 0) { + free(label); + return step; + } + step = step->next; + } + free(label); + return NULL; + } + step = step->next; + } + free(label); + return NULL; +} + + +/* find specified metadata key, if present */ +char * metavalue_for_key(char *key, element *list) { + char *label; + char *result; + element *step = NULL; + step = list; + + label = label_from_string(key,0); + + while (step != NULL) { + if (step->key == METADATA) { + /* search METAKEY children */ + step = step->children; + while ( step != NULL) { + if (strcmp(step->contents.str, label) == 0) { + /* Found a match */ + if ((strcmp(label,"latexmode") == 0) || + (strcmp(label,"quoteslanguage") == 0)) { + result = label_from_string(step->children->contents.str,0); + } else { + result = strdup(step->children->contents.str); + } + free(label); + return result; + } + step = step->next; + } + free(label); + return NULL; + } + step = step->next; + } + free(label); + return NULL; +} + +/* find attribute, if present */ +element * element_for_attribute(char *querystring, element *list) { + char *query; + element *step = NULL; + step = list; + query = label_from_string(querystring,0); + + while (step != NULL) { + if (strcmp(step->contents.str,query) == 0) { + free(query); + return step; + } + step = step->next; + } + free(query); + return NULL; +} + +/* convert attribute to dimensions suitable for LaTeX or ODF */ +/* returns c string that needs to be freed */ + +char * dimension_for_attribute(char *querystring, element *list) { + element *attribute; + char *dimension; + char *ptr; + int i; + char *upper; + GString *result; + + attribute = element_for_attribute(querystring, list); + if (attribute == NULL) return NULL; + + dimension = strdup(attribute->children->contents.str); + upper = strdup(attribute->children->contents.str); + + for(i = 0; dimension[ i ]; i++) + dimension[i] = tolower(dimension[ i ]); + + for(i = 0; upper[ i ]; i++) + upper[i] = toupper(upper[ i ]); + + if (strstr(dimension, "px")) { + ptr = strstr(dimension,"px"); + ptr[0] = '\0'; + strcat(ptr,"pt"); + } + + result = g_string_new(dimension); + + if ((strcmp(dimension,upper) == 0) && (dimension[strlen(dimension) -1] != '%')) { + /* no units */ + g_string_append_printf(result, "pt"); + } + + free(upper); + free(dimension); + + dimension = result->str; + g_string_free(result, false); + return(dimension); +} + +/* Check metadata keys and determine if I need a complete document */ +static bool is_html_complete_doc(element *meta) { + element *step; + step = meta->children; + + while (step != NULL) { + if ((strcmp(step->contents.str, "baseheaderlevel") != 0) && + (strcmp(step->contents.str, "xhtmlheaderlevel") != 0) && + (strcmp(step->contents.str, "htmlheaderlevel") != 0) && + (strcmp(step->contents.str, "latexheaderlevel") != 0) && + (strcmp(step->contents.str, "odfheaderlevel") != 0) && + (strcmp(step->contents.str, "quoteslanguage") != 0)) + { + return TRUE; + } + step = step->next; + } + + return FALSE; +} + +/* if citation has a locator, return as element and "prune", else NULL */ +element * locator_for_citation(element *elt) { + element *result; + + if ((elt->children != NULL) && (elt->children->key == LOCATOR)) { + /* Locator is present */ + result = elt->children; + elt->children = elt->children->next; + return result; + } else { + /* no locator exists */ + return NULL; + } +} + +/* print_opml_element_list - print an element list as OPML */ +void print_opml_element_list(GString *out, element *list) { + int lev; + while (list != NULL) { + if (list->key == HEADINGSECTION) { + lev = list->children->key; + + print_opml_section_and_children(out, list); + + while ((list->next != NULL) && (list->next->key == HEADINGSECTION) + && (list->next->children->key > lev)) { + list = list->next; + } + } else { + print_opml_element(out, list); + } + list = list->next; + } +} + +/* print_opml_section_and_children - print section and "children" */ +static void print_opml_section_and_children(GString *out, element *list) { + int lev = list->children->key; + /* Print current section, aka "parent" */ + print_opml_element(out, list); + + /* check for children */ + while ((list->next != NULL) && (list->next->key == HEADINGSECTION) + && (list->next->children->key > lev)) { + /* next item is also HEADINGSECTION and is child */ + if (list->next->children->key - lev == 1) + print_opml_section_and_children(out,list->next); + list = list->next; + } + g_string_append_printf(out, "\n"); +} + +/* print_opml_element - print an element as OPML */ +static void print_opml_element(GString *out, element *elt) { + switch (elt->key) { + case METADATA: + /* Metadata is present, so will need to be appended */ + html_footer = true; + break; + case METAKEY: + g_string_append_printf(out, "contents.str); + g_string_append_printf(out, "\" _note=\""); + print_opml_string(out, elt->children->contents.str); + g_string_append_printf(out, "\"/>"); + break; + case HEADINGSECTION: + /* Need to handle "nesting" properly */ + g_string_append_printf(out, "children); + + /* print remainder of paragraphs as note */ + g_string_append_printf(out, " _note=\""); + print_opml_element_list(out,elt->children->next); + g_string_append_printf(out, "\">"); + break; + case H1: case H2: case H3: case H4: case H5: case H6: + g_string_append_printf(out, "text=\""); + print_opml_string(out, elt->contents.str); + g_string_append_printf(out,"\""); + break; + case VERBATIM: + print_opml_string(out, elt->contents.str); + break; + case SPACE: + print_opml_string(out, elt->contents.str); + break; + case STR: + print_opml_string(out, elt->contents.str); + break; + case LINEBREAK: + g_string_append_printf(out, " "); + break; + case PLAIN: + print_opml_element_list(out,elt->children); + if ((elt->next != NULL) && (elt->next->key == PLAIN)) { + g_string_append_printf(out, " "); + } + break; + default: + fprintf(stderr, "print_opml_element encountered unknown element key = %d\n", elt->key); + /*exit(EXIT_FAILURE);*/ + } +} + +/* print_opml_string - print string, escaping for OPML */ +static void print_opml_string(GString *out, char *str) { + while (*str != '\0') { + switch (*str) { + case '&': + g_string_append_printf(out, "&"); + break; + case '<': + g_string_append_printf(out, "<"); + break; + case '>': + g_string_append_printf(out, ">"); + break; + case '"': + g_string_append_printf(out, """); + break; + case '\n': case '\r': + g_string_append_printf(out, " "); + break; + default: + g_string_append_c(out, *str); + } + str++; + } +} + + +/* print_opml_metadata - add metadata as last outline item */ +static void print_opml_metadata(GString *out, element *elt) { + g_string_append_printf(out, "\n"); + print_opml_element_list(out, elt->children); + g_string_append_printf(out, ""); +} + +/* print_odf_body_element - print an element as ODF */ +void print_odf_body_element(GString *out, element *elt) { + switch (elt->key) { + case PARA: + print_odf_element_list(out, elt->children); + break; + default: + print_odf_element(out, elt); + } +} + +/* print_odf_body_element_list - print an element list as ODF for specific + places, eg image captions */ +void print_odf_body_element_list(GString *out, element *list) { + while (list != NULL) { + print_odf_body_element(out, list); + list = list->next; + } +} + +/* bogus function just references a couple globals defined in utility_functions.c but not used in this source file */ +static void bogus_function() +{ + static char* bogus; + static element* bogus2; + bogus = charbuf; + bogus2 = parse_result; +} diff --git a/src/lib/multimarkdown/src/markdown_parser.bat b/src/lib/multimarkdown/src/markdown_parser.bat new file mode 100644 index 0000000..df823f1 --- /dev/null +++ b/src/lib/multimarkdown/src/markdown_parser.bat @@ -0,0 +1,3 @@ +cd /d %~dp0 +cd ../peg/leg +leg.exe -o ../../src/markdown_parser.c ../../src/markdown_parser.leg \ No newline at end of file diff --git a/src/lib/multimarkdown/src/markdown_parser.leg b/src/lib/multimarkdown/src/markdown_parser.leg new file mode 100644 index 0000000..d5b8d27 --- /dev/null +++ b/src/lib/multimarkdown/src/markdown_parser.leg @@ -0,0 +1,1358 @@ +%{ +/********************************************************************** + + markdown_parser.leg - markdown parser in C using a PEG grammar. + (c) 2008 John MacFarlane (jgm at berkeley dot edu). + + portions Copyright (c) 2010-2011 Fletcher T. Penney + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License or the MIT + license. See LICENSE for details. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + ***********************************************************************/ + +#include +#include +#include "markdown_peg.h" +#include "utility_functions.h" + + + +/********************************************************************** + + Definitions for leg parser generator. + YY_INPUT is the function the parser calls to get new input. + We take all new input from (static) charbuf. + + ***********************************************************************/ + + + +# define YYSTYPE element * +#ifdef __DEBUG__ +# define YY_DEBUG 1 +#endif + +#define YY_INPUT(buf, result, max_size) \ +{ \ + int yyc; \ + if (charbuf && *charbuf != '\0') { \ + yyc= *charbuf++; \ + } else { \ + yyc= EOF; \ + } \ + result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \ +} + +#define YY_RULE(T) T + + +#define YY_DEBUG_OFF + +/********************************************************************** + + PEG grammar and parser actions for markdown syntax. + + ***********************************************************************/ + +%} + +Doc = BOM? a:StartList ( Block { a = cons($$, a); } )* + { parse_result = reverse(a); } + +DocWithMetaData = BOM? a:StartList b:StartList + ( &{ !extension(EXT_COMPATIBILITY) } + &( MetaDataKey Sp ':' Sp (!Newline)) MetaData + { a = cons($$, a); b = mk_element(FOOTER);})? + ( Block { a = cons($$, a); } )* + { if (b != NULL) a = cons(b, a); + parse_result = reverse(a); + } + +MetaData = a:StartList !([A-Za-z]+ "://") + (MetaDataKeyValue { a = cons($$, a); })+ + { $$ = mk_list(LIST, a); + $$->key = METADATA; + } + +MetaDataOnly = BOM? a:StartList + ( MetaData { a = cons($$, a); } )? +# SkipBlock* + .* + { parse_result = reverse(a); } + +MetaDataOnly2 = BOM? a:StartList + ( MetaData { a = cons($$, a); } )? +# SkipBlock* + .* + { parse_result = mk_list(LIST,a); } + +MetaDataKeyValue = a:MetaDataKey + Sp ':' Sp b:MetaDataValue + { $$ = a; + $$->children = b; + } + +MetaDataKey = < !([A-Za-z]+ "://") AlphanumericAscii ( Sp ( AlphanumericAscii | '_' | ' ' | '-')+)* > +{ + char *label = label_from_string(yytext,0); + $$ = mk_str(label); + free(label); + $$->key = METAKEY; +} + +SingleLineMetaKeyValue = MetaDataKey Sp ':' Sp (!Newline .)* + +MetaDataValue = a:StartList + ((< (!Newline .)* > { a = cons(mk_str(yytext), a); }) + ((Newline &(!BlankLine !SingleLineMetaKeyValue Sp RawLine)) + { a = cons(mk_str("\n"), a);} | Newline) + (!BlankLine !SingleLineMetaKeyValue Sp RawLine + { a = cons(mk_str(yytext), a);} )* ) + { $$ = mk_str_from_list(a,false); + trim_trailing_whitespace($$->contents.str); + $$->key = METAVALUE; + } + +Block = BlankLine* + ( BlockQuote + | Verbatim + | &{ !extension(EXT_COMPATIBILITY) } DefinitionList + | &{ !extension(EXT_COMPATIBILITY) } Glossary + | Note + | Reference + | HorizontalRule + | HeadingSection + | OrderedList + | BulletList + | HtmlBlock + | MarkdownHtmlBlock + | StyleBlock + | &{ !extension(EXT_COMPATIBILITY) } Table + | &{ !extension(EXT_COMPATIBILITY) } ImageBlock + | !(Sp? HtmlBlockOpenDiv) Para + | Plain ) + +HeadingSectionBlock = + BlankLine* + !Heading + ( BlockQuote + | Verbatim + | &{ !extension(EXT_COMPATIBILITY) } DefinitionList + | &{ !extension(EXT_COMPATIBILITY) } Glossary + | Note + | Reference + | HorizontalRule + | OrderedList + | BulletList + | HtmlBlock + | MarkdownHtmlBlock + | StyleBlock + | &{ !extension(EXT_COMPATIBILITY) } Table + | &{ !extension(EXT_COMPATIBILITY) } ImageBlock + | !(Sp? HtmlBlockOpenDiv) Para + | Plain ) + +Para = NonindentSpace a:Inlines BlankLine+ + { $$ = a; $$->key = PARA; } + +Plain = a:Inlines + { $$ = a; $$->key = PLAIN; } + +AtxInline = !Newline !( &{ !extension(EXT_COMPATIBILITY) } Sp AutoLabel Sp? '#'* Sp Newline) !(Sp? '#'* Sp Newline) Inline + +AtxStart = < ( "######" | "#####" | "####" | "###" | "##" | "#" ) > + { $$ = mk_element(H1 + (strlen(yytext) - 1)); } + +AtxHeading = s:AtxStart Sp? a:StartList ( AtxInline { a = cons($$, a); } )+ ( Sp? b:AutoLabel { append_list(b,a);})? (Sp? '#'* Sp)? Newline + { $$ = mk_list(s->key,a); + free(s); } + +SetextHeading = SetextHeading1 | SetextHeading2 + +SetextBottom1 = '='+ Newline + +SetextBottom2 = '-'+ Newline + +SetextHeading1 = &(RawLine SetextBottom1) + a:StartList ( !Endline !( &{ !extension(EXT_COMPATIBILITY) } Sp AutoLabel ) Inline { a = cons($$, a); } )+ ( Sp b:AutoLabel { append_list(b,a);} Sp? )? Sp? Newline + SetextBottom1 { $$ = mk_list(H1, a); } + +SetextHeading2 = &(RawLine SetextBottom2) +a:StartList ( !Endline !( &{ !extension(EXT_COMPATIBILITY) } Sp AutoLabel ) Inline { a = cons($$, a); } )+ ( Sp b:AutoLabel { append_list(b,a)} Sp? )? Sp? Newline + SetextBottom2 { $$ = mk_list(H2, a); } + +Heading = SetextHeading | AtxHeading + +HeadingSection = a:StartList Heading { a = cons($$, a); } + (HeadingSectionBlock {a = cons($$, a); })* + { $$ = mk_list(HEADINGSECTION, a);} + +BlockQuote = a:BlockQuoteRaw + { $$ = mk_element(BLOCKQUOTE); + $$->children = a; + } + +BlockQuoteRaw = a:StartList + (( '>' ' '? Line { a = cons($$, a); } ) + ( !'>' !BlankLine Line { a = cons($$, a); } )* + ( BlankLine { a = cons(mk_str("\n"), a); } )* + )+ + { $$ = mk_str_from_list(a, true); + $$->key = RAW; + } + +NonblankIndentedLine = !BlankLine IndentedLine + +VerbatimChunk = a:StartList + ( BlankLine { a = cons(mk_str("\n"), a); } )* + ( NonblankIndentedLine { a = cons($$, a); } )+ + { $$ = mk_str_from_list(a, false); } + +Verbatim = a:StartList ( VerbatimChunk { a = cons($$, a); } )+ BlankLine* + { $$ = mk_str_from_list(a, false); + $$->key = VERBATIM; } + +HorizontalRule = NonindentSpace + ( '*' Sp '*' Sp '*' (Sp '*')* + | '-' Sp '-' Sp '-' (Sp '-')* + | '_' Sp '_' Sp '_' (Sp '_')*) + Sp Newline BlankLine+ + { $$ = mk_element(HRULE); } + +Bullet = !HorizontalRule NonindentSpace ('+' | '*' | '-') Spacechar+ + +BulletList = &Bullet (ListTight | ListLoose) + { $$->key = BULLETLIST; } + +ListTight = a:StartList + ( ListItemTight { a = cons($$, a); } )+ + BlankLine* !(Bullet | Enumerator) + { $$ = mk_list(LIST, a); } + +ListLoose = a:StartList + ( b:ListItem BlankLine* + { element *li; + li = b->children; + li->contents.str = realloc(li->contents.str, strlen(li->contents.str) + 3); + strcat(li->contents.str, "\n\n"); /* In loose list, \n\n added to end of each element */ + a = cons(b, a); + } )+ + { $$ = mk_list(LIST, a); } + +ListItem = ( Bullet | Enumerator ) + a:StartList + ListBlock { a = cons($$, a); } + ( ListContinuationBlock { a = cons($$, a); } )* + { element *raw; + raw = mk_str_from_list(a, false); + raw->key = RAW; + $$ = mk_element(LISTITEM); + $$->children = raw; + } + +ListItemTight = + ( Bullet | Enumerator ) + a:StartList + ListBlock { a = cons($$, a); } + ( !BlankLine + ListContinuationBlock { a = cons($$, a); } )* + !ListContinuationBlock + { element *raw; + raw = mk_str_from_list(a, false); + raw->key = RAW; + $$ = mk_element(LISTITEM); + $$->children = raw; + } + +ListBlock = a:StartList + !BlankLine Line { a = cons($$, a); } + ( ListBlockLine { a = cons($$, a); } )* + { $$ = mk_str_from_list(a, false); } + +ListContinuationBlock = a:StartList + ( < BlankLine* > + { if (strlen(yytext) == 0) + a = cons(mk_str("\001"), a); /* block separator */ + else + a = cons(mk_str(yytext), a); } ) + ( Indent ListBlock { a = cons($$, a); } )+ + { $$ = mk_str_from_list(a, false); } + +Enumerator = NonindentSpace [0-9]+ '.' Spacechar+ + +OrderedList = &Enumerator (ListTight | ListLoose) + { $$->key = ORDEREDLIST; } + +ListBlockLine = !BlankLine + !( Indent? (Bullet | Enumerator) ) + !HorizontalRule + OptionallyIndentedLine + +# Parsers for different kinds of block-level HTML content. +# This is repetitive due to constraints of PEG grammar. + +HtmlBlockOpenAddress = '<' Spnl ("address" | "ADDRESS") Spnl HtmlAttribute* '>' +HtmlBlockCloseAddress = '<' Spnl '/' ("address" | "ADDRESS") Spnl '>' +HtmlBlockAddress = HtmlBlockOpenAddress (HtmlBlockAddress | !HtmlBlockCloseAddress .)* HtmlBlockCloseAddress + +HtmlBlockOpenArticle = '<' Spnl ("article" | "ARTICLE") Spnl HtmlAttribute* '>' +HtmlBlockCloseArticle = '<' Spnl '/' ("article" | "ARTICLE") Spnl '>' +HtmlBlockArticle = HtmlBlockOpenArticle (HtmlBlockArticle | !HtmlBlockCloseArticle .)* HtmlBlockCloseArticle + +HtmlBlockOpenAside = '<' Spnl ("aside" | "ASIDE") Spnl HtmlAttribute* '>' +HtmlBlockCloseAside = '<' Spnl '/' ("aside" | "ASIDE") Spnl '>' +HtmlBlockAside = HtmlBlockOpenAside (HtmlBlockAside | !HtmlBlockCloseAside .)* HtmlBlockCloseAside + +HtmlBlockOpenBlockquote = '<' Spnl ("blockquote" | "BLOCKQUOTE") Spnl HtmlAttribute* '>' +HtmlBlockCloseBlockquote = '<' Spnl '/' ("blockquote" | "BLOCKQUOTE") Spnl '>' +HtmlBlockBlockquote = HtmlBlockOpenBlockquote (HtmlBlockBlockquote | !HtmlBlockCloseBlockquote .)* HtmlBlockCloseBlockquote + +HtmlBlockOpenCanvas = '<' Spnl ("canvas" | "CANVAS") Spnl HtmlAttribute* '>' +HtmlBlockCloseCanvas = '<' Spnl '/' ("canvas" | "CANVAS") Spnl '>' +HtmlBlockCanvas = HtmlBlockOpenCanvas (HtmlBlockCanvas | !HtmlBlockCloseCanvas .)* HtmlBlockCloseCanvas + +HtmlBlockOpenCenter = '<' Spnl ("center" | "CENTER") Spnl HtmlAttribute* '>' +HtmlBlockCloseCenter = '<' Spnl '/' ("center" | "CENTER") Spnl '>' +HtmlBlockCenter = HtmlBlockOpenCenter (HtmlBlockCenter | !HtmlBlockCloseCenter .)* HtmlBlockCloseCenter + +HtmlBlockOpenDir = '<' Spnl ("dir" | "DIR") Spnl HtmlAttribute* '>' +HtmlBlockCloseDir = '<' Spnl '/' ("dir" | "DIR") Spnl '>' +HtmlBlockDir = HtmlBlockOpenDir (HtmlBlockDir | !HtmlBlockCloseDir .)* HtmlBlockCloseDir + +HtmlBlockOpenDiv = '<' Spnl ("div" | "DIV") Spnl HtmlAttribute* '>' +HtmlBlockCloseDiv = '<' Spnl '/' ("div" | "DIV") Spnl '>' +HtmlBlockDiv = HtmlBlockOpenDiv (HtmlBlockDiv | !HtmlBlockCloseDiv .)* HtmlBlockCloseDiv + +HtmlBlockOpenDl = '<' Spnl ("dl" | "DL") Spnl HtmlAttribute* '>' +HtmlBlockCloseDl = '<' Spnl '/' ("dl" | "DL") Spnl '>' +HtmlBlockDl = HtmlBlockOpenDl (HtmlBlockDl | !HtmlBlockCloseDl .)* HtmlBlockCloseDl + +HtmlBlockOpenFieldset = '<' Spnl ("fieldset" | "FIELDSET") Spnl HtmlAttribute* '>' +HtmlBlockCloseFieldset = '<' Spnl '/' ("fieldset" | "FIELDSET") Spnl '>' +HtmlBlockFieldset = HtmlBlockOpenFieldset (HtmlBlockFieldset | !HtmlBlockCloseFieldset .)* HtmlBlockCloseFieldset + +HtmlBlockOpenFigure = '<' Spnl ("figure" | "FIGURE") Spnl HtmlAttribute* '>' +HtmlBlockCloseFigure = '<' Spnl '/' ("figure" | "FIGURE") Spnl '>' +HtmlBlockFigure = HtmlBlockOpenFigure (HtmlBlockFigure | !HtmlBlockCloseFigure .)* HtmlBlockCloseFigure + +HtmlBlockOpenFooter = '<' Spnl ("footer" | "FOOTER") Spnl HtmlAttribute* '>' +HtmlBlockCloseFooter = '<' Spnl '/' ("footer" | "FOOTER") Spnl '>' +HtmlBlockFooter = HtmlBlockOpenFooter (HtmlBlockFooter | !HtmlBlockCloseFooter .)* HtmlBlockCloseFooter + +HtmlBlockOpenForm = '<' Spnl ("form" | "FORM") Spnl HtmlAttribute* '>' +HtmlBlockCloseForm = '<' Spnl '/' ("form" | "FORM") Spnl '>' +HtmlBlockForm = HtmlBlockOpenForm (HtmlBlockForm | !HtmlBlockCloseForm .)* HtmlBlockCloseForm + +HtmlBlockOpenHeader = '<' Spnl ("header" | "HEADER") Spnl HtmlAttribute* '>' +HtmlBlockCloseHeader = '<' Spnl '/' ("header" | "HEADER") Spnl '>' +HtmlBlockHeader = HtmlBlockOpenHeader (HtmlBlockHeader | !HtmlBlockCloseHeader .)* HtmlBlockCloseHeader + +HtmlBlockOpenHgroup = '<' Spnl ("hgroup" | "HGROUP") Spnl HtmlAttribute* '>' +HtmlBlockCloseHgroup = '<' Spnl '/' ("hgroup" | "HGROUP") Spnl '>' +HtmlBlockHgroup = HtmlBlockOpenHgroup (HtmlBlockHgroup | !HtmlBlockCloseHgroup .)* HtmlBlockCloseHgroup + +HtmlBlockOpenH1 = '<' Spnl ("h1" | "H1") Spnl HtmlAttribute* '>' +HtmlBlockCloseH1 = '<' Spnl '/' ("h1" | "H1") Spnl '>' +HtmlBlockH1 = HtmlBlockOpenH1 (HtmlBlockH1 | !HtmlBlockCloseH1 .)* HtmlBlockCloseH1 + +HtmlBlockOpenH2 = '<' Spnl ("h2" | "H2") Spnl HtmlAttribute* '>' +HtmlBlockCloseH2 = '<' Spnl '/' ("h2" | "H2") Spnl '>' +HtmlBlockH2 = HtmlBlockOpenH2 (HtmlBlockH2 | !HtmlBlockCloseH2 .)* HtmlBlockCloseH2 + +HtmlBlockOpenH3 = '<' Spnl ("h3" | "H3") Spnl HtmlAttribute* '>' +HtmlBlockCloseH3 = '<' Spnl '/' ("h3" | "H3") Spnl '>' +HtmlBlockH3 = HtmlBlockOpenH3 (HtmlBlockH3 | !HtmlBlockCloseH3 .)* HtmlBlockCloseH3 + +HtmlBlockOpenH4 = '<' Spnl ("h4" | "H4") Spnl HtmlAttribute* '>' +HtmlBlockCloseH4 = '<' Spnl '/' ("h4" | "H4") Spnl '>' +HtmlBlockH4 = HtmlBlockOpenH4 (HtmlBlockH4 | !HtmlBlockCloseH4 .)* HtmlBlockCloseH4 + +HtmlBlockOpenH5 = '<' Spnl ("h5" | "H5") Spnl HtmlAttribute* '>' +HtmlBlockCloseH5 = '<' Spnl '/' ("h5" | "H5") Spnl '>' +HtmlBlockH5 = HtmlBlockOpenH5 (HtmlBlockH5 | !HtmlBlockCloseH5 .)* HtmlBlockCloseH5 + +HtmlBlockOpenH6 = '<' Spnl ("h6" | "H6") Spnl HtmlAttribute* '>' +HtmlBlockCloseH6 = '<' Spnl '/' ("h6" | "H6") Spnl '>' +HtmlBlockH6 = HtmlBlockOpenH6 (HtmlBlockH6 | !HtmlBlockCloseH6 .)* HtmlBlockCloseH6 + +HtmlBlockOpenMenu = '<' Spnl ("menu" | "MENU") Spnl HtmlAttribute* '>' +HtmlBlockCloseMenu = '<' Spnl '/' ("menu" | "MENU") Spnl '>' +HtmlBlockMenu = HtmlBlockOpenMenu (HtmlBlockMenu | !HtmlBlockCloseMenu .)* HtmlBlockCloseMenu + +HtmlBlockOpenNoframes = '<' Spnl ("noframes" | "NOFRAMES") Spnl HtmlAttribute* '>' +HtmlBlockCloseNoframes = '<' Spnl '/' ("noframes" | "NOFRAMES") Spnl '>' +HtmlBlockNoframes = HtmlBlockOpenNoframes (HtmlBlockNoframes | !HtmlBlockCloseNoframes .)* HtmlBlockCloseNoframes + +HtmlBlockOpenNoscript = '<' Spnl ("noscript" | "NOSCRIPT") Spnl HtmlAttribute* '>' +HtmlBlockCloseNoscript = '<' Spnl '/' ("noscript" | "NOSCRIPT") Spnl '>' +HtmlBlockNoscript = HtmlBlockOpenNoscript (HtmlBlockNoscript | !HtmlBlockCloseNoscript .)* HtmlBlockCloseNoscript + +HtmlBlockOpenOl = '<' Spnl ("ol" | "OL") Spnl HtmlAttribute* '>' +HtmlBlockCloseOl = '<' Spnl '/' ("ol" | "OL") Spnl '>' +HtmlBlockOl = HtmlBlockOpenOl (HtmlBlockOl | !HtmlBlockCloseOl .)* HtmlBlockCloseOl + +HtmlBlockOpenP = '<' Spnl ("p" | "P") Spnl HtmlAttribute* '>' +HtmlBlockCloseP = '<' Spnl '/' ("p" | "P") Spnl '>' +HtmlBlockP = HtmlBlockOpenP (HtmlBlockP | !HtmlBlockCloseP .)* HtmlBlockCloseP + +HtmlBlockOpenPre = '<' Spnl ("pre" | "PRE") Spnl HtmlAttribute* '>' +HtmlBlockClosePre = '<' Spnl '/' ("pre" | "PRE") Spnl '>' +HtmlBlockPre = HtmlBlockOpenPre (HtmlBlockPre | !HtmlBlockClosePre .)* HtmlBlockClosePre + +HtmlBlockOpenProgress = '<' Spnl ("progress" | "PROGRESS") Spnl HtmlAttribute* '>' +HtmlBlockCloseProgress = '<' Spnl '/' ("progress" | "PROGRESS") Spnl '>' +HtmlBlockProgress = HtmlBlockOpenProgress (HtmlBlockProgress | !HtmlBlockCloseProgress .)* HtmlBlockCloseProgress + +HtmlBlockOpenSection = '<' Spnl ("section" | "SECTION") Spnl HtmlAttribute* '>' +HtmlBlockCloseSection = '<' Spnl '/' ("section" | "SECTION") Spnl '>' +HtmlBlockSection = HtmlBlockOpenSection (HtmlBlockSection | !HtmlBlockCloseSection .)* HtmlBlockCloseSection + +HtmlBlockOpenTable = '<' Spnl ("table" | "TABLE") Spnl HtmlAttribute* '>' +HtmlBlockCloseTable = '<' Spnl '/' ("table" | "TABLE") Spnl '>' +HtmlBlockTable = HtmlBlockOpenTable (HtmlBlockTable | !HtmlBlockCloseTable .)* HtmlBlockCloseTable + +HtmlBlockOpenUl = '<' Spnl ("ul" | "UL") Spnl HtmlAttribute* '>' +HtmlBlockCloseUl = '<' Spnl '/' ("ul" | "UL") Spnl '>' +HtmlBlockUl = HtmlBlockOpenUl (HtmlBlockUl | !HtmlBlockCloseUl .)* HtmlBlockCloseUl + +HtmlBlockOpenVideo = '<' Spnl ("video" | "VIDEO") Spnl HtmlAttribute* '>' +HtmlBlockCloseVideo = '<' Spnl '/' ("video" | "VIDEO") Spnl '>' +HtmlBlockVideo = HtmlBlockOpenVideo (HtmlBlockVideo | !HtmlBlockCloseVideo .)* HtmlBlockCloseVideo + +HtmlBlockOpenDd = '<' Spnl ("dd" | "DD") Spnl HtmlAttribute* '>' +HtmlBlockCloseDd = '<' Spnl '/' ("dd" | "DD") Spnl '>' +HtmlBlockDd = HtmlBlockOpenDd (HtmlBlockDd | !HtmlBlockCloseDd .)* HtmlBlockCloseDd + +HtmlBlockOpenDt = '<' Spnl ("dt" | "DT") Spnl HtmlAttribute* '>' +HtmlBlockCloseDt = '<' Spnl '/' ("dt" | "DT") Spnl '>' +HtmlBlockDt = HtmlBlockOpenDt (HtmlBlockDt | !HtmlBlockCloseDt .)* HtmlBlockCloseDt + +HtmlBlockOpenFrameset = '<' Spnl ("frameset" | "FRAMESET") Spnl HtmlAttribute* '>' +HtmlBlockCloseFrameset = '<' Spnl '/' ("frameset" | "FRAMESET") Spnl '>' +HtmlBlockFrameset = HtmlBlockOpenFrameset (HtmlBlockFrameset | !HtmlBlockCloseFrameset .)* HtmlBlockCloseFrameset + +HtmlBlockOpenLi = '<' Spnl ("li" | "LI") Spnl HtmlAttribute* '>' +HtmlBlockCloseLi = '<' Spnl '/' ("li" | "LI") Spnl '>' +HtmlBlockLi = HtmlBlockOpenLi (HtmlBlockLi | !HtmlBlockCloseLi .)* HtmlBlockCloseLi + +HtmlBlockOpenTbody = '<' Spnl ("tbody" | "TBODY") Spnl HtmlAttribute* '>' +HtmlBlockCloseTbody = '<' Spnl '/' ("tbody" | "TBODY") Spnl '>' +HtmlBlockTbody = HtmlBlockOpenTbody (HtmlBlockTbody | !HtmlBlockCloseTbody .)* HtmlBlockCloseTbody + +HtmlBlockOpenTd = '<' Spnl ("td" | "TD") Spnl HtmlAttribute* '>' +HtmlBlockCloseTd = '<' Spnl '/' ("td" | "TD") Spnl '>' +HtmlBlockTd = HtmlBlockOpenTd (HtmlBlockTd | !HtmlBlockCloseTd .)* HtmlBlockCloseTd + +HtmlBlockOpenTfoot = '<' Spnl ("tfoot" | "TFOOT") Spnl HtmlAttribute* '>' +HtmlBlockCloseTfoot = '<' Spnl '/' ("tfoot" | "TFOOT") Spnl '>' +HtmlBlockTfoot = HtmlBlockOpenTfoot (HtmlBlockTfoot | !HtmlBlockCloseTfoot .)* HtmlBlockCloseTfoot + +HtmlBlockOpenTh = '<' Spnl ("th" | "TH") Spnl HtmlAttribute* '>' +HtmlBlockCloseTh = '<' Spnl '/' ("th" | "TH") Spnl '>' +HtmlBlockTh = HtmlBlockOpenTh (HtmlBlockTh | !HtmlBlockCloseTh .)* HtmlBlockCloseTh + +HtmlBlockOpenThead = '<' Spnl ("thead" | "THEAD") Spnl HtmlAttribute* '>' +HtmlBlockCloseThead = '<' Spnl '/' ("thead" | "THEAD") Spnl '>' +HtmlBlockThead = HtmlBlockOpenThead (HtmlBlockThead | !HtmlBlockCloseThead .)* HtmlBlockCloseThead + +HtmlBlockOpenTr = '<' Spnl ("tr" | "TR") Spnl HtmlAttribute* '>' +HtmlBlockCloseTr = '<' Spnl '/' ("tr" | "TR") Spnl '>' +HtmlBlockTr = HtmlBlockOpenTr (HtmlBlockTr | !HtmlBlockCloseTr .)* HtmlBlockCloseTr + +HtmlBlockOpenScript = '<' Spnl ("script" | "SCRIPT") Spnl HtmlAttribute* '>' +HtmlBlockCloseScript = '<' Spnl '/' ("script" | "SCRIPT") Spnl '>' +HtmlBlockScript = HtmlBlockOpenScript (!HtmlBlockCloseScript .)* HtmlBlockCloseScript + + +HtmlBlockInTags = HtmlBlockAddress + | HtmlBlockArticle + | HtmlBlockAside + | HtmlBlockCanvas + | HtmlBlockBlockquote + | HtmlBlockCenter + | HtmlBlockDir + | HtmlBlockDiv + | HtmlBlockDl + | HtmlBlockFieldset + | HtmlBlockFigure + | HtmlBlockFooter + | HtmlBlockForm + | HtmlBlockHeader + | HtmlBlockHgroup + | HtmlBlockH1 + | HtmlBlockH2 + | HtmlBlockH3 + | HtmlBlockH4 + | HtmlBlockH5 + | HtmlBlockH6 + | HtmlBlockMenu + | HtmlBlockNoframes + | HtmlBlockNoscript + | HtmlBlockOl + | HtmlBlockP + | HtmlBlockPre + | HtmlBlockProgress + | HtmlBlockSection + | HtmlBlockTable + | HtmlBlockUl + | HtmlBlockVideo + | HtmlBlockDd + | HtmlBlockDt + | HtmlBlockFrameset + | HtmlBlockLi + | HtmlBlockTbody + | HtmlBlockTd + | HtmlBlockTfoot + | HtmlBlockTh + | HtmlBlockThead + | HtmlBlockTr + | HtmlBlockScript + +HtmlBlock = !MarkdownHtmlTagOpen < ( HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing ) > + BlankLine+ + { if (extension(EXT_FILTER_HTML)) { + $$ = mk_list(LIST, NULL); + } else { + $$ = mk_str(yytext); + if ( extension(EXT_PROCESS_HTML)) $$->key = RAW; + else $$->key = HTMLBLOCK; + } + } + +MarkdownHtmlBlock = &MarkdownHtmlTagOpen < ( HtmlBlockInTags | HtmlComment | HtmlBlockSelfClosing) > + BlankLine+ + { $$ = mk_str(yytext); + $$->key = RAW; + } + +HtmlBlockSelfClosing = '<' Spnl HtmlBlockType Spnl HtmlAttribute* '/' Spnl '>' + +HtmlBlockType = "address" | "blockquote" | "center" | "dir" | "div" | "dl" | "fieldset" | "form" | "h1" | "h2" | "h3" | + "h4" | "h5" | "h6" | "hr" | "isindex" | "menu" | "noframes" | "noscript" | "ol" | "p" | "pre" | "table" | + "ul" | "dd" | "dt" | "frameset" | "li" | "tbody" | "td" | "tfoot" | "th" | "thead" | "tr" | "script" | + "ADDRESS" | "BLOCKQUOTE" | "CENTER" | "DIR" | "DIV" | "DL" | "FIELDSET" | "FORM" | "H1" | "H2" | "H3" | + "H4" | "H5" | "H6" | "HR" | "ISINDEX" | "MENU" | "NOFRAMES" | "NOSCRIPT" | "OL" | "P" | "PRE" | "TABLE" | + "UL" | "DD" | "DT" | "FRAMESET" | "LI" | "TBODY" | "TD" | "TFOOT" | "TH" | "THEAD" | "TR" | "SCRIPT" + +StyleOpen = '<' Spnl ("style" | "STYLE") Spnl HtmlAttribute* '>' +StyleClose = '<' Spnl '/' ("style" | "STYLE") Spnl '>' +InStyleTags = StyleOpen (!StyleClose .)* StyleClose +StyleBlock = < InStyleTags > + BlankLine* + { if (extension(EXT_FILTER_STYLES)) { + $$ = mk_list(LIST, NULL); + } else { + $$ = mk_str(yytext); + $$->key = HTMLBLOCK; + } + } + +Inlines = a:StartList ( !Endline Inline { a = cons($$, a); } + | c:Endline &Inline { a = cons(c, a); } )+ Endline? + { $$ = mk_list(LIST, a); } + +Inline = &{ check_timeout() } + Str + | &{ !extension(EXT_COMPATIBILITY) } MathSpan + | Endline + | UlOrStarLine + | Space + | Strong + | Emph + | &{ !extension(EXT_COMPATIBILITY) } CitationReference + | Image + | Link + | NoteReference + # | InlineNote # Not used in Markdown/MultiMarkdown + | Code + | MarkdownHtmlTagOpen + | RawHtml + | Entity + | EscapedChar + | Smart + | Symbol + +Space = Spacechar+ + { $$ = mk_str(" "); + $$->key = SPACE; } + +Str = a:StartList < NormalChar+ > { a = cons(mk_str(yytext), a); } + ( StrChunk { a = cons($$, a); } )* + { if (a->next == NULL) { $$ = a; } else { $$ = mk_list(LIST, a); } } + +StrChunk = < (NormalChar | '_'+ &Alphanumeric)+ > { $$ = mk_str(yytext); } | + AposChunk + +AposChunk = &{ extension(EXT_SMART) } '\'' &Alphanumeric + { $$ = mk_element(APOSTROPHE); } + +EscapedChar = '\\' !Newline < [-\\`|*_{}[\]()#+.!><] > + { $$ = mk_str(yytext); } + +Entity = ( HexEntity | DecEntity | CharEntity ) + { $$ = mk_str(yytext); $$->key = HTML; } + +Endline = LineBreak | TerminalEndline | NormalEndline + +NormalEndline = Sp Newline !BlankLine !'>' !AtxStart + !(Line ('='+ | '-'+) Newline) + { $$ = mk_str("\n"); + $$->key = SPACE; } + +TerminalEndline = Sp Newline Eof + { $$ = NULL; } + +LineBreak = " " NormalEndline + { $$ = mk_element(LINEBREAK); } + +Symbol = < SpecialChar > + { $$ = mk_str(yytext); } + +# This keeps the parser from getting bogged down on long strings of '*' or '_', +# or strings of '*' or '_' with space on each side: +UlOrStarLine = (UlLine | StarLine) { $$ = mk_str(yytext); } +StarLine = < "****" '*'* > | < Spacechar '*'+ &Spacechar > +UlLine = < "____" '_'* > | < Spacechar '_'+ &Spacechar > + +Emph = EmphStar | EmphUl + +Whitespace = Spacechar | Newline + +EmphStar = '*' !Whitespace + a:StartList + ( !'*' b:Inline { a = cons(b, a); } + | b:StrongStar { a = cons(b, a); } + )+ + '*' + { $$ = mk_list(EMPH, a); } + +EmphUl = '_' !Whitespace + a:StartList + ( !'_' b:Inline { a = cons(b, a); } + | b:StrongUl { a = cons(b, a); } + )+ + '_' + { $$ = mk_list(EMPH, a); } + +Strong = StrongStar | StrongUl + +StrongStar = "**" !Whitespace + a:StartList + ( !"**" b:Inline { a = cons(b, a); })+ + "**" + { $$ = mk_list(STRONG, a); } + +StrongUl = "__" !Whitespace + a:StartList + ( !"__" b:Inline { a = cons(b, a); })+ + "__" + { $$ = mk_list(STRONG, a); } + + +ImageBlock = Image Sp Newline BlankLine+ + { if ($$->key == IMAGE) $$->key = IMAGEBLOCK; } + +Image = '!' ( ExplicitLink | ReferenceLink ) + { if ($$->key == LINK) { + $$->key = IMAGE; + } else { + element *result; + result = $$; + $$->children = cons(mk_str("!"), result->children); + } } + +Link = ExplicitLink | ReferenceLink | AutoLink + +ReferenceLink = ReferenceLinkDouble | ReferenceLinkSingle + +ReferenceLinkDouble = a:Label < Spnl > !"[]" b:Label + { link match; + if (find_reference(&match, b->children)) { + $$ = mk_link(a->children, match.url, match.title, match.attr, match.identifier); + free(a); + free_element_list(b); + } else if ( !extension(EXT_COMPATIBILITY) && + find_label(&match, b->children)) { + char *lab; + GString *label; + GString *text = g_string_new(""); + print_raw_element_list(text, b->children); + lab = label_from_string(text->str,0); + label = g_string_new(lab); + g_string_prepend(label,"#"); + $$ = mk_link(a->children, label->str, "", NULL, lab); + free(lab); + g_string_free(text, TRUE); + g_string_free(label, TRUE); + free(a); + free_element_list(b); + } else { + element *result; + result = mk_element(LIST); + result->children = cons(mk_str("["), cons(a, cons(mk_str("]"), cons(mk_str("["), cons(b, mk_str("]")))))); + $$ = result; + } + } + +ReferenceLinkSingle = a:Label < (Spnl "[]")? > + { link match; + if (find_reference(&match, a->children)) { + $$ = mk_link(a->children, match.url, match.title, match.attr, match.identifier); + free(a); + } else if ( !extension(EXT_COMPATIBILITY) && + find_label(&match, a->children)) { + char *lab; + GString *label; + GString *text = g_string_new(""); + print_raw_element_list(text, a->children); + lab = label_from_string(text->str,0); + label = g_string_new(lab); + g_string_prepend(label,"#"); + $$ = mk_link(a->children, label->str, "", NULL, lab); + g_string_free(text, TRUE); + g_string_free(label, TRUE); + free(lab); + free(a); + } else { + element *result; + result = mk_element(LIST); + result->children = cons(mk_str("["), cons(a, cons(mk_str("]"), mk_str(yytext)))); + $$ = result; + } + } + +ExplicitLink = l:Label '(' Sp s:Source Spnl t:Title Sp ')' + { + $$ = mk_link(l->children, s->contents.str, t->contents.str, NULL, ""); + free_element(s); + free_element(t); + free(l); + } + +Source = ( '<' < SourceContents > '>' | < SourceContents > ) + { $$ = mk_str(yytext); } + +SourceContents = ( ( !'(' !')' !'>' Nonspacechar )+ | '(' SourceContents ')')* + +Title = ( TitleSingle | TitleDouble | < "" > ) + { $$ = mk_str(yytext); } + +TitleSingle = '\'' < ( !( '\'' Sp ( ')' | Newline ) ) . )* > '\'' + +TitleDouble = '"' < ( !( '"' Sp ( ')' | Newline ) ) . )* > '"' + +AutoLink = AutoLinkUrl | AutoLinkEmail + +AutoLinkUrl = '<' < [A-Za-z]+ "://" ( !Newline !'>' . )+ > '>' + { $$ = mk_link(mk_str(yytext), yytext, "", NULL, ""); } + +AutoLinkEmail = '<' ( "mailto:" )? < [-A-Za-z0-9+_./!%~$]+ '@' ( !Newline !'>' . )+ > '>' + { char *mailto = malloc(strlen(yytext) + 8); + sprintf(mailto, "mailto:%s", yytext); + $$ = mk_link(mk_str(yytext), mailto, "", NULL, ""); + free(mailto); + } + +Reference = a:StartList NonindentSpace !"[]" l:Label ':' Spnl s:RefSrc + t:RefTitle + ( &{ !extension(EXT_COMPATIBILITY) } + (Attributes { a = cons($$,a);})? )? + BlankLine+ + { + char *label; + GString *text = g_string_new(""); + print_raw_element_list(text, l->children); + label = label_from_string(text->str,0); + if (a == NULL) { + $$ = mk_link(l->children, s->contents.str, + t->contents.str, a, label); + } else { + $$ = mk_link(l->children, s->contents.str, + t->contents.str, a->children, label); + } + free_element(s); + free_element(t); + free(l); + free(label); + g_string_free(text, TRUE); + $$->key = REFERENCE; + } + + +Attributes = a:StartList (Attribute { a =cons($$,a);})+ + { $$ = mk_list(LIST,a); } + +Attribute = Spnl a:AttrKey '=' b:AttrValue + { + $$ = a; + $$->children = b; + } + +AttrKey = < AlphanumericAscii+ > + { + char *lab; + lab = label_from_string(yytext,0); + $$ = mk_str(lab); + $$->key = ATTRKEY; + free(lab); + } + +AttrValue = (QuotedValue | UnQuotedValue) + { $$ = mk_str(yytext); + $$->key = ATTRVALUE; + } + +QuotedValue = '"' < (!'"' .)* > '"' + +UnQuotedValue = < (AlphanumericAscii | '.')+ > + +Label = '[' !'[' ( !'^' !'#' &{ extension(EXT_NOTES) } | &. &{ !extension(EXT_NOTES) } ) + a:StartList + ( !']' Inline { a = cons($$, a); } )* + ']' + { $$ = mk_list(LIST, a); } + +RefSrc = < Nonspacechar+ > + { $$ = mk_str(yytext); + $$->key = HTML; } + +RefTitle = ( RefTitleSingle | RefTitleDouble | RefTitleParens | EmptyTitle ) + { $$ = mk_str(yytext); + $$->key = RAW;} + +EmptyTitle = < "" > + +RefTitleSingle = Spnl '\'' < ( !( '\'' Sp Newline | Newline | + &{ !extension(EXT_COMPATIBILITY) } '\'' Sp AlphanumericAscii+ '=' ) . )* > '\'' + +RefTitleDouble = Spnl '"' < ( !('"' Sp Newline | Newline | + &{ !extension(EXT_COMPATIBILITY) } '"' Sp AlphanumericAscii+ '=' ) . )* > '"' + +RefTitleParens = Spnl '(' < ( !(')' Sp Newline | Newline | + &{ !extension(EXT_COMPATIBILITY) } ')' Sp AlphanumericAscii+ '=' ) . )* > ')' + +References = a:StartList + ( b:Reference { a = cons(b, a); } | SkipBlock )* + { references = reverse(a); } + +Ticks1 = "`" !'`' +Ticks2 = "``" !'`' +Ticks3 = "```" !'`' +Ticks4 = "````" !'`' +Ticks5 = "`````" !'`' + +Code = ( Ticks1 Sp < ( ( !'`' Nonspacechar )+ | !Ticks1 '`'+ | !( Sp Ticks1 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks1 + | Ticks2 Sp < ( ( !'`' Nonspacechar )+ | !Ticks2 '`'+ | !( Sp Ticks2 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks2 + | Ticks3 Sp < ( ( !'`' Nonspacechar )+ | !Ticks3 '`'+ | !( Sp Ticks3 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks3 + | Ticks4 Sp < ( ( !'`' Nonspacechar )+ | !Ticks4 '`'+ | !( Sp Ticks4 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks4 + | Ticks5 Sp < ( ( !'`' Nonspacechar )+ | !Ticks5 '`'+ | !( Sp Ticks5 ) ( Spacechar | Newline !BlankLine ) )+ > Sp Ticks5 + ) + { $$ = mk_str(yytext); $$->key = CODE; } + +RawHtml = < (HtmlComment | HtmlBlockScript | HtmlTag) > + { if (extension(EXT_FILTER_HTML)) { + $$ = mk_list(LIST, NULL); + } else { + $$ = mk_str(yytext); + $$->key = HTML; + } + } + +BlankLine = Sp Newline + +Quoted = '"' (!'"' .)* '"' | '\'' (!'\'' .)* '\'' +HtmlAttribute = (AlphanumericAscii | '-')+ Spnl ('=' Spnl (Quoted | (!'>' Nonspacechar)+))? Spnl +HtmlComment = "" .)* "-->" +HtmlTag = '<' Spnl '/'? AlphanumericAscii+ Spnl HtmlAttribute* '/'? Spnl '>' +Eof = !. +Spacechar = ' ' | '\t' +Nonspacechar = !Spacechar !Newline . +Newline = '\n' | '\r' '\n'? +Sp = Spacechar* +Spnl = Sp (Newline Sp)? +SpecialChar = '*' | '_' | '`' | '&' | '[' | ']' | '(' | ')' | '<' | '!' | '#' | '\\' | '\'' | '"' | ExtendedSpecialChar +NormalChar = !( SpecialChar | Spacechar | Newline ) . +Alphanumeric = [0-9A-Za-z] | '\200' | '\201' | '\202' | '\203' | '\204' | '\205' | '\206' | '\207' | '\210' | '\211' | '\212' | '\213' | '\214' | '\215' | '\216' | '\217' | '\220' | '\221' | '\222' | '\223' | '\224' | '\225' | '\226' | '\227' | '\230' | '\231' | '\232' | '\233' | '\234' | '\235' | '\236' | '\237' | '\240' | '\241' | '\242' | '\243' | '\244' | '\245' | '\246' | '\247' | '\250' | '\251' | '\252' | '\253' | '\254' | '\255' | '\256' | '\257' | '\260' | '\261' | '\262' | '\263' | '\264' | '\265' | '\266' | '\267' | '\270' | '\271' | '\272' | '\273' | '\274' | '\275' | '\276' | '\277' | '\300' | '\301' | '\302' | '\303' | '\304' | '\305' | '\306' | '\307' | '\310' | '\311' | '\312' | '\313' | '\314' | '\315' | '\316' | '\317' | '\320' | '\321' | '\322' | '\323' | '\324' | '\325' | '\326' | '\327' | '\330' | '\331' | '\332' | '\333' | '\334' | '\335' | '\336' | '\337' | '\340' | '\341' | '\342' | '\343' | '\344' | '\345' | '\346' | '\347' | '\350' | '\351' | '\352' | '\353' | '\354' | '\355' | '\356' | '\357' | '\360' | '\361' | '\362' | '\363' | '\364' | '\365' | '\366' | '\367' | '\370' | '\371' | '\372' | '\373' | '\374' | '\375' | '\376' | '\377' +AlphanumericAscii = [A-Za-z0-9] +Digit = [0-9] +BOM = "\357\273\277" + +HexEntity = < '&' '#' [Xx] [0-9a-fA-F]+ ';' > +DecEntity = < '&' '#' [0-9]+ > ';' > +CharEntity = < '&' [A-Za-z0-9]+ ';' > + +NonindentSpace = " " | " " | " " | "" +Indent = "\t" | " " +IndentedLine = Indent Line +OptionallyIndentedLine = Indent? Line + +# StartList starts a list data structure that can be added to with cons: +StartList = &. + { $$ = NULL; } + +Line = RawLine + { $$ = mk_str(yytext); } +RawLine = ( < (!'\r' !'\n' .)* Newline > | < .+ > Eof ) + +SkipBlock = HtmlBlock + | ( !'#' !SetextBottom1 !SetextBottom2 !BlankLine RawLine )+ BlankLine* + | BlankLine+ + | RawLine + + +# Syntax extensions + +ExtendedSpecialChar = &{ extension(EXT_SMART) } ('.' | '-' | '\'' | '"') + | &{ extension(EXT_NOTES) } ( '^' ) + +Smart = &{ extension(EXT_SMART) } + ( Ellipsis | Dash | SingleQuoted | DoubleQuoted | Apostrophe ) + +Apostrophe = '\'' + { $$ = mk_element(APOSTROPHE); } + +Ellipsis = ("..." | ". . .") + { $$ = mk_element(ELLIPSIS); } + +Dash = EmDash | EnDash + +EnDash = < ( "--" | '-' &Digit) > + { $$ = mk_element(ENDASH); + $$->contents.str = strdup(yytext); + } + +EmDash = ( <"---"> ) + { $$ = mk_element(EMDASH); + $$->contents.str = strdup(yytext); + } + + +SingleQuoteStart = '\'' !(Spacechar | Newline) + +SingleQuoteEnd = '\'' !Alphanumeric + +SingleQuoted = SingleQuoteStart + a:StartList + ( !SingleQuoteEnd b:Inline { a = cons(b, a); } )+ + SingleQuoteEnd + { $$ = mk_list(SINGLEQUOTED, a); } + +DoubleQuoteStart = '"' + +DoubleQuoteEnd = '"' + +DoubleQuoted = DoubleQuoteStart + a:StartList + ( !DoubleQuoteEnd b:Inline { a = cons(b, a); } )+ + DoubleQuoteEnd + { $$ = mk_list(DOUBLEQUOTED, a); } + +NoteReference = &{ extension(EXT_NOTES) } + ref:RawNoteReference + { element *match; + if (find_note(&match, ref->contents.str)) { + $$ = mk_element(NOTE); + assert(match->children != NULL); + $$->children = match->children; + $$->contents.str = 0; + } else { + char *s; + s = malloc(strlen(ref->contents.str) + 4); + sprintf(s, "[^%s]", ref->contents.str); + $$ = mk_str(s); + free(s); + } + } + +RawNoteReference = ( "[^" | "[#" ) < ( !Newline !']' . )+ > ']' + { $$ = mk_str(yytext); } + +Glossary = &{ extension(EXT_NOTES) } + a:StartList + NonindentSpace ref:RawNoteReference ':' Sp + "glossary:" Sp (GlossaryTerm { a = cons($$, a); }) + (GlossarySortKey { a = cons($$, a); })? + Newline + ( RawNoteBlock { a = cons($$, a); } ) + ( &Indent RawNoteBlock { a = cons($$, a); } )* + { $$ = mk_list(GLOSSARY, a); + $$->contents.str = strdup(ref->contents.str); + } + +GlossaryTerm = < (!Newline !'(' .)+ > + { + $$ = mk_list(LIST, NULL); + $$->contents.str = 0; + $$->children = mk_str(yytext); + $$->key = GLOSSARYTERM; + } + +GlossarySortKey = '(' < (!')' !Newline .)* > ')' + { $$ = mk_str(yytext); + $$->key = GLOSSARYSORTKEY; } + +Note = &{ extension(EXT_NOTES) } + NonindentSpace ref:RawNoteReference ':' Sp + a:StartList + ( RawNoteBlock { a = cons($$, a); } ) + ( &Indent RawNoteBlock { a = cons($$, a); } )* + { element *label; + label = mk_str(ref->contents.str); + label->key = NOTELABEL; + a = cons(label,a); + $$ = mk_list(NOTE, a); + $$->contents.str = strdup(ref->contents.str); + } + +InlineNote = &{ extension(EXT_NOTES) } + "^[" + a:StartList + ( !']' Inline { a = cons($$, a); } )+ + ']' + { $$ = mk_list(NOTE, a); + $$->contents.str = 0; } + +Notes = a:StartList + ( (b:Glossary | b:Note) { a = cons(b, a); } | SkipBlock )* + { notes = reverse(a); } + +RawNoteBlock = a:StartList + ( !BlankLine OptionallyIndentedLine { a = cons($$, a); } )+ + ( < BlankLine* > { a = cons(mk_str(yytext), a); } ) + { $$ = mk_str_from_list(a, true); + $$->key = RAW; + } + + +# MultiMarkdown Citations + +CitationReference = CitationReferenceDouble | CitationReferenceSingle + +CitationReferenceDouble = !"[]" b:Label < Spnl > !"[]" ref:RawCitationReference + { element *match; + GString *label; + char *lab; + if (find_note(&match, ref->contents.str)) { + /* This citation is specified within the document */ + $$ = mk_element(CITATION); + assert(match->children != NULL); + b->next = match->children; + b->key = LOCATOR; + $$->children = b; + $$->contents.str = strdup(ref->contents.str); + } else { + /* Citation not specified - likely bibtex citation */ + /* TODO: fix this - need to print label as well */ + char *s; + s = malloc(strlen(ref->contents.str) + 4); + sprintf(s, "[#%s]", ref->contents.str); + $$ = mk_str(s); + $$->key = CITATION; + b->key = LOCATOR; + $$->children = b; + free(s); + } + label = g_string_new(""); + print_raw_element_list(label, b->children); + lab = label_from_string(label->str,0); + if (strcmp(lab,"notcited") == 0 ) { + $$->key = NOCITATION; + } + g_string_free(label, true); + free(lab); + } + +CitationReferenceSingle = (( "[]" Spnl ref:RawCitationReference ) + | ( ref:RawCitationReference < (Spnl "[]")? > )) + { element *match; + if (find_note(&match, ref->contents.str)) { + $$ = mk_element(CITATION); + assert(match->children != NULL); + $$->children = match->children; + $$->contents.str = strdup(ref->contents.str); + } else { + char *s; + s = malloc(strlen(ref->contents.str) + 4); + sprintf(s, "[#%s]", ref->contents.str); + $$ = mk_str(s); + $$->key = CITATION; + free(s); + } + } + + +RawCitationReference = "[#" < ( !Newline !']' . )+ > ']' + { $$ = mk_str(yytext); } + + +AutoLabels = ( &{ !extension(EXT_COMPATIBILITY) && !extension(EXT_NO_LABELS)} + a:StartList ( b:Heading + { + GString *label; + char *lab; + label = g_string_new(""); + print_raw_element_list(label, b->children); + if (b->children->key == AUTOLABEL) { + lab = label_from_string(b->children->contents.str,0); + } else { + lab = label_from_string(label->str,0); + } + a = cons(mk_str(lab), a); + free(lab); + g_string_free(label,true); + /* TODO: this causes segfault when trying to use a footnote in the header */ + /* I would like to fix it at some point */ + /* free_element_list(b); */ + } | c:TableCaption { + GString *label; + char *lab; + label = g_string_new(""); + if (c->children->key == TABLELABEL) { + print_raw_element_list(label, c->children->children); + } else { + print_raw_element_list(label, c->children); + } + lab = label_from_string(label->str,0); + a = cons(mk_str(lab), a); + free(lab); + g_string_free(label,true); + free_element_list(c);} TableBody + | (TableBody|SeparatorLine)+ c:TableCaption { + GString *label; + char *lab; + label = g_string_new(""); + if (c->children->key == TABLELABEL) { + print_raw_element_list(label, c->children->children); + } else { + print_raw_element_list(label, c->children); + } + lab = label_from_string(label->str,0); + a = cons(mk_str(lab), a); + free(lab); + g_string_free(label,true); + free_element_list(c);} + | SkipBlock )* + { labels = a; }) + +DefinitionList = a:StartList &(TermLine+ Newline? NonindentSpace ':') + ( + (Term { a = cons($$, a); } )+ + BlankLine? + (Definition { a = cons($$, a);})+ + BlankLine* + )+ + { $$ = mk_list(LIST, a); + $$->key = DEFLIST; + } + +TermLine = !':' !BlankLine (!Newline .)* Newline + +Term = a:StartList !BlankLine !':' + (!Newline !Endline Inline {a = cons($$, a);} )+ Newline + { + $$ = mk_list(TERM,a); + } + +Definition = (a:StartList b:StartList + (BlankLine { b = cons(mk_str("\n"),b); } )? + ( NonindentSpace ':' Sp RawLine { a = cons(mk_str(yytext), a);}) + ( !':' !BlankLine RawLine { a = cons(mk_str(yytext), a);})* + ( BlankLine {a = cons(mk_str("\n"),a);} + (IndentedLine { a = cons(mk_str(yytext),a);})+ + { a = cons(mk_str("\n"),a);} + )* + ) + { element *raw; + if (b != NULL) { a = cons(b,a);} + raw = mk_str_from_list(a, false); + raw->key = RAW; + $$ = mk_list(DEFINITION,raw); + } + +Table = a:StartList b:StartList (TableCaption { b = cons($$, b);})? + TableBody { $$->key = TABLEHEAD; a = cons($$, a); } + (SeparatorLine { append_list($$,a); } ) + (TableBody { a = cons($$, a);} ) + (BlankLine !TableCaption TableBody { a = cons($$, a); } + &(TableCaption | BlankLine) )* + ( (TableCaption { b = cons($$, b);} &BlankLine) | &BlankLine) + # Requires blank line to end table "block" + { + if (b != NULL) { append_list(b,a); }; + $$ = mk_list(TABLE, a); + } + +TableBody = a:StartList (TableRow {a = cons($$, a);})+ + { $$ = mk_list(TABLEBODY, a);} + +TableRow = a:StartList + (!SeparatorLine &(TableLine) + CellDivider? + (TableCell { a = cons($$, a); })+ ) Sp Newline + { $$ = mk_list(TABLEROW, a); } + +TableLine = (!Newline !CellDivider .)* CellDivider + +TableCell = ExtendedCell | EmptyCell | FullCell + +ExtendedCell = (EmptyCell | FullCell) + { + element *span; + span = mk_str(yytext); + span->key = CELLSPAN; + span->next = $$->children; + $$->children = span; + } + + +CellStr = < (!CellDivider NormalChar) (!CellDivider NormalChar | '_'+ &Alphanumeric)* > + { $$ = mk_str(yytext); } + + +FullCell = Sp a:StartList ((!CellDivider CellStr | !Newline !Endline !CellDivider !Str !(Sp &CellDivider) Inline ) { a = cons($$,a)})+ + Sp ( CellDivider )? + { $$ = mk_list(TABLECELL,a); } + +EmptyCell = Sp CellDivider +{ $$ = mk_element(TABLECELL);} + +SeparatorLine = a:StartList + &(TableLine) + CellDivider? + ( AlignmentCell { a = cons($$, a);})+ Sp Newline + { + $$ = mk_str_from_list(a,false); + $$->key = TABLESEPARATOR; + } + +AlignmentCell = Sp (!CellDivider ( LeftAlignWrap | CenterAlignWrap | RightAlignWrap | LeftAlign | CenterAlign | RightAlign)) + Sp ( CellDivider )? + +LeftAlignWrap = ':'? '-'+ '+' &(!'-' !':') + { $$ = mk_str("L");} + +LeftAlign = ':'? '-'+ &(!'-' !':') + { $$ = mk_str("l");} + +CenterAlignWrap = ':' '-'* '+' ':' &(!'-' !':') + { $$ = mk_str("C");} + +CenterAlign = ':' '-'* ':' &(!'-' !':') + { $$ = mk_str("c");} + +RightAlignWrap = '-'+ ':' '+' &(!'-' !':') + { $$ = mk_str("R");} + +RightAlign = '-'+ ':' &(!'-' !':') + { $$ = mk_str("r");} + +CellDivider = '|' + +TableCaption = b:StartList a:Label +( c:Label { b = c; b->key = TABLELABEL;})? Sp Newline +{ + $$ = a; + $$->key = TABLECAPTION; + if ( (b != NULL) && (b->key == TABLELABEL) ) { + b->next = $$->children; + $$->children = b; + } +} + +AutoLabel = '[' < (!Newline !'^' !'#' . )( !Newline !']' . )+ > ']' &(!(Sp? ('(' | '['))) +{ + char *label = label_from_string(yytext,0); + $$ = mk_str(label); + $$->key = AUTOLABEL; + free(label); +} + +MathSpan = '\\' < ( + ('\\[' (!'\\\\]' .)* '\\\\]') | + ('\\(' (!'\\\\)' .)* '\\\\)') ) > +{ + /* Basically, these delimiters indicate math in LaTeX syntax, and the + delimiters are compatible with MathJax and LaTeX + ASCIIMathML is *not* supported */ + $$ = mk_str(yytext); + $$->key = MATHSPAN; +} + + +DocForOPML = BOM? a:StartList + ( &{ !extension(EXT_COMPATIBILITY) } + &( MetaDataKey Sp ':' Sp (!Newline)) MetaData + { a = cons($$, a); })? + ( OPMLBlock { a = cons($$, a); } )* + { parse_result = reverse(a); + } + +OPMLBlock = BlankLine* + ( OPMLHeadingSection + | OPMLPlain ) + +OPMLHeadingSection = a:StartList OPMLHeading { a = cons($$, a); } + (OPMLSectionBlock {a = cons($$, a); })* + { $$ = mk_list(HEADINGSECTION, a);} + +OPMLHeading = OPMLAtxHeading | OPMLSetextHeading + +OPMLAtxHeading = &(Heading) s:AtxStart Sp? + < (!Newline !(Sp? '#'* Sp Newline) .)* > + (Sp? '#'+)? Sp? Newline + { + $$ = mk_str(yytext); + $$->key = s->key; + free(s); + } + +OPMLSetextHeading = OPMLSetextHeading1 | OPMLSetextHeading2 + +OPMLSetextHeading1 = < (!'\r' !'\n' .)* > Newline SetextBottom1 + { + $$ = mk_str(yytext); + $$->key = H1; + } + +OPMLSetextHeading2 = < (!'\r' !'\n' .)* > Newline SetextBottom2 + { + $$ = mk_str(yytext); + $$->key = H2; + } + +OPMLSectionBlock = + BlankLine* + !OPMLHeading + OPMLPlain + +OPMLPlain = a:StartList (!BlankLine !Heading Line { a = cons($$,a); })+ + { $$ = mk_list(PLAIN, a); } + + +MarkdownHtmlAttribute = ("markdown" | "MARKDOWN") + Spnl '=' Spnl ('"' Spnl)? "1" (Spnl '"')? Spnl + +MarkdownHtmlTagOpen = a:StartList '<' {a = cons(mk_str("<"),a);} + Spnl {a = cons(mk_str(yytext),a);} Spnl + (!MarkdownHtmlAttribute + {a = cons(mk_str(" "),a); + a = cons(mk_str(yytext),a);})* + MarkdownHtmlAttribute + ( {a = cons(mk_str(" "),a); + a = cons(mk_str(yytext),a);})* + '>' { a = cons(mk_str(">"),a);} + { + $$ = mk_str_from_list(a,false); + $$->key = HTML; + } + +%% + + diff --git a/src/lib/multimarkdown/src/markdown_parser.sh b/src/lib/multimarkdown/src/markdown_parser.sh new file mode 100755 index 0000000..866c61b --- /dev/null +++ b/src/lib/multimarkdown/src/markdown_parser.sh @@ -0,0 +1,7 @@ +#!/bin/sh +if [ -L $0 ]; then + cd $(dirname $(readlink -f $0)) ; +else + cd $(dirname $0); +fi ; +../peg/leg/leg -o markdown_parser.c markdown_parser.leg diff --git a/src/lib/multimarkdown/src/markdown_peg.h b/src/lib/multimarkdown/src/markdown_peg.h new file mode 100644 index 0000000..fdea31e --- /dev/null +++ b/src/lib/multimarkdown/src/markdown_peg.h @@ -0,0 +1,143 @@ +/* markdown_peg.h */ +#ifndef MARKDOWN_PEG_H +#define MARKDOWN_PEG_H + +#include "markdown_lib.h" +#include "glib.h" + +/* Information (label, URL and title) for a link. */ +struct Link { + struct Element *label; + char *url; + char *title; + struct Element *attr; + char *identifier; +}; + +typedef struct Link link; + +/* Union for contents of an Element (string, list, or link). */ +union Contents { + char *str; + struct Link *link; +}; + +/* Types of semantic values returned by parsers. */ +enum keys { LIST, /* A generic list of values. For ordered and bullet lists, see below. */ + RAW, /* Raw markdown to be processed further */ + SPACE, + LINEBREAK, + ELLIPSIS, + EMDASH, + ENDASH, + APOSTROPHE, + SINGLEQUOTED, + DOUBLEQUOTED, + STR, + LINK, + IMAGE, + IMAGEBLOCK, + CODE, + HTML, + EMPH, + STRONG, + PLAIN, + PARA, + LISTITEM, + BULLETLIST, + ORDEREDLIST, + H1, H2, H3, H4, H5, H6, H7, /* Code assumes that these are in order. */ + BLOCKQUOTE, + VERBATIM, + HTMLBLOCK, + HRULE, + REFERENCE, + NOTE, + CITATION, + NOCITATION, + LOCATOR, + NOTELABEL, + DEFLIST, + TERM, + DEFINITION, + METAKEY, + METAVALUE, + METADATA, + FOOTER, + LABEL, + HEADINGSECTION, + ENDHTML, + TABLE, + TABLEHEAD, + TABLEBODY, + TABLEROW, + TABLECELL, + CELLSPAN, + TABLECAPTION, + TABLELABEL, + TABLESEPARATOR, + AUTOLABEL, + ATTRIBUTE, + ATTRKEY, + ATTRVALUE, + GLOSSARY, + GLOSSARYTERM, + GLOSSARYSORTKEY, + MATHSPAN + }; + +/* constants for managing Smart Typography */ +enum smartelements { + LSQUOTE, + RSQUOTE, + LDQUOTE, + RDQUOTE, + NDASH, + MDASH, + ELLIP, + APOS, +}; + +enum smartoutput { + HTMLOUT, + LATEXOUT, +}; + +enum language { + DUTCH, + ENGLISH, + FRENCH, + GERMAN, + SWEDISH, + GERMANGUILL, +}; + +/* Semantic value of a parsing action. */ +struct Element { + int key; + union Contents contents; + struct Element *children; + struct Element *next; +}; + + + +typedef struct Element element; + +element * parse_references(char *string, int extensions); +element * parse_notes(char *string, int extensions, element *reference_list); +element * parse_labels(char *string, int extensions, element *reference_list, element *note_list); + +element * parse_markdown(char *string, int extensions, element *reference_list, element *note_list, element *label_list); +element * parse_markdown_with_metadata(char *string, int extensions, element *reference_list, element *note_list, element *label_list); +void free_element_list(element * elt); +void free_element(element *elt); +void print_element_list(GString *out, element *elt, int format, int exts); + +element * parse_metadata_only(char *string, int extensions); +char * extract_metadata_value(char *text, int extensions, char *key); + +char * metavalue_for_key(char *key, element *list); + +element * parse_markdown_for_opml(char *string, int extensions); +#endif diff --git a/src/lib/multimarkdown/src/multimarkdown.pro b/src/lib/multimarkdown/src/multimarkdown.pro new file mode 100644 index 0000000..5785ff6 --- /dev/null +++ b/src/lib/multimarkdown/src/multimarkdown.pro @@ -0,0 +1,45 @@ +TEMPLATE = app + +CONFIG += warn_off + +QCMAKE_CXXFLAGS += -fPIC + +# Generate markdown_parser.c +markdown_parser_c.target = markdown_parser.c +win32-msvc*: markdown_parser_c.commands = markdown_parser.bat +unix: markdown_parser_c.commands = $$PWD/markdown_parser.sh +markdown_parser_c.depends = markdown_parser_c_nonexist +markdown_parser_c.CONFIG += recursive + +markdown_parser_c_nonexist.commands = @echo generating markdown_parser.c +QMAKE_EXTRA_TARGETS += markdown_parser_c markdown_parser_c_nonexist +PRE_TARGETDEPS += markdown_parser.c +# end generation + +win32-msvc*: { + CONFIG += warn_off + INCLUDEPATH += win + SOURCES += win/getopt.c + HEADERS += win/getopt.h \ + win/libgen.h \ + win/stdbool.h \ + win/unistd.h +} + +HEADERS += glib.h \ + GLibFacade.h \ + markdown_lib.h \ + markdown_peg.h \ + MarkdownMacPrefix.h \ + parsing_functions.h \ + utility_functions.h \ + odf.h + +SOURCES += GLibFacade.c \ + markdown_lib.c \ + markdown_output.c \ + markdown_parser.c \ + parsing_functions.c \ + utility_functions.c \ + odf.c \ + main.c diff --git a/src/lib/multimarkdown/src/multimarkdown_lib.pri b/src/lib/multimarkdown/src/multimarkdown_lib.pri new file mode 100644 index 0000000..16e15b6 --- /dev/null +++ b/src/lib/multimarkdown/src/multimarkdown_lib.pri @@ -0,0 +1,28 @@ +win32-msvc*: { + CONFIG += warn_off + INCLUDEPATH += $$PWD/win + SOURCES += $$PWD/win/getopt.c + HEADERS += $$PWD/win/getopt.h \ + $$PWD/win/libgen.h \ + $$PWD/win/stdbool.h \ + $$PWD/win/unistd.h +} +win32{ +HEADERS += $$PWD/glib.h \ + $$PWD/GLibFacade.h \ +} +HEADERS += $$PWD/markdown_lib.h \ + $$PWD/markdown_peg.h \ + $$PWD/MarkdownMacPrefix.h \ + $$PWD/parsing_functions.h \ + $$PWD/utility_functions.h \ + $$PWD/odf.h +win32: SOURCES += $$PWD/GLibFacade.c + +SOURCES += $$PWD/markdown_lib.c \ + $$PWD/markdown_output.c \ + $$PWD/markdown_parser.c \ + $$PWD/parsing_functions.c \ + $$PWD/utility_functions.c \ + $$PWD/odf.c \ + $$PWD/main.c diff --git a/src/lib/multimarkdown/src/multimarkdown_lib.pro b/src/lib/multimarkdown/src/multimarkdown_lib.pro new file mode 100644 index 0000000..45db982 --- /dev/null +++ b/src/lib/multimarkdown/src/multimarkdown_lib.pro @@ -0,0 +1,49 @@ +TEMPLATE = lib + +win32:DEFINES += _EXPORTING + +CONFIG += warn_off + +QCMAKE_CXXFLAGS += -fPIC + +TARGET = multimarkdown + +# Generate markdown_parser.c +markdown_parser_c.target = markdown_parser.c +win32-msvc*: markdown_parser_c.commands = markdown_parser.bat +unix: markdown_parser_c.commands = $$PWD/markdown_parser.sh +markdown_parser_c.depends = markdown_parser_c_nonexist +markdown_parser_c.CONFIG += recursive + +markdown_parser_c_nonexist.commands = @echo generating markdown_parser.c +QMAKE_EXTRA_TARGETS += markdown_parser_c markdown_parser_c_nonexist +PRE_TARGETDEPS += markdown_parser.c +# end generation + +win32-msvc*: { + CONFIG += warn_off + INCLUDEPATH += win + SOURCES += win/getopt.c + HEADERS += win/getopt.h \ + win/libgen.h \ + win/stdbool.h \ + win/unistd.h +} + +HEADERS += glib.h \ + GLibFacade.h \ + markdown_lib.h \ + markdown_peg.h \ + MarkdownMacPrefix.h \ + parsing_functions.h \ + utility_functions.h \ + odf.h + +SOURCES += GLibFacade.c \ + markdown_lib.c \ + markdown_output.c \ + markdown_parser.c \ + parsing_functions.c \ + utility_functions.c \ + odf.c \ + main.c diff --git a/src/lib/multimarkdown/src/odf.c b/src/lib/multimarkdown/src/odf.c new file mode 100644 index 0000000..b63fc43 --- /dev/null +++ b/src/lib/multimarkdown/src/odf.c @@ -0,0 +1,185 @@ +/********************************************************************** + + odf.c - Utility routines to enable ODF support in peg-multimarkdown. + (c) 2011 Fletcher T. Penney (http://fletcherpenney.net/). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License or the MIT + license. See LICENSE for details. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + ***********************************************************************/ + +#include "odf.h" + + +void print_odf_header(GString *out){ + + /* Insert required XML header */ + g_string_append_printf(out, +"\n" \ +"\n"); + + /* Font Declarations */ + g_string_append_printf(out, "\n" \ + " \n" \ + "\n"); + + /* Append basic style information */ + g_string_append_printf(out, "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + " \n" \ + "\n" \ + "" \ + " " \ + " " \ + "\n"); + + /* Automatic style information */ + g_string_append_printf(out, "" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + "\n" \ + "\n" \ + " \n" \ + "\n" \ + "\n" \ + " \n" \ + "\n"); +} + +void print_odf_footer(GString *out) { + g_string_append_printf(out, "\n\n"); +} + diff --git a/src/lib/multimarkdown/src/odf.h b/src/lib/multimarkdown/src/odf.h new file mode 100644 index 0000000..e0af81c --- /dev/null +++ b/src/lib/multimarkdown/src/odf.h @@ -0,0 +1,11 @@ +#ifndef ODF_H +#define ODF_H + +#include +#include +#include "glib.h" + +void print_odf_header(GString *out); +void print_odf_footer(GString *out); +#endif + diff --git a/src/lib/multimarkdown/src/parsing_functions.c b/src/lib/multimarkdown/src/parsing_functions.c new file mode 100644 index 0000000..4664e3b --- /dev/null +++ b/src/lib/multimarkdown/src/parsing_functions.c @@ -0,0 +1,222 @@ +/* parsing_functions.c - Functions for parsing markdown and + * freeing element lists. */ + +/* These yy_* functions come from markdown_parser.c which is + * generated from markdown_parser.leg + * */ +typedef int (*yyrule)(); + +extern int yyparse(); +extern int yyparsefrom(yyrule); +extern int yy_References(); +extern int yy_Notes(); +extern int yy_Doc(); + +extern int yy_AutoLabels(); +extern int yy_DocWithMetaData(); +extern int yy_MetaDataOnly(); +extern int yy_DocForOPML(); + +#include "utility_functions.h" +#include "parsing_functions.h" +#include "markdown_peg.h" + +static void free_element_contents(element elt); + +/* free_element_list - free list of elements recursively */ +void free_element_list(element * elt) { + element * next = NULL; + while (elt != NULL) { + next = elt->next; + free_element_contents(*elt); + if (elt->children != NULL) { + free_element_list(elt->children); + elt->children = NULL; + } + free(elt); + elt = next; + } +} + +/* free_element_contents - free element contents depending on type */ +static void free_element_contents(element elt) { + switch (elt.key) { + case STR: + case SPACE: + case RAW: + case HTMLBLOCK: + case HTML: + case VERBATIM: + case CODE: + case NOTE: + case AUTOLABEL: + case CITATION: + case TERM: + case METAKEY: + case METAVALUE: + case TABLESEPARATOR: + case ATTRKEY: + case GLOSSARY: + case GLOSSARYTERM: + case NOTELABEL: + case CELLSPAN: + case EMDASH: + case ENDASH: + case GLOSSARYSORTKEY: + case MATHSPAN: + free(elt.contents.str); + elt.contents.str = NULL; + break; + case LINK: + case IMAGE: + case REFERENCE: + free(elt.contents.link->url); + elt.contents.link->url = NULL; + free(elt.contents.link->title); + elt.contents.link->title = NULL; + free_element_list(elt.contents.link->label); + free(elt.contents.link->identifier); + elt.contents.link->identifier = NULL; +/* free_element_list(elt.contents.link->attr);*/ + free(elt.contents.link); + elt.contents.link = NULL; + break; + default: + ; + } +} + +/* free_element - free element and contents */ +void free_element(element *elt) { + free_element_contents(*elt); + free(elt); +} + +element * parse_references(char *string, int extensions) { + + char *oldcharbuf; + syntax_extensions = extensions; + + oldcharbuf = charbuf; + charbuf = string; + yyparsefrom(yy_References); /* first pass, just to collect references */ + charbuf = oldcharbuf; + + return references; +} + +element * parse_notes(char *string, int extensions, element *reference_list) { + + char *oldcharbuf; + notes = NULL; + syntax_extensions = extensions; + + if (extension(EXT_NOTES)) { + references = reference_list; + oldcharbuf = charbuf; + charbuf = string; + yyparsefrom(yy_Notes); /* second pass for notes */ + charbuf = oldcharbuf; + } + + return notes; +} + +element * parse_labels(char *string, int extensions, element *reference_list, element *note_list) { + + char *oldcharbuf; + syntax_extensions = extensions; + references = reference_list; + notes = note_list; + labels = NULL; + + oldcharbuf = charbuf; + charbuf = string; + yyparsefrom(yy_AutoLabels); /* third pass, to collect labels */ + charbuf = oldcharbuf; + + return labels; +} + +element * parse_markdown(char *string, int extensions, element *reference_list, element *note_list, element *label_list) { + + char *oldcharbuf; + syntax_extensions = extensions; + references = reference_list; + notes = note_list; + labels = label_list; + + oldcharbuf = charbuf; + charbuf = string; + + yyparsefrom(yy_Doc); + + charbuf = oldcharbuf; /* restore charbuf to original value */ + +/* if (parse_aborted) { + free_element_list(parse_result); + return NULL; + }*/ + + return parse_result; + +} + +element * parse_markdown_with_metadata(char *string, int extensions, element *reference_list, element *note_list, element *label_list) { + + char *oldcharbuf; + syntax_extensions = extensions; + references = reference_list; + notes = note_list; + labels = label_list; + + oldcharbuf = charbuf; + charbuf = string; + + start_time = clock(); + + yyparsefrom(yy_DocWithMetaData); + charbuf = oldcharbuf; /* restore charbuf to original value */ + + /* reset start_time for subsequent passes */ + start_time = 0; + + if (parse_aborted) { + parse_aborted = 0; + free_element_list(parse_result); + return NULL; + } + + return parse_result; + +} + +element * parse_metadata_only(char *string, int extensions) { + + char *oldcharbuf; + syntax_extensions = extensions; + + oldcharbuf = charbuf; + charbuf = string; + + yyparsefrom(yy_MetaDataOnly); + + charbuf = oldcharbuf; /* restore charbuf to original value */ + return parse_result; + +} + +element * parse_markdown_for_opml(char *string, int extensions) { + + char *oldcharbuf; + syntax_extensions = extensions; + + oldcharbuf = charbuf; + charbuf = string; + + yyparsefrom(yy_DocForOPML); + + charbuf = oldcharbuf; /* restore charbuf to original value */ + return parse_result; + +} diff --git a/src/lib/multimarkdown/src/parsing_functions.h b/src/lib/multimarkdown/src/parsing_functions.h new file mode 100644 index 0000000..8ffa2d2 --- /dev/null +++ b/src/lib/multimarkdown/src/parsing_functions.h @@ -0,0 +1,17 @@ +#ifndef PARSING_FUNCTIONS_H +#define PARSING_FUNCTIONS_H +/* parsing_functions.c - Functions for parsing markdown and + * freeing element lists. */ + +#include "markdown_peg.h" + +/* free_element_list - free list of elements recursively */ +void free_element_list(element * elt); +/* free_element - free element and contents */ +void free_element(element *elt); + +element * parse_references(char *string, int extensions); +element * parse_notes(char *string, int extensions, element *reference_list); +element * parse_markdown(char *string, int extensions, element *reference_list, element *note_list, element *label_list); + +#endif diff --git a/src/lib/multimarkdown/src/utility_functions.c b/src/lib/multimarkdown/src/utility_functions.c new file mode 100644 index 0000000..66c4753 --- /dev/null +++ b/src/lib/multimarkdown/src/utility_functions.c @@ -0,0 +1,549 @@ +/* utility_functions.c - List manipulation functions, element + * constructors, and macro definitions for leg markdown parser. */ + +#include "utility_functions.h" +#include "markdown_peg.h" + +#include +#include + +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif + + +/********************************************************************** + + List manipulation functions + + ***********************************************************************/ + +/* cons - cons an element onto a list, returning pointer to new head */ +element * cons(element *new, element *list) { + assert(new != NULL); + new->next = list; + return new; +} + +/* reverse - reverse a list, returning pointer to new list */ +element *reverse(element *list) { + element *new = NULL; + element *next = NULL; + while (list != NULL) { + next = list->next; + new = cons(list, new); + list = next; + } + return new; +} + +/* append_list - add element to end of list */ +void append_list(element *new, element *list) { + element * step; + assert(new != NULL); + step = list; + + while (step->next != NULL) { + step = step->next; + } + + new->next = NULL; + step->next = new; +} + +/* concat_string_list - concatenates string contents of list of STR elements. + * Frees STR elements as they are added to the concatenation. */ +GString *concat_string_list(element *list) { + GString *result; + element *next; + result = g_string_new(""); + while (list != NULL) { + assert(list->key == STR); + assert(list->contents.str != NULL); + g_string_append(result, list->contents.str); + next = list->next; + free_element(list); + list = next; + } + return result; +} + +/********************************************************************** + + Global variables used in parsing + + ***********************************************************************/ + + +char *charbuf = ""; /* Buffer of characters to be parsed. */ +element *references = NULL; /* List of link references found. */ +element *notes = NULL; /* List of footnotes found. */ +element *parse_result; /* Results of parse. */ +int syntax_extensions; /* Syntax extensions selected. */ + +element *labels = NULL; /* List of labels found in document. */ +clock_t start_time = 0; /* Used for ensuring we're not stuck in a loop */ +bool parse_aborted = 0; /* flag indicating we ran out of time */ + +/********************************************************************** + + Auxiliary functions for parsing actions. + These make it easier to build up data structures (including lists) + in the parsing actions. + + ***********************************************************************/ + +/* mk_element - generic constructor for element */ +element * mk_element(int key) { + element *result = malloc(sizeof(element)); + result->key = key; + result->children = NULL; + result->next = NULL; + result->contents.str = NULL; + return result; +} + +/* mk_str - constructor for STR element */ +element * mk_str(char *string) { + element *result; + assert(string != NULL); + result = mk_element(STR); + result->contents.str = strdup(string); + return result; +} + +/* mk_str_from_list - makes STR element by concatenating a + * reversed list of strings, adding optional extra newline */ +element * mk_str_from_list(element *list, bool extra_newline) { + element *result; + GString *c = concat_string_list(reverse(list)); + if (extra_newline) + g_string_append(c, "\n"); + result = mk_element(STR); + result->contents.str = c->str; + g_string_free(c, false); + return result; +} + +/* mk_list - makes new list with key 'key' and children the reverse of 'lst'. + * This is designed to be used with cons to build lists in a parser action. + * The reversing is necessary because cons adds to the head of a list. */ +element * mk_list(int key, element *lst) { + element *result; + result = mk_element(key); + result->children = reverse(lst); + return result; +} + +/* mk_link - constructor for LINK element */ +element * mk_link(element *label, char *url, char *title, element *attr, char *id) { + element *result; + result = mk_element(LINK); + result->contents.link = malloc(sizeof(link)); + result->contents.link->label = label; + result->contents.link->url = strdup(url); + result->contents.link->title = strdup(title); + result->contents.link->attr = attr; + result->contents.link->identifier = strdup(id); + return result; +} + +/* extension = returns true if extension is selected */ +bool extension(int ext) { + return (syntax_extensions & ext); +} + +/* match_inlines - returns true if inline lists match (case-insensitive...) */ +bool match_inlines(element *l1, element *l2) { + while (l1 != NULL && l2 != NULL) { + if (l1->key != l2->key) + return false; + switch (l1->key) { + case SPACE: + case LINEBREAK: + case ELLIPSIS: + case EMDASH: + case ENDASH: + case APOSTROPHE: + break; + case CODE: + case STR: + case HTML: + if (strcasecmp(l1->contents.str, l2->contents.str) == 0) + break; + else + return false; + case EMPH: + case STRONG: + case LIST: + case SINGLEQUOTED: + case DOUBLEQUOTED: + if (match_inlines(l1->children, l2->children)) + break; + else + return false; + case LINK: + case IMAGE: + return false; /* No links or images within links */ + default: + fprintf(stderr, "match_inlines encountered unknown key = %d\n", l1->key); + exit(EXIT_FAILURE); + break; + } + l1 = l1->next; + l2 = l2->next; + } + return (l1 == NULL && l2 == NULL); /* return true if both lists exhausted */ +} + +/* find_reference - return true if link found in references matching label. + * 'link' is modified with the matching url and title. */ +bool find_reference(link *result, element *label) { + element *cur = references; /* pointer to walk up list of references */ + link *curitem; + while (cur != NULL) { + curitem = cur->contents.link; + if (match_inlines(label, curitem->label)) { + *result = *curitem; + return true; + } + else + cur = cur->next; + } + return false; +} + +/* find_note - return true if note found in notes matching label. +if found, 'result' is set to point to matched note. */ + +bool find_note(element **result, char *label) { + element *cur = notes; /* pointer to walk up list of notes */ + while (cur != NULL) { + if (strcmp(label, cur->contents.str) == 0) { + *result = cur; + return true; + } + else + cur = cur->next; + } + return false; +} + + +/* peg-multimarkdown additions */ + +/* print_raw_element - print an element as original text */ +void print_raw_element(GString *out, element *elt) { + if (elt->key == LINK) { + print_raw_element_list(out,elt->contents.link->label); + } else { + if (elt->contents.str != NULL) { + g_string_append_printf(out, "%s", elt->contents.str); + } else { + print_raw_element_list(out, elt->children); + } + } +} + +/* print_raw_element_list - print a list of elements as original text */ +void print_raw_element_list(GString *out, element *list) { + while (list != NULL) { + print_raw_element(out, list); + list = list->next; + } +} + +/* label_from_element_list */ +/* Returns a null-terminated string, which must be freed after use. */ + +char *label_from_element_list(element *list, bool obfuscate) { + char *label; + char *label2; + GString *raw = g_string_new(""); + print_raw_element_list(raw, list); + label = label_from_string(raw->str,obfuscate); + label2 = strdup(label); + free(label); + g_string_free(raw,true); + return label2; +} + +/* label_from_string - strip spaces and illegal characters to generate valid + HTML id */ +/* Returns a null-terminated string, which must be freed after use. */ + +char *label_from_string(char *str, bool obfuscate) { + bool valid = FALSE; + GString *out = g_string_new(""); + char *label; + + while (*str != '\0') { + if (valid) { + /* can relax on following characters */ + if ((*str >= '0' && *str <= '9') || (*str >= 'A' && *str <= 'Z') + || (*str >= 'a' && *str <= 'z') || (*str == '.') || (*str== '_') + || (*str== '-') || (*str== ':')) + { + g_string_append_c(out, tolower(*str)); + } + } else { + /* need alpha as first character */ + if ((*str >= 'A' && *str <= 'Z') || (*str >= 'a' && *str <= 'z')) + { + g_string_append_c(out, tolower(*str)); + valid = TRUE; + } + } + str++; + } + label = out->str; + g_string_free(out, false); + return label; +} + +/* find_label - return true if header, table, etc is found matching label. + * 'link' is modified with the matching url and title. */ +bool find_label(link *result, element *label) { + char *lab; + GString *query; + element *cur = labels; /* pointer to walk up list of references */ + GString *text = g_string_new(""); + print_raw_element_list(text, label); + lab = label_from_string(text->str,0); + + query = g_string_new(lab); + free(lab); + g_string_free(text, true); + + while (cur != NULL) { + if (strcmp(query->str,cur->contents.str) == 0) { + g_string_free(query, true); + return true; + } + else + cur = cur->next; + } + g_string_free(query, true); + return false; +} + + +/* localize_typography - return the proper string, based on language chosen */ +/* Default action is English */ + +void localize_typography(GString *out, int character, int lang, int output) { + + switch (output) { + case HTMLOUT: + switch (character) { + case LSQUOTE: + switch (lang) { + case SWEDISH: + g_string_append_printf(out, "’"); + break; + case FRENCH: + g_string_append_printf(out,"'"); + break; + case GERMAN: + g_string_append_printf(out,"‚"); + break; + case GERMANGUILL: + g_string_append_printf(out,"›"); + break; + default: + g_string_append_printf(out,"‘"); + } + break; + case RSQUOTE: + switch (lang) { + case GERMAN: + g_string_append_printf(out,"‘"); + break; + case GERMANGUILL: + g_string_append_printf(out,"‹"); + break; + default: + g_string_append_printf(out,"’"); + } + break; + case APOS: + g_string_append_printf(out,"’"); + break; + case LDQUOTE: + switch (lang) { + case DUTCH: + case GERMAN: + g_string_append_printf(out,"„"); + break; + case GERMANGUILL: + g_string_append_printf(out,"»"); + break; + case FRENCH: + g_string_append_printf(out,"«"); + break; + case SWEDISH: + g_string_append_printf(out, "”"); + break; + default: + g_string_append_printf(out,"“"); + } + break; + case RDQUOTE: + switch (lang) { + case SWEDISH: + case DUTCH: + g_string_append_printf(out,"”"); + break; + case GERMAN: + g_string_append_printf(out,"“"); + break; + case GERMANGUILL: + g_string_append_printf(out,"«"); + break; + case FRENCH: + g_string_append_printf(out,"»"); + break; + default: + g_string_append_printf(out,"”"); + } + break; + case NDASH: + g_string_append_printf(out,"–"); + break; + case MDASH: + g_string_append_printf(out,"—"); + break; + case ELLIP: + g_string_append_printf(out,"…"); + break; + default:; + } + break; + case LATEXOUT: + switch (character) { + case LSQUOTE: + switch (lang) { + case SWEDISH: + g_string_append_printf(out,"'"); + break; + case FRENCH: + g_string_append_printf(out,"'"); + break; + case GERMAN: + g_string_append_printf(out,"\u201A"); + break; + case GERMANGUILL: + g_string_append_printf(out,"\u203A"); + break; + default: + g_string_append_printf(out,"`"); + } + break; + case RSQUOTE: + switch (lang) { + case GERMAN: + g_string_append_printf(out,"`"); + break; + case GERMANGUILL: + g_string_append_printf(out,"\u2039"); + break; + default: + g_string_append_printf(out,"'"); + } + break; + case APOS: + g_string_append_printf(out,"'"); + break; + case LDQUOTE: + switch (lang) { + case DUTCH: + case GERMAN: + g_string_append_printf(out,"\u201E"); + break; + case GERMANGUILL: + g_string_append_printf(out,"\u00BB"); + break; + case FRENCH: + g_string_append_printf(out,"\u00AB"); + break; + case SWEDISH: + g_string_append_printf(out,"''"); + break; + default: + g_string_append_printf(out,"``"); + } + break; + case RDQUOTE: + switch (lang) { + case SWEDISH: + case DUTCH: + g_string_append_printf(out,"''"); + break; + case GERMAN: + g_string_append_printf(out,"``"); + break; + case GERMANGUILL: + g_string_append_printf(out,"\u00AB"); + break; + case FRENCH: + g_string_append_printf(out,"\u00BB"); + break; + default: + g_string_append_printf(out,"''"); + } + break; + case NDASH: + g_string_append_printf(out,"--"); + break; + case MDASH: + g_string_append_printf(out,"---"); + break; + case ELLIP: + g_string_append_printf(out,"{\\ldots}"); + break; + default:; + } + break; + default:; + } +} + +/* Trim spaces at end of string */ +void trim_trailing_whitespace(char *str) { + while ( ( str[strlen(str)-1] == ' ' ) || + ( str[strlen(str)-1] == '\n' ) || + ( str[strlen(str)-1] == '\r' ) || + ( str[strlen(str)-1] == '\t' ) ) { + str[strlen(str)-1] = '\0'; + } +} + +/* Don't let us get caught in "infinite" loop */ +bool check_timeout() { + clock_t end; + float max; + double elapsed; + /* Once we abort, keep aborting */ + if (parse_aborted) + return 0; + + /* We're not timing this run */ + if (start_time == 0) + return 1; + + end = clock(); + elapsed = ((double) (end - start_time)) / CLOCKS_PER_SEC; + + /* fprintf(stderr,"%2.2f elapsed; (%4.2f CLOCKS_PER_SEC)\n",elapsed,CLOCKS_PER_SEC); */ + /* fprintf(stderr,"%2.2f elapsed\n",elapsed); */ + + + /* If > 3 clock seconds, then abort */ + max = 3; + if (elapsed > max) { + parse_aborted = 1; + return 0; + } + return 1; +} + diff --git a/src/lib/multimarkdown/src/utility_functions.h b/src/lib/multimarkdown/src/utility_functions.h new file mode 100644 index 0000000..7da291a --- /dev/null +++ b/src/lib/multimarkdown/src/utility_functions.h @@ -0,0 +1,93 @@ +#ifndef UTILITY_FUNCTIONS_H +#define UTILITY_FUNCTIONS_H + +#include +#include "glib.h" + +#include "markdown_peg.h" + +#include + +/* utility_functions.h - List manipulation functions, element + * constructors, and macro definitions for leg markdown parser. */ + + +/* cons - cons an element onto a list, returning pointer to new head */ +element * cons(element *new, element *list); + +/* reverse - reverse a list, returning pointer to new list */ +element *reverse(element *list); +/* concat_string_list - concatenates string contents of list of STR elements. + * Frees STR elements as they are added to the concatenation. */ +GString *concat_string_list(element *list); +/********************************************************************** + + Global variables used in parsing + + ***********************************************************************/ + +extern char *charbuf; /* Buffer of characters to be parsed. */ +extern element *references; /* List of link references found. */ +extern element *notes; /* List of footnotes found. */ +extern element *parse_result; /* Results of parse. */ +extern int syntax_extensions; /* Syntax extensions selected. */ + + +extern element *labels; /* List of labels found in document. */ +extern clock_t start_time; /* Used for ensuring we're not stuck in a loop */ +extern bool parse_aborted; /* flag indicating we ran out of time */ + +/********************************************************************** + + Auxiliary functions for parsing actions. + These make it easier to build up data structures (including lists) + in the parsing actions. + + ***********************************************************************/ + +/* mk_element - generic constructor for element */ +element * mk_element(int key); + +/* mk_str - constructor for STR element */ +element * mk_str(char *string); + +/* mk_str_from_list - makes STR element by concatenating a + * reversed list of strings, adding optional extra newline */ +element * mk_str_from_list(element *list, bool extra_newline); + +/* mk_list - makes new list with key 'key' and children the reverse of 'lst'. + * This is designed to be used with cons to build lists in a parser action. + * The reversing is necessary because cons adds to the head of a list. */ +element * mk_list(int key, element *lst); + +/* mk_link - constructor for LINK element */ +element * mk_link(element *label, char *url, char *title, element *attr, char *id); +/* extension = returns true if extension is selected */ +bool extension(int ext); + +/* match_inlines - returns true if inline lists match (case-insensitive...) */ +bool match_inlines(element *l1, element *l2); + +/* find_reference - return true if link found in references matching label. + * 'link' is modified with the matching url and title. */ +bool find_reference(link *result, element *label); + +/* find_note - return true if note found in notes matching label. +if found, 'result' is set to point to matched note. */ + +bool find_note(element **result, char *label); + + +char *label_from_string(char *str, bool obfuscate); +void localize_typography(GString *out, int character, int language, int output); +void print_raw_element_list(GString *out, element *list); +void append_list(element *new, element *list); +bool find_label(link *result, element *label); +bool check_timeout(); +void trim_trailing_whitespace(char *str); +char *label_from_element_list(element *list, bool obfuscate); +void print_raw_element_list(GString *out, element *list); +void print_raw_element(GString *out, element *elt); + +#endif + diff --git a/src/lib/multimarkdown/src/win/getopt.c b/src/lib/multimarkdown/src/win/getopt.c new file mode 100644 index 0000000..08f9f88 --- /dev/null +++ b/src/lib/multimarkdown/src/win/getopt.c @@ -0,0 +1,230 @@ +/* + * pgetopt.c - Portable implementation of getopt() command line args parser, + * originally made available by IBM and the authors listed below. + * + * Created on 8/8/08. + * Portions of this document are Copyright (C) 2008, PlexFX, + * All Rights Reserved. + * + * History: + * Original Date Unknown + * This code is quite old, but it was originally called GETOPT.C + * in the comments, along with a GETOPT.H thin header, and used the + * same namespace as the getopt() implementation on my UNIX variant + * platforms. The original date has been lost. It may date back + * to even pre-ANSI C. The development team at PlexFX has been + * using it (primarily for Windows command line tools, but also on + * other platforms for many years. A search for historical dates + * via web search engines found it widely used, but no date stamps + * on its original form seem to have been preserved. + * It can be found in various forms in open source packages, such + * as using a search engine on one or both of the author strings + * shown in the original comment block below. For example, as of + * the creation date on this file, a slightly modified verion of + * it was used in library code found in the CVS tree for + * OpenSolaris. + * + * It was also included on at least some of the MSDN Library discs + * Around the early 2001-2003 time frame. + * + * 2008-08-08 This version is a modified version of the original IBM code, but + * the filename and namespace used has been altered along with some + * calling convention changes. As such, it can be used as a drop- + * in replacement for getopt() even on UNIX or Linux systems that + * have their own getopt() implementations in libc without naming + * collisions. This means it can be used portably on any OS with + * a conforming C compiler. It does *not* attempt to implement the + * more long-winded getopt_long() interface. Naming of APIs, + * headers and the optarg/optind externs have been prefixed with + * 'p' to accomplish this. Examples: pgetopt(), poptarg, poptind, + * pgetopt.c, pgetopt.h. + * Note: This interface keeps external state (to match original + * calling conventions). As such, it is not thread safe, + * and should be called in only one thread (use from main() + * before additional threads are started). As the command + * line should never change, this should not be an issue. + * + */ + +/* Original IBM "AS IS" license follows */ + +/***************************************************************************** + * + * MODULE NAME : GETOPT.C + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +/****************************************************************************** + * pgetopt() + * + * The pgetopt() function is a command line parser. It returns the next + * option character in argv that matches an option character in optstring. + * + * The argv argument points to an array of argc+1 elements containing argc + * pointers to character strings followed by a null pointer. + * + * The optstring argument points to a string of option characters; if an + * option character is followed by a colon, the option is expected to have + * an argument that may or may not be separated from it by white space. + * The external variable poptarg is set to point to the start of the option + * argument on return from pgetopt(). + * + * The pgetopt() function places in poptind the argv index of the next argument + * to be processed. The system initializes the external variable poptind to + * 1 before the first call to pgetopt(). + * + * When all options have been processed (that is, up to the first nonoption + * argument), pgetopt() returns EOF. The special option "--" may be used to + * delimit the end of the options; EOF will be returned, and "--" will be + * skipped. + * + * The pgetopt() function returns a question mark (?) when it encounters an + * option character not included in optstring. This error message can be + * disabled by setting popterr to zero. Otherwise, it returns the option + * character that was detected. + * + * If the special option "--" is detected, or all options have been + * processed, EOF is returned. + * + * Options are marked by either a minus sign (-) or a slash (/). + * + * No other errors are defined. + *****************************************************************************/ + +#include /* for EOF */ +#include /* for strchr() */ +#include "getopt.h" /* getopt() interface and example code */ + +/* global variables that are specified as exported by getopt() */ +char *optarg = NULL; /* pointer to the start of the option argument */ +int optind = 1; /* number of the next argv[] to be evaluated */ +int opterr = 1; /* non-zero if a question mark should be returned + * when a non-valid option character is detected */ + +/* handle possible future character set concerns by putting this in a macro */ +#define _next_char(string) (char)(*(string+1)) + +int +getopt(int argc, char *argv[], char *optstring) +{ + static char *IndexPosition = NULL; /* place inside current argv string */ + char *ArgString = NULL; /* where to start from next */ + char *OptString; /* the string in our program */ + + + if (IndexPosition != NULL) { + /* we last left off inside an argv string */ + if (*(++IndexPosition)) { + /* there is more to come in the most recent argv */ + ArgString = IndexPosition; + } + } + + if (ArgString == NULL) { + /* we didn't leave off in the middle of an argv string */ + if (optind >= argc) { + /* more command-line arguments than the argument count */ + IndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all command-line arguments */ + } + + /*--------------------------------------------------------------------- + * If the next argv[] is not an option, there can be no more options. + *-------------------------------------------------------------------*/ + ArgString = argv[optind++]; /* set this to the next argument ptr */ + + if (('/' != *ArgString) && /* doesn't start with a slash or a dash? */ + ('-' != *ArgString)) { + --optind; /* point to current arg once we're done */ + optarg = NULL; /* no argument follows the option */ + IndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* used up all the command-line flags */ + } + + /* check for special end-of-flags markers */ + if ((strcmp(ArgString, "-") == 0) || + (strcmp(ArgString, "--") == 0)) { + optarg = NULL; /* no argument follows the option */ + IndexPosition = NULL; /* not in the middle of anything */ + return EOF; /* encountered the special flag */ + } + + ArgString++; /* look past the / or - */ + } + + if (':' == *ArgString) { /* is it a colon? */ + /*--------------------------------------------------------------------- + * Rare case: if opterr is non-zero, return a question mark; + * otherwise, just return the colon we're on. + *-------------------------------------------------------------------*/ + return (opterr ? (int)'?' : (int)':'); + } + else if ((OptString = strchr(optstring, *ArgString)) == 0) { + /*--------------------------------------------------------------------- + * The letter on the command-line wasn't any good. + *-------------------------------------------------------------------*/ + optarg = NULL; /* no argument follows the option */ + IndexPosition = NULL; /* not in the middle of anything */ + return (opterr ? (int)'?' : (int)*ArgString); + } + else { + /*--------------------------------------------------------------------- + * The letter on the command-line matches one we expect to see + *-------------------------------------------------------------------*/ + if (':' == _next_char(OptString)) { /* is the next letter a colon? */ + /* It is a colon. Look for an argument string. */ + if ('\0' != _next_char(ArgString)) { /* argument in this argv? */ + optarg = &ArgString[1]; /* Yes, it is */ + } + else { + /*------------------------------------------------------------- + * The argument string must be in the next argv. + * But, what if there is none (bad input from the user)? + * In that case, return the letter, and optarg as NULL. + *-----------------------------------------------------------*/ + if (optind < argc) + optarg = argv[optind++]; + else { + optarg = NULL; + return (opterr ? (int)'?' : (int)*ArgString); + } + } + IndexPosition = NULL; /* not in the middle of anything */ + } + else { + /* it's not a colon, so just return the letter */ + optarg = NULL; /* no argument follows the option */ + IndexPosition = ArgString; /* point to the letter we're on */ + } + return (int)*ArgString; /* return the letter that matched */ + } +} + + diff --git a/src/lib/multimarkdown/src/win/getopt.h b/src/lib/multimarkdown/src/win/getopt.h new file mode 100644 index 0000000..bb50f4d --- /dev/null +++ b/src/lib/multimarkdown/src/win/getopt.h @@ -0,0 +1,152 @@ +/* + * pgetopt.h - Portable implementation of getopt() command line args parser, + * originally made available by IBM and the authors listed below. + * + * Created on 8/8/08. + * Portions of this document are Copyright (C) 2008, PlexFX, + * All Rights Reserved. + * + * History: + * Original Date Unknown + * This code is quite old, but it was originally called GETOPT.H + * in the comments, along with a GETOPT.C source file, and used the + * same namespace as the getopt() implementation on my UNIX variant + * platforms. The original date has been lost. It may date back + * to even pre-ANSI C. The development team at PlexFX has been + * using it (primarily for Windows command line tools, but also on + * other platforms for many years. A search for historical dates + * via web search engines found it widely used, but no date stamps + * on its original form seem to have been preserved. + * It can be found in various forms in open source packages, such + * as using a search engine on one or both of the author strings + * shown in the original comment block below. For example, as of + * the creation date on this file, a slightly modified verion of + * it was used in library code found in the CVS tree for + * OpenSolaris. + * + * It was also included on at least some of the MSDN Library discs + * Around the early 2001-2003 time frame. + * + * 2008-08-08 This version is a modified version of the original IBM code, but + * the filename and namespace used has been altered along with some + * calling convention changes. As such, it can be used as a drop- + * in replacement for getopt() even on UNIX or Linux systems that + * have their own getopt() implementations in libc without naming + * collisions. This means it can be used portably on any OS with + * a conforming C compiler. It does *not* attempt to implement the + * more long-winded getopt_long() interface. Naming of APIs, + * headers and the optarg/optind externs have been prefixed with + * 'p' to accomplish this. Examples: pgetopt(), poptarg, poptind, + * pgetopt.c, pgetopt.h. Also added some comments to clarify usage + * for the popterr extern. + * Note: This interface keeps external state (to match original + * calling conventions). As such, it is not thread safe, + * and should be called in only one thread (use from main() + * before additional threads are started). As the command + * line should never change, this should not be an issue. + * + */ + +/* Original IBM "AS IS" license follows */ + +/***************************************************************************** + * + * MODULE NAME : GETOPT.H + * + * COPYRIGHTS: + * This module contains code made available by IBM + * Corporation on an AS IS basis. Any one receiving the + * module is considered to be licensed under IBM copyrights + * to use the IBM-provided source code in any way he or she + * deems fit, including copying it, compiling it, modifying + * it, and redistributing it, with or without + * modifications. No license under any IBM patents or + * patent applications is to be implied from this copyright + * license. + * + * A user of the module should understand that IBM cannot + * provide technical support for the module and will not be + * responsible for any consequences of use of the program. + * + * Any notices, including this one, are not to be removed + * from the module without the prior written consent of + * IBM. + * + * AUTHOR: Original author: + * G. R. Blair (BOBBLAIR at AUSVM1) + * Internet: bobblair@bobblair.austin.ibm.com + * + * Extensively revised by: + * John Q. Walker II, Ph.D. (JOHHQ at RALVM6) + * Internet: johnq@ralvm6.vnet.ibm.com + * + *****************************************************************************/ + +#ifndef H_GETOPT +#define H_GETOPT 1 + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +extern char * optarg; /* carries the optional argument when a command line + * arg is specified with a ':' after it in the optstring + * and is usually handled by the caller in a switch() + * block. */ +extern int optind; /* The caller should not need to adjust this normally */ +extern int opterr; /* The getopt() function returns a question mark (?) + * when it encounters an option character not included in + * optstring. This error message can be disabled by + * setting opterr to zero. Otherwise, it returns the + * option character that was detected. */ + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +int getopt(int argc, char *argv[], char *optstring); + +/* Example code by PlexFX to demonstrate calling of and parsing optional extra + * args. This is a sample code fragment, untested, minimal or non-existent + * error handling, and some headers variable declarations are omitted. + */ + +#if 0 /* remove, for example purposes only */ + +/* Note the ':' shown below in the pattern string, to specify additional arg */ +char opt_pattern[] = "s:V?"; + +while ((c = getopt(argc, argv, opt_pattern)) != -1) +{ + switch(c) + { + case 's': /* specify a /s option with a numeric size parameter + * which is provided in optarg, a char * + * Example: $ someprogram /s 100 + */ + + some_size = atoi(optarg); /* should use strtol() in new code */ + break; + case 'V': /* specify a /V version option, with no parameter */ + puts("someprogram: Version 1.0"); + break; + case '?': /* explicit allows of -? or /? */ + default: /* and give usage any time invalid arguments are given */ + PrintUsage(); /* call some function to show usage */ + break; + } +} +#endif /* 0 */ + +#endif /* ! H_GETOPT */ + diff --git a/src/lib/multimarkdown/src/win/libgen.h b/src/lib/multimarkdown/src/win/libgen.h new file mode 100644 index 0000000..7c6850a --- /dev/null +++ b/src/lib/multimarkdown/src/win/libgen.h @@ -0,0 +1,9 @@ +#ifndef _LIBGEN_H +#define _LIBGEN_H + +char * basename (char *fname) +{ + return fname; +} + +#endif // _LIBGEN_H diff --git a/src/lib/multimarkdown/src/win/stdbool.h b/src/lib/multimarkdown/src/win/stdbool.h new file mode 100644 index 0000000..d993a45 --- /dev/null +++ b/src/lib/multimarkdown/src/win/stdbool.h @@ -0,0 +1,10 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +#ifndef __cplusplus +typedef int bool; +#define false 0 +#define true 1 +#endif + +#endif diff --git a/src/lib/multimarkdown/src/win/unistd.h b/src/lib/multimarkdown/src/win/unistd.h new file mode 100644 index 0000000..97deb86 --- /dev/null +++ b/src/lib/multimarkdown/src/win/unistd.h @@ -0,0 +1,45 @@ +#ifndef _UNISTD_H +#define _UNISTD_H + +#include "getopt.h" + +/* This file intended to serve as a drop-in replacement for + * unistd.h on Windows + * Please add functionality as neeeded + */ + +#include +#include +#include /* for getpid() and the exec..() family */ + +#define srandom srand +#define random rand + +/* Values for the second argument to access. + These may be OR'd together. */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +//#define X_OK 1 /* execute permission - unsupported in windows*/ +#define F_OK 0 /* Test for existence. */ + +#define access _access +#define ftruncate _chsize + +#define ssize_t int + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +/* should be in some equivalent to */ +#ifndef __cplusplus +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#endif + +#endif // _UNISTD_H diff --git a/src/lib/multimarkdown/update_sm.sh b/src/lib/multimarkdown/update_sm.sh new file mode 100755 index 0000000..1d32eb0 --- /dev/null +++ b/src/lib/multimarkdown/update_sm.sh @@ -0,0 +1,4 @@ +git submodule init +git submodule update --init --recursive +git submodule foreach --recursive git checkout master +git submodule foreach --recursive git pull diff --git a/src/lib/pcre/AUTHORS b/src/lib/pcre/AUTHORS new file mode 100644 index 0000000..ba4753d --- /dev/null +++ b/src/lib/pcre/AUTHORS @@ -0,0 +1,45 @@ +THE MAIN PCRE LIBRARY +--------------------- + +Written by: Philip Hazel +Email local part: ph10 +Email domain: cam.ac.uk + +University of Cambridge Computing Service, +Cambridge, England. + +Copyright (c) 1997-2012 University of Cambridge +All rights reserved + + +PCRE JUST-IN-TIME COMPILATION SUPPORT +------------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2010-2012 Zoltan Herczeg +All rights reserved. + + +STACK-LESS JUST-IN-TIME COMPILER +-------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2009-2012 Zoltan Herczeg +All rights reserved. + + +THE C++ WRAPPER LIBRARY +----------------------- + +Written by: Google Inc. + +Copyright (c) 2007-2012 Google Inc +All rights reserved + +#### diff --git a/src/lib/pcre/COPYING b/src/lib/pcre/COPYING new file mode 100644 index 0000000..58eed01 --- /dev/null +++ b/src/lib/pcre/COPYING @@ -0,0 +1,5 @@ +PCRE LICENCE + +Please see the file LICENCE in the PCRE distribution for licensing details. + +End diff --git a/src/lib/pcre/LICENCE b/src/lib/pcre/LICENCE new file mode 100644 index 0000000..5ce31a8 --- /dev/null +++ b/src/lib/pcre/LICENCE @@ -0,0 +1,92 @@ +PCRE LICENCE +------------ + +PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + +Release 8 of PCRE is distributed under the terms of the "BSD" licence, as +specified below. The documentation for PCRE, supplied in the "doc" +directory, is distributed under the same terms as the software itself. + +The basic library functions are written in C and are freestanding. Also +included in the distribution is a set of C++ wrapper functions, and a +just-in-time compiler that can be used to optimize pattern matching. These +are both optional features that can be omitted when the library is built. + + +THE BASIC LIBRARY FUNCTIONS +--------------------------- + +Written by: Philip Hazel +Email local part: ph10 +Email domain: cam.ac.uk + +University of Cambridge Computing Service, +Cambridge, England. + +Copyright (c) 1997-2012 University of Cambridge +All rights reserved. + + +PCRE JUST-IN-TIME COMPILATION SUPPORT +------------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2010-2012 Zoltan Herczeg +All rights reserved. + + +STACK-LESS JUST-IN-TIME COMPILER +-------------------------------- + +Written by: Zoltan Herczeg +Email local part: hzmester +Emain domain: freemail.hu + +Copyright(c) 2009-2012 Zoltan Herczeg +All rights reserved. + + +THE C++ WRAPPER FUNCTIONS +------------------------- + +Contributed by: Google Inc. + +Copyright (c) 2007-2012, Google Inc. +All rights reserved. + + +THE "BSD" LICENCE +----------------- + +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 the University of Cambridge nor the name of Google + Inc. nor the names of their contributors may be used to endorse or + promote products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. + +End diff --git a/src/lib/pcre/config.h b/src/lib/pcre/config.h new file mode 100644 index 0000000..6dda704 --- /dev/null +++ b/src/lib/pcre/config.h @@ -0,0 +1,34 @@ +#define HAVE_MEMMOVE 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 + +#define LINK_SIZE 2 +#define MATCH_LIMIT 10000000 +#define MATCH_LIMIT_RECURSION MATCH_LIMIT +#define MAX_NAME_COUNT 10000 +#define MAX_NAME_SIZE 32 +#define NEWLINE 10 + +#define POSIX_MALLOC_THRESHOLD 10 +#define SUPPORT_UCP +#define SUPPORT_UTF16 + +/* + man 3 pcrejit for a list of supported platforms; + as PCRE 8.30, stable JIT support is available for: + - ARM v5, v7, and Thumb2 (__GNUC__ compilers only) + - x86/x86-64 + - MIPS 32bit (__GNUC__ compilers only) +*/ +#if \ + /* ARM */ \ + (defined(__GNUC__) && (defined(__arm__) || defined(__TARGET_ARCH_ARM))) \ + /* x86 32/64 */ \ + || defined(__i386) || defined(__i386__) || defined(_M_IX86) \ + || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64) \ + /* MIPS32 */ \ + || (defined(__GNUC__) \ + && (defined(__mips) || defined(__mips__)) \ + && !(defined(_MIPS_ARCH_MIPS64) || defined(__mips64))) +# define SUPPORT_JIT +#endif diff --git a/src/lib/pcre/import_from_pcre_tarball.sh b/src/lib/pcre/import_from_pcre_tarball.sh new file mode 100644 index 0000000..914820e --- /dev/null +++ b/src/lib/pcre/import_from_pcre_tarball.sh @@ -0,0 +1,154 @@ +#! /bin/sh +############################################################################# +## +## Copyright (C) 2012 Giuseppe D'Angelo . +## Contact: http://www.qt-project.org/ +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## GNU Lesser General Public License Usage +## This file may be used under the terms of the GNU Lesser General Public +## License version 2.1 as published by the Free Software Foundation and +## appearing in the file LICENSE.LGPL included in the packaging of this +## file. Please review the following information to ensure the GNU Lesser +## General Public License version 2.1 requirements will be met: +## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## In addition, as a special exception, Nokia gives you certain additional +## rights. These rights are described in the Nokia Qt LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU General +## Public License version 3.0 as published by the Free Software Foundation +## and appearing in the file LICENSE.GPL included in the packaging of this +## file. Please review the following information to ensure the GNU General +## Public License version 3.0 requirements will be met: +## http://www.gnu.org/copyleft/gpl.html. +## +## Other Usage +## Alternatively, this file may be used in accordance with the terms and +## conditions contained in a signed written agreement between you and Nokia. +## +## +## +## +## +## +## $QT_END_LICENSE$ +## +############################################################################# + +# This is a small script to copy the required files from a PCRE tarball +# into 3rdparty/pcre/ , following the instructions found in the NON-UNIX-USE +# file. Documentation, tests, demos etc. are not imported. +# Also, a global s/HAVE_CONFIG_H/PCRE_HAVE_CONFIG_H/g is performed, to avoid +# tampering QtCore compilation with a -DHAVE_CONFIG_H. + +if [ $# -ne 2 ]; then + echo "Usage: $0 pcre_tarball_dir/ \$QTDIR/src/3rdparty/pcre/" + exit 1 +fi + +PCRE_DIR=$1 +TARGET_DIR=$2 + +if [ ! -d "$PCRE_DIR" -o ! -r "$PCRE_DIR" -o ! -d "$TARGET_DIR" -o ! -w "$TARGET_DIR" ]; then + echo "Either the PCRE source dir or the target dir do not exist," + echo "are not directories or have the wrong permissions." + exit 2 +fi + +# with 1 argument, copies PCRE_DIR/$1 to TARGET_DIR/$1 +# with 2 arguments, copies PCRE_DIR/$1 to TARGET_DIR/$2 +# every file copied gets a s/HAVE_CONFIG_H/PCRE_HAVE_CONFIG_H/g +copy_and_convert_file() { + if [ $# -lt 1 -o $# -gt 2 ]; then + echo "Wrong number of arguments to copy_and_convert_file" + exit 3 + fi + + SOURCE_FILE=$1 + if [ -n "$2" ]; then + DEST_FILE=$2 + else + DEST_FILE=$1 + fi + + mkdir -p "$TARGET_DIR/$(dirname "$SOURCE_FILE")" + sed 's/HAVE_CONFIG_H/PCRE_HAVE_CONFIG_H/g' < "$PCRE_DIR/$SOURCE_FILE" > "$TARGET_DIR/$DEST_FILE" +} + +copy_and_convert_file "pcre.h.generic" "pcre.h" +copy_and_convert_file "pcre_chartables.c.dist" "pcre_chartables.c" + +FILES=" + AUTHORS + COPYING + LICENCE + pcre_internal.h + ucp.h + pcre_byte_order.c + pcre_compile.c + pcre_config.c + pcre_dfa_exec.c + pcre_exec.c + pcre_fullinfo.c + pcre_get.c + pcre_globals.c + pcre_jit_compile.c + pcre_maketables.c + pcre_newline.c + pcre_ord2utf8.c + pcre_refcount.c + pcre_string_utils.c + pcre_study.c + pcre_tables.c + pcre_ucd.c + pcre_valid_utf8.c + pcre_version.c + pcre_xclass.c + pcre16_byte_order.c + pcre16_chartables.c + pcre16_compile.c + pcre16_config.c + pcre16_dfa_exec.c + pcre16_exec.c + pcre16_fullinfo.c + pcre16_get.c + pcre16_globals.c + pcre16_jit_compile.c + pcre16_maketables.c + pcre16_newline.c + pcre16_ord2utf16.c + pcre16_refcount.c + pcre16_string_utils.c + pcre16_study.c + pcre16_tables.c + pcre16_ucd.c + pcre16_utf16_utils.c + pcre16_valid_utf16.c + pcre16_version.c + pcre16_xclass.c + sljit/sljitLir.c + sljit/sljitLir.h + sljit/sljitNativePPC_common.c + sljit/sljitNativeX86_common.c + sljit/sljitNativeARM_v5.c + sljit/sljitNativeX86_32.c + sljit/sljitNativeX86_64.c + sljit/sljitNativePPC_32.c + sljit/sljitNativePPC_64.c + sljit/sljitConfig.h + sljit/sljitNativeMIPS_32.c + sljit/sljitUtils.c + sljit/sljitNativeMIPS_common.c + sljit/sljitExecAllocator.c + sljit/sljitConfigInternal.h + sljit/sljitNativeARM_Thumb2.c +" + +for i in $FILES; do + copy_and_convert_file "$i" +done diff --git a/src/lib/pcre/pcre.h b/src/lib/pcre/pcre.h new file mode 100644 index 0000000..712bd3d --- /dev/null +++ b/src/lib/pcre/pcre.h @@ -0,0 +1,503 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This is the public header file for the PCRE library, to be #included by +applications that call the PCRE functions. + + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 _PCRE_H +#define _PCRE_H + +/* The current PCRE version information. */ + +#define PCRE_MAJOR 8 +#define PCRE_MINOR 30 +#define PCRE_PRERELEASE +#define PCRE_DATE 2012-02-04 + +/* When an application links to a PCRE DLL in Windows, the symbols that are +imported have to be identified as such. When building PCRE, the appropriate +export setting is defined in pcre_internal.h, which includes this file. So we +don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */ + +#if defined(_WIN32) && !defined(PCRE_STATIC) +# ifndef PCRE_EXP_DECL +# define PCRE_EXP_DECL extern __declspec(dllimport) +# endif +# ifdef __cplusplus +# ifndef PCRECPP_EXP_DECL +# define PCRECPP_EXP_DECL extern __declspec(dllimport) +# endif +# ifndef PCRECPP_EXP_DEFN +# define PCRECPP_EXP_DEFN __declspec(dllimport) +# endif +# endif +#endif + +/* By default, we use the standard "extern" declarations. */ + +#ifndef PCRE_EXP_DECL +# ifdef __cplusplus +# define PCRE_EXP_DECL extern "C" +# else +# define PCRE_EXP_DECL extern +# endif +#endif + +#ifdef __cplusplus +# ifndef PCRECPP_EXP_DECL +# define PCRECPP_EXP_DECL extern +# endif +# ifndef PCRECPP_EXP_DEFN +# define PCRECPP_EXP_DEFN +# endif +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options. Some are compile-time only, some are run-time only, and some are +both, so we keep them all distinct. However, almost all the bits in the options +word are now used. In the long run, we may have to re-use some of the +compile-time only bits for runtime options, or vice versa. In the comments +below, "compile", "exec", and "DFA exec" mean that the option is permitted to +be set for those functions; "used in" means that an option may be set only for +compile, but is subsequently referenced in exec and/or DFA exec. Any of the +compile-time options may be inspected during studying (and therefore JIT +compiling). */ + +#define PCRE_CASELESS 0x00000001 /* Compile */ +#define PCRE_MULTILINE 0x00000002 /* Compile */ +#define PCRE_DOTALL 0x00000004 /* Compile */ +#define PCRE_EXTENDED 0x00000008 /* Compile */ +#define PCRE_ANCHORED 0x00000010 /* Compile, exec, DFA exec */ +#define PCRE_DOLLAR_ENDONLY 0x00000020 /* Compile, used in exec, DFA exec */ +#define PCRE_EXTRA 0x00000040 /* Compile */ +#define PCRE_NOTBOL 0x00000080 /* Exec, DFA exec */ +#define PCRE_NOTEOL 0x00000100 /* Exec, DFA exec */ +#define PCRE_UNGREEDY 0x00000200 /* Compile */ +#define PCRE_NOTEMPTY 0x00000400 /* Exec, DFA exec */ +/* The next two are also used in exec and DFA exec */ +#define PCRE_UTF8 0x00000800 /* Compile (same as PCRE_UTF16) */ +#define PCRE_UTF16 0x00000800 /* Compile (same as PCRE_UTF8) */ +#define PCRE_NO_AUTO_CAPTURE 0x00001000 /* Compile */ +/* The next two are also used in exec and DFA exec */ +#define PCRE_NO_UTF8_CHECK 0x00002000 /* Compile (same as PCRE_NO_UTF16_CHECK) */ +#define PCRE_NO_UTF16_CHECK 0x00002000 /* Compile (same as PCRE_NO_UTF8_CHECK) */ +#define PCRE_AUTO_CALLOUT 0x00004000 /* Compile */ +#define PCRE_PARTIAL_SOFT 0x00008000 /* Exec, DFA exec */ +#define PCRE_PARTIAL 0x00008000 /* Backwards compatible synonym */ +#define PCRE_DFA_SHORTEST 0x00010000 /* DFA exec */ +#define PCRE_DFA_RESTART 0x00020000 /* DFA exec */ +#define PCRE_FIRSTLINE 0x00040000 /* Compile, used in exec, DFA exec */ +#define PCRE_DUPNAMES 0x00080000 /* Compile */ +#define PCRE_NEWLINE_CR 0x00100000 /* Compile, exec, DFA exec */ +#define PCRE_NEWLINE_LF 0x00200000 /* Compile, exec, DFA exec */ +#define PCRE_NEWLINE_CRLF 0x00300000 /* Compile, exec, DFA exec */ +#define PCRE_NEWLINE_ANY 0x00400000 /* Compile, exec, DFA exec */ +#define PCRE_NEWLINE_ANYCRLF 0x00500000 /* Compile, exec, DFA exec */ +#define PCRE_BSR_ANYCRLF 0x00800000 /* Compile, exec, DFA exec */ +#define PCRE_BSR_UNICODE 0x01000000 /* Compile, exec, DFA exec */ +#define PCRE_JAVASCRIPT_COMPAT 0x02000000 /* Compile, used in exec */ +#define PCRE_NO_START_OPTIMIZE 0x04000000 /* Compile, exec, DFA exec */ +#define PCRE_NO_START_OPTIMISE 0x04000000 /* Synonym */ +#define PCRE_PARTIAL_HARD 0x08000000 /* Exec, DFA exec */ +#define PCRE_NOTEMPTY_ATSTART 0x10000000 /* Exec, DFA exec */ +#define PCRE_UCP 0x20000000 /* Compile, used in exec, DFA exec */ + +/* Exec-time and get/set-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_OPCODE (-5) +#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) +#define PCRE_ERROR_MATCHLIMIT (-8) +#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ +#define PCRE_ERROR_BADUTF8 (-10) /* Same for 8/16 */ +#define PCRE_ERROR_BADUTF16 (-10) /* Same for 8/16 */ +#define PCRE_ERROR_BADUTF8_OFFSET (-11) /* Same for 8/16 */ +#define PCRE_ERROR_BADUTF16_OFFSET (-11) /* Same for 8/16 */ +#define PCRE_ERROR_PARTIAL (-12) +#define PCRE_ERROR_BADPARTIAL (-13) +#define PCRE_ERROR_INTERNAL (-14) +#define PCRE_ERROR_BADCOUNT (-15) +#define PCRE_ERROR_DFA_UITEM (-16) +#define PCRE_ERROR_DFA_UCOND (-17) +#define PCRE_ERROR_DFA_UMLIMIT (-18) +#define PCRE_ERROR_DFA_WSSIZE (-19) +#define PCRE_ERROR_DFA_RECURSE (-20) +#define PCRE_ERROR_RECURSIONLIMIT (-21) +#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ +#define PCRE_ERROR_BADNEWLINE (-23) +#define PCRE_ERROR_BADOFFSET (-24) +#define PCRE_ERROR_SHORTUTF8 (-25) +#define PCRE_ERROR_SHORTUTF16 (-25) /* Same for 8/16 */ +#define PCRE_ERROR_RECURSELOOP (-26) +#define PCRE_ERROR_JIT_STACKLIMIT (-27) +#define PCRE_ERROR_BADMODE (-28) +#define PCRE_ERROR_BADENDIANNESS (-29) + +/* Specific error codes for UTF-8 validity checks */ + +#define PCRE_UTF8_ERR0 0 +#define PCRE_UTF8_ERR1 1 +#define PCRE_UTF8_ERR2 2 +#define PCRE_UTF8_ERR3 3 +#define PCRE_UTF8_ERR4 4 +#define PCRE_UTF8_ERR5 5 +#define PCRE_UTF8_ERR6 6 +#define PCRE_UTF8_ERR7 7 +#define PCRE_UTF8_ERR8 8 +#define PCRE_UTF8_ERR9 9 +#define PCRE_UTF8_ERR10 10 +#define PCRE_UTF8_ERR11 11 +#define PCRE_UTF8_ERR12 12 +#define PCRE_UTF8_ERR13 13 +#define PCRE_UTF8_ERR14 14 +#define PCRE_UTF8_ERR15 15 +#define PCRE_UTF8_ERR16 16 +#define PCRE_UTF8_ERR17 17 +#define PCRE_UTF8_ERR18 18 +#define PCRE_UTF8_ERR19 19 +#define PCRE_UTF8_ERR20 20 +#define PCRE_UTF8_ERR21 21 + +/* Specific error codes for UTF-16 validity checks */ + +#define PCRE_UTF16_ERR0 0 +#define PCRE_UTF16_ERR1 1 +#define PCRE_UTF16_ERR2 2 +#define PCRE_UTF16_ERR3 3 +#define PCRE_UTF16_ERR4 4 + +/* Request types for pcre_fullinfo() */ + +#define PCRE_INFO_OPTIONS 0 +#define PCRE_INFO_SIZE 1 +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_INFO_BACKREFMAX 3 +#define PCRE_INFO_FIRSTBYTE 4 +#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ +#define PCRE_INFO_FIRSTTABLE 5 +#define PCRE_INFO_LASTLITERAL 6 +#define PCRE_INFO_NAMEENTRYSIZE 7 +#define PCRE_INFO_NAMECOUNT 8 +#define PCRE_INFO_NAMETABLE 9 +#define PCRE_INFO_STUDYSIZE 10 +#define PCRE_INFO_DEFAULT_TABLES 11 +#define PCRE_INFO_OKPARTIAL 12 +#define PCRE_INFO_JCHANGED 13 +#define PCRE_INFO_HASCRORLF 14 +#define PCRE_INFO_MINLENGTH 15 +#define PCRE_INFO_JIT 16 +#define PCRE_INFO_JITSIZE 17 + +/* Request types for pcre_config(). Do not re-arrange, in order to remain +compatible. */ + +#define PCRE_CONFIG_UTF8 0 +#define PCRE_CONFIG_NEWLINE 1 +#define PCRE_CONFIG_LINK_SIZE 2 +#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 +#define PCRE_CONFIG_MATCH_LIMIT 4 +#define PCRE_CONFIG_STACKRECURSE 5 +#define PCRE_CONFIG_UNICODE_PROPERTIES 6 +#define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7 +#define PCRE_CONFIG_BSR 8 +#define PCRE_CONFIG_JIT 9 +#define PCRE_CONFIG_UTF16 10 +#define PCRE_CONFIG_JITTARGET 11 + +/* Request types for pcre_study(). Do not re-arrange, in order to remain +compatible. */ + +#define PCRE_STUDY_JIT_COMPILE 0x0001 + +/* Bit flags for the pcre[16]_extra structure. Do not re-arrange or redefine +these bits, just add new ones on the end, in order to remain compatible. */ + +#define PCRE_EXTRA_STUDY_DATA 0x0001 +#define PCRE_EXTRA_MATCH_LIMIT 0x0002 +#define PCRE_EXTRA_CALLOUT_DATA 0x0004 +#define PCRE_EXTRA_TABLES 0x0008 +#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 +#define PCRE_EXTRA_MARK 0x0020 +#define PCRE_EXTRA_EXECUTABLE_JIT 0x0040 + +/* Types */ + +struct real_pcre; /* declaration; the definition is private */ +typedef struct real_pcre pcre; + +struct real_pcre16; /* declaration; the definition is private */ +typedef struct real_pcre16 pcre16; + +struct real_pcre_jit_stack; /* declaration; the definition is private */ +typedef struct real_pcre_jit_stack pcre_jit_stack; + +struct real_pcre16_jit_stack; /* declaration; the definition is private */ +typedef struct real_pcre16_jit_stack pcre16_jit_stack; + +/* If PCRE is compiled with 16 bit character support, PCRE_UCHAR16 must contain +a 16 bit wide signed data type. Otherwise it can be a dummy data type since +pcre16 functions are not implemented. There is a check for this in pcre_internal.h. */ +#ifndef PCRE_UCHAR16 +#define PCRE_UCHAR16 unsigned short +#endif + +#ifndef PCRE_SPTR16 +#define PCRE_SPTR16 const PCRE_UCHAR16 * +#endif + +/* When PCRE is compiled as a C++ library, the subject pointer type can be +replaced with a custom type. For conventional use, the public interface is a +const char *. */ + +#ifndef PCRE_SPTR +#define PCRE_SPTR const char * +#endif + +/* The structure for passing additional data to pcre_exec(). This is defined in +such as way as to be extensible. Always add new fields at the end, in order to +remain compatible. */ + +typedef struct pcre_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ + unsigned long int match_limit_recursion; /* Max recursive calls to match() */ + unsigned char **mark; /* For passing back a mark pointer */ + void *executable_jit; /* Contains a pointer to a compiled jit code */ +} pcre_extra; + +/* Same structure as above, but with 16 bit char pointers. */ + +typedef struct pcre16_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ + unsigned long int match_limit_recursion; /* Max recursive calls to match() */ + PCRE_UCHAR16 **mark; /* For passing back a mark pointer */ + void *executable_jit; /* Contains a pointer to a compiled jit code */ +} pcre16_extra; + +/* The structure for passing out data via the pcre_callout_function. We use a +structure so that new fields can be added on the end in future versions, +without changing the API of the function, thereby allowing old clients to work +without modification. */ + +typedef struct pcre_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + PCRE_SPTR subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------- Added for Version 2 -------------------------- */ + const unsigned char *mark; /* Pointer to current mark or NULL */ + /* ------------------------------------------------------------------ */ +} pcre_callout_block; + +/* Same structure as above, but with 16 bit char pointers. */ + +typedef struct pcre16_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + PCRE_SPTR16 subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------- Added for Version 2 -------------------------- */ + const PCRE_UCHAR16 *mark; /* Pointer to current mark or NULL */ + /* ------------------------------------------------------------------ */ +} pcre16_callout_block; + +/* Indirection for store get and free functions. These can be set to +alternative malloc/free functions if required. Special ones are used in the +non-recursive case for "frames". There is also an optional callout function +that is triggered by the (?) regex item. For Virtual Pascal, these definitions +have to take another form. */ + +#ifndef VPCOMPAT +PCRE_EXP_DECL void *(*pcre_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_free)(void *); +PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_stack_free)(void *); +PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); + +PCRE_EXP_DECL void *(*pcre16_malloc)(size_t); +PCRE_EXP_DECL void (*pcre16_free)(void *); +PCRE_EXP_DECL void *(*pcre16_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre16_stack_free)(void *); +PCRE_EXP_DECL int (*pcre16_callout)(pcre16_callout_block *); +#else /* VPCOMPAT */ +PCRE_EXP_DECL void *pcre_malloc(size_t); +PCRE_EXP_DECL void pcre_free(void *); +PCRE_EXP_DECL void *pcre_stack_malloc(size_t); +PCRE_EXP_DECL void pcre_stack_free(void *); +PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); + +PCRE_EXP_DECL void *pcre16_malloc(size_t); +PCRE_EXP_DECL void pcre16_free(void *); +PCRE_EXP_DECL void *pcre16_stack_malloc(size_t); +PCRE_EXP_DECL void pcre16_stack_free(void *); +PCRE_EXP_DECL int pcre16_callout(pcre16_callout_block *); +#endif /* VPCOMPAT */ + +/* User defined callback which provides a stack just before the match starts. */ + +typedef pcre_jit_stack *(*pcre_jit_callback)(void *); +typedef pcre16_jit_stack *(*pcre16_jit_callback)(void *); + +/* Exported PCRE functions */ + +PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +PCRE_EXP_DECL pcre16 *pcre16_compile(PCRE_SPTR16, int, const char **, int *, + const unsigned char *); +PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, + int *, const unsigned char *); +PCRE_EXP_DECL pcre16 *pcre16_compile2(PCRE_SPTR16, int, int *, const char **, + int *, const unsigned char *); +PCRE_EXP_DECL int pcre_config(int, void *); +PCRE_EXP_DECL int pcre16_config(int, void *); +PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, + int *, int, const char *, char *, int); +PCRE_EXP_DECL int pcre16_copy_named_substring(const pcre16 *, PCRE_SPTR16, + int *, int, PCRE_SPTR16, PCRE_UCHAR16 *, int); +PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, + char *, int); +PCRE_EXP_DECL int pcre16_copy_substring(PCRE_SPTR16, int *, int, int, + PCRE_UCHAR16 *, int); +PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, + const char *, int, int, int, int *, int , int *, int); +PCRE_EXP_DECL int pcre16_dfa_exec(const pcre16 *, const pcre16_extra *, + PCRE_SPTR16, int, int, int, int *, int , int *, int); +PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, + int, int, int, int *, int); +PCRE_EXP_DECL int pcre16_exec(const pcre16 *, const pcre16_extra *, + PCRE_SPTR16, int, int, int, int *, int); +PCRE_EXP_DECL void pcre_free_substring(const char *); +PCRE_EXP_DECL void pcre16_free_substring(PCRE_SPTR16); +PCRE_EXP_DECL void pcre_free_substring_list(const char **); +PCRE_EXP_DECL void pcre16_free_substring_list(PCRE_SPTR16 *); +PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, + void *); +PCRE_EXP_DECL int pcre16_fullinfo(const pcre16 *, const pcre16_extra *, int, + void *); +PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, + int *, int, const char *, const char **); +PCRE_EXP_DECL int pcre16_get_named_substring(const pcre16 *, PCRE_SPTR16, + int *, int, PCRE_SPTR16, PCRE_SPTR16 *); +PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); +PCRE_EXP_DECL int pcre16_get_stringnumber(const pcre16 *, PCRE_SPTR16); +PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, + char **, char **); +PCRE_EXP_DECL int pcre16_get_stringtable_entries(const pcre16 *, PCRE_SPTR16, + PCRE_UCHAR16 **, PCRE_UCHAR16 **); +PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, + const char **); +PCRE_EXP_DECL int pcre16_get_substring(PCRE_SPTR16, int *, int, int, + PCRE_SPTR16 *); +PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, + const char ***); +PCRE_EXP_DECL int pcre16_get_substring_list(PCRE_SPTR16, int *, int, + PCRE_SPTR16 **); +PCRE_EXP_DECL const unsigned char *pcre_maketables(void); +PCRE_EXP_DECL const unsigned char *pcre16_maketables(void); +PCRE_EXP_DECL int pcre_refcount(pcre *, int); +PCRE_EXP_DECL int pcre16_refcount(pcre16 *, int); +PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); +PCRE_EXP_DECL pcre16_extra *pcre16_study(const pcre16 *, int, const char **); +PCRE_EXP_DECL void pcre_free_study(pcre_extra *); +PCRE_EXP_DECL void pcre16_free_study(pcre16_extra *); +PCRE_EXP_DECL const char *pcre_version(void); +PCRE_EXP_DECL const char *pcre16_version(void); + +/* Utility functions for byte order swaps. */ +PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *, pcre_extra *, + const unsigned char *); +PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *, pcre16_extra *, + const unsigned char *); +PCRE_EXP_DECL int pcre16_utf16_to_host_byte_order(PCRE_UCHAR16 *, + PCRE_SPTR16, int, int *, int); + +/* JIT compiler related functions. */ + +PCRE_EXP_DECL pcre_jit_stack *pcre_jit_stack_alloc(int, int); +PCRE_EXP_DECL pcre16_jit_stack *pcre16_jit_stack_alloc(int, int); +PCRE_EXP_DECL void pcre_jit_stack_free(pcre_jit_stack *); +PCRE_EXP_DECL void pcre16_jit_stack_free(pcre16_jit_stack *); +PCRE_EXP_DECL void pcre_assign_jit_stack(pcre_extra *, + pcre_jit_callback, void *); +PCRE_EXP_DECL void pcre16_assign_jit_stack(pcre16_extra *, + pcre16_jit_callback, void *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/src/lib/pcre/pcre.pro b/src/lib/pcre/pcre.pro new file mode 100644 index 0000000..edec4fc --- /dev/null +++ b/src/lib/pcre/pcre.pro @@ -0,0 +1,39 @@ +TEMPLATE = lib + +DEFINES += PCRE_HAVE_CONFIG_H + +TARGET = mdcharm_pcre + +unix: CONFIG += static + +CONFIG(release, debug|release){ + DESTDIR = ../../release +} else { + DESTDIR = ../../debug +} + +INCLUDEPATH += $$PWD +SOURCES += \ + $$PWD/pcre16_byte_order.c \ + $$PWD/pcre16_chartables.c \ + $$PWD/pcre16_compile.c \ + $$PWD/pcre16_config.c \ + $$PWD/pcre16_dfa_exec.c \ + $$PWD/pcre16_exec.c \ + $$PWD/pcre16_fullinfo.c \ + $$PWD/pcre16_get.c \ + $$PWD/pcre16_globals.c \ + $$PWD/pcre16_jit_compile.c \ + $$PWD/pcre16_maketables.c \ + $$PWD/pcre16_newline.c \ + $$PWD/pcre16_ord2utf16.c \ + $$PWD/pcre16_refcount.c \ + $$PWD/pcre16_string_utils.c \ + $$PWD/pcre16_study.c \ + $$PWD/pcre16_tables.c \ + $$PWD/pcre16_ucd.c \ + $$PWD/pcre16_utf16_utils.c \ + $$PWD/pcre16_valid_utf16.c \ + $$PWD/pcre16_version.c \ + $$PWD/pcre16_xclass.c + diff --git a/src/lib/pcre/pcre16_byte_order.c b/src/lib/pcre/pcre16_byte_order.c new file mode 100644 index 0000000..11d2973 --- /dev/null +++ b/src/lib/pcre/pcre16_byte_order.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_byte_order.c" + +/* End of pcre16_byte_order.c */ diff --git a/src/lib/pcre/pcre16_chartables.c b/src/lib/pcre/pcre16_chartables.c new file mode 100644 index 0000000..7c0ff35 --- /dev/null +++ b/src/lib/pcre/pcre16_chartables.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_chartables.c" + +/* End of pcre16_chartables.c */ diff --git a/src/lib/pcre/pcre16_compile.c b/src/lib/pcre/pcre16_compile.c new file mode 100644 index 0000000..e499b67 --- /dev/null +++ b/src/lib/pcre/pcre16_compile.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_compile.c" + +/* End of pcre16_compile.c */ diff --git a/src/lib/pcre/pcre16_config.c b/src/lib/pcre/pcre16_config.c new file mode 100644 index 0000000..b521387 --- /dev/null +++ b/src/lib/pcre/pcre16_config.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_config.c" + +/* End of pcre16_config.c */ diff --git a/src/lib/pcre/pcre16_dfa_exec.c b/src/lib/pcre/pcre16_dfa_exec.c new file mode 100644 index 0000000..2ba740e --- /dev/null +++ b/src/lib/pcre/pcre16_dfa_exec.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_dfa_exec.c" + +/* End of pcre16_dfa_exec.c */ diff --git a/src/lib/pcre/pcre16_exec.c b/src/lib/pcre/pcre16_exec.c new file mode 100644 index 0000000..7417b17 --- /dev/null +++ b/src/lib/pcre/pcre16_exec.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_exec.c" + +/* End of pcre16_exec.c */ diff --git a/src/lib/pcre/pcre16_fullinfo.c b/src/lib/pcre/pcre16_fullinfo.c new file mode 100644 index 0000000..544dca6 --- /dev/null +++ b/src/lib/pcre/pcre16_fullinfo.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_fullinfo.c" + +/* End of pcre16_fullinfo.c */ diff --git a/src/lib/pcre/pcre16_get.c b/src/lib/pcre/pcre16_get.c new file mode 100644 index 0000000..3ded08c --- /dev/null +++ b/src/lib/pcre/pcre16_get.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_get.c" + +/* End of pcre16_get.c */ diff --git a/src/lib/pcre/pcre16_globals.c b/src/lib/pcre/pcre16_globals.c new file mode 100644 index 0000000..a136b3d --- /dev/null +++ b/src/lib/pcre/pcre16_globals.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_globals.c" + +/* End of pcre16_globals.c */ diff --git a/src/lib/pcre/pcre16_jit_compile.c b/src/lib/pcre/pcre16_jit_compile.c new file mode 100644 index 0000000..ab0cacd --- /dev/null +++ b/src/lib/pcre/pcre16_jit_compile.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_jit_compile.c" + +/* End of pcre16_jit_compile.c */ diff --git a/src/lib/pcre/pcre16_maketables.c b/src/lib/pcre/pcre16_maketables.c new file mode 100644 index 0000000..b1cd1c5 --- /dev/null +++ b/src/lib/pcre/pcre16_maketables.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_maketables.c" + +/* End of pcre16_maketables.c */ diff --git a/src/lib/pcre/pcre16_newline.c b/src/lib/pcre/pcre16_newline.c new file mode 100644 index 0000000..7fe2014 --- /dev/null +++ b/src/lib/pcre/pcre16_newline.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_newline.c" + +/* End of pcre16_newline.c */ diff --git a/src/lib/pcre/pcre16_ord2utf16.c b/src/lib/pcre/pcre16_ord2utf16.c new file mode 100644 index 0000000..9f7db86 --- /dev/null +++ b/src/lib/pcre/pcre16_ord2utf16.c @@ -0,0 +1,95 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This file contains a private PCRE function that converts an ordinal +character value into a UTF16 string. */ + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_internal.h" + +/************************************************* +* Convert character value to UTF-16 * +*************************************************/ + +/* This function takes an integer value in the range 0 - 0x10ffff +and encodes it as a UTF-16 character in 1 to 2 pcre_uchars. + +Arguments: + cvalue the character value + buffer pointer to buffer for result - at least 2 pcre_uchars long + +Returns: number of characters placed in the buffer +*/ + +int +PRIV(ord2utf)(pcre_uint32 cvalue, pcre_uchar *buffer) +{ +#ifdef SUPPORT_UTF + +/* Checking invalid cvalue character, encoded as invalid UTF-16 character. +Should never happen in practice. */ +if ((cvalue & 0xf800) == 0xd800 || cvalue >= 0x110000) + cvalue = 0xfffe; + +if (cvalue <= 0xffff) + { + *buffer = (pcre_uchar)cvalue; + return 1; + } + +cvalue -= 0x10000; +*buffer++ = 0xd800 | (cvalue >> 10); +*buffer = 0xdc00 | (cvalue & 0x3ff); +return 2; + +#else /* SUPPORT_UTF */ +(void)(cvalue); /* Keep compiler happy; this function won't ever be */ +(void)(buffer); /* called when SUPPORT_UTF is not defined. */ +return 0; +#endif /* SUPPORT_UTF */ +} + +/* End of pcre16_ord2utf16.c */ diff --git a/src/lib/pcre/pcre16_refcount.c b/src/lib/pcre/pcre16_refcount.c new file mode 100644 index 0000000..d3d1543 --- /dev/null +++ b/src/lib/pcre/pcre16_refcount.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_refcount.c" + +/* End of pcre16_refcount.c */ diff --git a/src/lib/pcre/pcre16_string_utils.c b/src/lib/pcre/pcre16_string_utils.c new file mode 100644 index 0000000..382c407 --- /dev/null +++ b/src/lib/pcre/pcre16_string_utils.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_string_utils.c" + +/* End of pcre16_string_utils.c */ diff --git a/src/lib/pcre/pcre16_study.c b/src/lib/pcre/pcre16_study.c new file mode 100644 index 0000000..f87de08 --- /dev/null +++ b/src/lib/pcre/pcre16_study.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_study.c" + +/* End of pcre16_study.c */ diff --git a/src/lib/pcre/pcre16_tables.c b/src/lib/pcre/pcre16_tables.c new file mode 100644 index 0000000..d842970 --- /dev/null +++ b/src/lib/pcre/pcre16_tables.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_tables.c" + +/* End of pcre16_tables.c */ diff --git a/src/lib/pcre/pcre16_ucd.c b/src/lib/pcre/pcre16_ucd.c new file mode 100644 index 0000000..ee23439 --- /dev/null +++ b/src/lib/pcre/pcre16_ucd.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_ucd.c" + +/* End of pcre16_ucd.c */ diff --git a/src/lib/pcre/pcre16_utf16_utils.c b/src/lib/pcre/pcre16_utf16_utils.c new file mode 100644 index 0000000..7cfdbdd --- /dev/null +++ b/src/lib/pcre/pcre16_utf16_utils.c @@ -0,0 +1,129 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains a function for converting any UTF-16 character +strings to host byte order. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_internal.h" + +/************************************************* +* Convert any UTF-16 string to host byte order * +*************************************************/ + +/* This function takes an UTF-16 string and converts +it to host byte order. The length can be explicitly set, +or automatically detected for zero terminated strings. +BOMs can be kept or discarded during the conversion. +Conversion can be done in place (output == input). + +Arguments: + output the output buffer, its size must be greater + or equal than the input string + input any UTF-16 string + length the number of 16-bit units in the input string + can be less than zero for zero terminated strings + host_byte_order + A non-zero value means the input is in host byte + order, which can be dynamically changed by BOMs later. + Initially it contains the starting byte order and returns + with the last byte order so it can be used for stream + processing. It can be NULL, which set the host byte + order mode by default. + keep_boms for a non-zero value, the BOM (0xfeff) characters + are copied as well + +Returns: the number of 16-bit units placed into the output buffer, + including the zero-terminator +*/ + +int +pcre16_utf16_to_host_byte_order(PCRE_UCHAR16 *output, PCRE_SPTR16 input, + int length, int *host_byte_order, int keep_boms) +{ +#ifdef SUPPORT_UTF +/* This function converts any UTF-16 string to host byte order and optionally +removes any Byte Order Marks (BOMS). Returns with the remainig length. */ +int host_bo = host_byte_order != NULL ? *host_byte_order : 1; +pcre_uchar *optr = (pcre_uchar *)output; +const pcre_uchar *iptr = (const pcre_uchar *)input; +const pcre_uchar *end; +/* The c variable must be unsigned. */ +register pcre_uchar c; + +if (length < 0) + length = STRLEN_UC(iptr) + 1; +end = iptr + length; + +while (iptr < end) + { + c = *iptr++; + if (c == 0xfeff || c == 0xfffe) + { + /* Detecting the byte order of the machine is unnecessary, it is + enough to know that the UTF-16 string has the same byte order or not. */ + host_bo = c == 0xfeff; + if (keep_boms != 0) + *optr++ = 0xfeff; + else + length--; + } + else + *optr++ = host_bo ? c : ((c >> 8) | (c << 8)); /* Flip bytes if needed. */ + } +if (host_byte_order != NULL) + *host_byte_order = host_bo; + +#else /* SUPPORT_UTF */ +(void)(output); /* Keep picky compilers happy */ +(void)(input); +(void)(keep_boms); +#endif /* SUPPORT_UTF */ +return length; +} + +/* End of pcre16_utf16_utils.c */ diff --git a/src/lib/pcre/pcre16_valid_utf16.c b/src/lib/pcre/pcre16_valid_utf16.c new file mode 100644 index 0000000..90b9f86 --- /dev/null +++ b/src/lib/pcre/pcre16_valid_utf16.c @@ -0,0 +1,146 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function for validating UTF-16 character +strings. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_internal.h" + + +/************************************************* +* Validate a UTF-16 string * +*************************************************/ + +/* This function is called (optionally) at the start of compile or match, to +check that a supposed UTF-16 string is actually valid. The early check means +that subsequent code can assume it is dealing with a valid string. The check +can be turned off for maximum performance, but the consequences of supplying an +invalid string are then undefined. + +From release 8.21 more information about the details of the error are passed +back in the returned value: + +PCRE_UTF16_ERR0 No error +PCRE_UTF16_ERR1 Missing low surrogate at the end of the string +PCRE_UTF16_ERR2 Invalid low surrogate +PCRE_UTF16_ERR3 Isolated low surrogate +PCRE_UTF16_ERR4 Not allowed character + +Arguments: + string points to the string + length length of string, or -1 if the string is zero-terminated + errp pointer to an error position offset variable + +Returns: = 0 if the string is a valid UTF-16 string + > 0 otherwise, setting the offset of the bad character +*/ + +int +PRIV(valid_utf)(PCRE_PUCHAR string, int length, int *erroroffset) +{ +#ifdef SUPPORT_UTF +register PCRE_PUCHAR p; +register pcre_uchar c; + +if (length < 0) + { + for (p = string; *p != 0; p++); + length = p - string; + } + +for (p = string; length-- > 0; p++) + { + c = *p; + + if ((c & 0xf800) != 0xd800) + { + /* Normal UTF-16 code point. Neither high nor low surrogate. */ + + /* This is probably a BOM from a different byte-order. + Regardless, the string is rejected. */ + if (c == 0xfffe) + { + *erroroffset = p - string; + return PCRE_UTF16_ERR4; + } + } + else if ((c & 0x0400) == 0) + { + /* High surrogate. */ + + /* Must be a followed by a low surrogate. */ + if (length == 0) + { + *erroroffset = p - string; + return PCRE_UTF16_ERR1; + } + p++; + length--; + if ((*p & 0xfc00) != 0xdc00) + { + *erroroffset = p - string; + return PCRE_UTF16_ERR2; + } + } + else + { + /* Isolated low surrogate. Always an error. */ + *erroroffset = p - string; + return PCRE_UTF16_ERR3; + } + } + +#else /* SUPPORT_UTF */ +(void)(string); /* Keep picky compilers happy */ +(void)(length); +#endif /* SUPPORT_UTF */ + +return PCRE_UTF16_ERR0; /* This indicates success */ +} + +/* End of pcre16_valid_utf16.c */ diff --git a/src/lib/pcre/pcre16_version.c b/src/lib/pcre/pcre16_version.c new file mode 100644 index 0000000..e991b1a --- /dev/null +++ b/src/lib/pcre/pcre16_version.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_version.c" + +/* End of pcre16_version.c */ diff --git a/src/lib/pcre/pcre16_xclass.c b/src/lib/pcre/pcre16_xclass.c new file mode 100644 index 0000000..5aac2a3 --- /dev/null +++ b/src/lib/pcre/pcre16_xclass.c @@ -0,0 +1,45 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +/* Generate code with 16 bit character support. */ +#define COMPILE_PCRE16 + +#include "pcre_xclass.c" + +/* End of pcre16_xclass.c */ diff --git a/src/lib/pcre/pcre_byte_order.c b/src/lib/pcre/pcre_byte_order.c new file mode 100644 index 0000000..6f5fa74 --- /dev/null +++ b/src/lib/pcre/pcre_byte_order.c @@ -0,0 +1,288 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function that tests a compiled pattern to +see if it was compiled with the opposite endianness. If so, it uses an +auxiliary local function to flip the appropriate bytes. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Swap byte functions * +*************************************************/ + +/* The following functions swap the bytes of a pcre_uint16 +and pcre_uint32 value. + +Arguments: + value any number + +Returns: the byte swapped value +*/ + +static pcre_uint32 +swap_uint32(pcre_uint32 value) +{ +return ((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + (value >> 24); +} + +static pcre_uint16 +swap_uint16(pcre_uint16 value) +{ +return (value >> 8) | (value << 8); +} + + +/************************************************* +* Test for a byte-flipped compiled regex * +*************************************************/ + +/* This function swaps the bytes of a compiled pattern usually +loaded form the disk. It also sets the tables pointer, which +is likely an invalid pointer after reload. + +Arguments: + argument_re points to the compiled expression + extra_data points to extra data or is NULL + tables points to the character tables or NULL + +Returns: 0 if the swap is successful, negative on error +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL int pcre_pattern_to_host_byte_order(pcre *argument_re, + pcre_extra *extra_data, const unsigned char *tables) +#else +PCRE_EXP_DECL int pcre16_pattern_to_host_byte_order(pcre16 *argument_re, + pcre16_extra *extra_data, const unsigned char *tables) +#endif +{ +REAL_PCRE *re = (REAL_PCRE *)argument_re; +pcre_study_data *study; +#ifndef COMPILE_PCRE8 +pcre_uchar *ptr; +int length; +#ifdef SUPPORT_UTF +BOOL utf; +BOOL utf16_char; +#endif /* SUPPORT_UTF */ +#endif /* !COMPILE_PCRE8 */ + +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number == MAGIC_NUMBER) + { + if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + re->tables = tables; + return 0; + } + +if (re->magic_number != REVERSED_MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if ((swap_uint16(re->flags) & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +re->magic_number = MAGIC_NUMBER; +re->size = swap_uint32(re->size); +re->options = swap_uint32(re->options); +re->flags = swap_uint16(re->flags); +re->top_bracket = swap_uint16(re->top_bracket); +re->top_backref = swap_uint16(re->top_backref); +re->first_char = swap_uint16(re->first_char); +re->req_char = swap_uint16(re->req_char); +re->name_table_offset = swap_uint16(re->name_table_offset); +re->name_entry_size = swap_uint16(re->name_entry_size); +re->name_count = swap_uint16(re->name_count); +re->ref_count = swap_uint16(re->ref_count); +re->tables = tables; + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0) + { + study = (pcre_study_data *)extra_data->study_data; + study->size = swap_uint32(study->size); + study->flags = swap_uint32(study->flags); + study->minlength = swap_uint32(study->minlength); + } + +#ifndef COMPILE_PCRE8 +ptr = (pcre_uchar *)re + re->name_table_offset; +length = re->name_count * re->name_entry_size; +#ifdef SUPPORT_UTF +utf = (re->options & PCRE_UTF16) != 0; +utf16_char = FALSE; +#endif + +while(TRUE) + { + /* Swap previous characters. */ + while (length-- > 0) + { + *ptr = swap_uint16(*ptr); + ptr++; + } +#ifdef SUPPORT_UTF + if (utf16_char) + { + if (HAS_EXTRALEN(ptr[-1])) + { + /* We know that there is only one extra character in UTF-16. */ + *ptr = swap_uint16(*ptr); + ptr++; + } + } + utf16_char = FALSE; +#endif /* SUPPORT_UTF */ + + /* Get next opcode. */ + length = 0; + *ptr = swap_uint16(*ptr); + switch (*ptr) + { + case OP_END: + return 0; + +#ifdef SUPPORT_UTF + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + if (utf) utf16_char = TRUE; +#endif + /* Fall through. */ + + default: + length = PRIV(OP_lengths)[*ptr] - 1; + break; + + case OP_CLASS: + case OP_NCLASS: + /* Skip the character bit map. */ + ptr += 32/sizeof(pcre_uchar); + length = 0; + break; + + case OP_XCLASS: + /* Reverse the size of the XCLASS instance. */ + ptr++; + *ptr = swap_uint16(*ptr); + if (LINK_SIZE > 1) + { + /* LINK_SIZE can be 1 or 2 in 16 bit mode. */ + ptr++; + *ptr = swap_uint16(*ptr); + } + ptr++; + length = (GET(ptr, -LINK_SIZE)) - (1 + LINK_SIZE + 1); + *ptr = swap_uint16(*ptr); + if ((*ptr & XCL_MAP) != 0) + { + /* Skip the character bit map. */ + ptr += 32/sizeof(pcre_uchar); + length -= 32/sizeof(pcre_uchar); + } + break; + } + ptr++; + } +/* Control should never reach here in 16 bit mode. */ +#endif /* !COMPILE_PCRE8 */ + +return 0; +} + +/* End of pcre_byte_order.c */ diff --git a/src/lib/pcre/pcre_chartables.c b/src/lib/pcre/pcre_chartables.c new file mode 100644 index 0000000..55df497 --- /dev/null +++ b/src/lib/pcre/pcre_chartables.c @@ -0,0 +1,198 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file contains character tables that are used when no external tables +are passed to PCRE by the application that calls it. The tables are used only +for characters whose code values are less than 256. + +This is a default version of the tables that assumes ASCII encoding. A program +called dftables (which is distributed with PCRE) can be used to build +alternative versions of this file. This is necessary if you are running in an +EBCDIC environment, or if you want to default to a different encoding, for +example ISO-8859-1. When dftables is run, it creates these tables in the +current locale. If PCRE is configured with --enable-rebuild-chartables, this +happens automatically. + +The following #includes are present because without them gcc 4.x may remove the +array definition from the final binary if PCRE is built into a static library +and dead code stripping is activated. This leads to link errors. Pulling in the +header ensures that the array gets flagged as "someone outside this compilation +unit might reference this" and so it will always be supplied to the linker. */ + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +const pcre_uint8 PRIV(default_tables)[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table contains bit maps for various character classes. Each map is 32 +bytes long and the bits run from the least significant end of each byte. The +classes that have their own maps are: space, xdigit, digit, upper, lower, word, +graph, print, punct, and cntrl. Other classes are built from combinations. */ + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, + 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* End of pcre_chartables.c */ diff --git a/src/lib/pcre/pcre_compile.c b/src/lib/pcre/pcre_compile.c new file mode 100644 index 0000000..b0c57ca --- /dev/null +++ b/src/lib/pcre/pcre_compile.c @@ -0,0 +1,8162 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_compile(), along with +supporting internal functions that are not used by other modules. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK cd /* Block containing newline information */ +#define PSSTART start_pattern /* Field containing processed string start */ +#define PSEND end_pattern /* Field containing processed string end */ + +#include "pcre_internal.h" + + +/* When PCRE_DEBUG is defined, we need the pcre(16)_printint() function, which +is also used by pcretest. PCRE_DEBUG is not defined when building a production +library. We do not need to select pcre16_printint.c specially, because the +COMPILE_PCREx macro will already be appropriately set. */ + +#ifdef PCRE_DEBUG +/* pcre_printint.c should not include any headers */ +#define PCRE_INCLUDED +#include "pcre_printint.c" +#undef PCRE_INCLUDED +#endif + + +/* Macro for setting individual bits in class bitmaps. */ + +#define SETBIT(a,b) a[b/8] |= (1 << (b%8)) + +/* Maximum length value to check against when making sure that the integer that +holds the compiled pattern length does not overflow. We make it a bit less than +INT_MAX to allow for adding in group terminating bytes, so that we don't have +to check them every time. */ + +#define OFLOW_MAX (INT_MAX - 20) + + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +/* This value specifies the size of stack workspace that is used during the +first pre-compile phase that determines how much memory is required. The regex +is partly compiled into this space, but the compiled parts are discarded as +soon as they can be, so that hopefully there will never be an overrun. The code +does, however, check for an overrun. The largest amount I've seen used is 218, +so this number is very generous. + +The same workspace is used during the second, actual compile phase for +remembering forward references to groups so that they can be filled in at the +end. Each entry in this list occupies LINK_SIZE bytes, so even when LINK_SIZE +is 4 there is plenty of room for most patterns. However, the memory can get +filled up by repetitions of forward references, for example patterns like +/(?1){0,1999}(b)/, and one user did hit the limit. The code has been changed so +that the workspace is expanded using malloc() in this situation. The value +below is therefore a minimum, and we put a maximum on it for safety. The +minimum is now also defined in terms of LINK_SIZE so that the use of malloc() +kicks in at the same number of forward references in all cases. */ + +#define COMPILE_WORK_SIZE (2048*LINK_SIZE) +#define COMPILE_WORK_SIZE_MAX (100*COMPILE_WORK_SIZE) + +/* The overrun tests check for a slightly smaller size so that they detect the +overrun before it actually does run off the end of the data block. */ + +#define WORK_SIZE_SAFETY_MARGIN (100) + +/* Private flags added to firstchar and reqchar. */ + +#define REQ_CASELESS 0x10000000l /* Indicates caselessness */ +#define REQ_VARY 0x20000000l /* Reqchar followed non-literal item */ + +/* Repeated character flags. */ + +#define UTF_LENGTH 0x10000000l /* The char contains its length. */ + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +#ifndef EBCDIC + +/* This is the "normal" table for ASCII systems or for EBCDIC systems running +in UTF-8 mode. */ + +static const short int escapes[] = { + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + CHAR_COLON, CHAR_SEMICOLON, + CHAR_LESS_THAN_SIGN, CHAR_EQUALS_SIGN, + CHAR_GREATER_THAN_SIGN, CHAR_QUESTION_MARK, + CHAR_COMMERCIAL_AT, -ESC_A, + -ESC_B, -ESC_C, + -ESC_D, -ESC_E, + 0, -ESC_G, + -ESC_H, 0, + 0, -ESC_K, + 0, 0, + -ESC_N, 0, + -ESC_P, -ESC_Q, + -ESC_R, -ESC_S, + 0, 0, + -ESC_V, -ESC_W, + -ESC_X, 0, + -ESC_Z, CHAR_LEFT_SQUARE_BRACKET, + CHAR_BACKSLASH, CHAR_RIGHT_SQUARE_BRACKET, + CHAR_CIRCUMFLEX_ACCENT, CHAR_UNDERSCORE, + CHAR_GRAVE_ACCENT, 7, + -ESC_b, 0, + -ESC_d, ESC_e, + ESC_f, 0, + -ESC_h, 0, + 0, -ESC_k, + 0, 0, + ESC_n, 0, + -ESC_p, 0, + ESC_r, -ESC_s, + ESC_tee, 0, + -ESC_v, -ESC_w, + 0, 0, + -ESC_z +}; + +#else + +/* This is the "abnormal" table for EBCDIC systems without UTF-8 support. */ + +static const short int escapes[] = { +/* 48 */ 0, 0, 0, '.', '<', '(', '+', '|', +/* 50 */ '&', 0, 0, 0, 0, 0, 0, 0, +/* 58 */ 0, 0, '!', '$', '*', ')', ';', '~', +/* 60 */ '-', '/', 0, 0, 0, 0, 0, 0, +/* 68 */ 0, 0, '|', ',', '%', '_', '>', '?', +/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* 78 */ 0, '`', ':', '#', '@', '\'', '=', '"', +/* 80 */ 0, 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, +/* 88 */-ESC_h, 0, 0, '{', 0, 0, 0, 0, +/* 90 */ 0, 0, -ESC_k, 'l', 0, ESC_n, 0, -ESC_p, +/* 98 */ 0, ESC_r, 0, '}', 0, 0, 0, 0, +/* A0 */ 0, '~', -ESC_s, ESC_tee, 0,-ESC_v, -ESC_w, 0, +/* A8 */ 0,-ESC_z, 0, 0, 0, '[', 0, 0, +/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', +/* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G, +/* C8 */-ESC_H, 0, 0, 0, 0, 0, 0, 0, +/* D0 */ '}', 0, -ESC_K, 0, 0,-ESC_N, 0, -ESC_P, +/* D8 */-ESC_Q,-ESC_R, 0, 0, 0, 0, 0, 0, +/* E0 */ '\\', 0, -ESC_S, 0, 0,-ESC_V, -ESC_W, -ESC_X, +/* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0, +/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* F8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + + +/* Table of special "verbs" like (*PRUNE). This is a short table, so it is +searched linearly. Put all the names into a single string, in order to reduce +the number of relocations when a shared library is dynamically linked. The +string is built from string macros so that it works in UTF-8 mode on EBCDIC +platforms. */ + +typedef struct verbitem { + int len; /* Length of verb name */ + int op; /* Op when no arg, or -1 if arg mandatory */ + int op_arg; /* Op when arg present, or -1 if not allowed */ +} verbitem; + +static const char verbnames[] = + "\0" /* Empty name is a shorthand for MARK */ + STRING_MARK0 + STRING_ACCEPT0 + STRING_COMMIT0 + STRING_F0 + STRING_FAIL0 + STRING_PRUNE0 + STRING_SKIP0 + STRING_THEN; + +static const verbitem verbs[] = { + { 0, -1, OP_MARK }, + { 4, -1, OP_MARK }, + { 6, OP_ACCEPT, -1 }, + { 6, OP_COMMIT, -1 }, + { 1, OP_FAIL, -1 }, + { 4, OP_FAIL, -1 }, + { 5, OP_PRUNE, OP_PRUNE_ARG }, + { 4, OP_SKIP, OP_SKIP_ARG }, + { 4, OP_THEN, OP_THEN_ARG } +}; + +static const int verbcount = sizeof(verbs)/sizeof(verbitem); + + +/* Tables of names of POSIX character classes and their lengths. The names are +now all in a single string, to reduce the number of relocations when a shared +library is dynamically loaded. The list of lengths is terminated by a zero +length entry. The first three must be alpha, lower, upper, as this is assumed +for handling case independence. */ + +static const char posix_names[] = + STRING_alpha0 STRING_lower0 STRING_upper0 STRING_alnum0 + STRING_ascii0 STRING_blank0 STRING_cntrl0 STRING_digit0 + STRING_graph0 STRING_print0 STRING_punct0 STRING_space0 + STRING_word0 STRING_xdigit; + +static const pcre_uint8 posix_name_lengths[] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; + +/* Table of class bit maps for each POSIX class. Each class is formed from a +base map, with an optional addition or removal of another map. Then, for some +classes, there is some additional tweaking: for [:blank:] the vertical space +characters are removed, and for [:alpha:] and [:alnum:] the underscore +character is removed. The triples in the table consist of the base map offset, +second map offset or -1 if no second map, and a non-negative value for map +addition or a negative value for map subtraction (if there are two maps). The +absolute value of the third field has these meanings: 0 => no tweaking, 1 => +remove vertical space characters, 2 => remove underscore. */ + +static const int posix_class_maps[] = { + cbit_word, cbit_digit, -2, /* alpha */ + cbit_lower, -1, 0, /* lower */ + cbit_upper, -1, 0, /* upper */ + cbit_word, -1, 2, /* alnum - word without underscore */ + cbit_print, cbit_cntrl, 0, /* ascii */ + cbit_space, -1, 1, /* blank - a GNU extension */ + cbit_cntrl, -1, 0, /* cntrl */ + cbit_digit, -1, 0, /* digit */ + cbit_graph, -1, 0, /* graph */ + cbit_print, -1, 0, /* print */ + cbit_punct, -1, 0, /* punct */ + cbit_space, -1, 0, /* space */ + cbit_word, -1, 0, /* word - a Perl extension */ + cbit_xdigit,-1, 0 /* xdigit */ +}; + +/* Table of substitutes for \d etc when PCRE_UCP is set. The POSIX class +substitutes must be in the order of the names, defined above, and there are +both positive and negative cases. NULL means no substitute. */ + +#ifdef SUPPORT_UCP +static const pcre_uchar string_PNd[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pNd[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_N, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PXsp[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pXsp[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_s, CHAR_p, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PXwd[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pXwd[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_w, CHAR_d, CHAR_RIGHT_CURLY_BRACKET, '\0' }; + +static const pcre_uchar *substitutes[] = { + string_PNd, /* \D */ + string_pNd, /* \d */ + string_PXsp, /* \S */ /* NOTE: Xsp is Perl space */ + string_pXsp, /* \s */ + string_PXwd, /* \W */ + string_pXwd /* \w */ +}; + +static const pcre_uchar string_pL[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pLl[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pLu[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_pXan[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_h[] = { + CHAR_BACKSLASH, CHAR_h, '\0' }; +static const pcre_uchar string_pXps[] = { + CHAR_BACKSLASH, CHAR_p, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PL[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PLl[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_l, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PLu[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_L, CHAR_u, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_PXan[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_a, CHAR_n, CHAR_RIGHT_CURLY_BRACKET, '\0' }; +static const pcre_uchar string_H[] = { + CHAR_BACKSLASH, CHAR_H, '\0' }; +static const pcre_uchar string_PXps[] = { + CHAR_BACKSLASH, CHAR_P, CHAR_LEFT_CURLY_BRACKET, + CHAR_X, CHAR_p, CHAR_s, CHAR_RIGHT_CURLY_BRACKET, '\0' }; + +static const pcre_uchar *posix_substitutes[] = { + string_pL, /* alpha */ + string_pLl, /* lower */ + string_pLu, /* upper */ + string_pXan, /* alnum */ + NULL, /* ascii */ + string_h, /* blank */ + NULL, /* cntrl */ + string_pNd, /* digit */ + NULL, /* graph */ + NULL, /* print */ + NULL, /* punct */ + string_pXps, /* space */ /* NOTE: Xps is POSIX space */ + string_pXwd, /* word */ + NULL, /* xdigit */ + /* Negated cases */ + string_PL, /* ^alpha */ + string_PLl, /* ^lower */ + string_PLu, /* ^upper */ + string_PXan, /* ^alnum */ + NULL, /* ^ascii */ + string_H, /* ^blank */ + NULL, /* ^cntrl */ + string_PNd, /* ^digit */ + NULL, /* ^graph */ + NULL, /* ^print */ + NULL, /* ^punct */ + string_PXps, /* ^space */ /* NOTE: Xps is POSIX space */ + string_PXwd, /* ^word */ + NULL /* ^xdigit */ +}; +#define POSIX_SUBSIZE (sizeof(posix_substitutes) / sizeof(pcre_uchar *)) +#endif + +#define STRING(a) # a +#define XSTRING(s) STRING(s) + +/* The texts of compile-time error messages. These are "char *" because they +are passed to the outside world. Do not ever re-use any error number, because +they are documented. Always add a new error instead. Messages marked DEAD below +are no longer used. This used to be a table of strings, but in order to reduce +the number of relocations needed when a shared library is loaded dynamically, +it is now one long string. We cannot use a table of offsets, because the +lengths of inserts such as XSTRING(MAX_NAME_SIZE) are not known. Instead, we +simply count through to the one we want - this isn't a performance issue +because these strings are used only when there is a compilation error. + +Each substring ends with \0 to insert a null character. This includes the final +substring, so that the whole string ends with \0\0, which can be detected when +counting through. */ + +static const char error_texts[] = + "no error\0" + "\\ at end of pattern\0" + "\\c at end of pattern\0" + "unrecognized character follows \\\0" + "numbers out of order in {} quantifier\0" + /* 5 */ + "number too big in {} quantifier\0" + "missing terminating ] for character class\0" + "invalid escape sequence in character class\0" + "range out of order in character class\0" + "nothing to repeat\0" + /* 10 */ + "operand of unlimited repeat could match the empty string\0" /** DEAD **/ + "internal error: unexpected repeat\0" + "unrecognized character after (? or (?-\0" + "POSIX named classes are supported only within a class\0" + "missing )\0" + /* 15 */ + "reference to non-existent subpattern\0" + "erroffset passed as NULL\0" + "unknown option bit(s) set\0" + "missing ) after comment\0" + "parentheses nested too deeply\0" /** DEAD **/ + /* 20 */ + "regular expression is too large\0" + "failed to get memory\0" + "unmatched parentheses\0" + "internal error: code overflow\0" + "unrecognized character after (?<\0" + /* 25 */ + "lookbehind assertion is not fixed length\0" + "malformed number or name after (?(\0" + "conditional group contains more than two branches\0" + "assertion expected after (?(\0" + "(?R or (?[+-]digits must be followed by )\0" + /* 30 */ + "unknown POSIX class name\0" + "POSIX collating elements are not supported\0" + "this version of PCRE is compiled without UTF support\0" + "spare error\0" /** DEAD **/ + "character value in \\x{...} sequence is too large\0" + /* 35 */ + "invalid condition (?(0)\0" + "\\C not allowed in lookbehind assertion\0" + "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u\0" + "number after (?C is > 255\0" + "closing ) for (?C expected\0" + /* 40 */ + "recursive call could loop indefinitely\0" + "unrecognized character after (?P\0" + "syntax error in subpattern name (missing terminator)\0" + "two named subpatterns have the same name\0" + "invalid UTF-8 string\0" + /* 45 */ + "support for \\P, \\p, and \\X has not been compiled\0" + "malformed \\P or \\p sequence\0" + "unknown property name after \\P or \\p\0" + "subpattern name is too long (maximum " XSTRING(MAX_NAME_SIZE) " characters)\0" + "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0" + /* 50 */ + "repeated subpattern is too long\0" /** DEAD **/ + "octal value is greater than \\377 in 8-bit non-UTF-8 mode\0" + "internal error: overran compiling workspace\0" + "internal error: previously-checked referenced subpattern not found\0" + "DEFINE group contains more than one branch\0" + /* 55 */ + "repeating a DEFINE group is not allowed\0" /** DEAD **/ + "inconsistent NEWLINE options\0" + "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number\0" + "a numbered reference must not be zero\0" + "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)\0" + /* 60 */ + "(*VERB) not recognized\0" + "number is too big\0" + "subpattern name expected\0" + "digit expected after (?+\0" + "] is an invalid data character in JavaScript compatibility mode\0" + /* 65 */ + "different names for subpatterns of the same number are not allowed\0" + "(*MARK) must have an argument\0" + "this version of PCRE is not compiled with Unicode property support\0" + "\\c must be followed by an ASCII character\0" + "\\k is not followed by a braced, angle-bracketed, or quoted name\0" + /* 70 */ + "internal error: unknown opcode in find_fixedlength()\0" + "\\N is not supported in a class\0" + "too many forward references\0" + "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)\0" + "invalid UTF-16 string\0" + ; + +/* Table to identify digits and hex digits. This is used when compiling +patterns. Note that the tables in chartables are dependent on the locale, and +may mark arbitrary characters as digits - but the PCRE compiling code expects +to handle only 0-9, a-z, and A-Z as digits when compiling. That is why we have +a private table here. It costs 256 bytes, but it is a lot faster than doing +character value tests (at least in some simple cases I timed), and in some +applications one wants PCRE to compile efficiently as well as match +efficiently. + +For convenience, we use the same bit definitions as in chartables: + + 0x04 decimal digit + 0x08 hexadecimal digit + +Then we can use ctype_digit and ctype_xdigit in the code. */ + +/* Using a simple comparison for decimal numbers rather than a memory read +is much faster, and the resulting code is simpler (the compiler turns it +into a subtraction and unsigned comparison). */ + +#define IS_DIGIT(x) ((x) >= CHAR_0 && (x) <= CHAR_9) + +#ifndef EBCDIC + +/* This is the "normal" case, for ASCII systems, and EBCDIC systems running in +UTF-8 mode. */ + +static const pcre_uint8 digitab[] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */ + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 */ + 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* @ - G */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H - O */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* P - W */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* X - _ */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* ` - g */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h - o */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* p - w */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +#else + +/* This is the "abnormal" case, for EBCDIC systems not running in UTF-8 mode. */ + +static const pcre_uint8 digitab[] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 10 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 32- 39 20 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 40- 47 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 48- 55 30 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 56- 63 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - 71 40 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 72- | */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* & - 87 50 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 88- 95 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - -103 60 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 104- ? */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 70 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- " */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* 128- g 80 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h -143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144- p 90 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* q -159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160- x A0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* y -175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ^ -183 B0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* { - G C0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H -207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* } - P D0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Q -223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* \ - X E0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Y -239 */ + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 F0 */ + 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */ + +static const pcre_uint8 ebcdic_chartab[] = { /* chartable partial dup */ + 0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 32- 39 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 40- 47 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 48- 55 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 56- 63 */ + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - 71 */ + 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80, /* 72- | */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* & - 87 */ + 0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00, /* 88- 95 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - -103 */ + 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80, /* 104- ? */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- " */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* 128- g */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* h -143 */ + 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* 144- p */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* q -159 */ + 0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* 160- x */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* y -175 */ + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ^ -183 */ + 0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x80,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* { - G */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* H -207 */ + 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* } - P */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* Q -223 */ + 0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* \ - X */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* Y -239 */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */ +#endif + + +/* Definition to allow mutual recursion */ + +static BOOL + compile_regex(int, pcre_uchar **, const pcre_uchar **, int *, BOOL, BOOL, int, int, + int *, int *, branch_chain *, compile_data *, int *); + + + +/************************************************* +* Find an error text * +*************************************************/ + +/* The error texts are now all in one long string, to save on relocations. As +some of the text is of unknown length, we can't use a table of offsets. +Instead, just count through the strings. This is not a performance issue +because it happens only when there has been a compilation error. + +Argument: the error number +Returns: pointer to the error string +*/ + +static const char * +find_error_text(int n) +{ +const char *s = error_texts; +for (; n > 0; n--) + { + while (*s++ != 0) {}; + if (*s == 0) return "Error text not found (please report)"; + } +return s; +} + + +/************************************************* +* Expand the workspace * +*************************************************/ + +/* This function is called during the second compiling phase, if the number of +forward references fills the existing workspace, which is originally a block on +the stack. A larger block is obtained from malloc() unless the ultimate limit +has been reached or the increase will be rather small. + +Argument: pointer to the compile data block +Returns: 0 if all went well, else an error number +*/ + +static int +expand_workspace(compile_data *cd) +{ +pcre_uchar *newspace; +int newsize = cd->workspace_size * 2; + +if (newsize > COMPILE_WORK_SIZE_MAX) newsize = COMPILE_WORK_SIZE_MAX; +if (cd->workspace_size >= COMPILE_WORK_SIZE_MAX || + newsize - cd->workspace_size < WORK_SIZE_SAFETY_MARGIN) + return ERR72; + +newspace = (PUBL(malloc))(IN_UCHARS(newsize)); +if (newspace == NULL) return ERR21; +memcpy(newspace, cd->start_workspace, cd->workspace_size * sizeof(pcre_uchar)); +cd->hwm = (pcre_uchar *)newspace + (cd->hwm - cd->start_workspace); +if (cd->workspace_size > COMPILE_WORK_SIZE) + (PUBL(free))((void *)cd->start_workspace); +cd->start_workspace = newspace; +cd->workspace_size = newsize; +return 0; +} + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const pcre_uchar *p) +{ +if (!IS_DIGIT(*p)) return FALSE; +p++; +while (IS_DIGIT(*p)) p++; +if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; + +if (*p++ != CHAR_COMMA) return FALSE; +if (*p == CHAR_RIGHT_CURLY_BRACKET) return TRUE; + +if (!IS_DIGIT(*p)) return FALSE; +p++; +while (IS_DIGIT(*p)) p++; + +return (*p == CHAR_RIGHT_CURLY_BRACKET); +} + + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. A backreference to group +n is returned as -(ESC_REF + n); ESC_REF is the highest ESC_xxx macro. When +UTF-8 is enabled, a positive value greater than 255 may be returned. On entry, +ptr is pointing at the \. On exit, it is on the final character of the escape +sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorcodeptr points to the errorcode variable + bracount number of previous extracting brackets + options the options bits + isclass TRUE if inside a character class + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorcodeptr is set +*/ + +static int +check_escape(const pcre_uchar **ptrptr, int *errorcodeptr, int bracount, + int options, BOOL isclass) +{ +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +const pcre_uchar *ptr = *ptrptr + 1; +pcre_int32 c; +int i; + +GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ +ptr--; /* Set pointer back to the last byte */ + +/* If backslash is at the end of the pattern, it's an error. */ + +if (c == 0) *errorcodeptr = ERR1; + +/* Non-alphanumerics are literals. For digits or letters, do an initial lookup +in a table. A non-zero result is something that can be returned immediately. +Otherwise further processing may be required. */ + +#ifndef EBCDIC /* ASCII/UTF-8 coding */ +/* Not alphanumeric */ +else if (c < CHAR_0 || c > CHAR_z) {} +else if ((i = escapes[c - CHAR_0]) != 0) c = i; + +#else /* EBCDIC coding */ +/* Not alphanumeric */ +else if (c < 'a' || (!MAX_255(c) || (ebcdic_chartab[c] & 0x0E) == 0)) {} +else if ((i = escapes[c - 0x48]) != 0) c = i; +#endif + +/* Escapes that need further processing, or are illegal. */ + +else + { + const pcre_uchar *oldptr; + BOOL braced, negated; + + switch (c) + { + /* A number of Perl escapes are not handled by PCRE. We give an explicit + error. */ + + case CHAR_l: + case CHAR_L: + *errorcodeptr = ERR37; + break; + + case CHAR_u: + if ((options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + /* In JavaScript, \u must be followed by four hexadecimal numbers. + Otherwise it is a lowercase u letter. */ + if (MAX_255(ptr[1]) && (digitab[ptr[1]] & ctype_xdigit) != 0 + && MAX_255(ptr[2]) && (digitab[ptr[2]] & ctype_xdigit) != 0 + && MAX_255(ptr[3]) && (digitab[ptr[3]] & ctype_xdigit) != 0 + && MAX_255(ptr[4]) && (digitab[ptr[4]] & ctype_xdigit) != 0) + { + c = 0; + for (i = 0; i < 4; ++i) + { + register int cc = *(++ptr); +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); +#else /* EBCDIC coding */ + if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + } + } + } + else + *errorcodeptr = ERR37; + break; + + case CHAR_U: + /* In JavaScript, \U is an uppercase U letter. */ + if ((options & PCRE_JAVASCRIPT_COMPAT) == 0) *errorcodeptr = ERR37; + break; + + /* In a character class, \g is just a literal "g". Outside a character + class, \g must be followed by one of a number of specific things: + + (1) A number, either plain or braced. If positive, it is an absolute + backreference. If negative, it is a relative backreference. This is a Perl + 5.10 feature. + + (2) Perl 5.10 also supports \g{name} as a reference to a named group. This + is part of Perl's movement towards a unified syntax for back references. As + this is synonymous with \k{name}, we fudge it up by pretending it really + was \k. + + (3) For Oniguruma compatibility we also support \g followed by a name or a + number either in angle brackets or in single quotes. However, these are + (possibly recursive) subroutine calls, _not_ backreferences. Just return + the -ESC_g code (cf \k). */ + + case CHAR_g: + if (isclass) break; + if (ptr[1] == CHAR_LESS_THAN_SIGN || ptr[1] == CHAR_APOSTROPHE) + { + c = -ESC_g; + break; + } + + /* Handle the Perl-compatible cases */ + + if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) + { + const pcre_uchar *p; + for (p = ptr+2; *p != 0 && *p != CHAR_RIGHT_CURLY_BRACKET; p++) + if (*p != CHAR_MINUS && !IS_DIGIT(*p)) break; + if (*p != 0 && *p != CHAR_RIGHT_CURLY_BRACKET) + { + c = -ESC_k; + break; + } + braced = TRUE; + ptr++; + } + else braced = FALSE; + + if (ptr[1] == CHAR_MINUS) + { + negated = TRUE; + ptr++; + } + else negated = FALSE; + + /* The integer range is limited by the machine's int representation. */ + c = 0; + while (IS_DIGIT(ptr[1])) + { + if (((unsigned int)c) > INT_MAX / 10) /* Integer overflow */ + { + c = -1; + break; + } + c = c * 10 + *(++ptr) - CHAR_0; + } + if (((unsigned int)c) > INT_MAX) /* Integer overflow */ + { + while (IS_DIGIT(ptr[1])) + ptr++; + *errorcodeptr = ERR61; + break; + } + + if (braced && *(++ptr) != CHAR_RIGHT_CURLY_BRACKET) + { + *errorcodeptr = ERR57; + break; + } + + if (c == 0) + { + *errorcodeptr = ERR58; + break; + } + + if (negated) + { + if (c > bracount) + { + *errorcodeptr = ERR15; + break; + } + c = bracount - (c - 1); + } + + c = -(ESC_REF + c); + break; + + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. By experiment, + the way Perl works seems to be as follows: + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting + left brackets, then it is a back reference. Otherwise, up to three octal + digits are read to form an escaped byte. Thus \123 is likely to be octal + 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal + value is greater than 377, the least significant 8 bits are taken. Inside a + character class, \ followed by a digit is always an octal number. */ + + case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: case CHAR_5: + case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: + + if (!isclass) + { + oldptr = ptr; + /* The integer range is limited by the machine's int representation. */ + c -= CHAR_0; + while (IS_DIGIT(ptr[1])) + { + if (((unsigned int)c) > INT_MAX / 10) /* Integer overflow */ + { + c = -1; + break; + } + c = c * 10 + *(++ptr) - CHAR_0; + } + if (((unsigned int)c) > INT_MAX) /* Integer overflow */ + { + while (IS_DIGIT(ptr[1])) + ptr++; + *errorcodeptr = ERR61; + break; + } + if (c < 10 || c <= bracount) + { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, Perl + generates a binary zero byte and treats the digit as a following literal. + Thus we have to pull back the pointer by one. */ + + if ((c = *ptr) >= CHAR_8) + { + ptr--; + c = 0; + break; + } + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. The original code used just to take the least + significant 8 bits of octal numbers (I think this is what early Perls used + to do). Nowadays we allow for larger numbers in UTF-8 mode and 16-bit mode, + but no more than 3 octal digits. */ + + case CHAR_0: + c -= CHAR_0; + while(i++ < 2 && ptr[1] >= CHAR_0 && ptr[1] <= CHAR_7) + c = c * 8 + *(++ptr) - CHAR_0; +#ifdef COMPILE_PCRE8 + if (!utf && c > 0xff) *errorcodeptr = ERR51; +#endif + break; + + /* \x is complicated. \x{ddd} is a character number which can be greater + than 0xff in utf or non-8bit mode, but only if the ddd are hex digits. + If not, { is treated as a data character. */ + + case CHAR_x: + if ((options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + /* In JavaScript, \x must be followed by two hexadecimal numbers. + Otherwise it is a lowercase x letter. */ + if (MAX_255(ptr[1]) && (digitab[ptr[1]] & ctype_xdigit) != 0 + && MAX_255(ptr[2]) && (digitab[ptr[2]] & ctype_xdigit) != 0) + { + c = 0; + for (i = 0; i < 2; ++i) + { + register int cc = *(++ptr); +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); +#else /* EBCDIC coding */ + if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + } + } + break; + } + + if (ptr[1] == CHAR_LEFT_CURLY_BRACKET) + { + const pcre_uchar *pt = ptr + 2; + + c = 0; + while (MAX_255(*pt) && (digitab[*pt] & ctype_xdigit) != 0) + { + register int cc = *pt++; + if (c == 0 && cc == CHAR_0) continue; /* Leading zeroes */ + +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); +#else /* EBCDIC coding */ + if (cc >= CHAR_a && cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + +#ifdef COMPILE_PCRE8 + if (c > (utf ? 0x10ffff : 0xff)) { c = -1; break; } +#else +#ifdef COMPILE_PCRE16 + if (c > (utf ? 0x10ffff : 0xffff)) { c = -1; break; } +#endif +#endif + } + + if (c < 0) + { + while (MAX_255(*pt) && (digitab[*pt] & ctype_xdigit) != 0) pt++; + *errorcodeptr = ERR34; + } + + if (*pt == CHAR_RIGHT_CURLY_BRACKET) + { + if (utf && c >= 0xd800 && c <= 0xdfff) *errorcodeptr = ERR73; + ptr = pt; + break; + } + + /* If the sequence of hex digits does not end with '}', then we don't + recognize this construct; fall through to the normal \x handling. */ + } + + /* Read just a single-byte hex-defined char */ + + c = 0; + while (i++ < 2 && MAX_255(ptr[1]) && (digitab[ptr[1]] & ctype_xdigit) != 0) + { + int cc; /* Some compilers don't like */ + cc = *(++ptr); /* ++ in initializers */ +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (cc >= CHAR_a) cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < CHAR_A)? CHAR_0 : (CHAR_A - 10)); +#else /* EBCDIC coding */ + if (cc <= CHAR_z) cc += 64; /* Convert to upper case */ + c = c * 16 + cc - ((cc >= CHAR_0)? CHAR_0 : (CHAR_A - 10)); +#endif + } + break; + + /* For \c, a following letter is upper-cased; then the 0x40 bit is flipped. + An error is given if the byte following \c is not an ASCII character. This + coding is ASCII-specific, but then the whole concept of \cx is + ASCII-specific. (However, an EBCDIC equivalent has now been added.) */ + + case CHAR_c: + c = *(++ptr); + if (c == 0) + { + *errorcodeptr = ERR2; + break; + } +#ifndef EBCDIC /* ASCII/UTF-8 coding */ + if (c > 127) /* Excludes all non-ASCII in either mode */ + { + *errorcodeptr = ERR68; + break; + } + if (c >= CHAR_a && c <= CHAR_z) c -= 32; + c ^= 0x40; +#else /* EBCDIC coding */ + if (c >= CHAR_a && c <= CHAR_z) c += 64; + c ^= 0xC0; +#endif + break; + + /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any + other alphanumeric following \ is an error if PCRE_EXTRA was set; + otherwise, for Perl compatibility, it is a literal. This code looks a bit + odd, but there used to be some cases other than the default, and there may + be again in future, so I haven't "optimized" it. */ + + default: + if ((options & PCRE_EXTRA) != 0) switch(c) + { + default: + *errorcodeptr = ERR3; + break; + } + break; + } + } + +/* Perl supports \N{name} for character names, as well as plain \N for "not +newline". PCRE does not support \N{name}. However, it does support +quantification such as \N{2,3}. */ + +if (c == -ESC_N && ptr[1] == CHAR_LEFT_CURLY_BRACKET && + !is_counted_repeat(ptr+2)) + *errorcodeptr = ERR37; + +/* If PCRE_UCP is set, we change the values for \d etc. */ + +if ((options & PCRE_UCP) != 0 && c <= -ESC_D && c >= -ESC_w) + c -= (ESC_DU - ESC_D); + +/* Set the pointer to the final character before returning. */ + +*ptrptr = ptr; +return c; +} + + + +#ifdef SUPPORT_UCP +/************************************************* +* Handle \P and \p * +*************************************************/ + +/* This function is called after \P or \p has been encountered, provided that +PCRE is compiled with support for Unicode properties. On entry, ptrptr is +pointing at the P or p. On exit, it is pointing at the final character of the +escape sequence. + +Argument: + ptrptr points to the pattern position pointer + negptr points to a boolean that is set TRUE for negation else FALSE + dptr points to an int that is set to the detailed property value + errorcodeptr points to the error code variable + +Returns: type value from ucp_type_table, or -1 for an invalid type +*/ + +static int +get_ucp(const pcre_uchar **ptrptr, BOOL *negptr, int *dptr, int *errorcodeptr) +{ +int c, i, bot, top; +const pcre_uchar *ptr = *ptrptr; +pcre_uchar name[32]; + +c = *(++ptr); +if (c == 0) goto ERROR_RETURN; + +*negptr = FALSE; + +/* \P or \p can be followed by a name in {}, optionally preceded by ^ for +negation. */ + +if (c == CHAR_LEFT_CURLY_BRACKET) + { + if (ptr[1] == CHAR_CIRCUMFLEX_ACCENT) + { + *negptr = TRUE; + ptr++; + } + for (i = 0; i < (int)(sizeof(name) / sizeof(pcre_uchar)) - 1; i++) + { + c = *(++ptr); + if (c == 0) goto ERROR_RETURN; + if (c == CHAR_RIGHT_CURLY_BRACKET) break; + name[i] = c; + } + if (c != CHAR_RIGHT_CURLY_BRACKET) goto ERROR_RETURN; + name[i] = 0; + } + +/* Otherwise there is just one following character */ + +else + { + name[0] = c; + name[1] = 0; + } + +*ptrptr = ptr; + +/* Search for a recognized property name using binary chop */ + +bot = 0; +top = PRIV(utt_size); + +while (bot < top) + { + i = (bot + top) >> 1; + c = STRCMP_UC_C8(name, PRIV(utt_names) + PRIV(utt)[i].name_offset); + if (c == 0) + { + *dptr = PRIV(utt)[i].value; + return PRIV(utt)[i].type; + } + if (c > 0) bot = i + 1; else top = i; + } + +*errorcodeptr = ERR47; +*ptrptr = ptr; +return -1; + +ERROR_RETURN: +*errorcodeptr = ERR46; +*ptrptr = ptr; +return -1; +} +#endif + + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after is_counted_repeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorcodeptr points to error code variable + +Returns: pointer to '}' on success; + current ptr on error, with errorcodeptr set non-zero +*/ + +static const pcre_uchar * +read_repeat_counts(const pcre_uchar *p, int *minp, int *maxp, int *errorcodeptr) +{ +int min = 0; +int max = -1; + +/* Read the minimum value and do a paranoid check: a negative value indicates +an integer overflow. */ + +while (IS_DIGIT(*p)) min = min * 10 + *p++ - CHAR_0; +if (min < 0 || min > 65535) + { + *errorcodeptr = ERR5; + return p; + } + +/* Read the maximum value if there is one, and again do a paranoid on its size. +Also, max must not be less than min. */ + +if (*p == CHAR_RIGHT_CURLY_BRACKET) max = min; else + { + if (*(++p) != CHAR_RIGHT_CURLY_BRACKET) + { + max = 0; + while(IS_DIGIT(*p)) max = max * 10 + *p++ - CHAR_0; + if (max < 0 || max > 65535) + { + *errorcodeptr = ERR5; + return p; + } + if (max < min) + { + *errorcodeptr = ERR4; + return p; + } + } + } + +/* Fill in the required variables, and pass back the pointer to the terminating +'}'. */ + +*minp = min; +*maxp = max; +return p; +} + + + +/************************************************* +* Subroutine for finding forward reference * +*************************************************/ + +/* This recursive function is called only from find_parens() below. The +top-level call starts at the beginning of the pattern. All other calls must +start at a parenthesis. It scans along a pattern's text looking for capturing +subpatterns, and counting them. If it finds a named pattern that matches the +name it is given, it returns its number. Alternatively, if the name is NULL, it +returns when it reaches a given numbered subpattern. Recursion is used to keep +track of subpatterns that reset the capturing group numbers - the (?| feature. + +This function was originally called only from the second pass, in which we know +that if (?< or (?' or (?P< is encountered, the name will be correctly +terminated because that is checked in the first pass. There is now one call to +this function in the first pass, to check for a recursive back reference by +name (so that we can make the whole group atomic). In this case, we need check +only up to the current position in the pattern, and that is still OK because +and previous occurrences will have been checked. To make this work, the test +for "end of pattern" is a check against cd->end_pattern in the main loop, +instead of looking for a binary zero. This means that the special first-pass +call can adjust cd->end_pattern temporarily. (Checks for binary zero while +processing items within the loop are OK, because afterwards the main loop will +terminate.) + +Arguments: + ptrptr address of the current character pointer (updated) + cd compile background data + name name to seek, or NULL if seeking a numbered subpattern + lorn name length, or subpattern number if name is NULL + xmode TRUE if we are in /x mode + utf TRUE if we are in UTF-8 / UTF-16 mode + count pointer to the current capturing subpattern number (updated) + +Returns: the number of the named subpattern, or -1 if not found +*/ + +static int +find_parens_sub(pcre_uchar **ptrptr, compile_data *cd, const pcre_uchar *name, int lorn, + BOOL xmode, BOOL utf, int *count) +{ +pcre_uchar *ptr = *ptrptr; +int start_count = *count; +int hwm_count = start_count; +BOOL dup_parens = FALSE; + +/* If the first character is a parenthesis, check on the type of group we are +dealing with. The very first call may not start with a parenthesis. */ + +if (ptr[0] == CHAR_LEFT_PARENTHESIS) + { + /* Handle specials such as (*SKIP) or (*UTF8) etc. */ + + if (ptr[1] == CHAR_ASTERISK) ptr += 2; + + /* Handle a normal, unnamed capturing parenthesis. */ + + else if (ptr[1] != CHAR_QUESTION_MARK) + { + *count += 1; + if (name == NULL && *count == lorn) return *count; + ptr++; + } + + /* All cases now have (? at the start. Remember when we are in a group + where the parenthesis numbers are duplicated. */ + + else if (ptr[2] == CHAR_VERTICAL_LINE) + { + ptr += 3; + dup_parens = TRUE; + } + + /* Handle comments; all characters are allowed until a ket is reached. */ + + else if (ptr[2] == CHAR_NUMBER_SIGN) + { + for (ptr += 3; *ptr != 0; ptr++) if (*ptr == CHAR_RIGHT_PARENTHESIS) break; + goto FAIL_EXIT; + } + + /* Handle a condition. If it is an assertion, just carry on so that it + is processed as normal. If not, skip to the closing parenthesis of the + condition (there can't be any nested parens). */ + + else if (ptr[2] == CHAR_LEFT_PARENTHESIS) + { + ptr += 2; + if (ptr[1] != CHAR_QUESTION_MARK) + { + while (*ptr != 0 && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + if (*ptr != 0) ptr++; + } + } + + /* Start with (? but not a condition. */ + + else + { + ptr += 2; + if (*ptr == CHAR_P) ptr++; /* Allow optional P */ + + /* We have to disambiguate (? for named groups */ + + if ((*ptr == CHAR_LESS_THAN_SIGN && ptr[1] != CHAR_EXCLAMATION_MARK && + ptr[1] != CHAR_EQUALS_SIGN) || *ptr == CHAR_APOSTROPHE) + { + int term; + const pcre_uchar *thisname; + *count += 1; + if (name == NULL && *count == lorn) return *count; + term = *ptr++; + if (term == CHAR_LESS_THAN_SIGN) term = CHAR_GREATER_THAN_SIGN; + thisname = ptr; + while (*ptr != term) ptr++; + if (name != NULL && lorn == ptr - thisname && + STRNCMP_UC_UC(name, thisname, lorn) == 0) + return *count; + term++; + } + } + } + +/* Past any initial parenthesis handling, scan for parentheses or vertical +bars. Stop if we get to cd->end_pattern. Note that this is important for the +first-pass call when this value is temporarily adjusted to stop at the current +position. So DO NOT change this to a test for binary zero. */ + +for (; ptr < cd->end_pattern; ptr++) + { + /* Skip over backslashed characters and also entire \Q...\E */ + + if (*ptr == CHAR_BACKSLASH) + { + if (*(++ptr) == 0) goto FAIL_EXIT; + if (*ptr == CHAR_Q) for (;;) + { + while (*(++ptr) != 0 && *ptr != CHAR_BACKSLASH) {}; + if (*ptr == 0) goto FAIL_EXIT; + if (*(++ptr) == CHAR_E) break; + } + continue; + } + + /* Skip over character classes; this logic must be similar to the way they + are handled for real. If the first character is '^', skip it. Also, if the + first few characters (either before or after ^) are \Q\E or \E we skip them + too. This makes for compatibility with Perl. Note the use of STR macros to + encode "Q\\E" so that it works in UTF-8 on EBCDIC platforms. */ + + if (*ptr == CHAR_LEFT_SQUARE_BRACKET) + { + BOOL negate_class = FALSE; + for (;;) + { + if (ptr[1] == CHAR_BACKSLASH) + { + if (ptr[2] == CHAR_E) + ptr+= 2; + else if (STRNCMP_UC_C8(ptr + 2, + STR_Q STR_BACKSLASH STR_E, 3) == 0) + ptr += 4; + else + break; + } + else if (!negate_class && ptr[1] == CHAR_CIRCUMFLEX_ACCENT) + { + negate_class = TRUE; + ptr++; + } + else break; + } + + /* If the next character is ']', it is a data character that must be + skipped, except in JavaScript compatibility mode. */ + + if (ptr[1] == CHAR_RIGHT_SQUARE_BRACKET && + (cd->external_options & PCRE_JAVASCRIPT_COMPAT) == 0) + ptr++; + + while (*(++ptr) != CHAR_RIGHT_SQUARE_BRACKET) + { + if (*ptr == 0) return -1; + if (*ptr == CHAR_BACKSLASH) + { + if (*(++ptr) == 0) goto FAIL_EXIT; + if (*ptr == CHAR_Q) for (;;) + { + while (*(++ptr) != 0 && *ptr != CHAR_BACKSLASH) {}; + if (*ptr == 0) goto FAIL_EXIT; + if (*(++ptr) == CHAR_E) break; + } + continue; + } + } + continue; + } + + /* Skip comments in /x mode */ + + if (xmode && *ptr == CHAR_NUMBER_SIGN) + { + ptr++; + while (*ptr != 0) + { + if (IS_NEWLINE(ptr)) { ptr += cd->nllen - 1; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } + if (*ptr == 0) goto FAIL_EXIT; + continue; + } + + /* Check for the special metacharacters */ + + if (*ptr == CHAR_LEFT_PARENTHESIS) + { + int rc = find_parens_sub(&ptr, cd, name, lorn, xmode, utf, count); + if (rc > 0) return rc; + if (*ptr == 0) goto FAIL_EXIT; + } + + else if (*ptr == CHAR_RIGHT_PARENTHESIS) + { + if (dup_parens && *count < hwm_count) *count = hwm_count; + goto FAIL_EXIT; + } + + else if (*ptr == CHAR_VERTICAL_LINE && dup_parens) + { + if (*count > hwm_count) hwm_count = *count; + *count = start_count; + } + } + +FAIL_EXIT: +*ptrptr = ptr; +return -1; +} + + + + +/************************************************* +* Find forward referenced subpattern * +*************************************************/ + +/* This function scans along a pattern's text looking for capturing +subpatterns, and counting them. If it finds a named pattern that matches the +name it is given, it returns its number. Alternatively, if the name is NULL, it +returns when it reaches a given numbered subpattern. This is used for forward +references to subpatterns. We used to be able to start this scan from the +current compiling point, using the current count value from cd->bracount, and +do it all in a single loop, but the addition of the possibility of duplicate +subpattern numbers means that we have to scan from the very start, in order to +take account of such duplicates, and to use a recursive function to keep track +of the different types of group. + +Arguments: + cd compile background data + name name to seek, or NULL if seeking a numbered subpattern + lorn name length, or subpattern number if name is NULL + xmode TRUE if we are in /x mode + utf TRUE if we are in UTF-8 / UTF-16 mode + +Returns: the number of the found subpattern, or -1 if not found +*/ + +static int +find_parens(compile_data *cd, const pcre_uchar *name, int lorn, BOOL xmode, + BOOL utf) +{ +pcre_uchar *ptr = (pcre_uchar *)cd->start_pattern; +int count = 0; +int rc; + +/* If the pattern does not start with an opening parenthesis, the first call +to find_parens_sub() will scan right to the end (if necessary). However, if it +does start with a parenthesis, find_parens_sub() will return when it hits the +matching closing parens. That is why we have to have a loop. */ + +for (;;) + { + rc = find_parens_sub(&ptr, cd, name, lorn, xmode, utf, &count); + if (rc > 0 || *ptr++ == 0) break; + } + +return rc; +} + + + + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. For some calls, it makes sense to skip negative +forward and all backward assertions, and also the \b assertion; for others it +does not. + +Arguments: + code pointer to the start of the group + skipassert TRUE if certain assertions are to be skipped + +Returns: pointer to the first significant opcode +*/ + +static const pcre_uchar* +first_significant_code(const pcre_uchar *code, BOOL skipassert) +{ +for (;;) + { + switch ((int)*code) + { + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + if (!skipassert) return code; + do code += GET(code, 1); while (*code == OP_ALT); + code += PRIV(OP_lengths)[*code]; + break; + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + if (!skipassert) return code; + /* Fall through */ + + case OP_CALLOUT: + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + code += PRIV(OP_lengths)[*code]; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Find the fixed length of a branch * +*************************************************/ + +/* Scan a branch and compute the fixed length of subject that will match it, +if the length is fixed. This is needed for dealing with backward assertions. +In UTF8 mode, the result is in characters rather than bytes. The branch is +temporarily terminated with OP_END when this function is called. + +This function is called when a backward assertion is encountered, so that if it +fails, the error message can point to the correct place in the pattern. +However, we cannot do this when the assertion contains subroutine calls, +because they can be forward references. We solve this by remembering this case +and doing the check at the end; a flag specifies which mode we are running in. + +Arguments: + code points to the start of the pattern (the bracket) + utf TRUE in UTF-8 / UTF-16 mode + atend TRUE if called when the pattern is complete + cd the "compile data" structure + +Returns: the fixed length, + or -1 if there is no fixed length, + or -2 if \C was encountered (in UTF-8 mode only) + or -3 if an OP_RECURSE item was encountered and atend is FALSE + or -4 if an unknown opcode was encountered (internal error) +*/ + +static int +find_fixedlength(pcre_uchar *code, BOOL utf, BOOL atend, compile_data *cd) +{ +int length = -1; + +register int branchlength = 0; +register pcre_uchar *cc = code + 1 + LINK_SIZE; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d; + pcre_uchar *ce, *cs; + register int op = *cc; + + switch (op) + { + /* We only need to continue for OP_CBRA (normal capturing bracket) and + OP_BRA (normal non-capturing bracket) because the other variants of these + opcodes are all concerned with unlimited repeated groups, which of course + are not of fixed length. */ + + case OP_CBRA: + case OP_BRA: + case OP_ONCE: + case OP_ONCE_NC: + case OP_COND: + d = find_fixedlength(cc + ((op == OP_CBRA)? IMM2_SIZE : 0), utf, atend, cd); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* Reached end of a branch; if it's a ket it is the end of a nested call. + If it's ALT it is an alternation in a nested call. An ACCEPT is effectively + an ALT. If it is END it's the end of the outer call. All can be handled by + the same code. Note that we must not include the OP_KETRxxx opcodes here, + because they all imply an unlimited repeat. */ + + case OP_ALT: + case OP_KET: + case OP_END: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + if (length < 0) length = branchlength; + else if (length != branchlength) return -1; + if (*cc != OP_ALT) return length; + cc += 1 + LINK_SIZE; + branchlength = 0; + break; + + /* A true recursion implies not fixed length, but a subroutine call may + be OK. If the subroutine is a forward reference, we can't deal with + it until the end of the pattern, so return -3. */ + + case OP_RECURSE: + if (!atend) return -3; + cs = ce = (pcre_uchar *)cd->start_code + GET(cc, 1); /* Start subpattern */ + do ce += GET(ce, 1); while (*ce == OP_ALT); /* End subpattern */ + if (cc > cs && cc < ce) return -1; /* Recursion */ + d = find_fixedlength(cs + IMM2_SIZE, utf, atend, cd); + if (d < 0) return d; + branchlength += d; + cc += 1 + LINK_SIZE; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += PRIV(OP_lengths)[*cc]; + break; + + /* Skip over things that don't match chars */ + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + cc += cc[1] + PRIV(OP_lengths)[*cc]; + break; + + case OP_CALLOUT: + case OP_CIRC: + case OP_CIRCM: + case OP_CLOSE: + case OP_COMMIT: + case OP_CREF: + case OP_DEF: + case OP_DOLL: + case OP_DOLLM: + case OP_EOD: + case OP_EODN: + case OP_FAIL: + case OP_NCREF: + case OP_NRREF: + case OP_NOT_WORD_BOUNDARY: + case OP_PRUNE: + case OP_REVERSE: + case OP_RREF: + case OP_SET_SOM: + case OP_SKIP: + case OP_SOD: + case OP_SOM: + case OP_THEN: + case OP_WORD_BOUNDARY: + cc += PRIV(OP_lengths)[*cc]; + break; + + /* Handle literal characters */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + branchlength++; + cc += 2; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + /* Handle exact repetitions. The count is already in characters, but we + need to skip over a multibyte character in UTF8 mode. */ + + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEEXACT: + branchlength += GET2(cc,1); + if (cc[1 + IMM2_SIZE] == OP_PROP || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2; + cc += 1 + IMM2_SIZE + 1; + break; + + /* Handle single-char matchers */ + + case OP_PROP: + case OP_NOTPROP: + cc += 2; + /* Fall through */ + + case OP_HSPACE: + case OP_VSPACE: + case OP_NOT_HSPACE: + case OP_NOT_VSPACE: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + branchlength++; + cc++; + break; + + /* The single-byte matcher isn't allowed. This only happens in UTF-8 mode; + otherwise \C is coded as OP_ALLANY. */ + + case OP_ANYBYTE: + return -2; + + /* Check a class for variable quantification */ + +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 + case OP_XCLASS: + cc += GET(cc, 1) - PRIV(OP_lengths)[OP_CLASS]; + /* Fall through */ +#endif + + case OP_CLASS: + case OP_NCLASS: + cc += PRIV(OP_lengths)[OP_CLASS]; + + switch (*cc) + { + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + return -1; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(cc,1) != GET2(cc,1+IMM2_SIZE)) return -1; + branchlength += GET2(cc,1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + branchlength++; + } + break; + + /* Anything else is variable length */ + + case OP_ANYNL: + case OP_BRAMINZERO: + case OP_BRAPOS: + case OP_BRAPOSZERO: + case OP_BRAZERO: + case OP_CBRAPOS: + case OP_EXTUNI: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_PLUS: + case OP_PLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_QUERY: + case OP_QUERYI: + case OP_REF: + case OP_REFI: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + case OP_SCOND: + case OP_SKIPZERO: + case OP_STAR: + case OP_STARI: + case OP_TYPEMINPLUS: + case OP_TYPEMINQUERY: + case OP_TYPEMINSTAR: + case OP_TYPEMINUPTO: + case OP_TYPEPLUS: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSUPTO: + case OP_TYPEQUERY: + case OP_TYPESTAR: + case OP_TYPEUPTO: + case OP_UPTO: + case OP_UPTOI: + return -1; + + /* Catch unrecognized opcodes so that when new ones are added they + are not forgotten, as has happened in the past. */ + + default: + return -4; + } + } +/* Control never gets here */ +} + + + + +/************************************************* +* Scan compiled regex for specific bracket * +*************************************************/ + +/* This little function scans through a compiled pattern until it finds a +capturing bracket with the given number, or, if the number is negative, an +instance of OP_REVERSE for a lookbehind. The function is global in the C sense +so that it can be called from pcre_study() when finding the minimum matching +length. + +Arguments: + code points to start of expression + utf TRUE in UTF-8 / UTF-16 mode + number the required bracket number or negative to find a lookbehind + +Returns: pointer to the opcode for the bracket, or NULL if not found +*/ + +const pcre_uchar * +PRIV(find_bracket)(const pcre_uchar *code, BOOL utf, int number) +{ +for (;;) + { + register int c = *code; + + if (c == OP_END) return NULL; + + /* XCLASS is used for classes that cannot be represented just by a bit + map. This includes negated single high-valued characters. The length in + the table is zero; the actual length is stored in the compiled code. */ + + if (c == OP_XCLASS) code += GET(code, 1); + + /* Handle recursion */ + + else if (c == OP_REVERSE) + { + if (number < 0) return (pcre_uchar *)code; + code += PRIV(OP_lengths)[c]; + } + + /* Handle capturing bracket */ + + else if (c == OP_CBRA || c == OP_SCBRA || + c == OP_CBRAPOS || c == OP_SCBRAPOS) + { + int n = GET2(code, 1+LINK_SIZE); + if (n == number) return (pcre_uchar *)code; + code += PRIV(OP_lengths)[c]; + } + + /* Otherwise, we can get the item's length from the table, except that for + repeated character types, we have to test for \p and \P, which have an extra + two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we + must add in its length. */ + + else + { + switch(c) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSUPTO: + if (code[1 + IMM2_SIZE] == OP_PROP + || code[1 + IMM2_SIZE] == OP_NOTPROP) code += 2; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + code += code[1]; + break; + + case OP_THEN_ARG: + code += code[1]; + break; + } + + /* Add in the fixed length from the table */ + + code += PRIV(OP_lengths)[c]; + + /* In UTF-8 mode, opcodes that are followed by a character may be followed by + a multi-byte character. The length in the table is a minimum, so we have to + arrange to skip the extra bytes. */ + +#ifdef SUPPORT_UTF + if (utf) switch(c) + { + case OP_CHAR: + case OP_CHARI: + case OP_EXACT: + case OP_EXACTI: + case OP_UPTO: + case OP_UPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_STAR: + case OP_STARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); + break; + } +#else + (void)(utf); /* Keep compiler happy by referencing function argument */ +#endif + } + } +} + + + +/************************************************* +* Scan compiled regex for recursion reference * +*************************************************/ + +/* This little function scans through a compiled pattern until it finds an +instance of OP_RECURSE. + +Arguments: + code points to start of expression + utf TRUE in UTF-8 / UTF-16 mode + +Returns: pointer to the opcode for OP_RECURSE, or NULL if not found +*/ + +static const pcre_uchar * +find_recurse(const pcre_uchar *code, BOOL utf) +{ +for (;;) + { + register int c = *code; + if (c == OP_END) return NULL; + if (c == OP_RECURSE) return code; + + /* XCLASS is used for classes that cannot be represented just by a bit + map. This includes negated single high-valued characters. The length in + the table is zero; the actual length is stored in the compiled code. */ + + if (c == OP_XCLASS) code += GET(code, 1); + + /* Otherwise, we can get the item's length from the table, except that for + repeated character types, we have to test for \p and \P, which have an extra + two bytes of parameters, and for MARK/PRUNE/SKIP/THEN with an argument, we + must add in its length. */ + + else + { + switch(c) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + case OP_TYPEPOSUPTO: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + if (code[1 + IMM2_SIZE] == OP_PROP + || code[1 + IMM2_SIZE] == OP_NOTPROP) code += 2; + break; + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + code += code[1]; + break; + + case OP_THEN_ARG: + code += code[1]; + break; + } + + /* Add in the fixed length from the table */ + + code += PRIV(OP_lengths)[c]; + + /* In UTF-8 mode, opcodes that are followed by a character may be followed + by a multi-byte character. The length in the table is a minimum, so we have + to arrange to skip the extra bytes. */ + +#ifdef SUPPORT_UTF + if (utf) switch(c) + { + case OP_CHAR: + case OP_CHARI: + case OP_EXACT: + case OP_EXACTI: + case OP_UPTO: + case OP_UPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_STAR: + case OP_STARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + if (HAS_EXTRALEN(code[-1])) code += GET_EXTRALEN(code[-1]); + break; + } +#else + (void)(utf); /* Keep compiler happy by referencing function argument */ +#endif + } + } +} + + + +/************************************************* +* Scan compiled branch for non-emptiness * +*************************************************/ + +/* This function scans through a branch of a compiled pattern to see whether it +can match the empty string or not. It is called from could_be_empty() +below and from compile_branch() when checking for an unlimited repeat of a +group that can match nothing. Note that first_significant_code() skips over +backward and negative forward assertions when its final argument is TRUE. If we +hit an unclosed bracket, we return "empty" - this means we've struck an inner +bracket whose current branch will already have been scanned. + +Arguments: + code points to start of search + endcode points to where to stop + utf TRUE if in UTF-8 / UTF-16 mode + cd contains pointers to tables etc. + +Returns: TRUE if what is matched could be empty +*/ + +static BOOL +could_be_empty_branch(const pcre_uchar *code, const pcre_uchar *endcode, + BOOL utf, compile_data *cd) +{ +register int c; +for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); + code < endcode; + code = first_significant_code(code + PRIV(OP_lengths)[c], TRUE)) + { + const pcre_uchar *ccode; + + c = *code; + + /* Skip over forward assertions; the other assertions are skipped by + first_significant_code() with a TRUE final argument. */ + + if (c == OP_ASSERT) + { + do code += GET(code, 1); while (*code == OP_ALT); + c = *code; + continue; + } + + /* For a recursion/subroutine call, if its end has been reached, which + implies a backward reference subroutine call, we can scan it. If it's a + forward reference subroutine call, we can't. To detect forward reference + we have to scan up the list that is kept in the workspace. This function is + called only when doing the real compile, not during the pre-compile that + measures the size of the compiled pattern. */ + + if (c == OP_RECURSE) + { + const pcre_uchar *scode; + BOOL empty_branch; + + /* Test for forward reference */ + + for (scode = cd->start_workspace; scode < cd->hwm; scode += LINK_SIZE) + if (GET(scode, 0) == code + 1 - cd->start_code) return TRUE; + + /* Not a forward reference, test for completed backward reference */ + + empty_branch = FALSE; + scode = cd->start_code + GET(code, 1); + if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ + + /* Completed backwards reference */ + + do + { + if (could_be_empty_branch(scode, endcode, utf, cd)) + { + empty_branch = TRUE; + break; + } + scode += GET(scode, 1); + } + while (*scode == OP_ALT); + + if (!empty_branch) return FALSE; /* All branches are non-empty */ + continue; + } + + /* Groups with zero repeats can of course be empty; skip them. */ + + if (c == OP_BRAZERO || c == OP_BRAMINZERO || c == OP_SKIPZERO || + c == OP_BRAPOSZERO) + { + code += PRIV(OP_lengths)[c]; + do code += GET(code, 1); while (*code == OP_ALT); + c = *code; + continue; + } + + /* A nested group that is already marked as "could be empty" can just be + skipped. */ + + if (c == OP_SBRA || c == OP_SBRAPOS || + c == OP_SCBRA || c == OP_SCBRAPOS) + { + do code += GET(code, 1); while (*code == OP_ALT); + c = *code; + continue; + } + + /* For other groups, scan the branches. */ + + if (c == OP_BRA || c == OP_BRAPOS || + c == OP_CBRA || c == OP_CBRAPOS || + c == OP_ONCE || c == OP_ONCE_NC || + c == OP_COND) + { + BOOL empty_branch; + if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */ + + /* If a conditional group has only one branch, there is a second, implied, + empty branch, so just skip over the conditional, because it could be empty. + Otherwise, scan the individual branches of the group. */ + + if (c == OP_COND && code[GET(code, 1)] != OP_ALT) + code += GET(code, 1); + else + { + empty_branch = FALSE; + do + { + if (!empty_branch && could_be_empty_branch(code, endcode, utf, cd)) + empty_branch = TRUE; + code += GET(code, 1); + } + while (*code == OP_ALT); + if (!empty_branch) return FALSE; /* All branches are non-empty */ + } + + c = *code; + continue; + } + + /* Handle the other opcodes */ + + switch (c) + { + /* Check for quantifiers after a class. XCLASS is used for classes that + cannot be represented just by a bit map. This includes negated single + high-valued characters. The length in PRIV(OP_lengths)[] is zero; the + actual length is stored in the compiled code, so we must update "code" + here. */ + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + ccode = code += GET(code, 1); + goto CHECK_CLASS_REPEAT; +#endif + + case OP_CLASS: + case OP_NCLASS: + ccode = code + PRIV(OP_lengths)[OP_CLASS]; + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + CHECK_CLASS_REPEAT: +#endif + + switch (*ccode) + { + case OP_CRSTAR: /* These could be empty; continue */ + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + break; + + default: /* Non-repeat => class must match */ + case OP_CRPLUS: /* These repeats aren't empty */ + case OP_CRMINPLUS: + return FALSE; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(ccode, 1) > 0) return FALSE; /* Minimum > 0 */ + break; + } + break; + + /* Opcodes that must match a character */ + + case OP_PROP: + case OP_NOTPROP: + case OP_EXTUNI: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_ANYBYTE: + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_EXACT: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTPOSPLUS: + case OP_NOTEXACT: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + case OP_TYPEEXACT: + return FALSE; + + /* These are going to continue, as they may be empty, but we have to + fudge the length for the \p and \P cases. */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + /* Same for these */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + if (code[1 + IMM2_SIZE] == OP_PROP + || code[1 + IMM2_SIZE] == OP_NOTPROP) code += 2; + break; + + /* End of branch */ + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_ALT: + return TRUE; + + /* In UTF-8 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, POSQUERY, UPTO, + MINUPTO, and POSUPTO may be followed by a multibyte character */ + +#ifdef SUPPORT_UTF + case OP_STAR: + case OP_STARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_QUERY: + case OP_QUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + if (utf && HAS_EXTRALEN(code[1])) code += GET_EXTRALEN(code[1]); + break; + + case OP_UPTO: + case OP_UPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + if (utf && HAS_EXTRALEN(code[1 + IMM2_SIZE])) code += GET_EXTRALEN(code[1 + IMM2_SIZE]); + break; +#endif + + /* MARK, and PRUNE/SKIP/THEN with an argument must skip over the argument + string. */ + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + code += code[1]; + break; + + case OP_THEN_ARG: + code += code[1]; + break; + + /* None of the remaining opcodes are required to match a character. */ + + default: + break; + } + } + +return TRUE; +} + + + +/************************************************* +* Scan compiled regex for non-emptiness * +*************************************************/ + +/* This function is called to check for left recursive calls. We want to check +the current branch of the current pattern to see if it could match the empty +string. If it could, we must look outwards for branches at other levels, +stopping when we pass beyond the bracket which is the subject of the recursion. +This function is called only during the real compile, not during the +pre-compile. + +Arguments: + code points to start of the recursion + endcode points to where to stop (current RECURSE item) + bcptr points to the chain of current (unclosed) branch starts + utf TRUE if in UTF-8 / UTF-16 mode + cd pointers to tables etc + +Returns: TRUE if what is matched could be empty +*/ + +static BOOL +could_be_empty(const pcre_uchar *code, const pcre_uchar *endcode, + branch_chain *bcptr, BOOL utf, compile_data *cd) +{ +while (bcptr != NULL && bcptr->current_branch >= code) + { + if (!could_be_empty_branch(bcptr->current_branch, endcode, utf, cd)) + return FALSE; + bcptr = bcptr->outer; + } +return TRUE; +} + + + +/************************************************* +* Check for POSIX class syntax * +*************************************************/ + +/* This function is called when the sequence "[:" or "[." or "[=" is +encountered in a character class. It checks whether this is followed by a +sequence of characters terminated by a matching ":]" or ".]" or "=]". If we +reach an unescaped ']' without the special preceding character, return FALSE. + +Originally, this function only recognized a sequence of letters between the +terminators, but it seems that Perl recognizes any sequence of characters, +though of course unknown POSIX names are subsequently rejected. Perl gives an +"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE +didn't consider this to be a POSIX class. Likewise for [:1234:]. + +The problem in trying to be exactly like Perl is in the handling of escapes. We +have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX +class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code +below handles the special case of \], but does not try to do any other escape +processing. This makes it different from Perl for cases such as [:l\ower:] +where Perl recognizes it as the POSIX class "lower" but PCRE does not recognize +"l\ower". This is a lesser evil that not diagnosing bad classes when Perl does, +I think. + +A user pointed out that PCRE was rejecting [:a[:digit:]] whereas Perl was not. +It seems that the appearance of a nested POSIX class supersedes an apparent +external class. For example, [:a[:digit:]b:] matches "a", "b", ":", or +a digit. + +In Perl, unescaped square brackets may also appear as part of class names. For +example, [:a[:abc]b:] gives unknown POSIX class "[:abc]b:]". However, for +[:a[:abc]b][b:] it gives unknown POSIX class "[:abc]b][b:]", which does not +seem right at all. PCRE does not allow closing square brackets in POSIX class +names. + +Arguments: + ptr pointer to the initial [ + endptr where to return the end pointer + +Returns: TRUE or FALSE +*/ + +static BOOL +check_posix_syntax(const pcre_uchar *ptr, const pcre_uchar **endptr) +{ +int terminator; /* Don't combine these lines; the Solaris cc */ +terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ +for (++ptr; *ptr != 0; ptr++) + { + if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) + ptr++; + else if (*ptr == CHAR_RIGHT_SQUARE_BRACKET) return FALSE; + else + { + if (*ptr == terminator && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) + { + *endptr = ptr; + return TRUE; + } + if (*ptr == CHAR_LEFT_SQUARE_BRACKET && + (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || + ptr[1] == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, endptr)) + return FALSE; + } + } +return FALSE; +} + + + + +/************************************************* +* Check POSIX class name * +*************************************************/ + +/* This function is called to check the name given in a POSIX-style class entry +such as [:alnum:]. + +Arguments: + ptr points to the first letter + len the length of the name + +Returns: a value representing the name, or -1 if unknown +*/ + +static int +check_posix_name(const pcre_uchar *ptr, int len) +{ +const char *pn = posix_names; +register int yield = 0; +while (posix_name_lengths[yield] != 0) + { + if (len == posix_name_lengths[yield] && + STRNCMP_UC_C8(ptr, pn, len) == 0) return yield; + pn += posix_name_lengths[yield] + 1; + yield++; + } +return -1; +} + + +/************************************************* +* Adjust OP_RECURSE items in repeated group * +*************************************************/ + +/* OP_RECURSE items contain an offset from the start of the regex to the group +that is referenced. This means that groups can be replicated for fixed +repetition simply by copying (because the recursion is allowed to refer to +earlier groups that are outside the current group). However, when a group is +optional (i.e. the minimum quantifier is zero), OP_BRAZERO or OP_SKIPZERO is +inserted before it, after it has been compiled. This means that any OP_RECURSE +items within it that refer to the group itself or any contained groups have to +have their offsets adjusted. That one of the jobs of this function. Before it +is called, the partially compiled regex must be temporarily terminated with +OP_END. + +This function has been extended with the possibility of forward references for +recursions and subroutine calls. It must also check the list of such references +for the group we are dealing with. If it finds that one of the recursions in +the current group is on this list, it adjusts the offset in the list, not the +value in the reference (which is a group number). + +Arguments: + group points to the start of the group + adjust the amount by which the group is to be moved + utf TRUE in UTF-8 / UTF-16 mode + cd contains pointers to tables etc. + save_hwm the hwm forward reference pointer at the start of the group + +Returns: nothing +*/ + +static void +adjust_recurse(pcre_uchar *group, int adjust, BOOL utf, compile_data *cd, + pcre_uchar *save_hwm) +{ +pcre_uchar *ptr = group; + +while ((ptr = (pcre_uchar *)find_recurse(ptr, utf)) != NULL) + { + int offset; + pcre_uchar *hc; + + /* See if this recursion is on the forward reference list. If so, adjust the + reference. */ + + for (hc = save_hwm; hc < cd->hwm; hc += LINK_SIZE) + { + offset = GET(hc, 0); + if (cd->start_code + offset == ptr + 1) + { + PUT(hc, 0, offset + adjust); + break; + } + } + + /* Otherwise, adjust the recursion offset if it's after the start of this + group. */ + + if (hc >= cd->hwm) + { + offset = GET(ptr, 1); + if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust); + } + + ptr += 1 + LINK_SIZE; + } +} + + + +/************************************************* +* Insert an automatic callout point * +*************************************************/ + +/* This function is called when the PCRE_AUTO_CALLOUT option is set, to insert +callout points before each pattern item. + +Arguments: + code current code pointer + ptr current pattern pointer + cd pointers to tables etc + +Returns: new code pointer +*/ + +static pcre_uchar * +auto_callout(pcre_uchar *code, const pcre_uchar *ptr, compile_data *cd) +{ +*code++ = OP_CALLOUT; +*code++ = 255; +PUT(code, 0, (int)(ptr - cd->start_pattern)); /* Pattern offset */ +PUT(code, LINK_SIZE, 0); /* Default length */ +return code + 2 * LINK_SIZE; +} + + + +/************************************************* +* Complete a callout item * +*************************************************/ + +/* A callout item contains the length of the next item in the pattern, which +we can't fill in till after we have reached the relevant point. This is used +for both automatic and manual callouts. + +Arguments: + previous_callout points to previous callout item + ptr current pattern pointer + cd pointers to tables etc + +Returns: nothing +*/ + +static void +complete_callout(pcre_uchar *previous_callout, const pcre_uchar *ptr, compile_data *cd) +{ +int length = (int)(ptr - cd->start_pattern - GET(previous_callout, 2)); +PUT(previous_callout, 2 + LINK_SIZE, length); +} + + + +#ifdef SUPPORT_UCP +/************************************************* +* Get othercase range * +*************************************************/ + +/* This function is passed the start and end of a class range, in UTF-8 mode +with UCP support. It searches up the characters, looking for internal ranges of +characters in the "other" case. Each call returns the next one, updating the +start address. + +Arguments: + cptr points to starting character value; updated + d end value + ocptr where to put start of othercase range + odptr where to put end of othercase range + +Yield: TRUE when range returned; FALSE when no more +*/ + +static BOOL +get_othercase_range(unsigned int *cptr, unsigned int d, unsigned int *ocptr, + unsigned int *odptr) +{ +unsigned int c, othercase, next; + +for (c = *cptr; c <= d; c++) + { if ((othercase = UCD_OTHERCASE(c)) != c) break; } + +if (c > d) return FALSE; + +*ocptr = othercase; +next = othercase + 1; + +for (++c; c <= d; c++) + { + if (UCD_OTHERCASE(c) != next) break; + next++; + } + +*odptr = next - 1; +*cptr = c; + +return TRUE; +} + + + +/************************************************* +* Check a character and a property * +*************************************************/ + +/* This function is called by check_auto_possessive() when a property item +is adjacent to a fixed character. + +Arguments: + c the character + ptype the property type + pdata the data for the type + negated TRUE if it's a negated property (\P or \p{^) + +Returns: TRUE if auto-possessifying is OK +*/ + +static BOOL +check_char_prop(int c, int ptype, int pdata, BOOL negated) +{ +const ucd_record *prop = GET_UCD(c); +switch(ptype) + { + case PT_LAMP: + return (prop->chartype == ucp_Lu || + prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == negated; + + case PT_GC: + return (pdata == PRIV(ucp_gentype)[prop->chartype]) == negated; + + case PT_PC: + return (pdata == prop->chartype) == negated; + + case PT_SC: + return (pdata == prop->script) == negated; + + /* These are specials */ + + case PT_ALNUM: + return (PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == negated; + + case PT_SPACE: /* Perl space */ + return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR) + == negated; + + case PT_PXSPACE: /* POSIX space */ + return (PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR) + == negated; + + case PT_WORD: + return (PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE) == negated; + } +return FALSE; +} +#endif /* SUPPORT_UCP */ + + + +/************************************************* +* Check if auto-possessifying is possible * +*************************************************/ + +/* This function is called for unlimited repeats of certain items, to see +whether the next thing could possibly match the repeated item. If not, it makes +sense to automatically possessify the repeated item. + +Arguments: + previous pointer to the repeated opcode + utf TRUE in UTF-8 / UTF-16 mode + ptr next character in pattern + options options bits + cd contains pointers to tables etc. + +Returns: TRUE if possessifying is wanted +*/ + +static BOOL +check_auto_possessive(const pcre_uchar *previous, BOOL utf, + const pcre_uchar *ptr, int options, compile_data *cd) +{ +pcre_int32 c, next; +int op_code = *previous++; + +/* Skip whitespace and comments in extended mode */ + +if ((options & PCRE_EXTENDED) != 0) + { + for (;;) + { + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr == CHAR_NUMBER_SIGN) + { + ptr++; + while (*ptr != 0) + { + if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } + } + else break; + } + } + +/* If the next item is one that we can handle, get its value. A non-negative +value is a character, a negative value is an escape value. */ + +if (*ptr == CHAR_BACKSLASH) + { + int temperrorcode = 0; + next = check_escape(&ptr, &temperrorcode, cd->bracount, options, FALSE); + if (temperrorcode != 0) return FALSE; + ptr++; /* Point after the escape sequence */ + } +else if (!MAX_255(*ptr) || (cd->ctypes[*ptr] & ctype_meta) == 0) + { +#ifdef SUPPORT_UTF + if (utf) { GETCHARINC(next, ptr); } else +#endif + next = *ptr++; + } +else return FALSE; + +/* Skip whitespace and comments in extended mode */ + +if ((options & PCRE_EXTENDED) != 0) + { + for (;;) + { + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr == CHAR_NUMBER_SIGN) + { + ptr++; + while (*ptr != 0) + { + if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } + } + else break; + } + } + +/* If the next thing is itself optional, we have to give up. */ + +if (*ptr == CHAR_ASTERISK || *ptr == CHAR_QUESTION_MARK || + STRNCMP_UC_C8(ptr, STR_LEFT_CURLY_BRACKET STR_0 STR_COMMA, 3) == 0) + return FALSE; + +/* Now compare the next item with the previous opcode. First, handle cases when +the next item is a character. */ + +if (next >= 0) switch(op_code) + { + case OP_CHAR: +#ifdef SUPPORT_UTF + GETCHARTEST(c, previous); +#else + c = *previous; +#endif + return c != next; + + /* For CHARI (caseless character) we must check the other case. If we have + Unicode property support, we can use it to test the other case of + high-valued characters. */ + + case OP_CHARI: +#ifdef SUPPORT_UTF + GETCHARTEST(c, previous); +#else + c = *previous; +#endif + if (c == next) return FALSE; +#ifdef SUPPORT_UTF + if (utf) + { + unsigned int othercase; + if (next < 128) othercase = cd->fcc[next]; else +#ifdef SUPPORT_UCP + othercase = UCD_OTHERCASE((unsigned int)next); +#else + othercase = NOTACHAR; +#endif + return (unsigned int)c != othercase; + } + else +#endif /* SUPPORT_UTF */ + return (c != TABLE_GET((unsigned int)next, cd->fcc, next)); /* Non-UTF-8 mode */ + + /* For OP_NOT and OP_NOTI, the data is always a single-byte character. These + opcodes are not used for multi-byte characters, because they are coded using + an XCLASS instead. */ + + case OP_NOT: + return (c = *previous) == next; + + case OP_NOTI: + if ((c = *previous) == next) return TRUE; +#ifdef SUPPORT_UTF + if (utf) + { + unsigned int othercase; + if (next < 128) othercase = cd->fcc[next]; else +#ifdef SUPPORT_UCP + othercase = UCD_OTHERCASE(next); +#else + othercase = NOTACHAR; +#endif + return (unsigned int)c == othercase; + } + else +#endif /* SUPPORT_UTF */ + return (c == (int)(TABLE_GET((unsigned int)next, cd->fcc, next))); /* Non-UTF-8 mode */ + + /* Note that OP_DIGIT etc. are generated only when PCRE_UCP is *not* set. + When it is set, \d etc. are converted into OP_(NOT_)PROP codes. */ + + case OP_DIGIT: + return next > 127 || (cd->ctypes[next] & ctype_digit) == 0; + + case OP_NOT_DIGIT: + return next <= 127 && (cd->ctypes[next] & ctype_digit) != 0; + + case OP_WHITESPACE: + return next > 127 || (cd->ctypes[next] & ctype_space) == 0; + + case OP_NOT_WHITESPACE: + return next <= 127 && (cd->ctypes[next] & ctype_space) != 0; + + case OP_WORDCHAR: + return next > 127 || (cd->ctypes[next] & ctype_word) == 0; + + case OP_NOT_WORDCHAR: + return next <= 127 && (cd->ctypes[next] & ctype_word) != 0; + + case OP_HSPACE: + case OP_NOT_HSPACE: + switch(next) + { + case 0x09: + case 0x20: + case 0xa0: + case 0x1680: + case 0x180e: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x202f: + case 0x205f: + case 0x3000: + return op_code == OP_NOT_HSPACE; + default: + return op_code != OP_NOT_HSPACE; + } + + case OP_ANYNL: + case OP_VSPACE: + case OP_NOT_VSPACE: + switch(next) + { + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x85: + case 0x2028: + case 0x2029: + return op_code == OP_NOT_VSPACE; + default: + return op_code != OP_NOT_VSPACE; + } + +#ifdef SUPPORT_UCP + case OP_PROP: + return check_char_prop(next, previous[0], previous[1], FALSE); + + case OP_NOTPROP: + return check_char_prop(next, previous[0], previous[1], TRUE); +#endif + + default: + return FALSE; + } + + +/* Handle the case when the next item is \d, \s, etc. Note that when PCRE_UCP +is set, \d turns into ESC_du rather than ESC_d, etc., so ESC_d etc. are +generated only when PCRE_UCP is *not* set, that is, when only ASCII +characteristics are recognized. Similarly, the opcodes OP_DIGIT etc. are +replaced by OP_PROP codes when PCRE_UCP is set. */ + +switch(op_code) + { + case OP_CHAR: + case OP_CHARI: +#ifdef SUPPORT_UTF + GETCHARTEST(c, previous); +#else + c = *previous; +#endif + switch(-next) + { + case ESC_d: + return c > 127 || (cd->ctypes[c] & ctype_digit) == 0; + + case ESC_D: + return c <= 127 && (cd->ctypes[c] & ctype_digit) != 0; + + case ESC_s: + return c > 127 || (cd->ctypes[c] & ctype_space) == 0; + + case ESC_S: + return c <= 127 && (cd->ctypes[c] & ctype_space) != 0; + + case ESC_w: + return c > 127 || (cd->ctypes[c] & ctype_word) == 0; + + case ESC_W: + return c <= 127 && (cd->ctypes[c] & ctype_word) != 0; + + case ESC_h: + case ESC_H: + switch(c) + { + case 0x09: + case 0x20: + case 0xa0: + case 0x1680: + case 0x180e: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x202f: + case 0x205f: + case 0x3000: + return -next != ESC_h; + default: + return -next == ESC_h; + } + + case ESC_v: + case ESC_V: + switch(c) + { + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x85: + case 0x2028: + case 0x2029: + return -next != ESC_v; + default: + return -next == ESC_v; + } + + /* When PCRE_UCP is set, these values get generated for \d etc. Find + their substitutions and process them. The result will always be either + -ESC_p or -ESC_P. Then fall through to process those values. */ + +#ifdef SUPPORT_UCP + case ESC_du: + case ESC_DU: + case ESC_wu: + case ESC_WU: + case ESC_su: + case ESC_SU: + { + int temperrorcode = 0; + ptr = substitutes[-next - ESC_DU]; + next = check_escape(&ptr, &temperrorcode, 0, options, FALSE); + if (temperrorcode != 0) return FALSE; + ptr++; /* For compatibility */ + } + /* Fall through */ + + case ESC_p: + case ESC_P: + { + int ptype, pdata, errorcodeptr; + BOOL negated; + + ptr--; /* Make ptr point at the p or P */ + ptype = get_ucp(&ptr, &negated, &pdata, &errorcodeptr); + if (ptype < 0) return FALSE; + ptr++; /* Point past the final curly ket */ + + /* If the property item is optional, we have to give up. (When generated + from \d etc by PCRE_UCP, this test will have been applied much earlier, + to the original \d etc. At this point, ptr will point to a zero byte. */ + + if (*ptr == CHAR_ASTERISK || *ptr == CHAR_QUESTION_MARK || + STRNCMP_UC_C8(ptr, STR_LEFT_CURLY_BRACKET STR_0 STR_COMMA, 3) == 0) + return FALSE; + + /* Do the property check. */ + + return check_char_prop(c, ptype, pdata, (next == -ESC_P) != negated); + } +#endif + + default: + return FALSE; + } + + /* In principle, support for Unicode properties should be integrated here as + well. It means re-organizing the above code so as to get hold of the property + values before switching on the op-code. However, I wonder how many patterns + combine ASCII \d etc with Unicode properties? (Note that if PCRE_UCP is set, + these op-codes are never generated.) */ + + case OP_DIGIT: + return next == -ESC_D || next == -ESC_s || next == -ESC_W || + next == -ESC_h || next == -ESC_v || next == -ESC_R; + + case OP_NOT_DIGIT: + return next == -ESC_d; + + case OP_WHITESPACE: + return next == -ESC_S || next == -ESC_d || next == -ESC_w || next == -ESC_R; + + case OP_NOT_WHITESPACE: + return next == -ESC_s || next == -ESC_h || next == -ESC_v; + + case OP_HSPACE: + return next == -ESC_S || next == -ESC_H || next == -ESC_d || + next == -ESC_w || next == -ESC_v || next == -ESC_R; + + case OP_NOT_HSPACE: + return next == -ESC_h; + + /* Can't have \S in here because VT matches \S (Perl anomaly) */ + case OP_ANYNL: + case OP_VSPACE: + return next == -ESC_V || next == -ESC_d || next == -ESC_w; + + case OP_NOT_VSPACE: + return next == -ESC_v || next == -ESC_R; + + case OP_WORDCHAR: + return next == -ESC_W || next == -ESC_s || next == -ESC_h || + next == -ESC_v || next == -ESC_R; + + case OP_NOT_WORDCHAR: + return next == -ESC_w || next == -ESC_d; + + default: + return FALSE; + } + +/* Control does not reach here */ +} + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the a vector. If the options are +changed during the branch, the pointer is used to change the external options +bits. This function is used during the pre-compile phase when we are trying +to find out the amount of memory needed, as well as during the real compile +phase. The value of lengthptr distinguishes the two phases. + +Arguments: + optionsptr pointer to the option bits + codeptr points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorcodeptr points to error code variable + firstcharptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) + reqcharptr set to the last literal character required, else < 0 + bcptr points to current branch chain + cond_depth conditional nesting depth + cd contains pointers to tables etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: TRUE on success + FALSE, with *errorcodeptr set non-zero on error +*/ + +static BOOL +compile_branch(int *optionsptr, pcre_uchar **codeptr, + const pcre_uchar **ptrptr, int *errorcodeptr, pcre_int32 *firstcharptr, + pcre_int32 *reqcharptr, branch_chain *bcptr, int cond_depth, + compile_data *cd, int *lengthptr) +{ +int repeat_type, op_type; +int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ +int bravalue = 0; +int greedy_default, greedy_non_default; +pcre_int32 firstchar, reqchar; +pcre_int32 zeroreqchar, zerofirstchar; +pcre_int32 req_caseopt, reqvary, tempreqvary; +int options = *optionsptr; /* May change dynamically */ +int after_manual_callout = 0; +int length_prevgroup = 0; +register int c; +register pcre_uchar *code = *codeptr; +pcre_uchar *last_code = code; +pcre_uchar *orig_code = code; +pcre_uchar *tempcode; +BOOL inescq = FALSE; +BOOL groupsetfirstchar = FALSE; +const pcre_uchar *ptr = *ptrptr; +const pcre_uchar *tempptr; +const pcre_uchar *nestptr = NULL; +pcre_uchar *previous = NULL; +pcre_uchar *previous_callout = NULL; +pcre_uchar *save_hwm = NULL; +pcre_uint8 classbits[32]; + +/* We can fish out the UTF-8 setting once and for all into a BOOL, but we +must not do this for other options (e.g. PCRE_EXTENDED) because they may change +dynamically as we process the pattern. */ + +#ifdef SUPPORT_UTF +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +pcre_uchar utf_chars[6]; +#else +BOOL utf = FALSE; +#endif + +/* Helper variables for OP_XCLASS opcode (for characters > 255). */ + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 +BOOL xclass; +pcre_uchar *class_uchardata; +pcre_uchar *class_uchardata_base; +#endif + +#ifdef PCRE_DEBUG +if (lengthptr != NULL) DPRINTF((">> start branch\n")); +#endif + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Initialize no first byte, no required byte. REQ_UNSET means "no char +matching encountered yet". It gets changed to REQ_NONE if we hit something that +matches a non-fixed char first char; reqchar just remains unset if we never +find one. + +When we hit a repeat whose minimum is zero, we may have to adjust these values +to take the zero repeat into account. This is implemented by setting them to +zerofirstbyte and zeroreqchar when such a repeat is encountered. The individual +item types that can be repeated set these backoff variables appropriately. */ + +firstchar = reqchar = zerofirstchar = zeroreqchar = REQ_UNSET; + +/* The variable req_caseopt contains either the REQ_CASELESS value +or zero, according to the current setting of the caseless flag. The +REQ_CASELESS leaves the lower 28 bit empty. It is added into the +firstchar or reqchar variables to record the case status of the +value. This is used only for ASCII characters. */ + +req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS:0; + +/* Switch on next character until the end of the branch */ + +for (;; ptr++) + { + BOOL negate_class; + BOOL should_flip_negation; + BOOL possessive_quantifier; + BOOL is_quantifier; + BOOL is_recurse; + BOOL reset_bracount; + int class_has_8bitchar; + int class_single_char; + int newoptions; + int recno; + int refsign; + int skipbytes; + int subreqchar; + int subfirstchar; + int terminator; + int mclength; + int tempbracount; + pcre_uchar mcbuffer[8]; + + /* Get next character in the pattern */ + + c = *ptr; + + /* If we are at the end of a nested substitution, revert to the outer level + string. Nesting only happens one level deep. */ + + if (c == 0 && nestptr != NULL) + { + ptr = nestptr; + nestptr = NULL; + c = *ptr; + } + + /* If we are in the pre-compile phase, accumulate the length used for the + previous cycle of this loop. */ + + if (lengthptr != NULL) + { +#ifdef PCRE_DEBUG + if (code > cd->hwm) cd->hwm = code; /* High water info */ +#endif + if (code > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN) /* Check for overrun */ + { + *errorcodeptr = ERR52; + goto FAILED; + } + + /* There is at least one situation where code goes backwards: this is the + case of a zero quantifier after a class (e.g. [ab]{0}). At compile time, + the class is simply eliminated. However, it is created first, so we have to + allow memory for it. Therefore, don't ever reduce the length at this point. + */ + + if (code < last_code) code = last_code; + + /* Paranoid check for integer overflow */ + + if (OFLOW_MAX - *lengthptr < code - last_code) + { + *errorcodeptr = ERR20; + goto FAILED; + } + + *lengthptr += (int)(code - last_code); + DPRINTF(("length=%d added %d c=%c (0x%x)\n", *lengthptr, + (int)(code - last_code), c, c)); + + /* If "previous" is set and it is not at the start of the work space, move + it back to there, in order to avoid filling up the work space. Otherwise, + if "previous" is NULL, reset the current code pointer to the start. */ + + if (previous != NULL) + { + if (previous > orig_code) + { + memmove(orig_code, previous, IN_UCHARS(code - previous)); + code -= previous - orig_code; + previous = orig_code; + } + } + else code = orig_code; + + /* Remember where this code item starts so we can pick up the length + next time round. */ + + last_code = code; + } + + /* In the real compile phase, just check the workspace used by the forward + reference list. */ + + else if (cd->hwm > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN) + { + *errorcodeptr = ERR52; + goto FAILED; + } + + /* If in \Q...\E, check for the end; if not, we have a literal */ + + if (inescq && c != 0) + { + if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) + { + inescq = FALSE; + ptr++; + continue; + } + else + { + if (previous_callout != NULL) + { + if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ + complete_callout(previous_callout, ptr, cd); + previous_callout = NULL; + } + if ((options & PCRE_AUTO_CALLOUT) != 0) + { + previous_callout = code; + code = auto_callout(code, ptr, cd); + } + goto NORMAL_CHAR; + } + } + + /* Fill in length of a previous callout, except when the next thing is + a quantifier. */ + + is_quantifier = + c == CHAR_ASTERISK || c == CHAR_PLUS || c == CHAR_QUESTION_MARK || + (c == CHAR_LEFT_CURLY_BRACKET && is_counted_repeat(ptr+1)); + + if (!is_quantifier && previous_callout != NULL && + after_manual_callout-- <= 0) + { + if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ + complete_callout(previous_callout, ptr, cd); + previous_callout = NULL; + } + + /* In extended mode, skip white space and comments. */ + + if ((options & PCRE_EXTENDED) != 0) + { + if (MAX_255(*ptr) && (cd->ctypes[c] & ctype_space) != 0) continue; + if (c == CHAR_NUMBER_SIGN) + { + ptr++; + while (*ptr != 0) + { + if (IS_NEWLINE(ptr)) { ptr += cd->nllen - 1; break; } + ptr++; +#ifdef SUPPORT_UTF + if (utf) FORWARDCHAR(ptr); +#endif + } + if (*ptr != 0) continue; + + /* Else fall through to handle end of string */ + c = 0; + } + } + + /* No auto callout for quantifiers. */ + + if ((options & PCRE_AUTO_CALLOUT) != 0 && !is_quantifier) + { + previous_callout = code; + code = auto_callout(code, ptr, cd); + } + + switch(c) + { + /* ===================================================================*/ + case 0: /* The branch terminates at string end */ + case CHAR_VERTICAL_LINE: /* or | or ) */ + case CHAR_RIGHT_PARENTHESIS: + *firstcharptr = firstchar; + *reqcharptr = reqchar; + *codeptr = code; + *ptrptr = ptr; + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < code - last_code) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += (int)(code - last_code); /* To include callout length */ + DPRINTF((">> end branch\n")); + } + return TRUE; + + + /* ===================================================================*/ + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case CHAR_CIRCUMFLEX_ACCENT: + previous = NULL; + if ((options & PCRE_MULTILINE) != 0) + { + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + *code++ = OP_CIRCM; + } + else *code++ = OP_CIRC; + break; + + case CHAR_DOLLAR_SIGN: + previous = NULL; + *code++ = ((options & PCRE_MULTILINE) != 0)? OP_DOLLM : OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqchar doesn't change either. */ + + case CHAR_DOT: + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + zerofirstchar = firstchar; + zeroreqchar = reqchar; + previous = code; + *code++ = ((options & PCRE_DOTALL) != 0)? OP_ALLANY: OP_ANY; + break; + + + /* ===================================================================*/ + /* Character classes. If the included characters are all < 256, we build a + 32-byte bitmap of the permitted characters, except in the special case + where there is only one such character. For negated classes, we build the + map as usual, then invert it at the end. However, we use a different opcode + so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag byte tells + whether the bitmap is present, and whether this is a negated class or not. + + In JavaScript compatibility mode, an isolated ']' causes an error. In + default (Perl) mode, it is treated as a data character. */ + + case CHAR_RIGHT_SQUARE_BRACKET: + if ((cd->external_options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + *errorcodeptr = ERR64; + goto FAILED; + } + goto NORMAL_CHAR; + + case CHAR_LEFT_SQUARE_BRACKET: + previous = code; + + /* PCRE supports POSIX class stuff inside a class. Perl gives an error if + they are encountered at the top level, so we'll do that too. */ + + if ((ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || + ptr[1] == CHAR_EQUALS_SIGN) && + check_posix_syntax(ptr, &tempptr)) + { + *errorcodeptr = (ptr[1] == CHAR_COLON)? ERR13 : ERR31; + goto FAILED; + } + + /* If the first character is '^', set the negation flag and skip it. Also, + if the first few characters (either before or after ^) are \Q\E or \E we + skip them too. This makes for compatibility with Perl. */ + + negate_class = FALSE; + for (;;) + { + c = *(++ptr); + if (c == CHAR_BACKSLASH) + { + if (ptr[1] == CHAR_E) + ptr++; + else if (STRNCMP_UC_C8(ptr + 1, STR_Q STR_BACKSLASH STR_E, 3) == 0) + ptr += 3; + else + break; + } + else if (!negate_class && c == CHAR_CIRCUMFLEX_ACCENT) + negate_class = TRUE; + else break; + } + + /* Empty classes are allowed in JavaScript compatibility mode. Otherwise, + an initial ']' is taken as a data character -- the code below handles + that. In JS mode, [] must always fail, so generate OP_FAIL, whereas + [^] must match any character, so generate OP_ALLANY. */ + + if (c == CHAR_RIGHT_SQUARE_BRACKET && + (cd->external_options & PCRE_JAVASCRIPT_COMPAT) != 0) + { + *code++ = negate_class? OP_ALLANY : OP_FAIL; + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + zerofirstchar = firstchar; + break; + } + + /* If a class contains a negative special such as \S, we need to flip the + negation flag at the end, so that support for characters > 255 works + correctly (they are all included in the class). */ + + should_flip_negation = FALSE; + + /* For optimization purposes, we track some properties of the class. + class_has_8bitchar will be non-zero, if the class contains at least one + < 256 character. class_single_char will be 1 if the class contains only + a single character. */ + + class_has_8bitchar = 0; + class_single_char = 0; + + /* Initialize the 32-char bit map to all zeros. We build the map in a + temporary bit of memory, in case the class contains only 1 character (less + than 256), because in that case the compiled code doesn't use the bit map. + */ + + memset(classbits, 0, 32 * sizeof(pcre_uint8)); + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + xclass = FALSE; /* No chars >= 256 */ + class_uchardata = code + LINK_SIZE + 2; /* For UTF-8 items */ + class_uchardata_base = class_uchardata; /* For resetting in pass 1 */ +#endif + + /* Process characters until ] is reached. By writing this as a "do" it + means that an initial ] is taken as a data character. At the start of the + loop, c contains the first byte of the character. */ + + if (c != 0) do + { + const pcre_uchar *oldptr; + +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(c)) + { /* Braces are required because the */ + GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ + } +#endif + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + /* In the pre-compile phase, accumulate the length of any extra + data and reset the pointer. This is so that very large classes that + contain a zillion > 255 characters no longer overwrite the work space + (which is on the stack). */ + + if (lengthptr != NULL) + { + *lengthptr += class_uchardata - class_uchardata_base; + class_uchardata = class_uchardata_base; + } +#endif + + /* Inside \Q...\E everything is literal except \E */ + + if (inescq) + { + if (c == CHAR_BACKSLASH && ptr[1] == CHAR_E) /* If we are at \E */ + { + inescq = FALSE; /* Reset literal state */ + ptr++; /* Skip the 'E' */ + continue; /* Carry on with next */ + } + goto CHECK_RANGE; /* Could be range if \E follows */ + } + + /* Handle POSIX class names. Perl allows a negation extension of the + form [:^name:]. A square bracket that doesn't match the syntax is + treated as a literal. We also recognize the POSIX constructions + [.ch.] and [=ch=] ("collating elements") and fault them, as Perl + 5.6 and 5.8 do. */ + + if (c == CHAR_LEFT_SQUARE_BRACKET && + (ptr[1] == CHAR_COLON || ptr[1] == CHAR_DOT || + ptr[1] == CHAR_EQUALS_SIGN) && check_posix_syntax(ptr, &tempptr)) + { + BOOL local_negate = FALSE; + int posix_class, taboffset, tabopt; + register const pcre_uint8 *cbits = cd->cbits; + pcre_uint8 pbits[32]; + + if (ptr[1] != CHAR_COLON) + { + *errorcodeptr = ERR31; + goto FAILED; + } + + ptr += 2; + if (*ptr == CHAR_CIRCUMFLEX_ACCENT) + { + local_negate = TRUE; + should_flip_negation = TRUE; /* Note negative special */ + ptr++; + } + + posix_class = check_posix_name(ptr, (int)(tempptr - ptr)); + if (posix_class < 0) + { + *errorcodeptr = ERR30; + goto FAILED; + } + + /* If matching is caseless, upper and lower are converted to + alpha. This relies on the fact that the class table starts with + alpha, lower, upper as the first 3 entries. */ + + if ((options & PCRE_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; + + /* When PCRE_UCP is set, some of the POSIX classes are converted to + different escape sequences that use Unicode properties. */ + +#ifdef SUPPORT_UCP + if ((options & PCRE_UCP) != 0) + { + int pc = posix_class + ((local_negate)? POSIX_SUBSIZE/2 : 0); + if (posix_substitutes[pc] != NULL) + { + nestptr = tempptr + 1; + ptr = posix_substitutes[pc] - 1; + continue; + } + } +#endif + /* In the non-UCP case, we build the bit map for the POSIX class in a + chunk of local store because we may be adding and subtracting from it, + and we don't want to subtract bits that may be in the main map already. + At the end we or the result into the bit map that is being built. */ + + posix_class *= 3; + + /* Copy in the first table (always present) */ + + memcpy(pbits, cbits + posix_class_maps[posix_class], + 32 * sizeof(pcre_uint8)); + + /* If there is a second table, add or remove it as required. */ + + taboffset = posix_class_maps[posix_class + 1]; + tabopt = posix_class_maps[posix_class + 2]; + + if (taboffset >= 0) + { + if (tabopt >= 0) + for (c = 0; c < 32; c++) pbits[c] |= cbits[c + taboffset]; + else + for (c = 0; c < 32; c++) pbits[c] &= ~cbits[c + taboffset]; + } + + /* Not see if we need to remove any special characters. An option + value of 1 removes vertical space and 2 removes underscore. */ + + if (tabopt < 0) tabopt = -tabopt; + if (tabopt == 1) pbits[1] &= ~0x3c; + else if (tabopt == 2) pbits[11] &= 0x7f; + + /* Add the POSIX table or its complement into the main table that is + being built and we are done. */ + + if (local_negate) + for (c = 0; c < 32; c++) classbits[c] |= ~pbits[c]; + else + for (c = 0; c < 32; c++) classbits[c] |= pbits[c]; + + ptr = tempptr + 1; + /* Every class contains at least one < 256 characters. */ + class_has_8bitchar = 1; + /* Every class contains at least two characters. */ + class_single_char = 2; + continue; /* End of POSIX syntax handling */ + } + + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. The sequence \b is a special + case. Inside a class (and only there) it is treated as backspace. We + assume that other escapes have more than one character in them, so + speculatively set both class_has_8bitchar and class_single_char bigger + than one. Unrecognized escapes fall through and are either treated + as literal characters (by default), or are faulted if + PCRE_EXTRA is set. */ + + if (c == CHAR_BACKSLASH) + { + c = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE); + if (*errorcodeptr != 0) goto FAILED; + + if (-c == ESC_b) c = CHAR_BS; /* \b is backspace in a class */ + else if (-c == ESC_N) /* \N is not supported in a class */ + { + *errorcodeptr = ERR71; + goto FAILED; + } + else if (-c == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) + { + ptr += 2; /* avoid empty string */ + } + else inescq = TRUE; + continue; + } + else if (-c == ESC_E) continue; /* Ignore orphan \E */ + + if (c < 0) + { + register const pcre_uint8 *cbits = cd->cbits; + /* Every class contains at least two < 256 characters. */ + class_has_8bitchar++; + /* Every class contains at least two characters. */ + class_single_char += 2; + + switch (-c) + { +#ifdef SUPPORT_UCP + case ESC_du: /* These are the values given for \d etc */ + case ESC_DU: /* when PCRE_UCP is set. We replace the */ + case ESC_wu: /* escape sequence with an appropriate \p */ + case ESC_WU: /* or \P to test Unicode properties instead */ + case ESC_su: /* of the default ASCII testing. */ + case ESC_SU: + nestptr = ptr; + ptr = substitutes[-c - ESC_DU] - 1; /* Just before substitute */ + class_has_8bitchar--; /* Undo! */ + continue; +#endif + case ESC_d: + for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit]; + continue; + + case ESC_D: + should_flip_negation = TRUE; + for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_digit]; + continue; + + case ESC_w: + for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_word]; + continue; + + case ESC_W: + should_flip_negation = TRUE; + for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word]; + continue; + + /* Perl 5.004 onwards omits VT from \s, but we must preserve it + if it was previously set by something earlier in the character + class. */ + + case ESC_s: + classbits[0] |= cbits[cbit_space]; + classbits[1] |= cbits[cbit_space+1] & ~0x08; + for (c = 2; c < 32; c++) classbits[c] |= cbits[c+cbit_space]; + continue; + + case ESC_S: + should_flip_negation = TRUE; + for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_space]; + classbits[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */ + continue; + + case ESC_h: + SETBIT(classbits, 0x09); /* VT */ + SETBIT(classbits, 0x20); /* SPACE */ + SETBIT(classbits, 0xa0); /* NSBP */ +#ifndef COMPILE_PCRE8 + xclass = TRUE; + *class_uchardata++ = XCL_SINGLE; + *class_uchardata++ = 0x1680; + *class_uchardata++ = XCL_SINGLE; + *class_uchardata++ = 0x180e; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x2000; + *class_uchardata++ = 0x200a; + *class_uchardata++ = XCL_SINGLE; + *class_uchardata++ = 0x202f; + *class_uchardata++ = XCL_SINGLE; + *class_uchardata++ = 0x205f; + *class_uchardata++ = XCL_SINGLE; + *class_uchardata++ = 0x3000; +#elif defined SUPPORT_UTF + if (utf) + { + xclass = TRUE; + *class_uchardata++ = XCL_SINGLE; + class_uchardata += PRIV(ord2utf)(0x1680, class_uchardata); + *class_uchardata++ = XCL_SINGLE; + class_uchardata += PRIV(ord2utf)(0x180e, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x2000, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x200a, class_uchardata); + *class_uchardata++ = XCL_SINGLE; + class_uchardata += PRIV(ord2utf)(0x202f, class_uchardata); + *class_uchardata++ = XCL_SINGLE; + class_uchardata += PRIV(ord2utf)(0x205f, class_uchardata); + *class_uchardata++ = XCL_SINGLE; + class_uchardata += PRIV(ord2utf)(0x3000, class_uchardata); + } +#endif + continue; + + case ESC_H: + for (c = 0; c < 32; c++) + { + int x = 0xff; + switch (c) + { + case 0x09/8: x ^= 1 << (0x09%8); break; + case 0x20/8: x ^= 1 << (0x20%8); break; + case 0xa0/8: x ^= 1 << (0xa0%8); break; + default: break; + } + classbits[c] |= x; + } +#ifndef COMPILE_PCRE8 + xclass = TRUE; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x0100; + *class_uchardata++ = 0x167f; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x1681; + *class_uchardata++ = 0x180d; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x180f; + *class_uchardata++ = 0x1fff; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x200b; + *class_uchardata++ = 0x202e; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x2030; + *class_uchardata++ = 0x205e; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x2060; + *class_uchardata++ = 0x2fff; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x3001; +#ifdef SUPPORT_UTF + if (utf) + class_uchardata += PRIV(ord2utf)(0x10ffff, class_uchardata); + else +#endif + *class_uchardata++ = 0xffff; +#elif defined SUPPORT_UTF + if (utf) + { + xclass = TRUE; + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x0100, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x167f, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x1681, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x180d, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x180f, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x1fff, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x200b, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x202e, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x2030, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x205e, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x2060, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x2fff, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x3001, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x10ffff, class_uchardata); + } +#endif + continue; + + case ESC_v: + SETBIT(classbits, 0x0a); /* LF */ + SETBIT(classbits, 0x0b); /* VT */ + SETBIT(classbits, 0x0c); /* FF */ + SETBIT(classbits, 0x0d); /* CR */ + SETBIT(classbits, 0x85); /* NEL */ +#ifndef COMPILE_PCRE8 + xclass = TRUE; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x2028; + *class_uchardata++ = 0x2029; +#elif defined SUPPORT_UTF + if (utf) + { + xclass = TRUE; + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x2028, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x2029, class_uchardata); + } +#endif + continue; + + case ESC_V: + for (c = 0; c < 32; c++) + { + int x = 0xff; + switch (c) + { + case 0x0a/8: x ^= 1 << (0x0a%8); + x ^= 1 << (0x0b%8); + x ^= 1 << (0x0c%8); + x ^= 1 << (0x0d%8); + break; + case 0x85/8: x ^= 1 << (0x85%8); break; + default: break; + } + classbits[c] |= x; + } + +#ifndef COMPILE_PCRE8 + xclass = TRUE; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x0100; + *class_uchardata++ = 0x2027; + *class_uchardata++ = XCL_RANGE; + *class_uchardata++ = 0x202a; +#ifdef SUPPORT_UTF + if (utf) + class_uchardata += PRIV(ord2utf)(0x10ffff, class_uchardata); + else +#endif + *class_uchardata++ = 0xffff; +#elif defined SUPPORT_UTF + if (utf) + { + xclass = TRUE; + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x0100, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x2027, class_uchardata); + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(0x202a, class_uchardata); + class_uchardata += PRIV(ord2utf)(0x10ffff, class_uchardata); + } +#endif + continue; + +#ifdef SUPPORT_UCP + case ESC_p: + case ESC_P: + { + BOOL negated; + int pdata; + int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr); + if (ptype < 0) goto FAILED; + xclass = TRUE; + *class_uchardata++ = ((-c == ESC_p) != negated)? + XCL_PROP : XCL_NOTPROP; + *class_uchardata++ = ptype; + *class_uchardata++ = pdata; + class_has_8bitchar--; /* Undo! */ + continue; + } +#endif + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ + + default: + if ((options & PCRE_EXTRA) != 0) + { + *errorcodeptr = ERR7; + goto FAILED; + } + class_has_8bitchar--; /* Undo the speculative increase. */ + class_single_char -= 2; /* Undo the speculative increase. */ + c = *ptr; /* Get the final character and fall through */ + break; + } + } + + /* Fall through if we have a single character (c >= 0). This may be + greater than 256. */ + + } /* End of backslash handling */ + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + at the end is treated as a literal. Perl ignores orphaned \E sequences + entirely. The code for handling \Q and \E is messy. */ + + CHECK_RANGE: + while (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) + { + inescq = FALSE; + ptr += 2; + } + + oldptr = ptr; + + /* Remember \r or \n */ + + if (c == CHAR_CR || c == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF; + + /* Check for range */ + + if (!inescq && ptr[1] == CHAR_MINUS) + { + int d; + ptr += 2; + while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) ptr += 2; + + /* If we hit \Q (not followed by \E) at this point, go into escaped + mode. */ + + while (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_Q) + { + ptr += 2; + if (*ptr == CHAR_BACKSLASH && ptr[1] == CHAR_E) + { ptr += 2; continue; } + inescq = TRUE; + break; + } + + if (*ptr == 0 || (!inescq && *ptr == CHAR_RIGHT_SQUARE_BRACKET)) + { + ptr = oldptr; + goto LONE_SINGLE_CHARACTER; + } + +#ifdef SUPPORT_UTF + if (utf) + { /* Braces are required because the */ + GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ + } + else +#endif + d = *ptr; /* Not UTF-8 mode */ + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. Perl 5.6 treats a hyphen as a literal + in such circumstances. */ + + if (!inescq && d == CHAR_BACKSLASH) + { + d = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE); + if (*errorcodeptr != 0) goto FAILED; + + /* \b is backspace; any other special means the '-' was literal */ + + if (d < 0) + { + if (d == -ESC_b) d = CHAR_BS; else + { + ptr = oldptr; + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + } + } + } + + /* Check that the two values are in the correct order. Optimize + one-character ranges */ + + if (d < c) + { + *errorcodeptr = ERR8; + goto FAILED; + } + + if (d == c) goto LONE_SINGLE_CHARACTER; /* A few lines below */ + + /* Remember \r or \n */ + + if (d == CHAR_CR || d == CHAR_NL) cd->external_flags |= PCRE_HASCRORLF; + + /* Since we found a character range, single character optimizations + cannot be done anymore. */ + class_single_char = 2; + + /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless + matching, we have to use an XCLASS with extra data items. Caseless + matching for characters > 127 is available only if UCP support is + available. */ + +#if defined SUPPORT_UTF && !(defined COMPILE_PCRE8) + if ((d > 255) || (utf && ((options & PCRE_CASELESS) != 0 && d > 127))) +#elif defined SUPPORT_UTF + if (utf && (d > 255 || ((options & PCRE_CASELESS) != 0 && d > 127))) +#elif !(defined COMPILE_PCRE8) + if (d > 255) +#endif +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + { + xclass = TRUE; + + /* With UCP support, we can find the other case equivalents of + the relevant characters. There may be several ranges. Optimize how + they fit with the basic range. */ + +#ifdef SUPPORT_UCP +#ifndef COMPILE_PCRE8 + if (utf && (options & PCRE_CASELESS) != 0) +#else + if ((options & PCRE_CASELESS) != 0) +#endif + { + unsigned int occ, ocd; + unsigned int cc = c; + unsigned int origd = d; + while (get_othercase_range(&cc, origd, &occ, &ocd)) + { + if (occ >= (unsigned int)c && + ocd <= (unsigned int)d) + continue; /* Skip embedded ranges */ + + if (occ < (unsigned int)c && + ocd >= (unsigned int)c - 1) /* Extend the basic range */ + { /* if there is overlap, */ + c = occ; /* noting that if occ < c */ + continue; /* we can't have ocd > d */ + } /* because a subrange is */ + if (ocd > (unsigned int)d && + occ <= (unsigned int)d + 1) /* always shorter than */ + { /* the basic range. */ + d = ocd; + continue; + } + + if (occ == ocd) + { + *class_uchardata++ = XCL_SINGLE; + } + else + { + *class_uchardata++ = XCL_RANGE; + class_uchardata += PRIV(ord2utf)(occ, class_uchardata); + } + class_uchardata += PRIV(ord2utf)(ocd, class_uchardata); + } + } +#endif /* SUPPORT_UCP */ + + /* Now record the original range, possibly modified for UCP caseless + overlapping ranges. */ + + *class_uchardata++ = XCL_RANGE; +#ifdef SUPPORT_UTF +#ifndef COMPILE_PCRE8 + if (utf) + { + class_uchardata += PRIV(ord2utf)(c, class_uchardata); + class_uchardata += PRIV(ord2utf)(d, class_uchardata); + } + else + { + *class_uchardata++ = c; + *class_uchardata++ = d; + } +#else + class_uchardata += PRIV(ord2utf)(c, class_uchardata); + class_uchardata += PRIV(ord2utf)(d, class_uchardata); +#endif +#else /* SUPPORT_UTF */ + *class_uchardata++ = c; + *class_uchardata++ = d; +#endif /* SUPPORT_UTF */ + + /* With UCP support, we are done. Without UCP support, there is no + caseless matching for UTF characters > 127; we can use the bit map + for the smaller ones. As for 16 bit characters without UTF, we + can still use */ + +#ifdef SUPPORT_UCP +#ifndef COMPILE_PCRE8 + if (utf) +#endif + continue; /* With next character in the class */ +#endif /* SUPPORT_UCP */ + +#if defined SUPPORT_UTF && !defined(SUPPORT_UCP) && !(defined COMPILE_PCRE8) + if (utf) + { + if ((options & PCRE_CASELESS) == 0 || c > 127) continue; + /* Adjust upper limit and fall through to set up the map */ + d = 127; + } + else + { + if (c > 255) continue; + /* Adjust upper limit and fall through to set up the map */ + d = 255; + } +#elif defined SUPPORT_UTF && !defined(SUPPORT_UCP) + if ((options & PCRE_CASELESS) == 0 || c > 127) continue; + /* Adjust upper limit and fall through to set up the map */ + d = 127; +#else + if (c > 255) continue; + /* Adjust upper limit and fall through to set up the map */ + d = 255; +#endif /* SUPPORT_UTF && !SUPPORT_UCP && !COMPILE_PCRE8 */ + } +#endif /* SUPPORT_UTF || !COMPILE_PCRE8 */ + + /* We use the bit map for 8 bit mode, or when the characters fall + partially or entirely to [0-255] ([0-127] for UCP) ranges. */ + + class_has_8bitchar = 1; + + /* We can save a bit of time by skipping this in the pre-compile. */ + + if (lengthptr == NULL) for (; c <= d; c++) + { + classbits[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + int uc = cd->fcc[c]; /* flip case */ + classbits[uc/8] |= (1 << (uc&7)); + } + } + + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character or for an + apparent range that isn't. */ + + LONE_SINGLE_CHARACTER: + + /* Only the value of 1 matters for class_single_char. */ + if (class_single_char < 2) class_single_char++; + + /* If class_charcount is 1, we saw precisely one character. As long as + there were no negated characters >= 128 and there was no use of \p or \P, + in other words, no use of any XCLASS features, we can optimize. + + In UTF-8 mode, we can optimize the negative case only if there were no + characters >= 128 because OP_NOT and the related opcodes like OP_NOTSTAR + operate on single-bytes characters only. This is an historical hangover. + Maybe one day we can tidy these opcodes to handle multi-byte characters. + + The optimization throws away the bit map. We turn the item into a + 1-character OP_CHAR[I] if it's positive, or OP_NOT[I] if it's negative. + Note that OP_NOT[I] does not support multibyte characters. In the positive + case, it can cause firstchar to be set. Otherwise, there can be no first + char if this item is first, whatever repeat count may follow. In the case + of reqchar, save the previous value for reinstating. */ + +#ifdef SUPPORT_UTF + if (class_single_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET + && (!utf || !negate_class || c < (MAX_VALUE_FOR_SINGLE_CHAR + 1))) +#else + if (class_single_char == 1 && ptr[1] == CHAR_RIGHT_SQUARE_BRACKET) +#endif + { + ptr++; + zeroreqchar = reqchar; + + /* The OP_NOT[I] opcodes work on single characters only. */ + + if (negate_class) + { + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + zerofirstchar = firstchar; + *code++ = ((options & PCRE_CASELESS) != 0)? OP_NOTI: OP_NOT; + *code++ = c; + goto NOT_CHAR; + } + + /* For a single, positive character, get the value into mcbuffer, and + then we can handle this with the normal one-character code. */ + +#ifdef SUPPORT_UTF + if (utf && c > MAX_VALUE_FOR_SINGLE_CHAR) + mclength = PRIV(ord2utf)(c, mcbuffer); + else +#endif + { + mcbuffer[0] = c; + mclength = 1; + } + goto ONE_CHAR; + } /* End of 1-char optimization */ + + /* Handle a character that cannot go in the bit map. */ + +#if defined SUPPORT_UTF && !(defined COMPILE_PCRE8) + if ((c > 255) || (utf && ((options & PCRE_CASELESS) != 0 && c > 127))) +#elif defined SUPPORT_UTF + if (utf && (c > 255 || ((options & PCRE_CASELESS) != 0 && c > 127))) +#elif !(defined COMPILE_PCRE8) + if (c > 255) +#endif + +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + { + xclass = TRUE; + *class_uchardata++ = XCL_SINGLE; +#ifdef SUPPORT_UTF +#ifndef COMPILE_PCRE8 + /* In non 8 bit mode, we can get here even if we are not in UTF mode. */ + if (!utf) + *class_uchardata++ = c; + else +#endif + class_uchardata += PRIV(ord2utf)(c, class_uchardata); +#else /* SUPPORT_UTF */ + *class_uchardata++ = c; +#endif /* SUPPORT_UTF */ + +#ifdef SUPPORT_UCP +#ifdef COMPILE_PCRE8 + if ((options & PCRE_CASELESS) != 0) +#else + /* In non 8 bit mode, we can get here even if we are not in UTF mode. */ + if (utf && (options & PCRE_CASELESS) != 0) +#endif + { + unsigned int othercase; + if ((int)(othercase = UCD_OTHERCASE(c)) != c) + { + *class_uchardata++ = XCL_SINGLE; + class_uchardata += PRIV(ord2utf)(othercase, class_uchardata); + } + } +#endif /* SUPPORT_UCP */ + + } + else +#endif /* SUPPORT_UTF || COMPILE_PCRE16 */ + + /* Handle a single-byte character */ + { + class_has_8bitchar = 1; + classbits[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + c = cd->fcc[c]; /* flip case */ + classbits[c/8] |= (1 << (c&7)); + } + } + } + + /* Loop until ']' reached. This "while" is the end of the "do" far above. + If we are at the end of an internal nested string, revert to the outer + string. */ + + while (((c = *(++ptr)) != 0 || + (nestptr != NULL && + (ptr = nestptr, nestptr = NULL, c = *(++ptr)) != 0)) && + (c != CHAR_RIGHT_SQUARE_BRACKET || inescq)); + + /* Check for missing terminating ']' */ + + if (c == 0) + { + *errorcodeptr = ERR6; + goto FAILED; + } + + /* If this is the first thing in the branch, there can be no first char + setting, whatever the repeat count. Any reqchar setting must remain + unchanged after any kind of repeat. */ + + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + zerofirstchar = firstchar; + zeroreqchar = reqchar; + + /* If there are characters with values > 255, we have to compile an + extended class, with its own opcode, unless there was a negated special + such as \S in the class, and PCRE_UCP is not set, because in that case all + characters > 255 are in the class, so any that were explicitly given as + well can be ignored. If (when there are explicit characters > 255 that must + be listed) there are no characters < 256, we can omit the bitmap in the + actual compiled code. */ + +#ifdef SUPPORT_UTF + if (xclass && (!should_flip_negation || (options & PCRE_UCP) != 0)) +#elif !defined COMPILE_PCRE8 + if (xclass && !should_flip_negation) +#endif +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + { + *class_uchardata++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negate_class? XCL_NOT:0; + + /* If the map is required, move up the extra data to make room for it; + otherwise just move the code pointer to the end of the extra data. */ + + if (class_has_8bitchar > 0) + { + *code++ |= XCL_MAP; + memmove(code + (32 / sizeof(pcre_uchar)), code, + IN_UCHARS(class_uchardata - code)); + memcpy(code, classbits, 32); + code = class_uchardata + (32 / sizeof(pcre_uchar)); + } + else code = class_uchardata; + + /* Now fill in the complete length of the item */ + + PUT(previous, 1, (int)(code - previous)); + break; /* End of class handling */ + } +#endif + + /* If there are no characters > 255, or they are all to be included or + excluded, set the opcode to OP_CLASS or OP_NCLASS, depending on whether the + whole class was negated and whether there were negative specials such as \S + (non-UCP) in the class. Then copy the 32-byte map into the code vector, + negating it if necessary. */ + + *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; + if (lengthptr == NULL) /* Save time in the pre-compile phase */ + { + if (negate_class) + for (c = 0; c < 32; c++) classbits[c] = ~classbits[c]; + memcpy(code, classbits, 32); + } + code += 32 / sizeof(pcre_uchar); + NOT_CHAR: + break; + + + /* ===================================================================*/ + /* Various kinds of repeat; '{' is not necessarily a quantifier, but this + has been tested above. */ + + case CHAR_LEFT_CURLY_BRACKET: + if (!is_quantifier) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorcodeptr); + if (*errorcodeptr != 0) goto FAILED; + goto REPEAT; + + case CHAR_ASTERISK: + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case CHAR_PLUS: + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case CHAR_QUESTION_MARK: + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous == NULL) + { + *errorcodeptr = ERR9; + goto FAILED; + } + + if (repeat_min == 0) + { + firstchar = zerofirstchar; /* Adjust for zero repeat */ + reqchar = zeroreqchar; /* Ditto */ + } + + /* Remember whether this is a variable length repeat */ + + reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + + op_type = 0; /* Default single-char op codes */ + possessive_quantifier = FALSE; /* Default not possessive quantifier */ + + /* Save start of previous item, in case we have to move it up in order to + insert something before it. */ + + tempcode = previous; + + /* If the next character is '+', we have a possessive quantifier. This + implies greediness, whatever the setting of the PCRE_UNGREEDY option. + If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. We change the + repeat type to the non-default. */ + + if (ptr[1] == CHAR_PLUS) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + ptr++; + } + else if (ptr[1] == CHAR_QUESTION_MARK) + { + repeat_type = greedy_non_default; + ptr++; + } + else repeat_type = greedy_default; + + /* If previous was a recursion call, wrap it in atomic brackets so that + previous becomes the atomic group. All recursions were so wrapped in the + past, but it no longer happens for non-repeated recursions. In fact, the + repeated ones could be re-implemented independently so as not to need this, + but for the moment we rely on the code for repeating groups. */ + + if (*previous == OP_RECURSE) + { + memmove(previous + 1 + LINK_SIZE, previous, IN_UCHARS(1 + LINK_SIZE)); + *previous = OP_ONCE; + PUT(previous, 1, 2 + 2*LINK_SIZE); + previous[2 + 2*LINK_SIZE] = OP_KET; + PUT(previous, 3 + 2*LINK_SIZE, 2 + 2*LINK_SIZE); + code += 2 + 2 * LINK_SIZE; + length_prevgroup = 3 + 3*LINK_SIZE; + + /* When actually compiling, we need to check whether this was a forward + reference, and if so, adjust the offset. */ + + if (lengthptr == NULL && cd->hwm >= cd->start_workspace + LINK_SIZE) + { + int offset = GET(cd->hwm, -LINK_SIZE); + if (offset == previous + 1 - cd->start_code) + PUT(cd->hwm, -LINK_SIZE, offset + 1 + LINK_SIZE); + } + } + + /* Now handle repetition for the different types of item. */ + + /* If previous was a character match, abolish the item and generate a + repeat item instead. If a char item has a minumum of more than one, ensure + that it is set in reqchar - it might not be if a sequence such as x{3} is + the first thing in a branch because the x will have gone into firstchar + instead. */ + + if (*previous == OP_CHAR || *previous == OP_CHARI) + { + op_type = (*previous == OP_CHAR)? 0 : OP_STARI - OP_STAR; + + /* Deal with UTF characters that take up more than one character. It's + easier to write this out separately than try to macrify it. Use c to + hold the length of the character in bytes, plus UTF_LENGTH to flag that + it's a length rather than a small character. */ + +#ifdef SUPPORT_UTF + if (utf && NOT_FIRSTCHAR(code[-1])) + { + pcre_uchar *lastchar = code - 1; + BACKCHAR(lastchar); + c = (int)(code - lastchar); /* Length of UTF-8 character */ + memcpy(utf_chars, lastchar, IN_UCHARS(c)); /* Save the char */ + c |= UTF_LENGTH; /* Flag c as a length */ + } + else +#endif /* SUPPORT_UTF */ + + /* Handle the case of a single charater - either with no UTF support, or + with UTF disabled, or for a single character UTF character. */ + { + c = code[-1]; + if (repeat_min > 1) reqchar = c | req_caseopt | cd->req_varyopt; + } + + /* If the repetition is unlimited, it pays to see if the next thing on + the line is something that cannot possibly match this character. If so, + automatically possessifying this item gains some performance in the case + where the match fails. */ + + if (!possessive_quantifier && + repeat_max < 0 && + check_auto_possessive(previous, utf, ptr + 1, options, cd)) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + } + + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by setting opt_type to add a suitable offset into + repeat_type. We can also test for auto-possessification. OP_NOT and OP_NOTI + are currently used only for single-byte chars. */ + + else if (*previous == OP_NOT || *previous == OP_NOTI) + { + op_type = ((*previous == OP_NOT)? OP_NOTSTAR : OP_NOTSTARI) - OP_STAR; + c = previous[1]; + if (!possessive_quantifier && + repeat_max < 0 && + check_auto_possessive(previous, utf, ptr + 1, options, cd)) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + } + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting op_type to add a suitable offset into repeat_type. Note + the the Unicode property types will be present only when SUPPORT_UCP is + defined, but we don't wrap the little bits of code here because it just + makes it horribly messy. */ + + else if (*previous < OP_EODN) + { + pcre_uchar *oldcode; + int prop_type, prop_value; + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + + if (!possessive_quantifier && + repeat_max < 0 && + check_auto_possessive(previous, utf, ptr + 1, options, cd)) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + } + + OUTPUT_SINGLE_REPEAT: + if (*previous == OP_PROP || *previous == OP_NOTPROP) + { + prop_type = previous[1]; + prop_value = previous[2]; + } + else prop_type = prop_value = -1; + + oldcode = code; + code = previous; /* Usually overwrite previous item */ + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) goto END_REPEAT; + + /*--------------------------------------------------------------------*/ + /* This code is obsolete from release 8.00; the restriction was finally + removed: */ + + /* All real repeats make it impossible to handle partial matching (maybe + one day we will be able to remove this restriction). */ + + /* if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; */ + /*--------------------------------------------------------------------*/ + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == -1) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + + /* A repeat minimum of 1 is optimized into some special cases. If the + maximum is unlimited, we use OP_PLUS. Otherwise, the original item is + left in place and, if the maximum is greater than 1, we use OP_UPTO with + one less than the maximum. */ + + else if (repeat_min == 1) + { + if (repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + else + { + code = oldcode; /* leave previous item in place */ + if (repeat_max == 1) goto END_REPEAT; + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max - 1); + } + } + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. */ + + else + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + PUT2INC(code, 0, repeat_min); + + /* If the maximum is unlimited, insert an OP_STAR. Before doing so, + we have to insert the character for the previous code. For a repeated + Unicode property match, there are two extra bytes that define the + required property. In UTF-8 mode, long characters have their length in + c, with the UTF_LENGTH bit as a flag. */ + + if (repeat_max < 0) + { +#ifdef SUPPORT_UTF + if (utf && (c & UTF_LENGTH) != 0) + { + memcpy(code, utf_chars, IN_UCHARS(c & 7)); + code += c & 7; + } + else +#endif + { + *code++ = c; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + } + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min, again + preceded by the character, for the previously inserted code. If the + UPTO is just for 1 instance, we can use QUERY instead. */ + + else if (repeat_max != repeat_min) + { +#ifdef SUPPORT_UTF + if (utf && (c & UTF_LENGTH) != 0) + { + memcpy(code, utf_chars, IN_UCHARS(c & 7)); + code += c & 7; + } + else +#endif + *code++ = c; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + repeat_max -= repeat_min; + + if (repeat_max == 1) + { + *code++ = OP_QUERY + repeat_type; + } + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + } + + /* The character or character type itself comes last in all cases. */ + +#ifdef SUPPORT_UTF + if (utf && (c & UTF_LENGTH) != 0) + { + memcpy(code, utf_chars, IN_UCHARS(c & 7)); + code += c & 7; + } + else +#endif + *code++ = c; + + /* For a repeated Unicode property match, there are two extra bytes that + define the required property. */ + +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } +#endif + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it, but just skip the item if the repeat was {0,0}. */ + + else if (*previous == OP_CLASS || + *previous == OP_NCLASS || +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + *previous == OP_XCLASS || +#endif + *previous == OP_REF || + *previous == OP_REFI) + { + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + /*--------------------------------------------------------------------*/ + /* This code is obsolete from release 8.00; the restriction was finally + removed: */ + + /* All real repeats make it impossible to handle partial matching (maybe + one day we will be able to remove this restriction). */ + + /* if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; */ + /*--------------------------------------------------------------------*/ + + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + PUT2INC(code, 0, repeat_min); + if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ + PUT2INC(code, 0, repeat_max); + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. Note that at this point we can encounter only the "basic" bracket + opcodes such as BRA and CBRA, as this is the place where they get converted + into the more special varieties such as BRAPOS and SBRA. A test for >= + OP_ASSERT and <= OP_COND includes ASSERT, ASSERT_NOT, ASSERTBACK, + ASSERTBACK_NOT, ONCE, BRA, CBRA, and COND. Originally, PCRE did not allow + repetition of assertions, but now it does, for Perl compatibility. */ + + else if (*previous >= OP_ASSERT && *previous <= OP_COND) + { + register int i; + int len = (int)(code - previous); + pcre_uchar *bralink = NULL; + pcre_uchar *brazeroptr = NULL; + + /* Repeating a DEFINE group is pointless, but Perl allows the syntax, so + we just ignore the repeat. */ + + if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_DEF) + goto END_REPEAT; + + /* There is no sense in actually repeating assertions. The only potential + use of repetition is in cases when the assertion is optional. Therefore, + if the minimum is greater than zero, just ignore the repeat. If the + maximum is not not zero or one, set it to 1. */ + + if (*previous < OP_ONCE) /* Assertion */ + { + if (repeat_min > 0) goto END_REPEAT; + if (repeat_max < 0 || repeat_max > 1) repeat_max = 1; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) + { + /* If the maximum is also zero, we used to just omit the group from the + output altogether, like this: + + ** if (repeat_max == 0) + ** { + ** code = previous; + ** goto END_REPEAT; + ** } + + However, that fails when a group or a subgroup within it is referenced + as a subroutine from elsewhere in the pattern, so now we stick in + OP_SKIPZERO in front of it so that it is skipped on execution. As we + don't have a list of which groups are referenced, we cannot do this + selectively. + + If the maximum is 1 or unlimited, we just have to stick in the BRAZERO + and do no more at this point. However, we do need to adjust any + OP_RECURSE calls inside the group that refer to the group itself or any + internal or forward referenced group, because the offset is from the + start of the whole regex. Temporarily terminate the pattern while doing + this. */ + + if (repeat_max <= 1) /* Covers 0, 1, and unlimited */ + { + *code = OP_END; + adjust_recurse(previous, 1, utf, cd, save_hwm); + memmove(previous + 1, previous, IN_UCHARS(len)); + code++; + if (repeat_max == 0) + { + *previous++ = OP_SKIPZERO; + goto END_REPEAT; + } + brazeroptr = previous; /* Save for possessive optimizing */ + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We have to + adjust the value or repeat_max, since one less copy is required. Once + again, we may have to adjust any OP_RECURSE calls inside the group. */ + + else + { + int offset; + *code = OP_END; + adjust_recurse(previous, 2 + LINK_SIZE, utf, cd, save_hwm); + memmove(previous + 2 + LINK_SIZE, previous, IN_UCHARS(len)); + code += 2 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + offset = (bralink == NULL)? 0 : (int)(previous - bralink); + bralink = previous; + PUTINC(previous, 0, offset); + } + + repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. If we set a first char from the group, and didn't + set a required char, copy the latter from the former. If there are any + forward reference subroutine calls in the group, there will be entries on + the workspace list; replicate these with an appropriate increment. */ + + else + { + if (repeat_min > 1) + { + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. Do some paranoid checks for + potential integer overflow. The INT64_OR_DOUBLE type is a 64-bit + integer type when available, otherwise double. */ + + if (lengthptr != NULL) + { + int delta = (repeat_min - 1)*length_prevgroup; + if ((INT64_OR_DOUBLE)(repeat_min - 1)* + (INT64_OR_DOUBLE)length_prevgroup > + (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += delta; + } + + /* This is compiling for real. If there is a set first byte for + the group, and we have not yet set a "required byte", set it. Make + sure there is enough workspace for copying forward references before + doing the copy. */ + + else + { + if (groupsetfirstchar && reqchar < 0) reqchar = firstchar; + + for (i = 1; i < repeat_min; i++) + { + pcre_uchar *hc; + pcre_uchar *this_hwm = cd->hwm; + memcpy(code, previous, IN_UCHARS(len)); + + while (cd->hwm > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN - (this_hwm - save_hwm)) + { + int save_offset = save_hwm - cd->start_workspace; + int this_offset = this_hwm - cd->start_workspace; + *errorcodeptr = expand_workspace(cd); + if (*errorcodeptr != 0) goto FAILED; + save_hwm = (pcre_uchar *)cd->start_workspace + save_offset; + this_hwm = (pcre_uchar *)cd->start_workspace + this_offset; + } + + for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE) + { + PUT(cd->hwm, 0, GET(hc, 0) + len); + cd->hwm += LINK_SIZE; + } + save_hwm = this_hwm; + code += len; + } + } + } + + if (repeat_max > 0) repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero minimum, + the first one was set up above. In all cases the repeat_max now specifies + the number of additional copies needed. Again, we must remember to + replicate entries on the forward reference list. */ + + if (repeat_max >= 0) + { + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. For each repetition we must add 1 + to the length for BRAZERO and for all but the last repetition we must + add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some + paranoid checks to avoid integer overflow. The INT64_OR_DOUBLE type is + a 64-bit integer type when available, otherwise double. */ + + if (lengthptr != NULL && repeat_max > 0) + { + int delta = repeat_max * (length_prevgroup + 1 + 2 + 2*LINK_SIZE) - + 2 - 2*LINK_SIZE; /* Last one doesn't nest */ + if ((INT64_OR_DOUBLE)repeat_max * + (INT64_OR_DOUBLE)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) + > (INT64_OR_DOUBLE)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += delta; + } + + /* This is compiling for real */ + + else for (i = repeat_max - 1; i >= 0; i--) + { + pcre_uchar *hc; + pcre_uchar *this_hwm = cd->hwm; + + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) + { + int offset; + *code++ = OP_BRA; + offset = (bralink == NULL)? 0 : (int)(code - bralink); + bralink = code; + PUTINC(code, 0, offset); + } + + memcpy(code, previous, IN_UCHARS(len)); + + /* Ensure there is enough workspace for forward references before + copying them. */ + + while (cd->hwm > cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN - (this_hwm - save_hwm)) + { + int save_offset = save_hwm - cd->start_workspace; + int this_offset = this_hwm - cd->start_workspace; + *errorcodeptr = expand_workspace(cd); + if (*errorcodeptr != 0) goto FAILED; + save_hwm = (pcre_uchar *)cd->start_workspace + save_offset; + this_hwm = (pcre_uchar *)cd->start_workspace + this_offset; + } + + for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE) + { + PUT(cd->hwm, 0, GET(hc, 0) + len + ((i != 0)? 2+LINK_SIZE : 1)); + cd->hwm += LINK_SIZE; + } + save_hwm = this_hwm; + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink != NULL) + { + int oldlinkoffset; + int offset = (int)(code - bralink + 1); + pcre_uchar *bra = code - offset; + oldlinkoffset = GET(bra, 1); + bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; + *code++ = OP_KET; + PUTINC(code, 0, offset); + PUT(bra, 1, offset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. For + ONCE brackets, that's all we need to do. However, possessively repeated + ONCE brackets can be converted into non-capturing brackets, as the + behaviour of (?:xx)++ is the same as (?>xx)++ and this saves having to + deal with possessive ONCEs specially. + + Otherwise, when we are doing the actual compile phase, check to see + whether this group is one that could match an empty string. If so, + convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so + that runtime checking can be done. [This check is also applied to ONCE + groups at runtime, but in a different way.] + + Then, if the quantifier was possessive and the bracket is not a + conditional, we convert the BRA code to the POS form, and the KET code to + KETRPOS. (It turns out to be convenient at runtime to detect this kind of + subpattern at both the start and at the end.) The use of special opcodes + makes it possible to reduce greatly the stack usage in pcre_exec(). If + the group is preceded by OP_BRAZERO, convert this to OP_BRAPOSZERO. + + Then, if the minimum number of matches is 1 or 0, cancel the possessive + flag so that the default action below, of wrapping everything inside + atomic brackets, does not happen. When the minimum is greater than 1, + there will be earlier copies of the group, and so we still have to wrap + the whole thing. */ + + else + { + pcre_uchar *ketcode = code - 1 - LINK_SIZE; + pcre_uchar *bracode = ketcode - GET(ketcode, 1); + + /* Convert possessive ONCE brackets to non-capturing */ + + if ((*bracode == OP_ONCE || *bracode == OP_ONCE_NC) && + possessive_quantifier) *bracode = OP_BRA; + + /* For non-possessive ONCE brackets, all we need to do is to + set the KET. */ + + if (*bracode == OP_ONCE || *bracode == OP_ONCE_NC) + *ketcode = OP_KETRMAX + repeat_type; + + /* Handle non-ONCE brackets and possessive ONCEs (which have been + converted to non-capturing above). */ + + else + { + /* In the compile phase, check for empty string matching. */ + + if (lengthptr == NULL) + { + pcre_uchar *scode = bracode; + do + { + if (could_be_empty_branch(scode, ketcode, utf, cd)) + { + *bracode += OP_SBRA - OP_BRA; + break; + } + scode += GET(scode, 1); + } + while (*scode == OP_ALT); + } + + /* Handle possessive quantifiers. */ + + if (possessive_quantifier) + { + /* For COND brackets, we wrap the whole thing in a possessively + repeated non-capturing bracket, because we have not invented POS + versions of the COND opcodes. Because we are moving code along, we + must ensure that any pending recursive references are updated. */ + + if (*bracode == OP_COND || *bracode == OP_SCOND) + { + int nlen = (int)(code - bracode); + *code = OP_END; + adjust_recurse(bracode, 1 + LINK_SIZE, utf, cd, save_hwm); + memmove(bracode + 1 + LINK_SIZE, bracode, IN_UCHARS(nlen)); + code += 1 + LINK_SIZE; + nlen += 1 + LINK_SIZE; + *bracode = OP_BRAPOS; + *code++ = OP_KETRPOS; + PUTINC(code, 0, nlen); + PUT(bracode, 1, nlen); + } + + /* For non-COND brackets, we modify the BRA code and use KETRPOS. */ + + else + { + *bracode += 1; /* Switch to xxxPOS opcodes */ + *ketcode = OP_KETRPOS; + } + + /* If the minimum is zero, mark it as possessive, then unset the + possessive flag when the minimum is 0 or 1. */ + + if (brazeroptr != NULL) *brazeroptr = OP_BRAPOSZERO; + if (repeat_min < 2) possessive_quantifier = FALSE; + } + + /* Non-possessive quantifier */ + + else *ketcode = OP_KETRMAX + repeat_type; + } + } + } + + /* If previous is OP_FAIL, it was generated by an empty class [] in + JavaScript mode. The other ways in which OP_FAIL can be generated, that is + by (*FAIL) or (?!) set previous to NULL, which gives a "nothing to repeat" + error above. We can just ignore the repeat in JS case. */ + + else if (*previous == OP_FAIL) goto END_REPEAT; + + /* Else there's some kind of shambles */ + + else + { + *errorcodeptr = ERR11; + goto FAILED; + } + + /* If the character following a repeat is '+', or if certain optimization + tests above succeeded, possessive_quantifier is TRUE. For some opcodes, + there are special alternative opcodes for this case. For anything else, we + wrap the entire repeated item inside OP_ONCE brackets. Logically, the '+' + notation is just syntactic sugar, taken from Sun's Java package, but the + special opcodes can optimize it. + + Some (but not all) possessively repeated subpatterns have already been + completely handled in the code just above. For them, possessive_quantifier + is always FALSE at this stage. + + Note that the repeated item starts at tempcode, not at previous, which + might be the first part of a string whose (former) last char we repeated. + + Possessifying an 'exact' quantifier has no effect, so we can ignore it. But + an 'upto' may follow. We skip over an 'exact' item, and then test the + length of what remains before proceeding. */ + + if (possessive_quantifier) + { + int len; + + if (*tempcode == OP_TYPEEXACT) + tempcode += PRIV(OP_lengths)[*tempcode] + + ((tempcode[1 + IMM2_SIZE] == OP_PROP + || tempcode[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + + else if (*tempcode == OP_EXACT || *tempcode == OP_NOTEXACT) + { + tempcode += PRIV(OP_lengths)[*tempcode]; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(tempcode[-1])) + tempcode += GET_EXTRALEN(tempcode[-1]); +#endif + } + + len = (int)(code - tempcode); + if (len > 0) switch (*tempcode) + { + case OP_STAR: *tempcode = OP_POSSTAR; break; + case OP_PLUS: *tempcode = OP_POSPLUS; break; + case OP_QUERY: *tempcode = OP_POSQUERY; break; + case OP_UPTO: *tempcode = OP_POSUPTO; break; + + case OP_STARI: *tempcode = OP_POSSTARI; break; + case OP_PLUSI: *tempcode = OP_POSPLUSI; break; + case OP_QUERYI: *tempcode = OP_POSQUERYI; break; + case OP_UPTOI: *tempcode = OP_POSUPTOI; break; + + case OP_NOTSTAR: *tempcode = OP_NOTPOSSTAR; break; + case OP_NOTPLUS: *tempcode = OP_NOTPOSPLUS; break; + case OP_NOTQUERY: *tempcode = OP_NOTPOSQUERY; break; + case OP_NOTUPTO: *tempcode = OP_NOTPOSUPTO; break; + + case OP_NOTSTARI: *tempcode = OP_NOTPOSSTARI; break; + case OP_NOTPLUSI: *tempcode = OP_NOTPOSPLUSI; break; + case OP_NOTQUERYI: *tempcode = OP_NOTPOSQUERYI; break; + case OP_NOTUPTOI: *tempcode = OP_NOTPOSUPTOI; break; + + case OP_TYPESTAR: *tempcode = OP_TYPEPOSSTAR; break; + case OP_TYPEPLUS: *tempcode = OP_TYPEPOSPLUS; break; + case OP_TYPEQUERY: *tempcode = OP_TYPEPOSQUERY; break; + case OP_TYPEUPTO: *tempcode = OP_TYPEPOSUPTO; break; + + /* Because we are moving code along, we must ensure that any + pending recursive references are updated. */ + + default: + *code = OP_END; + adjust_recurse(tempcode, 1 + LINK_SIZE, utf, cd, save_hwm); + memmove(tempcode + 1 + LINK_SIZE, tempcode, IN_UCHARS(len)); + code += 1 + LINK_SIZE; + len += 1 + LINK_SIZE; + tempcode[0] = OP_ONCE; + *code++ = OP_KET; + PUTINC(code, 0, len); + PUT(tempcode, 1, len); + break; + } + } + + /* In all case we no longer have a previous item. We also set the + "follows varying string" flag for subsequently encountered reqchars if + it isn't already set and we have just passed a varying length item. */ + + END_REPEAT: + previous = NULL; + cd->req_varyopt |= reqvary; + break; + + + /* ===================================================================*/ + /* Start of nested parenthesized sub-expression, or comment or lookahead or + lookbehind or option setting or condition or all the other extended + parenthesis forms. */ + + case CHAR_LEFT_PARENTHESIS: + newoptions = options; + skipbytes = 0; + bravalue = OP_CBRA; + save_hwm = cd->hwm; + reset_bracount = FALSE; + + /* First deal with various "verbs" that can be introduced by '*'. */ + + ptr++; + if (ptr[0] == CHAR_ASTERISK && (ptr[1] == ':' + || (MAX_255(ptr[1]) && ((cd->ctypes[ptr[1]] & ctype_letter) != 0)))) + { + int i, namelen; + int arglen = 0; + const char *vn = verbnames; + const pcre_uchar *name = ptr + 1; + const pcre_uchar *arg = NULL; + previous = NULL; + ptr++; + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_letter) != 0) ptr++; + namelen = (int)(ptr - name); + + /* It appears that Perl allows any characters whatsoever, other than + a closing parenthesis, to appear in arguments, so we no longer insist on + letters, digits, and underscores. */ + + if (*ptr == CHAR_COLON) + { + arg = ++ptr; + while (*ptr != 0 && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + arglen = (int)(ptr - arg); + } + + if (*ptr != CHAR_RIGHT_PARENTHESIS) + { + *errorcodeptr = ERR60; + goto FAILED; + } + + /* Scan the table of verb names */ + + for (i = 0; i < verbcount; i++) + { + if (namelen == verbs[i].len && + STRNCMP_UC_C8(name, vn, namelen) == 0) + { + /* Check for open captures before ACCEPT and convert it to + ASSERT_ACCEPT if in an assertion. */ + + if (verbs[i].op == OP_ACCEPT) + { + open_capitem *oc; + if (arglen != 0) + { + *errorcodeptr = ERR59; + goto FAILED; + } + cd->had_accept = TRUE; + for (oc = cd->open_caps; oc != NULL; oc = oc->next) + { + *code++ = OP_CLOSE; + PUT2INC(code, 0, oc->number); + } + *code++ = (cd->assert_depth > 0)? OP_ASSERT_ACCEPT : OP_ACCEPT; + + /* Do not set firstchar after *ACCEPT */ + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + } + + /* Handle other cases with/without an argument */ + + else if (arglen == 0) + { + if (verbs[i].op < 0) /* Argument is mandatory */ + { + *errorcodeptr = ERR66; + goto FAILED; + } + *code = verbs[i].op; + if (*code++ == OP_THEN) cd->external_flags |= PCRE_HASTHEN; + } + + else + { + if (verbs[i].op_arg < 0) /* Argument is forbidden */ + { + *errorcodeptr = ERR59; + goto FAILED; + } + *code = verbs[i].op_arg; + if (*code++ == OP_THEN_ARG) cd->external_flags |= PCRE_HASTHEN; + *code++ = arglen; + memcpy(code, arg, IN_UCHARS(arglen)); + code += arglen; + *code++ = 0; + } + + break; /* Found verb, exit loop */ + } + + vn += verbs[i].len + 1; + } + + if (i < verbcount) continue; /* Successfully handled a verb */ + *errorcodeptr = ERR60; /* Verb not recognized */ + goto FAILED; + } + + /* Deal with the extended parentheses; all are introduced by '?', and the + appearance of any of them means that this is not a capturing group. */ + + else if (*ptr == CHAR_QUESTION_MARK) + { + int i, set, unset, namelen; + int *optset; + const pcre_uchar *name; + pcre_uchar *slot; + + switch (*(++ptr)) + { + case CHAR_NUMBER_SIGN: /* Comment; skip to ket */ + ptr++; + while (*ptr != 0 && *ptr != CHAR_RIGHT_PARENTHESIS) ptr++; + if (*ptr == 0) + { + *errorcodeptr = ERR18; + goto FAILED; + } + continue; + + + /* ------------------------------------------------------------ */ + case CHAR_VERTICAL_LINE: /* Reset capture count for each branch */ + reset_bracount = TRUE; + /* Fall through */ + + /* ------------------------------------------------------------ */ + case CHAR_COLON: /* Non-capturing bracket */ + bravalue = OP_BRA; + ptr++; + break; + + + /* ------------------------------------------------------------ */ + case CHAR_LEFT_PARENTHESIS: + bravalue = OP_COND; /* Conditional group */ + + /* A condition can be an assertion, a number (referring to a numbered + group), a name (referring to a named group), or 'R', referring to + recursion. R and R&name are also permitted for recursion tests. + + There are several syntaxes for testing a named group: (?(name)) is used + by Python; Perl 5.10 onwards uses (?() or (?('name')). + + There are two unfortunate ambiguities, caused by history. (a) 'R' can + be the recursive thing or the name 'R' (and similarly for 'R' followed + by digits), and (b) a number could be a name that consists of digits. + In both cases, we look for a name first; if not found, we try the other + cases. */ + + /* For conditions that are assertions, check the syntax, and then exit + the switch. This will take control down to where bracketed groups, + including assertions, are processed. */ + + if (ptr[1] == CHAR_QUESTION_MARK && (ptr[2] == CHAR_EQUALS_SIGN || + ptr[2] == CHAR_EXCLAMATION_MARK || ptr[2] == CHAR_LESS_THAN_SIGN)) + break; + + /* Most other conditions use OP_CREF (a couple change to OP_RREF + below), and all need to skip 1+IMM2_SIZE bytes at the start of the group. */ + + code[1+LINK_SIZE] = OP_CREF; + skipbytes = 1+IMM2_SIZE; + refsign = -1; + + /* Check for a test for recursion in a named group. */ + + if (ptr[1] == CHAR_R && ptr[2] == CHAR_AMPERSAND) + { + terminator = -1; + ptr += 2; + code[1+LINK_SIZE] = OP_RREF; /* Change the type of test */ + } + + /* Check for a test for a named group's having been set, using the Perl + syntax (?() or (?('name') */ + + else if (ptr[1] == CHAR_LESS_THAN_SIGN) + { + terminator = CHAR_GREATER_THAN_SIGN; + ptr++; + } + else if (ptr[1] == CHAR_APOSTROPHE) + { + terminator = CHAR_APOSTROPHE; + ptr++; + } + else + { + terminator = 0; + if (ptr[1] == CHAR_MINUS || ptr[1] == CHAR_PLUS) refsign = *(++ptr); + } + + /* We now expect to read a name; any thing else is an error */ + + if (!MAX_255(ptr[1]) || (cd->ctypes[ptr[1]] & ctype_word) == 0) + { + ptr += 1; /* To get the right offset */ + *errorcodeptr = ERR28; + goto FAILED; + } + + /* Read the name, but also get it as a number if it's all digits */ + + recno = 0; + name = ++ptr; + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_word) != 0) + { + if (recno >= 0) + recno = (IS_DIGIT(*ptr))? recno * 10 + *ptr - CHAR_0 : -1; + ptr++; + } + namelen = (int)(ptr - name); + + if ((terminator > 0 && *ptr++ != terminator) || + *ptr++ != CHAR_RIGHT_PARENTHESIS) + { + ptr--; /* Error offset */ + *errorcodeptr = ERR26; + goto FAILED; + } + + /* Do no further checking in the pre-compile phase. */ + + if (lengthptr != NULL) break; + + /* In the real compile we do the work of looking for the actual + reference. If the string started with "+" or "-" we require the rest to + be digits, in which case recno will be set. */ + + if (refsign > 0) + { + if (recno <= 0) + { + *errorcodeptr = ERR58; + goto FAILED; + } + recno = (refsign == CHAR_MINUS)? + cd->bracount - recno + 1 : recno +cd->bracount; + if (recno <= 0 || recno > cd->final_bracount) + { + *errorcodeptr = ERR15; + goto FAILED; + } + PUT2(code, 2+LINK_SIZE, recno); + break; + } + + /* Otherwise (did not start with "+" or "-"), start by looking for the + name. If we find a name, add one to the opcode to change OP_CREF or + OP_RREF into OP_NCREF or OP_NRREF. These behave exactly the same, + except they record that the reference was originally to a name. The + information is used to check duplicate names. */ + + slot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0) break; + slot += cd->name_entry_size; + } + + /* Found a previous named subpattern */ + + if (i < cd->names_found) + { + recno = GET2(slot, 0); + PUT2(code, 2+LINK_SIZE, recno); + code[1+LINK_SIZE]++; + } + + /* Search the pattern for a forward reference */ + + else if ((i = find_parens(cd, name, namelen, + (options & PCRE_EXTENDED) != 0, utf)) > 0) + { + PUT2(code, 2+LINK_SIZE, i); + code[1+LINK_SIZE]++; + } + + /* If terminator == 0 it means that the name followed directly after + the opening parenthesis [e.g. (?(abc)...] and in this case there are + some further alternatives to try. For the cases where terminator != 0 + [things like (?(... or (?('name')... or (?(R&name)... ] we have + now checked all the possibilities, so give an error. */ + + else if (terminator != 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + + /* Check for (?(R) for recursion. Allow digits after R to specify a + specific group number. */ + + else if (*name == CHAR_R) + { + recno = 0; + for (i = 1; i < namelen; i++) + { + if (!IS_DIGIT(name[i])) + { + *errorcodeptr = ERR15; + goto FAILED; + } + recno = recno * 10 + name[i] - CHAR_0; + } + if (recno == 0) recno = RREF_ANY; + code[1+LINK_SIZE] = OP_RREF; /* Change test type */ + PUT2(code, 2+LINK_SIZE, recno); + } + + /* Similarly, check for the (?(DEFINE) "condition", which is always + false. */ + + else if (namelen == 6 && STRNCMP_UC_C8(name, STRING_DEFINE, 6) == 0) + { + code[1+LINK_SIZE] = OP_DEF; + skipbytes = 1; + } + + /* Check for the "name" actually being a subpattern number. We are + in the second pass here, so final_bracount is set. */ + + else if (recno > 0 && recno <= cd->final_bracount) + { + PUT2(code, 2+LINK_SIZE, recno); + } + + /* Either an unidentified subpattern, or a reference to (?(0) */ + + else + { + *errorcodeptr = (recno == 0)? ERR35: ERR15; + goto FAILED; + } + break; + + + /* ------------------------------------------------------------ */ + case CHAR_EQUALS_SIGN: /* Positive lookahead */ + bravalue = OP_ASSERT; + cd->assert_depth += 1; + ptr++; + break; + + + /* ------------------------------------------------------------ */ + case CHAR_EXCLAMATION_MARK: /* Negative lookahead */ + ptr++; + if (*ptr == CHAR_RIGHT_PARENTHESIS) /* Optimize (?!) */ + { + *code++ = OP_FAIL; + previous = NULL; + continue; + } + bravalue = OP_ASSERT_NOT; + cd->assert_depth += 1; + break; + + + /* ------------------------------------------------------------ */ + case CHAR_LESS_THAN_SIGN: /* Lookbehind or named define */ + switch (ptr[1]) + { + case CHAR_EQUALS_SIGN: /* Positive lookbehind */ + bravalue = OP_ASSERTBACK; + cd->assert_depth += 1; + ptr += 2; + break; + + case CHAR_EXCLAMATION_MARK: /* Negative lookbehind */ + bravalue = OP_ASSERTBACK_NOT; + cd->assert_depth += 1; + ptr += 2; + break; + + default: /* Could be name define, else bad */ + if (MAX_255(ptr[1]) && (cd->ctypes[ptr[1]] & ctype_word) != 0) + goto DEFINE_NAME; + ptr++; /* Correct offset for error */ + *errorcodeptr = ERR24; + goto FAILED; + } + break; + + + /* ------------------------------------------------------------ */ + case CHAR_GREATER_THAN_SIGN: /* One-time brackets */ + bravalue = OP_ONCE; + ptr++; + break; + + + /* ------------------------------------------------------------ */ + case CHAR_C: /* Callout - may be followed by digits; */ + previous_callout = code; /* Save for later completion */ + after_manual_callout = 1; /* Skip one item before completing */ + *code++ = OP_CALLOUT; + { + int n = 0; + ptr++; + while(IS_DIGIT(*ptr)) + n = n * 10 + *ptr++ - CHAR_0; + if (*ptr != CHAR_RIGHT_PARENTHESIS) + { + *errorcodeptr = ERR39; + goto FAILED; + } + if (n > 255) + { + *errorcodeptr = ERR38; + goto FAILED; + } + *code++ = n; + PUT(code, 0, (int)(ptr - cd->start_pattern + 1)); /* Pattern offset */ + PUT(code, LINK_SIZE, 0); /* Default length */ + code += 2 * LINK_SIZE; + } + previous = NULL; + continue; + + + /* ------------------------------------------------------------ */ + case CHAR_P: /* Python-style named subpattern handling */ + if (*(++ptr) == CHAR_EQUALS_SIGN || + *ptr == CHAR_GREATER_THAN_SIGN) /* Reference or recursion */ + { + is_recurse = *ptr == CHAR_GREATER_THAN_SIGN; + terminator = CHAR_RIGHT_PARENTHESIS; + goto NAMED_REF_OR_RECURSE; + } + else if (*ptr != CHAR_LESS_THAN_SIGN) /* Test for Python-style defn */ + { + *errorcodeptr = ERR41; + goto FAILED; + } + /* Fall through to handle (?P< as (?< is handled */ + + + /* ------------------------------------------------------------ */ + DEFINE_NAME: /* Come here from (?< handling */ + case CHAR_APOSTROPHE: + { + terminator = (*ptr == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; + name = ++ptr; + + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_word) != 0) ptr++; + namelen = (int)(ptr - name); + + /* In the pre-compile phase, just do a syntax check. */ + + if (lengthptr != NULL) + { + if (*ptr != terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + if (cd->names_found >= MAX_NAME_COUNT) + { + *errorcodeptr = ERR49; + goto FAILED; + } + if (namelen + IMM2_SIZE + 1 > cd->name_entry_size) + { + cd->name_entry_size = namelen + IMM2_SIZE + 1; + if (namelen > MAX_NAME_SIZE) + { + *errorcodeptr = ERR48; + goto FAILED; + } + } + } + + /* In the real compile, create the entry in the table, maintaining + alphabetical order. Duplicate names for different numbers are + permitted only if PCRE_DUPNAMES is set. Duplicate names for the same + number are always OK. (An existing number can be re-used if (?| + appears in the pattern.) In either event, a duplicate name results in + a duplicate entry in the table, even if the number is the same. This + is because the number of names, and hence the table size, is computed + in the pre-compile, and it affects various numbers and pointers which + would all have to be modified, and the compiled code moved down, if + duplicates with the same number were omitted from the table. This + doesn't seem worth the hassle. However, *different* names for the + same number are not permitted. */ + + else + { + BOOL dupname = FALSE; + slot = cd->name_table; + + for (i = 0; i < cd->names_found; i++) + { + int crc = memcmp(name, slot+IMM2_SIZE, IN_UCHARS(namelen)); + if (crc == 0) + { + if (slot[IMM2_SIZE+namelen] == 0) + { + if (GET2(slot, 0) != cd->bracount + 1 && + (options & PCRE_DUPNAMES) == 0) + { + *errorcodeptr = ERR43; + goto FAILED; + } + else dupname = TRUE; + } + else crc = -1; /* Current name is a substring */ + } + + /* Make space in the table and break the loop for an earlier + name. For a duplicate or later name, carry on. We do this for + duplicates so that in the simple case (when ?(| is not used) they + are in order of their numbers. */ + + if (crc < 0) + { + memmove(slot + cd->name_entry_size, slot, + IN_UCHARS((cd->names_found - i) * cd->name_entry_size)); + break; + } + + /* Continue the loop for a later or duplicate name */ + + slot += cd->name_entry_size; + } + + /* For non-duplicate names, check for a duplicate number before + adding the new name. */ + + if (!dupname) + { + pcre_uchar *cslot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + if (cslot != slot) + { + if (GET2(cslot, 0) == cd->bracount + 1) + { + *errorcodeptr = ERR65; + goto FAILED; + } + } + else i--; + cslot += cd->name_entry_size; + } + } + + PUT2(slot, 0, cd->bracount + 1); + memcpy(slot + IMM2_SIZE, name, IN_UCHARS(namelen)); + slot[IMM2_SIZE + namelen] = 0; + } + } + + /* In both pre-compile and compile, count the number of names we've + encountered. */ + + cd->names_found++; + ptr++; /* Move past > or ' */ + goto NUMBERED_GROUP; + + + /* ------------------------------------------------------------ */ + case CHAR_AMPERSAND: /* Perl recursion/subroutine syntax */ + terminator = CHAR_RIGHT_PARENTHESIS; + is_recurse = TRUE; + /* Fall through */ + + /* We come here from the Python syntax above that handles both + references (?P=name) and recursion (?P>name), as well as falling + through from the Perl recursion syntax (?&name). We also come here from + the Perl \k or \k'name' back reference syntax and the \k{name} + .NET syntax, and the Oniguruma \g<...> and \g'...' subroutine syntax. */ + + NAMED_REF_OR_RECURSE: + name = ++ptr; + while (MAX_255(*ptr) && (cd->ctypes[*ptr] & ctype_word) != 0) ptr++; + namelen = (int)(ptr - name); + + /* In the pre-compile phase, do a syntax check. We used to just set + a dummy reference number, because it was not used in the first pass. + However, with the change of recursive back references to be atomic, + we have to look for the number so that this state can be identified, as + otherwise the incorrect length is computed. If it's not a backwards + reference, the dummy number will do. */ + + if (lengthptr != NULL) + { + const pcre_uchar *temp; + + if (namelen == 0) + { + *errorcodeptr = ERR62; + goto FAILED; + } + if (*ptr != terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + if (namelen > MAX_NAME_SIZE) + { + *errorcodeptr = ERR48; + goto FAILED; + } + + /* The name table does not exist in the first pass, so we cannot + do a simple search as in the code below. Instead, we have to scan the + pattern to find the number. It is important that we scan it only as + far as we have got because the syntax of named subpatterns has not + been checked for the rest of the pattern, and find_parens() assumes + correct syntax. In any case, it's a waste of resources to scan + further. We stop the scan at the current point by temporarily + adjusting the value of cd->endpattern. */ + + temp = cd->end_pattern; + cd->end_pattern = ptr; + recno = find_parens(cd, name, namelen, + (options & PCRE_EXTENDED) != 0, utf); + cd->end_pattern = temp; + if (recno < 0) recno = 0; /* Forward ref; set dummy number */ + } + + /* In the real compile, seek the name in the table. We check the name + first, and then check that we have reached the end of the name in the + table. That way, if the name that is longer than any in the table, + the comparison will fail without reading beyond the table entry. */ + + else + { + slot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + if (STRNCMP_UC_UC(name, slot+IMM2_SIZE, namelen) == 0 && + slot[IMM2_SIZE+namelen] == 0) + break; + slot += cd->name_entry_size; + } + + if (i < cd->names_found) /* Back reference */ + { + recno = GET2(slot, 0); + } + else if ((recno = /* Forward back reference */ + find_parens(cd, name, namelen, + (options & PCRE_EXTENDED) != 0, utf)) <= 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + } + + /* In both phases, we can now go to the code than handles numerical + recursion or backreferences. */ + + if (is_recurse) goto HANDLE_RECURSION; + else goto HANDLE_REFERENCE; + + + /* ------------------------------------------------------------ */ + case CHAR_R: /* Recursion */ + ptr++; /* Same as (?0) */ + /* Fall through */ + + + /* ------------------------------------------------------------ */ + case CHAR_MINUS: case CHAR_PLUS: /* Recursion or subroutine */ + case CHAR_0: case CHAR_1: case CHAR_2: case CHAR_3: case CHAR_4: + case CHAR_5: case CHAR_6: case CHAR_7: case CHAR_8: case CHAR_9: + { + const pcre_uchar *called; + terminator = CHAR_RIGHT_PARENTHESIS; + + /* Come here from the \g<...> and \g'...' code (Oniguruma + compatibility). However, the syntax has been checked to ensure that + the ... are a (signed) number, so that neither ERR63 nor ERR29 will + be called on this path, nor with the jump to OTHER_CHAR_AFTER_QUERY + ever be taken. */ + + HANDLE_NUMERICAL_RECURSION: + + if ((refsign = *ptr) == CHAR_PLUS) + { + ptr++; + if (!IS_DIGIT(*ptr)) + { + *errorcodeptr = ERR63; + goto FAILED; + } + } + else if (refsign == CHAR_MINUS) + { + if (!IS_DIGIT(ptr[1])) + goto OTHER_CHAR_AFTER_QUERY; + ptr++; + } + + recno = 0; + while(IS_DIGIT(*ptr)) + recno = recno * 10 + *ptr++ - CHAR_0; + + if (*ptr != terminator) + { + *errorcodeptr = ERR29; + goto FAILED; + } + + if (refsign == CHAR_MINUS) + { + if (recno == 0) + { + *errorcodeptr = ERR58; + goto FAILED; + } + recno = cd->bracount - recno + 1; + if (recno <= 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + } + else if (refsign == CHAR_PLUS) + { + if (recno == 0) + { + *errorcodeptr = ERR58; + goto FAILED; + } + recno += cd->bracount; + } + + /* Come here from code above that handles a named recursion */ + + HANDLE_RECURSION: + + previous = code; + called = cd->start_code; + + /* When we are actually compiling, find the bracket that is being + referenced. Temporarily end the regex in case it doesn't exist before + this point. If we end up with a forward reference, first check that + the bracket does occur later so we can give the error (and position) + now. Then remember this forward reference in the workspace so it can + be filled in at the end. */ + + if (lengthptr == NULL) + { + *code = OP_END; + if (recno != 0) + called = PRIV(find_bracket)(cd->start_code, utf, recno); + + /* Forward reference */ + + if (called == NULL) + { + if (find_parens(cd, NULL, recno, + (options & PCRE_EXTENDED) != 0, utf) < 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + + /* Fudge the value of "called" so that when it is inserted as an + offset below, what it actually inserted is the reference number + of the group. Then remember the forward reference. */ + + called = cd->start_code + recno; + if (cd->hwm >= cd->start_workspace + cd->workspace_size - + WORK_SIZE_SAFETY_MARGIN) + { + *errorcodeptr = expand_workspace(cd); + if (*errorcodeptr != 0) goto FAILED; + } + PUTINC(cd->hwm, 0, (int)(code + 1 - cd->start_code)); + } + + /* If not a forward reference, and the subpattern is still open, + this is a recursive call. We check to see if this is a left + recursion that could loop for ever, and diagnose that case. We + must not, however, do this check if we are in a conditional + subpattern because the condition might be testing for recursion in + a pattern such as /(?(R)a+|(?R)b)/, which is perfectly valid. + Forever loops are also detected at runtime, so those that occur in + conditional subpatterns will be picked up then. */ + + else if (GET(called, 1) == 0 && cond_depth <= 0 && + could_be_empty(called, code, bcptr, utf, cd)) + { + *errorcodeptr = ERR40; + goto FAILED; + } + } + + /* Insert the recursion/subroutine item. It does not have a set first + character (relevant if it is repeated, because it will then be + wrapped with ONCE brackets). */ + + *code = OP_RECURSE; + PUT(code, 1, (int)(called - cd->start_code)); + code += 1 + LINK_SIZE; + groupsetfirstchar = FALSE; + } + + /* Can't determine a first byte now */ + + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + continue; + + + /* ------------------------------------------------------------ */ + default: /* Other characters: check option setting */ + OTHER_CHAR_AFTER_QUERY: + set = unset = 0; + optset = &set; + + while (*ptr != CHAR_RIGHT_PARENTHESIS && *ptr != CHAR_COLON) + { + switch (*ptr++) + { + case CHAR_MINUS: optset = &unset; break; + + case CHAR_J: /* Record that it changed in the external options */ + *optset |= PCRE_DUPNAMES; + cd->external_flags |= PCRE_JCHANGED; + break; + + case CHAR_i: *optset |= PCRE_CASELESS; break; + case CHAR_m: *optset |= PCRE_MULTILINE; break; + case CHAR_s: *optset |= PCRE_DOTALL; break; + case CHAR_x: *optset |= PCRE_EXTENDED; break; + case CHAR_U: *optset |= PCRE_UNGREEDY; break; + case CHAR_X: *optset |= PCRE_EXTRA; break; + + default: *errorcodeptr = ERR12; + ptr--; /* Correct the offset */ + goto FAILED; + } + } + + /* Set up the changed option bits, but don't change anything yet. */ + + newoptions = (options | set) & (~unset); + + /* If the options ended with ')' this is not the start of a nested + group with option changes, so the options change at this level. If this + item is right at the start of the pattern, the options can be + abstracted and made external in the pre-compile phase, and ignored in + the compile phase. This can be helpful when matching -- for instance in + caseless checking of required bytes. + + If the code pointer is not (cd->start_code + 1 + LINK_SIZE), we are + definitely *not* at the start of the pattern because something has been + compiled. In the pre-compile phase, however, the code pointer can have + that value after the start, because it gets reset as code is discarded + during the pre-compile. However, this can happen only at top level - if + we are within parentheses, the starting BRA will still be present. At + any parenthesis level, the length value can be used to test if anything + has been compiled at that level. Thus, a test for both these conditions + is necessary to ensure we correctly detect the start of the pattern in + both phases. + + If we are not at the pattern start, reset the greedy defaults and the + case value for firstchar and reqchar. */ + + if (*ptr == CHAR_RIGHT_PARENTHESIS) + { + if (code == cd->start_code + 1 + LINK_SIZE && + (lengthptr == NULL || *lengthptr == 2 + 2*LINK_SIZE)) + { + cd->external_options = newoptions; + } + else + { + greedy_default = ((newoptions & PCRE_UNGREEDY) != 0); + greedy_non_default = greedy_default ^ 1; + req_caseopt = ((newoptions & PCRE_CASELESS) != 0)? REQ_CASELESS:0; + } + + /* Change options at this level, and pass them back for use + in subsequent branches. */ + + *optionsptr = options = newoptions; + previous = NULL; /* This item can't be repeated */ + continue; /* It is complete */ + } + + /* If the options ended with ':' we are heading into a nested group + with possible change of options. Such groups are non-capturing and are + not assertions of any kind. All we need to do is skip over the ':'; + the newoptions value is handled below. */ + + bravalue = OP_BRA; + ptr++; + } /* End of switch for character following (? */ + } /* End of (? handling */ + + /* Opening parenthesis not followed by '*' or '?'. If PCRE_NO_AUTO_CAPTURE + is set, all unadorned brackets become non-capturing and behave like (?:...) + brackets. */ + + else if ((options & PCRE_NO_AUTO_CAPTURE) != 0) + { + bravalue = OP_BRA; + } + + /* Else we have a capturing group. */ + + else + { + NUMBERED_GROUP: + cd->bracount += 1; + PUT2(code, 1+LINK_SIZE, cd->bracount); + skipbytes = IMM2_SIZE; + } + + /* Process nested bracketed regex. Assertions used not to be repeatable, + but this was changed for Perl compatibility, so all kinds can now be + repeated. We copy code into a non-register variable (tempcode) in order to + be able to pass its address because some compilers complain otherwise. */ + + previous = code; /* For handling repetition */ + *code = bravalue; + tempcode = code; + tempreqvary = cd->req_varyopt; /* Save value before bracket */ + tempbracount = cd->bracount; /* Save value before bracket */ + length_prevgroup = 0; /* Initialize for pre-compile phase */ + + if (!compile_regex( + newoptions, /* The complete new option state */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + errorcodeptr, /* Where to put an error message */ + (bravalue == OP_ASSERTBACK || + bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ + reset_bracount, /* True if (?| group */ + skipbytes, /* Skip over bracket number */ + cond_depth + + ((bravalue == OP_COND)?1:0), /* Depth of condition subpatterns */ + &subfirstchar, /* For possible first char */ + &subreqchar, /* For possible last char */ + bcptr, /* Current branch chain */ + cd, /* Tables block */ + (lengthptr == NULL)? NULL : /* Actual compile phase */ + &length_prevgroup /* Pre-compile phase */ + )) + goto FAILED; + + /* If this was an atomic group and there are no capturing groups within it, + generate OP_ONCE_NC instead of OP_ONCE. */ + + if (bravalue == OP_ONCE && cd->bracount <= tempbracount) + *code = OP_ONCE_NC; + + if (bravalue >= OP_ASSERT && bravalue <= OP_ASSERTBACK_NOT) + cd->assert_depth -= 1; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group. + The pattern pointer (ptr) is on the bracket. + + If this is a conditional bracket, check that there are no more than + two branches in the group, or just one if it's a DEFINE group. We do this + in the real compile phase, not in the pre-pass, where the whole group may + not be available. */ + + if (bravalue == OP_COND && lengthptr == NULL) + { + pcre_uchar *tc = code; + int condcount = 0; + + do { + condcount++; + tc += GET(tc,1); + } + while (*tc != OP_KET); + + /* A DEFINE group is never obeyed inline (the "condition" is always + false). It must have only one branch. */ + + if (code[LINK_SIZE+1] == OP_DEF) + { + if (condcount > 1) + { + *errorcodeptr = ERR54; + goto FAILED; + } + bravalue = OP_DEF; /* Just a flag to suppress char handling below */ + } + + /* A "normal" conditional group. If there is just one branch, we must not + make use of its firstchar or reqchar, because this is equivalent to an + empty second branch. */ + + else + { + if (condcount > 2) + { + *errorcodeptr = ERR27; + goto FAILED; + } + if (condcount == 1) subfirstchar = subreqchar = REQ_NONE; + } + } + + /* Error if hit end of pattern */ + + if (*ptr != CHAR_RIGHT_PARENTHESIS) + { + *errorcodeptr = ERR14; + goto FAILED; + } + + /* In the pre-compile phase, update the length by the length of the group, + less the brackets at either end. Then reduce the compiled code to just a + set of non-capturing brackets so that it doesn't use much memory if it is + duplicated by a quantifier.*/ + + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; + code++; /* This already contains bravalue */ + PUTINC(code, 0, 1 + LINK_SIZE); + *code++ = OP_KET; + PUTINC(code, 0, 1 + LINK_SIZE); + break; /* No need to waste time with special character handling */ + } + + /* Otherwise update the main code pointer to the end of the group. */ + + code = tempcode; + + /* For a DEFINE group, required and first character settings are not + relevant. */ + + if (bravalue == OP_DEF) break; + + /* Handle updating of the required and first characters for other types of + group. Update for normal brackets of all kinds, and conditions with two + branches (see code above). If the bracket is followed by a quantifier with + zero repeat, we have to back off. Hence the definition of zeroreqchar and + zerofirstchar outside the main loop so that they can be accessed for the + back off. */ + + zeroreqchar = reqchar; + zerofirstchar = firstchar; + groupsetfirstchar = FALSE; + + if (bravalue >= OP_ONCE) + { + /* If we have not yet set a firstchar in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqchar if necessary. If the subpattern has + no firstchar, set "none" for the whole branch. In both cases, a zero + repeat forces firstchar to "none". */ + + if (firstchar == REQ_UNSET) + { + if (subfirstchar >= 0) + { + firstchar = subfirstchar; + groupsetfirstchar = TRUE; + } + else firstchar = REQ_NONE; + zerofirstchar = REQ_NONE; + } + + /* If firstchar was previously set, convert the subpattern's firstchar + into reqchar if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subfirstchar >= 0 && subreqchar < 0) + subreqchar = subfirstchar | tempreqvary; + + /* If the subpattern set a required byte (or set a first byte that isn't + really the first byte - see above), set it. */ + + if (subreqchar >= 0) reqchar = subreqchar; + } + + /* For a forward assertion, we take the reqchar, if set. This can be + helpful if the pattern that follows the assertion doesn't set a different + char. For example, it's useful for /(?=abcde).+/. We can't set firstchar + for an assertion, however because it leads to incorrect effect for patterns + such as /(?=a)a.+/ when the "real" "a" would then become a reqchar instead + of a firstchar. This is overcome by a scan at the end if there's no + firstchar, looking for an asserted first char. */ + + else if (bravalue == OP_ASSERT && subreqchar >= 0) reqchar = subreqchar; + break; /* End of processing '(' */ + + + /* ===================================================================*/ + /* Handle metasequences introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values in the + default case when PCRE_UCP is not set. For the back references, the values + are ESC_REF plus the reference number. Only back references and those types + that consume a character may be repeated. We can test for values between + ESC_b and ESC_Z for the latter; this may have to change if any new ones are + ever created. */ + + case CHAR_BACKSLASH: + tempptr = ptr; + c = check_escape(&ptr, errorcodeptr, cd->bracount, options, FALSE); + if (*errorcodeptr != 0) goto FAILED; + + if (c < 0) + { + if (-c == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == CHAR_BACKSLASH && ptr[2] == CHAR_E) + ptr += 2; /* avoid empty string */ + else inescq = TRUE; + continue; + } + + if (-c == ESC_E) continue; /* Perl ignores an orphan \E */ + + /* For metasequences that actually match a character, we disable the + setting of a first character if it hasn't already been set. */ + + if (firstchar == REQ_UNSET && -c > ESC_b && -c < ESC_Z) + firstchar = REQ_NONE; + + /* Set values to reset to if this is followed by a zero repeat. */ + + zerofirstchar = firstchar; + zeroreqchar = reqchar; + + /* \g or \g'name' is a subroutine call by name and \g or \g'n' + is a subroutine call by number (Oniguruma syntax). In fact, the value + -ESC_g is returned only for these cases. So we don't need to check for < + or ' if the value is -ESC_g. For the Perl syntax \g{n} the value is + -ESC_REF+n, and for the Perl syntax \g{name} the result is -ESC_k (as + that is a synonym for a named back reference). */ + + if (-c == ESC_g) + { + const pcre_uchar *p; + save_hwm = cd->hwm; /* Normally this is set when '(' is read */ + terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : CHAR_APOSTROPHE; + + /* These two statements stop the compiler for warning about possibly + unset variables caused by the jump to HANDLE_NUMERICAL_RECURSION. In + fact, because we actually check for a number below, the paths that + would actually be in error are never taken. */ + + skipbytes = 0; + reset_bracount = FALSE; + + /* Test for a name */ + + if (ptr[1] != CHAR_PLUS && ptr[1] != CHAR_MINUS) + { + BOOL is_a_number = TRUE; + for (p = ptr + 1; *p != 0 && *p != terminator; p++) + { + if (!MAX_255(*p)) { is_a_number = FALSE; break; } + if ((cd->ctypes[*p] & ctype_digit) == 0) is_a_number = FALSE; + if ((cd->ctypes[*p] & ctype_word) == 0) break; + } + if (*p != terminator) + { + *errorcodeptr = ERR57; + break; + } + if (is_a_number) + { + ptr++; + goto HANDLE_NUMERICAL_RECURSION; + } + is_recurse = TRUE; + goto NAMED_REF_OR_RECURSE; + } + + /* Test a signed number in angle brackets or quotes. */ + + p = ptr + 2; + while (IS_DIGIT(*p)) p++; + if (*p != terminator) + { + *errorcodeptr = ERR57; + break; + } + ptr++; + goto HANDLE_NUMERICAL_RECURSION; + } + + /* \k or \k'name' is a back reference by name (Perl syntax). + We also support \k{name} (.NET syntax). */ + + if (-c == ESC_k) + { + if ((ptr[1] != CHAR_LESS_THAN_SIGN && + ptr[1] != CHAR_APOSTROPHE && ptr[1] != CHAR_LEFT_CURLY_BRACKET)) + { + *errorcodeptr = ERR69; + break; + } + is_recurse = FALSE; + terminator = (*(++ptr) == CHAR_LESS_THAN_SIGN)? + CHAR_GREATER_THAN_SIGN : (*ptr == CHAR_APOSTROPHE)? + CHAR_APOSTROPHE : CHAR_RIGHT_CURLY_BRACKET; + goto NAMED_REF_OR_RECURSE; + } + + /* Back references are handled specially; must disable firstchar if + not set to cope with cases like (?=(\w+))\1: which would otherwise set + ':' later. */ + + if (-c >= ESC_REF) + { + open_capitem *oc; + recno = -c - ESC_REF; + + HANDLE_REFERENCE: /* Come here from named backref handling */ + if (firstchar == REQ_UNSET) firstchar = REQ_NONE; + previous = code; + *code++ = ((options & PCRE_CASELESS) != 0)? OP_REFI : OP_REF; + PUT2INC(code, 0, recno); + cd->backref_map |= (recno < 32)? (1 << recno) : 1; + if (recno > cd->top_backref) cd->top_backref = recno; + + /* Check to see if this back reference is recursive, that it, it + is inside the group that it references. A flag is set so that the + group can be made atomic. */ + + for (oc = cd->open_caps; oc != NULL; oc = oc->next) + { + if (oc->number == recno) + { + oc->flag = TRUE; + break; + } + } + } + + /* So are Unicode property matches, if supported. */ + +#ifdef SUPPORT_UCP + else if (-c == ESC_P || -c == ESC_p) + { + BOOL negated; + int pdata; + int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr); + if (ptype < 0) goto FAILED; + previous = code; + *code++ = ((-c == ESC_p) != negated)? OP_PROP : OP_NOTPROP; + *code++ = ptype; + *code++ = pdata; + } +#else + + /* If Unicode properties are not supported, \X, \P, and \p are not + allowed. */ + + else if (-c == ESC_X || -c == ESC_P || -c == ESC_p) + { + *errorcodeptr = ERR45; + goto FAILED; + } +#endif + + /* For the rest (including \X when Unicode properties are supported), we + can obtain the OP value by negating the escape value in the default + situation when PCRE_UCP is not set. When it *is* set, we substitute + Unicode property tests. */ + + else + { +#ifdef SUPPORT_UCP + if (-c >= ESC_DU && -c <= ESC_wu) + { + nestptr = ptr + 1; /* Where to resume */ + ptr = substitutes[-c - ESC_DU] - 1; /* Just before substitute */ + } + else +#endif + /* In non-UTF-8 mode, we turn \C into OP_ALLANY instead of OP_ANYBYTE + so that it works in DFA mode and in lookbehinds. */ + + { + previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; + *code++ = (!utf && c == -ESC_C)? OP_ALLANY : -c; + } + } + continue; + } + + /* We have a data character whose value is in c. In UTF-8 mode it may have + a value > 127. We set its representation in the length/buffer, and then + handle it as a data character. */ + +#ifdef SUPPORT_UTF + if (utf && c > MAX_VALUE_FOR_SINGLE_CHAR) + mclength = PRIV(ord2utf)(c, mcbuffer); + else +#endif + + { + mcbuffer[0] = c; + mclength = 1; + } + goto ONE_CHAR; + + + /* ===================================================================*/ + /* Handle a literal character. It is guaranteed not to be whitespace or # + when the extended flag is set. If we are in UTF-8 mode, it may be a + multi-byte literal character. */ + + default: + NORMAL_CHAR: + mclength = 1; + mcbuffer[0] = c; + +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(c)) + ACROSSCHAR(TRUE, ptr[1], mcbuffer[mclength++] = *(++ptr)); +#endif + + /* At this point we have the character's bytes in mcbuffer, and the length + in mclength. When not in UTF-8 mode, the length is always 1. */ + + ONE_CHAR: + previous = code; + *code++ = ((options & PCRE_CASELESS) != 0)? OP_CHARI : OP_CHAR; + for (c = 0; c < mclength; c++) *code++ = mcbuffer[c]; + + /* Remember if \r or \n were seen */ + + if (mcbuffer[0] == CHAR_CR || mcbuffer[0] == CHAR_NL) + cd->external_flags |= PCRE_HASCRORLF; + + /* Set the first and required bytes appropriately. If no previous first + byte, set it from this character, but revert to none on a zero repeat. + Otherwise, leave the firstchar value alone, and don't change it on a zero + repeat. */ + + if (firstchar == REQ_UNSET) + { + zerofirstchar = REQ_NONE; + zeroreqchar = reqchar; + + /* If the character is more than one byte long, we can set firstchar + only if it is not to be matched caselessly. */ + + if (mclength == 1 || req_caseopt == 0) + { + firstchar = mcbuffer[0] | req_caseopt; + if (mclength != 1) reqchar = code[-1] | cd->req_varyopt; + } + else firstchar = reqchar = REQ_NONE; + } + + /* firstchar was previously set; we can set reqchar only if the length is + 1 or the matching is caseful. */ + + else + { + zerofirstchar = firstchar; + zeroreqchar = reqchar; + if (mclength == 1 || req_caseopt == 0) + reqchar = code[-1] | req_caseopt | cd->req_varyopt; + } + + break; /* End of literal character handling */ + } + } /* end of big loop */ + + +/* Control never reaches here by falling through, only by a goto for all the +error states. Pass back the position in the pattern so that it can be displayed +to the user for diagnosing the error. */ + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return it +points to the closing bracket, or vertical bar, or end of string. The code +variable is pointing at the byte into which the BRA operator has been stored. +This function is used during the pre-compile phase when we are trying to find +out the amount of memory needed, as well as during the real compile phase. The +value of lengthptr distinguishes the two phases. + +Arguments: + options option bits, including any changes for this subpattern + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorcodeptr -> pointer to error code variable + lookbehind TRUE if this is a lookbehind assertion + reset_bracount TRUE to reset the count for each branch + skipbytes skip this many bytes at start (for brackets and OP_COND) + cond_depth depth of nesting for conditional subpatterns + firstcharptr place to put the first required character, or a negative number + reqcharptr place to put the last required character, or a negative number + bcptr pointer to the chain of currently open branches + cd points to the data block with tables pointers etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: TRUE on success +*/ + +static BOOL +compile_regex(int options, pcre_uchar **codeptr, const pcre_uchar **ptrptr, + int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, int skipbytes, + int cond_depth, pcre_int32 *firstcharptr, pcre_int32 *reqcharptr, + branch_chain *bcptr, compile_data *cd, int *lengthptr) +{ +const pcre_uchar *ptr = *ptrptr; +pcre_uchar *code = *codeptr; +pcre_uchar *last_branch = code; +pcre_uchar *start_bracket = code; +pcre_uchar *reverse_count = NULL; +open_capitem capitem; +int capnumber = 0; +pcre_int32 firstchar, reqchar; +pcre_int32 branchfirstchar, branchreqchar; +int length; +int orig_bracount; +int max_bracount; +branch_chain bc; + +bc.outer = bcptr; +bc.current_branch = code; + +firstchar = reqchar = REQ_UNSET; + +/* Accumulate the length for use in the pre-compile phase. Start with the +length of the BRA and KET and any extra bytes that are required at the +beginning. We accumulate in a local variable to save frequent testing of +lenthptr for NULL. We cannot do this by looking at the value of code at the +start and end of each alternative, because compiled items are discarded during +the pre-compile phase so that the work space is not exceeded. */ + +length = 2 + 2*LINK_SIZE + skipbytes; + +/* WARNING: If the above line is changed for any reason, you must also change +the code that abstracts option settings at the start of the pattern and makes +them global. It tests the value of length for (2 + 2*LINK_SIZE) in the +pre-compile phase to find out whether anything has yet been compiled or not. */ + +/* If this is a capturing subpattern, add to the chain of open capturing items +so that we can detect them if (*ACCEPT) is encountered. This is also used to +detect groups that contain recursive back references to themselves. Note that +only OP_CBRA need be tested here; changing this opcode to one of its variants, +e.g. OP_SCBRAPOS, happens later, after the group has been compiled. */ + +if (*code == OP_CBRA) + { + capnumber = GET2(code, 1 + LINK_SIZE); + capitem.number = capnumber; + capitem.next = cd->open_caps; + capitem.flag = FALSE; + cd->open_caps = &capitem; + } + +/* Offset is set zero to mark that this bracket is still open */ + +PUT(code, 1, 0); +code += 1 + LINK_SIZE + skipbytes; + +/* Loop for each alternative branch */ + +orig_bracount = max_bracount = cd->bracount; +for (;;) + { + /* For a (?| group, reset the capturing bracket count so that each branch + uses the same numbers. */ + + if (reset_bracount) cd->bracount = orig_bracount; + + /* Set up dummy OP_REVERSE if lookbehind assertion */ + + if (lookbehind) + { + *code++ = OP_REVERSE; + reverse_count = code; + PUTINC(code, 0, 0); + length += 1 + LINK_SIZE; + } + + /* Now compile the branch; in the pre-compile phase its length gets added + into the length. */ + + if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstchar, + &branchreqchar, &bc, cond_depth, cd, + (lengthptr == NULL)? NULL : &length)) + { + *ptrptr = ptr; + return FALSE; + } + + /* Keep the highest bracket count in case (?| was used and some branch + has fewer than the rest. */ + + if (cd->bracount > max_bracount) max_bracount = cd->bracount; + + /* In the real compile phase, there is some post-processing to be done. */ + + if (lengthptr == NULL) + { + /* If this is the first branch, the firstchar and reqchar values for the + branch become the values for the regex. */ + + if (*last_branch != OP_ALT) + { + firstchar = branchfirstchar; + reqchar = branchreqchar; + } + + /* If this is not the first branch, the first char and reqchar have to + match the values from all the previous branches, except that if the + previous value for reqchar didn't have REQ_VARY set, it can still match, + and we set REQ_VARY for the regex. */ + + else + { + /* If we previously had a firstchar, but it doesn't match the new branch, + we have to abandon the firstchar for the regex, but if there was + previously no reqchar, it takes on the value of the old firstchar. */ + + if (firstchar >= 0 && firstchar != branchfirstchar) + { + if (reqchar < 0) reqchar = firstchar; + firstchar = REQ_NONE; + } + + /* If we (now or from before) have no firstchar, a firstchar from the + branch becomes a reqchar if there isn't a branch reqchar. */ + + if (firstchar < 0 && branchfirstchar >= 0 && branchreqchar < 0) + branchreqchar = branchfirstchar; + + /* Now ensure that the reqchars match */ + + if ((reqchar & ~REQ_VARY) != (branchreqchar & ~REQ_VARY)) + reqchar = REQ_NONE; + else reqchar |= branchreqchar; /* To "or" REQ_VARY */ + } + + /* If lookbehind, check that this branch matches a fixed-length string, and + put the length into the OP_REVERSE item. Temporarily mark the end of the + branch with OP_END. If the branch contains OP_RECURSE, the result is -3 + because there may be forward references that we can't check here. Set a + flag to cause another lookbehind check at the end. Why not do it all at the + end? Because common, erroneous checks are picked up here and the offset of + the problem can be shown. */ + + if (lookbehind) + { + int fixed_length; + *code = OP_END; + fixed_length = find_fixedlength(last_branch, (options & PCRE_UTF8) != 0, + FALSE, cd); + DPRINTF(("fixed length = %d\n", fixed_length)); + if (fixed_length == -3) + { + cd->check_lookbehind = TRUE; + } + else if (fixed_length < 0) + { + *errorcodeptr = (fixed_length == -2)? ERR36 : + (fixed_length == -4)? ERR70: ERR25; + *ptrptr = ptr; + return FALSE; + } + else { PUT(reverse_count, 0, fixed_length); } + } + } + + /* Reached end of expression, either ')' or end of pattern. In the real + compile phase, go back through the alternative branches and reverse the chain + of offsets, with the field in the BRA item now becoming an offset to the + first alternative. If there are no alternatives, it points to the end of the + group. The length in the terminating ket is always the length of the whole + bracketed item. Return leaving the pointer at the terminating char. */ + + if (*ptr != CHAR_VERTICAL_LINE) + { + if (lengthptr == NULL) + { + int branch_length = (int)(code - last_branch); + do + { + int prev_length = GET(last_branch, 1); + PUT(last_branch, 1, branch_length); + branch_length = prev_length; + last_branch -= branch_length; + } + while (branch_length > 0); + } + + /* Fill in the ket */ + + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + code += 1 + LINK_SIZE; + + /* If it was a capturing subpattern, check to see if it contained any + recursive back references. If so, we must wrap it in atomic brackets. + In any event, remove the block from the chain. */ + + if (capnumber > 0) + { + if (cd->open_caps->flag) + { + memmove(start_bracket + 1 + LINK_SIZE, start_bracket, + IN_UCHARS(code - start_bracket)); + *start_bracket = OP_ONCE; + code += 1 + LINK_SIZE; + PUT(start_bracket, 1, (int)(code - start_bracket)); + *code = OP_KET; + PUT(code, 1, (int)(code - start_bracket)); + code += 1 + LINK_SIZE; + length += 2 + 2*LINK_SIZE; + } + cd->open_caps = cd->open_caps->next; + } + + /* Retain the highest bracket number, in case resetting was used. */ + + cd->bracount = max_bracount; + + /* Set values to pass back */ + + *codeptr = code; + *ptrptr = ptr; + *firstcharptr = firstchar; + *reqcharptr = reqchar; + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length) + { + *errorcodeptr = ERR20; + return FALSE; + } + *lengthptr += length; + } + return TRUE; + } + + /* Another branch follows. In the pre-compile phase, we can move the code + pointer back to where it was for the start of the first branch. (That is, + pretend that each branch is the only one.) + + In the real compile phase, insert an ALT node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + if (lengthptr != NULL) + { + code = *codeptr + 1 + LINK_SIZE + skipbytes; + length += 1 + LINK_SIZE; + } + else + { + *code = OP_ALT; + PUT(code, 1, (int)(code - last_branch)); + bc.current_branch = last_branch = code; + code += 1 + LINK_SIZE; + } + + ptr++; + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD will +be found, because ^ generates OP_CIRCM in that mode. + +We can also consider a regex to be anchored if OP_SOM starts all its branches. +This is the code for \G, which means "match at start of match position, taking +into account the match offset". + +A branch is also implicitly anchored if it starts with .* and DOTALL is set, +because that will try the rest of the pattern at all possible matching points, +so there is no point trying again.... er .... + +.... except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. We haven't enough information +to catch that case precisely. + +At first, the best we could do was to detect when .* was in capturing brackets +and the highest back reference was greater than or equal to that level. +However, by keeping a bitmap of the first 31 back references, we can catch some +of the more common cases more precisely. + +Arguments: + code points to start of expression (the bracket) + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + backref_map the back reference bitmap + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(register const pcre_uchar *code, unsigned int bracket_map, + unsigned int backref_map) +{ +do { + const pcre_uchar *scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); + register int op = *scode; + + /* Non-capturing brackets */ + + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) + { + if (!is_anchored(scode, bracket_map, backref_map)) return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1 << n) : 1); + if (!is_anchored(scode, new_map, backref_map)) return FALSE; + } + + /* Other brackets */ + + else if (op == OP_ASSERT || op == OP_ONCE || op == OP_ONCE_NC || + op == OP_COND) + { + if (!is_anchored(scode, bracket_map, backref_map)) return FALSE; + } + + /* .* is not anchored unless DOTALL is set (which generates OP_ALLANY) and + it isn't in brackets that are or may be referenced. */ + + else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || + op == OP_TYPEPOSSTAR)) + { + if (scode[1] != OP_ALLANY || (bracket_map & backref_map) != 0) + return FALSE; + } + + /* Check for explicit anchoring */ + + else if (op != OP_SOD && op != OP_SOM && op != OP_CIRC) return FALSE; + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n). As in the case of is_anchored() (see above), we +have to take account of back references to capturing brackets that contain .* +because in that case we can't make the assumption. + +Arguments: + code points to start of expression (the bracket) + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + backref_map the back reference bitmap + +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(const pcre_uchar *code, unsigned int bracket_map, + unsigned int backref_map) +{ +do { + const pcre_uchar *scode = first_significant_code( + code + PRIV(OP_lengths)[*code], FALSE); + register int op = *scode; + + /* If we are at the start of a conditional assertion group, *both* the + conditional assertion *and* what follows the condition must satisfy the test + for start of line. Other kinds of condition fail. Note that there may be an + auto-callout at the start of a condition. */ + + if (op == OP_COND) + { + scode += 1 + LINK_SIZE; + if (*scode == OP_CALLOUT) scode += PRIV(OP_lengths)[OP_CALLOUT]; + switch (*scode) + { + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + return FALSE; + + default: /* Assertion */ + if (!is_startline(scode, bracket_map, backref_map)) return FALSE; + do scode += GET(scode, 1); while (*scode == OP_ALT); + scode += 1 + LINK_SIZE; + break; + } + scode = first_significant_code(scode, FALSE); + op = *scode; + } + + /* Non-capturing brackets */ + + if (op == OP_BRA || op == OP_BRAPOS || + op == OP_SBRA || op == OP_SBRAPOS) + { + if (!is_startline(scode, bracket_map, backref_map)) return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA || op == OP_CBRAPOS || + op == OP_SCBRA || op == OP_SCBRAPOS) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1 << n) : 1); + if (!is_startline(scode, new_map, backref_map)) return FALSE; + } + + /* Other brackets */ + + else if (op == OP_ASSERT || op == OP_ONCE || op == OP_ONCE_NC) + { + if (!is_startline(scode, bracket_map, backref_map)) return FALSE; + } + + /* .* means "start at start or after \n" if it isn't in brackets that + may be referenced. */ + + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) + { + if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + } + + /* Check for explicit circumflex */ + + else if (op != OP_CIRC && op != OP_CIRCM) return FALSE; + + /* Move on to the next alternative */ + + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for asserted fixed first char * +*************************************************/ + +/* During compilation, the "first char" settings from forward assertions are +discarded, because they can cause conflicts with actual literals that follow. +However, if we end up without a first char setting for an unanchored pattern, +it is worth scanning the regex to see if there is an initial asserted first +char. If all branches start with the same asserted char, or with a bracket all +of whose alternatives start with the same asserted char (recurse ad lib), then +we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + inassert TRUE if in an assertion + +Returns: -1 or the fixed first char +*/ + +static int +find_firstassertedchar(const pcre_uchar *code, BOOL inassert) +{ +register int c = -1; +do { + int d; + int xl = (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS)? IMM2_SIZE:0; + const pcre_uchar *scode = first_significant_code(code + 1+LINK_SIZE + xl, + TRUE); + register int op = *scode; + + switch(op) + { + default: + return -1; + + case OP_BRA: + case OP_BRAPOS: + case OP_CBRA: + case OP_SCBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_ASSERT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_COND: + if ((d = find_firstassertedchar(scode, op == OP_ASSERT)) < 0) + return -1; + if (c < 0) c = d; else if (c != d) return -1; + break; + + case OP_EXACT: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + if (!inassert) return -1; + if (c < 0) c = scode[1]; + else if (c != scode[1]) return -1; + break; + + case OP_EXACTI: + scode += IMM2_SIZE; + /* Fall through */ + + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + if (!inassert) return -1; + if (c < 0) c = scode[1] | REQ_CASELESS; + else if (c != scode[1]) return -1; + break; + } + + code += GET(code, 1); + } +while (*code == OP_ALT); +return c; +} + + + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. The original API for this +function had no error code return variable; it is retained for backwards +compatibility. The new function is given a new name. + +Arguments: + pattern the regular expression + options various option bits + errorcodeptr pointer to error code variable (pcre_compile2() only) + can be NULL if you don't want a code value + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION +pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +#else +PCRE_EXP_DEFN pcre16 * PCRE_CALL_CONVENTION +pcre16_compile(PCRE_SPTR16 pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +#endif +{ +#ifdef COMPILE_PCRE8 +return pcre_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#else +return pcre16_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +#endif +} + + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN pcre * PCRE_CALL_CONVENTION +pcre_compile2(const char *pattern, int options, int *errorcodeptr, + const char **errorptr, int *erroroffset, const unsigned char *tables) +#else +PCRE_EXP_DEFN pcre16 * PCRE_CALL_CONVENTION +pcre16_compile2(PCRE_SPTR16 pattern, int options, int *errorcodeptr, + const char **errorptr, int *erroroffset, const unsigned char *tables) +#endif +{ +REAL_PCRE *re; +int length = 1; /* For final END opcode */ +pcre_int32 firstchar, reqchar; +int newline; +int errorcode = 0; +int skipatstart = 0; +BOOL utf; +size_t size; +pcre_uchar *code; +const pcre_uchar *codestart; +const pcre_uchar *ptr; +compile_data compile_block; +compile_data *cd = &compile_block; + +/* This space is used for "compiling" into during the first phase, when we are +computing the amount of memory that is needed. Compiled items are thrown away +as soon as possible, so that a fairly large buffer should be sufficient for +this purpose. The same space is used in the second phase for remembering where +to fill in forward references to subpatterns. That may overflow, in which case +new memory is obtained from malloc(). */ + +pcre_uchar cworkspace[COMPILE_WORK_SIZE]; + +/* Set this early so that early errors get offset 0. */ + +ptr = (const pcre_uchar *)pattern; + +/* We can't pass back an error message if errorptr is NULL; I guess the best we +can do is just return NULL, but we can set a code value if there is a code +pointer. */ + +if (errorptr == NULL) + { + if (errorcodeptr != NULL) *errorcodeptr = 99; + return NULL; + } + +*errorptr = NULL; +if (errorcodeptr != NULL) *errorcodeptr = ERR0; + +/* However, we can give a message for this error */ + +if (erroroffset == NULL) + { + errorcode = ERR16; + goto PCRE_EARLY_ERROR_RETURN2; + } + +*erroroffset = 0; + +/* Set up pointers to the individual character tables */ + +if (tables == NULL) tables = PRIV(default_tables); +cd->lcc = tables + lcc_offset; +cd->fcc = tables + fcc_offset; +cd->cbits = tables + cbits_offset; +cd->ctypes = tables + ctypes_offset; + +/* Check that all undefined public option bits are zero */ + +if ((options & ~PUBLIC_COMPILE_OPTIONS) != 0) + { + errorcode = ERR17; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Check for global one-time settings at the start of the pattern, and remember +the offset for later. */ + +while (ptr[skipatstart] == CHAR_LEFT_PARENTHESIS && + ptr[skipatstart+1] == CHAR_ASTERISK) + { + int newnl = 0; + int newbsr = 0; + +#ifdef COMPILE_PCRE8 + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UTF_RIGHTPAR, 5) == 0) + { skipatstart += 7; options |= PCRE_UTF8; continue; } +#endif +#ifdef COMPILE_PCRE16 + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UTF_RIGHTPAR, 6) == 0) + { skipatstart += 8; options |= PCRE_UTF16; continue; } +#endif + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_UCP_RIGHTPAR, 4) == 0) + { skipatstart += 6; options |= PCRE_UCP; continue; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_NO_START_OPT_RIGHTPAR, 13) == 0) + { skipatstart += 15; options |= PCRE_NO_START_OPTIMIZE; continue; } + + if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_CR_RIGHTPAR, 3) == 0) + { skipatstart += 5; newnl = PCRE_NEWLINE_CR; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_LF_RIGHTPAR, 3) == 0) + { skipatstart += 5; newnl = PCRE_NEWLINE_LF; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_CRLF_RIGHTPAR, 5) == 0) + { skipatstart += 7; newnl = PCRE_NEWLINE_CR + PCRE_NEWLINE_LF; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_ANY_RIGHTPAR, 4) == 0) + { skipatstart += 6; newnl = PCRE_NEWLINE_ANY; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_ANYCRLF_RIGHTPAR, 8) == 0) + { skipatstart += 10; newnl = PCRE_NEWLINE_ANYCRLF; } + + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_BSR_ANYCRLF_RIGHTPAR, 12) == 0) + { skipatstart += 14; newbsr = PCRE_BSR_ANYCRLF; } + else if (STRNCMP_UC_C8(ptr+skipatstart+2, STRING_BSR_UNICODE_RIGHTPAR, 12) == 0) + { skipatstart += 14; newbsr = PCRE_BSR_UNICODE; } + + if (newnl != 0) + options = (options & ~PCRE_NEWLINE_BITS) | newnl; + else if (newbsr != 0) + options = (options & ~(PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) | newbsr; + else break; + } + +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +utf = (options & PCRE_UTF8) != 0; + +/* Can't support UTF unless PCRE has been compiled to include the code. The +return of an error code from PRIV(valid_utf)() is a new feature, introduced in +release 8.13. It is passed back from pcre_[dfa_]exec(), but at the moment is +not used here. */ + +#ifdef SUPPORT_UTF +if (utf && (options & PCRE_NO_UTF8_CHECK) == 0 && + (errorcode = PRIV(valid_utf)((PCRE_PUCHAR)pattern, -1, erroroffset)) != 0) + { +#ifdef COMPILE_PCRE8 + errorcode = ERR44; +#else + errorcode = ERR74; +#endif + goto PCRE_EARLY_ERROR_RETURN2; + } +#else +if (utf) + { + errorcode = ERR32; + goto PCRE_EARLY_ERROR_RETURN; + } +#endif + +/* Can't support UCP unless PCRE has been compiled to include the code. */ + +#ifndef SUPPORT_UCP +if ((options & PCRE_UCP) != 0) + { + errorcode = ERR67; + goto PCRE_EARLY_ERROR_RETURN; + } +#endif + +/* Check validity of \R options. */ + +if ((options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) == + (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) + { + errorcode = ERR56; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Handle different types of newline. The three bits give seven cases. The +current code allows for fixed one- or two-byte sequences, plus "any" and +"anycrlf". */ + +switch (options & PCRE_NEWLINE_BITS) + { + case 0: newline = NEWLINE; break; /* Build-time default */ + case PCRE_NEWLINE_CR: newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: newline = CHAR_NL; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break; + case PCRE_NEWLINE_ANY: newline = -1; break; + case PCRE_NEWLINE_ANYCRLF: newline = -2; break; + default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN; + } + +if (newline == -2) + { + cd->nltype = NLTYPE_ANYCRLF; + } +else if (newline < 0) + { + cd->nltype = NLTYPE_ANY; + } +else + { + cd->nltype = NLTYPE_FIXED; + if (newline > 255) + { + cd->nllen = 2; + cd->nl[0] = (newline >> 8) & 255; + cd->nl[1] = newline & 255; + } + else + { + cd->nllen = 1; + cd->nl[0] = newline; + } + } + +/* Maximum back reference and backref bitmap. The bitmap records up to 31 back +references to help in deciding whether (.*) can be treated as anchored or not. +*/ + +cd->top_backref = 0; +cd->backref_map = 0; + +/* Reflect pattern for debugging output */ + +DPRINTF(("------------------------------------------------------------------\n")); +#ifdef PCRE_DEBUG +print_puchar(stdout, (PCRE_PUCHAR)pattern); +#endif +DPRINTF(("\n")); + +/* Pretend to compile the pattern while actually just accumulating the length +of memory required. This behaviour is triggered by passing a non-NULL final +argument to compile_regex(). We pass a block of workspace (cworkspace) for it +to compile parts of the pattern into; the compiled code is discarded when it is +no longer needed, so hopefully this workspace will never overflow, though there +is a test for its doing so. */ + +cd->bracount = cd->final_bracount = 0; +cd->names_found = 0; +cd->name_entry_size = 0; +cd->name_table = NULL; +cd->start_code = cworkspace; +cd->hwm = cworkspace; +cd->start_workspace = cworkspace; +cd->workspace_size = COMPILE_WORK_SIZE; +cd->start_pattern = (const pcre_uchar *)pattern; +cd->end_pattern = (const pcre_uchar *)(pattern + STRLEN_UC((const pcre_uchar *)pattern)); +cd->req_varyopt = 0; +cd->assert_depth = 0; +cd->external_options = options; +cd->external_flags = 0; +cd->open_caps = NULL; + +/* Now do the pre-compile. On error, errorcode will be set non-zero, so we +don't need to look at the result of the function here. The initial options have +been put into the cd block so that they can be changed if an option setting is +found within the regex right at the beginning. Bringing initial option settings +outside can help speed up starting point checks. */ + +ptr += skipatstart; +code = cworkspace; +*code = OP_BRA; +(void)compile_regex(cd->external_options, &code, &ptr, &errorcode, FALSE, + FALSE, 0, 0, &firstchar, &reqchar, NULL, cd, &length); +if (errorcode != 0) goto PCRE_EARLY_ERROR_RETURN; + +DPRINTF(("end pre-compile: length=%d workspace=%d\n", length, + (int)(cd->hwm - cworkspace))); + +if (length > MAX_PATTERN_SIZE) + { + errorcode = ERR20; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Compute the size of data block needed and get it, either from malloc or +externally provided function. Integer overflow should no longer be possible +because nowadays we limit the maximum value of cd->names_found and +cd->name_entry_size. */ + +size = sizeof(REAL_PCRE) + (length + cd->names_found * cd->name_entry_size) * sizeof(pcre_uchar); +re = (REAL_PCRE *)(PUBL(malloc))(size); + +if (re == NULL) + { + errorcode = ERR21; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Put in the magic number, and save the sizes, initial options, internal +flags, and character table pointer. NULL is used for the default character +tables. The nullpad field is at the end; it's there to help in the case when a +regex compiled on a system with 4-byte pointers is run on another with 8-byte +pointers. */ + +re->magic_number = MAGIC_NUMBER; +re->size = (int)size; +re->options = cd->external_options; +re->flags = cd->external_flags; +re->dummy1 = 0; +re->first_char = 0; +re->req_char = 0; +re->name_table_offset = sizeof(REAL_PCRE) / sizeof(pcre_uchar); +re->name_entry_size = cd->name_entry_size; +re->name_count = cd->names_found; +re->ref_count = 0; +re->tables = (tables == PRIV(default_tables))? NULL : tables; +re->nullpad = NULL; + +/* The starting points of the name/number translation table and of the code are +passed around in the compile data block. The start/end pattern and initial +options are already set from the pre-compile phase, as is the name_entry_size +field. Reset the bracket count and the names_found field. Also reset the hwm +field; this time it's used for remembering forward references to subpatterns. +*/ + +cd->final_bracount = cd->bracount; /* Save for checking forward references */ +cd->assert_depth = 0; +cd->bracount = 0; +cd->names_found = 0; +cd->name_table = (pcre_uchar *)re + re->name_table_offset; +codestart = cd->name_table + re->name_entry_size * re->name_count; +cd->start_code = codestart; +cd->hwm = (pcre_uchar *)(cd->start_workspace); +cd->req_varyopt = 0; +cd->had_accept = FALSE; +cd->check_lookbehind = FALSE; +cd->open_caps = NULL; + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, errorcode will be set non-zero, so we don't need to look at the result +of the function here. */ + +ptr = (const pcre_uchar *)pattern + skipatstart; +code = (pcre_uchar *)codestart; +*code = OP_BRA; +(void)compile_regex(re->options, &code, &ptr, &errorcode, FALSE, FALSE, 0, 0, + &firstchar, &reqchar, NULL, cd, NULL); +re->top_bracket = cd->bracount; +re->top_backref = cd->top_backref; +re->flags = cd->external_flags | PCRE_MODE; + +if (cd->had_accept) reqchar = REQ_NONE; /* Must disable after (*ACCEPT) */ + +/* If not reached end of pattern on success, there's an excess bracket. */ + +if (errorcode == 0 && *ptr != 0) errorcode = ERR22; + +/* Fill in the terminating state and check for disastrous overflow, but +if debugging, leave the test till after things are printed out. */ + +*code++ = OP_END; + +#ifndef PCRE_DEBUG +if (code - codestart > length) errorcode = ERR23; +#endif + +/* Fill in any forward references that are required. There may be repeated +references; optimize for them, as searching a large regex takes time. */ + +if (cd->hwm > cd->start_workspace) + { + int prev_recno = -1; + const pcre_uchar *groupptr = NULL; + while (errorcode == 0 && cd->hwm > cd->start_workspace) + { + int offset, recno; + cd->hwm -= LINK_SIZE; + offset = GET(cd->hwm, 0); + recno = GET(codestart, offset); + if (recno != prev_recno) + { + groupptr = PRIV(find_bracket)(codestart, utf, recno); + prev_recno = recno; + } + if (groupptr == NULL) errorcode = ERR53; + else PUT(((pcre_uchar *)codestart), offset, (int)(groupptr - codestart)); + } + } + +/* If the workspace had to be expanded, free the new memory. */ + +if (cd->workspace_size > COMPILE_WORK_SIZE) + (PUBL(free))((void *)cd->start_workspace); + +/* Give an error if there's back reference to a non-existent capturing +subpattern. */ + +if (errorcode == 0 && re->top_backref > re->top_bracket) errorcode = ERR15; + +/* If there were any lookbehind assertions that contained OP_RECURSE +(recursions or subroutine calls), a flag is set for them to be checked here, +because they may contain forward references. Actual recursions can't be fixed +length, but subroutine calls can. It is done like this so that those without +OP_RECURSE that are not fixed length get a diagnosic with a useful offset. The +exceptional ones forgo this. We scan the pattern to check that they are fixed +length, and set their lengths. */ + +if (cd->check_lookbehind) + { + pcre_uchar *cc = (pcre_uchar *)codestart; + + /* Loop, searching for OP_REVERSE items, and process those that do not have + their length set. (Actually, it will also re-process any that have a length + of zero, but that is a pathological case, and it does no harm.) When we find + one, we temporarily terminate the branch it is in while we scan it. */ + + for (cc = (pcre_uchar *)PRIV(find_bracket)(codestart, utf, -1); + cc != NULL; + cc = (pcre_uchar *)PRIV(find_bracket)(cc, utf, -1)) + { + if (GET(cc, 1) == 0) + { + int fixed_length; + pcre_uchar *be = cc - 1 - LINK_SIZE + GET(cc, -LINK_SIZE); + int end_op = *be; + *be = OP_END; + fixed_length = find_fixedlength(cc, (re->options & PCRE_UTF8) != 0, TRUE, + cd); + *be = end_op; + DPRINTF(("fixed length = %d\n", fixed_length)); + if (fixed_length < 0) + { + errorcode = (fixed_length == -2)? ERR36 : + (fixed_length == -4)? ERR70 : ERR25; + break; + } + PUT(cc, 1, fixed_length); + } + cc += 1 + LINK_SIZE; + } + } + +/* Failed to compile, or error while post-processing */ + +if (errorcode != 0) + { + (PUBL(free))(re); + PCRE_EARLY_ERROR_RETURN: + *erroroffset = (int)(ptr - (const pcre_uchar *)pattern); + PCRE_EARLY_ERROR_RETURN2: + *errorptr = find_error_text(errorcode); + if (errorcodeptr != NULL) *errorcodeptr = errorcode; + return NULL; + } + +/* If the anchored option was not passed, set the flag if we can determine that +the pattern is anchored by virtue of ^ characters or \A or anything else (such +as starting with .* when DOTALL is set). + +Otherwise, if we know what the first byte has to be, save it, because that +speeds up unanchored matches no end. If not, see if we can set the +PCRE_STARTLINE flag. This is helpful for multiline matches when all branches +start with ^. and also when all branches start with .* for non-DOTALL matches. +*/ + +if ((re->options & PCRE_ANCHORED) == 0) + { + if (is_anchored(codestart, 0, cd->backref_map)) + re->options |= PCRE_ANCHORED; + else + { + if (firstchar < 0) + firstchar = find_firstassertedchar(codestart, FALSE); + if (firstchar >= 0) /* Remove caseless flag for non-caseable chars */ + { +#ifdef COMPILE_PCRE8 + re->first_char = firstchar & 0xff; +#else +#ifdef COMPILE_PCRE16 + re->first_char = firstchar & 0xffff; +#endif +#endif + if ((firstchar & REQ_CASELESS) != 0) + { +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + /* We ignore non-ASCII first chars in 8 bit mode. */ + if (utf) + { + if (re->first_char < 128) + { + if (cd->fcc[re->first_char] != re->first_char) + re->flags |= PCRE_FCH_CASELESS; + } + else if (UCD_OTHERCASE(re->first_char) != re->first_char) + re->flags |= PCRE_FCH_CASELESS; + } + else +#endif + if (MAX_255(re->first_char) + && cd->fcc[re->first_char] != re->first_char) + re->flags |= PCRE_FCH_CASELESS; + } + + re->flags |= PCRE_FIRSTSET; + } + else if (is_startline(codestart, 0, cd->backref_map)) + re->flags |= PCRE_STARTLINE; + } + } + +/* For an anchored pattern, we use the "required byte" only if it follows a +variable length item in the regex. Remove the caseless flag for non-caseable +bytes. */ + +if (reqchar >= 0 && + ((re->options & PCRE_ANCHORED) == 0 || (reqchar & REQ_VARY) != 0)) + { +#ifdef COMPILE_PCRE8 + re->req_char = reqchar & 0xff; +#else +#ifdef COMPILE_PCRE16 + re->req_char = reqchar & 0xffff; +#endif +#endif + if ((reqchar & REQ_CASELESS) != 0) + { +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + /* We ignore non-ASCII first chars in 8 bit mode. */ + if (utf) + { + if (re->req_char < 128) + { + if (cd->fcc[re->req_char] != re->req_char) + re->flags |= PCRE_RCH_CASELESS; + } + else if (UCD_OTHERCASE(re->req_char) != re->req_char) + re->flags |= PCRE_RCH_CASELESS; + } + else +#endif + if (MAX_255(re->req_char) && cd->fcc[re->req_char] != re->req_char) + re->flags |= PCRE_RCH_CASELESS; + } + + re->flags |= PCRE_REQCHSET; + } + +/* Print out the compiled data if debugging is enabled. This is never the +case when building a production library. */ + +#ifdef PCRE_DEBUG +printf("Length = %d top_bracket = %d top_backref = %d\n", + length, re->top_bracket, re->top_backref); + +printf("Options=%08x\n", re->options); + +if ((re->flags & PCRE_FIRSTSET) != 0) + { + pcre_uchar ch = re->first_char; + const char *caseless = + ((re->flags & PCRE_FCH_CASELESS) == 0)? "" : " (caseless)"; + if (PRINTABLE(ch)) printf("First char = %c%s\n", ch, caseless); + else printf("First char = \\x%02x%s\n", ch, caseless); + } + +if ((re->flags & PCRE_REQCHSET) != 0) + { + pcre_uchar ch = re->req_char; + const char *caseless = + ((re->flags & PCRE_RCH_CASELESS) == 0)? "" : " (caseless)"; + if (PRINTABLE(ch)) printf("Req char = %c%s\n", ch, caseless); + else printf("Req char = \\x%02x%s\n", ch, caseless); + } + +#ifdef COMPILE_PCRE8 +pcre_printint((pcre *)re, stdout, TRUE); +#else +pcre16_printint((pcre *)re, stdout, TRUE); +#endif + +/* This check is done here in the debugging case so that the code that +was compiled can be seen. */ + +if (code - codestart > length) + { + (PUBL(free))(re); + *errorptr = find_error_text(ERR23); + *erroroffset = ptr - (pcre_uchar *)pattern; + if (errorcodeptr != NULL) *errorcodeptr = ERR23; + return NULL; + } +#endif /* PCRE_DEBUG */ + +#ifdef COMPILE_PCRE8 +return (pcre *)re; +#else +return (pcre16 *)re; +#endif +} + +/* End of pcre_compile.c */ diff --git a/src/lib/pcre/pcre_config.c b/src/lib/pcre/pcre_config.c new file mode 100644 index 0000000..6ebafaf --- /dev/null +++ b/src/lib/pcre/pcre_config.c @@ -0,0 +1,170 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_config(). */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +/* Keep the original link size. */ +static int real_link_size = LINK_SIZE; + +#include "pcre_internal.h" + + +/************************************************* +* Return info about what features are configured * +*************************************************/ + +/* This function has an extensible interface so that additional items can be +added compatibly. + +Arguments: + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_config(int what, void *where) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_config(int what, void *where) +#endif +{ +switch (what) + { + case PCRE_CONFIG_UTF8: +#if defined COMPILE_PCRE16 + *((int *)where) = 0; + return PCRE_ERROR_BADOPTION; +#else +#if defined SUPPORT_UTF + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; +#endif + + case PCRE_CONFIG_UTF16: +#if defined COMPILE_PCRE8 + *((int *)where) = 0; + return PCRE_ERROR_BADOPTION; +#else +#if defined SUPPORT_UTF + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; +#endif + + case PCRE_CONFIG_UNICODE_PROPERTIES: +#ifdef SUPPORT_UCP + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_JIT: +#ifdef SUPPORT_JIT + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_JITTARGET: +#ifdef SUPPORT_JIT + *((const char **)where) = PRIV(jit_get_target)(); +#else + *((const char **)where) = NULL; +#endif + break; + + case PCRE_CONFIG_NEWLINE: + *((int *)where) = NEWLINE; + break; + + case PCRE_CONFIG_BSR: +#ifdef BSR_ANYCRLF + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_LINK_SIZE: + *((int *)where) = real_link_size; + break; + + case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD: + *((int *)where) = POSIX_MALLOC_THRESHOLD; + break; + + case PCRE_CONFIG_MATCH_LIMIT: + *((unsigned long int *)where) = MATCH_LIMIT; + break; + + case PCRE_CONFIG_MATCH_LIMIT_RECURSION: + *((unsigned long int *)where) = MATCH_LIMIT_RECURSION; + break; + + case PCRE_CONFIG_STACKRECURSE: +#ifdef NO_RECURSE + *((int *)where) = 0; +#else + *((int *)where) = 1; +#endif + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + +/* End of pcre_config.c */ diff --git a/src/lib/pcre/pcre_dfa_exec.c b/src/lib/pcre/pcre_dfa_exec.c new file mode 100644 index 0000000..bc89c0d --- /dev/null +++ b/src/lib/pcre/pcre_dfa_exec.c @@ -0,0 +1,3490 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language (but see +below for why this module is different). + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_dfa_exec(), which is an +alternative matching function that uses a sort of DFA algorithm (not a true +FSM). This is NOT Perl- compatible, but it has advantages in certain +applications. */ + + +/* NOTE ABOUT PERFORMANCE: A user of this function sent some code that improved +the performance of his patterns greatly. I could not use it as it stood, as it +was not thread safe, and made assumptions about pattern sizes. Also, it caused +test 7 to loop, and test 9 to crash with a segfault. + +The issue is the check for duplicate states, which is done by a simple linear +search up the state list. (Grep for "duplicate" below to find the code.) For +many patterns, there will never be many states active at one time, so a simple +linear search is fine. In patterns that have many active states, it might be a +bottleneck. The suggested code used an indexing scheme to remember which states +had previously been used for each character, and avoided the linear search when +it knew there was no chance of a duplicate. This was implemented when adding +states to the state lists. + +I wrote some thread-safe, not-limited code to try something similar at the time +of checking for duplicates (instead of when adding states), using index vectors +on the stack. It did give a 13% improvement with one specially constructed +pattern for certain subject strings, but on other strings and on many of the +simpler patterns in the test suite it did worse. The major problem, I think, +was the extra time to initialize the index. This had to be done for each call +of internal_dfa_exec(). (The supplied patch used a static vector, initialized +only once - I suspect this was the cause of the problems with the tests.) + +Overall, I concluded that the gains in some cases did not outweigh the losses +in others, so I abandoned this code. */ + + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK md /* Block containing newline information */ +#define PSSTART start_subject /* Field containing processed string start */ +#define PSEND end_subject /* Field containing processed string end */ + +#include "pcre_internal.h" + + +/* For use to indent debugging output */ + +#define SP " " + + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +/* These are offsets that are used to turn the OP_TYPESTAR and friends opcodes +into others, under special conditions. A gap of 20 between the blocks should be +enough. The resulting opcodes don't have to be less than 256 because they are +never stored, so we push them well clear of the normal opcodes. */ + +#define OP_PROP_EXTRA 300 +#define OP_EXTUNI_EXTRA 320 +#define OP_ANYNL_EXTRA 340 +#define OP_HSPACE_EXTRA 360 +#define OP_VSPACE_EXTRA 380 + + +/* This table identifies those opcodes that are followed immediately by a +character that is to be tested in some way. This makes it possible to +centralize the loading of these characters. In the case of Type * etc, the +"character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a +small value. Non-zero values in the table are the offsets from the opcode where +the character is to be found. ***NOTE*** If the start of this table is +modified, the three tables that follow must also be modified. */ + +static const pcre_uint8 coptable[] = { + 0, /* End */ + 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */ + 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */ + 0, 0, 0, /* Any, AllAny, Anybyte */ + 0, 0, /* \P, \p */ + 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */ + 0, /* \X */ + 0, 0, 0, 0, 0, 0, /* \Z, \z, ^, ^M, $, $M */ + 1, /* Char */ + 1, /* Chari */ + 1, /* not */ + 1, /* noti */ + /* Positive single-char repeats */ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto, minupto */ + 1+IMM2_SIZE, /* exact */ + 1, 1, 1, 1+IMM2_SIZE, /* *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* upto I, minupto I */ + 1+IMM2_SIZE, /* exact I */ + 1, 1, 1, 1+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ + /* Negative single-char repeats - only for chars < 256 */ + 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto, minupto */ + 1+IMM2_SIZE, /* NOT exact */ + 1, 1, 1, 1+IMM2_SIZE, /* NOT *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* NOT upto I, minupto I */ + 1+IMM2_SIZE, /* NOT exact I */ + 1, 1, 1, 1+IMM2_SIZE, /* NOT *+I, ++I, ?+I, upto+I */ + /* Positive type repeats */ + 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* Type upto, minupto */ + 1+IMM2_SIZE, /* Type exact */ + 1, 1, 1, 1+IMM2_SIZE, /* Type *+, ++, ?+, upto+ */ + /* Character class & ref repeats */ + 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */ + 0, 0, /* CRRANGE, CRMINRANGE */ + 0, /* CLASS */ + 0, /* NCLASS */ + 0, /* XCLASS - variable length */ + 0, /* REF */ + 0, /* REFI */ + 0, /* RECURSE */ + 0, /* CALLOUT */ + 0, /* Alt */ + 0, /* Ket */ + 0, /* KetRmax */ + 0, /* KetRmin */ + 0, /* KetRpos */ + 0, /* Reverse */ + 0, /* Assert */ + 0, /* Assert not */ + 0, /* Assert behind */ + 0, /* Assert behind not */ + 0, 0, /* ONCE, ONCE_NC */ + 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ + 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ + 0, 0, /* CREF, NCREF */ + 0, 0, /* RREF, NRREF */ + 0, /* DEF */ + 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ + 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ + 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ + 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0 /* CLOSE, SKIPZERO */ +}; + +/* This table identifies those opcodes that inspect a character. It is used to +remember the fact that a character could have been inspected when the end of +the subject is reached. ***NOTE*** If the start of this table is modified, the +two tables that follow must also be modified. */ + +static const pcre_uint8 poptable[] = { + 0, /* End */ + 0, 0, 0, 1, 1, /* \A, \G, \K, \B, \b */ + 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ + 1, 1, 1, /* Any, AllAny, Anybyte */ + 1, 1, /* \P, \p */ + 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ + 1, /* \X */ + 0, 0, 0, 0, 0, 0, /* \Z, \z, ^, ^M, $, $M */ + 1, /* Char */ + 1, /* Chari */ + 1, /* not */ + 1, /* noti */ + /* Positive single-char repeats */ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ + 1, 1, 1, /* upto, minupto, exact */ + 1, 1, 1, 1, /* *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* *I, *?I, +I, +?I, ?I, ??I */ + 1, 1, 1, /* upto I, minupto I, exact I */ + 1, 1, 1, 1, /* *+I, ++I, ?+I, upto+I */ + /* Negative single-char repeats - only for chars < 256 */ + 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ + 1, 1, 1, /* NOT upto, minupto, exact */ + 1, 1, 1, 1, /* NOT *+, ++, ?+, upto+ */ + 1, 1, 1, 1, 1, 1, /* NOT *I, *?I, +I, +?I, ?I, ??I */ + 1, 1, 1, /* NOT upto I, minupto I, exact I */ + 1, 1, 1, 1, /* NOT *+I, ++I, ?+I, upto+I */ + /* Positive type repeats */ + 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ + 1, 1, 1, /* Type upto, minupto, exact */ + 1, 1, 1, 1, /* Type *+, ++, ?+, upto+ */ + /* Character class & ref repeats */ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ + 1, 1, /* CRRANGE, CRMINRANGE */ + 1, /* CLASS */ + 1, /* NCLASS */ + 1, /* XCLASS - variable length */ + 0, /* REF */ + 0, /* REFI */ + 0, /* RECURSE */ + 0, /* CALLOUT */ + 0, /* Alt */ + 0, /* Ket */ + 0, /* KetRmax */ + 0, /* KetRmin */ + 0, /* KetRpos */ + 0, /* Reverse */ + 0, /* Assert */ + 0, /* Assert not */ + 0, /* Assert behind */ + 0, /* Assert behind not */ + 0, 0, /* ONCE, ONCE_NC */ + 0, 0, 0, 0, 0, /* BRA, BRAPOS, CBRA, CBRAPOS, COND */ + 0, 0, 0, 0, 0, /* SBRA, SBRAPOS, SCBRA, SCBRAPOS, SCOND */ + 0, 0, /* CREF, NCREF */ + 0, 0, /* RREF, NRREF */ + 0, /* DEF */ + 0, 0, 0, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ + 0, 0, 0, /* MARK, PRUNE, PRUNE_ARG */ + 0, 0, 0, 0, /* SKIP, SKIP_ARG, THEN, THEN_ARG */ + 0, 0, 0, 0, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ + 0, 0 /* CLOSE, SKIPZERO */ +}; + +/* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W, +and \w */ + +static const pcre_uint8 toptable1[] = { + 0, 0, 0, 0, 0, 0, + ctype_digit, ctype_digit, + ctype_space, ctype_space, + ctype_word, ctype_word, + 0, 0 /* OP_ANY, OP_ALLANY */ +}; + +static const pcre_uint8 toptable2[] = { + 0, 0, 0, 0, 0, 0, + ctype_digit, 0, + ctype_space, 0, + ctype_word, 0, + 1, 1 /* OP_ANY, OP_ALLANY */ +}; + + +/* Structure for holding data about a particular state, which is in effect the +current data for an active path through the match tree. It must consist +entirely of ints because the working vector we are passed, and which we put +these structures in, is a vector of ints. */ + +typedef struct stateblock { + int offset; /* Offset to opcode */ + int count; /* Count for repeats */ + int data; /* Some use extra data */ +} stateblock; + +#define INTS_PER_STATEBLOCK (sizeof(stateblock)/sizeof(int)) + + +#ifdef PCRE_DEBUG +/************************************************* +* Print character string * +*************************************************/ + +/* Character string printing function for debugging. + +Arguments: + p points to string + length number of bytes + f where to print + +Returns: nothing +*/ + +static void +pchars(const pcre_uchar *p, int length, FILE *f) +{ +int c; +while (length-- > 0) + { + if (isprint(c = *(p++))) + fprintf(f, "%c", c); + else + fprintf(f, "\\x%02x", c); + } +} +#endif + + + +/************************************************* +* Execute a Regular Expression - DFA engine * +*************************************************/ + +/* This internal function applies a compiled pattern to a subject string, +starting at a given point, using a DFA engine. This function is called from the +external one, possibly multiple times if the pattern is not anchored. The +function calls itself recursively for some kinds of subpattern. + +Arguments: + md the match_data block with fixed information + this_start_code the opening bracket of this subexpression's code + current_subject where we currently are in the subject string + start_offset start offset in the subject string + offsets vector to contain the matching string offsets + offsetcount size of same + workspace vector of workspace + wscount size of same + rlevel function call recursion level + +Returns: > 0 => number of match offset pairs placed in offsets + = 0 => offsets overflowed; longest matches are present + -1 => failed to match + < -1 => some kind of unexpected problem + +The following macros are used for adding states to the two state vectors (one +for the current character, one for the following character). */ + +#define ADD_ACTIVE(x,y) \ + if (active_count++ < wscount) \ + { \ + next_active_state->offset = (x); \ + next_active_state->count = (y); \ + next_active_state++; \ + DPRINTF(("%.*sADD_ACTIVE(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +#define ADD_ACTIVE_DATA(x,y,z) \ + if (active_count++ < wscount) \ + { \ + next_active_state->offset = (x); \ + next_active_state->count = (y); \ + next_active_state->data = (z); \ + next_active_state++; \ + DPRINTF(("%.*sADD_ACTIVE_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +#define ADD_NEW(x,y) \ + if (new_count++ < wscount) \ + { \ + next_new_state->offset = (x); \ + next_new_state->count = (y); \ + next_new_state++; \ + DPRINTF(("%.*sADD_NEW(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +#define ADD_NEW_DATA(x,y,z) \ + if (new_count++ < wscount) \ + { \ + next_new_state->offset = (x); \ + next_new_state->count = (y); \ + next_new_state->data = (z); \ + next_new_state++; \ + DPRINTF(("%.*sADD_NEW_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +/* And now, here is the code */ + +static int +internal_dfa_exec( + dfa_match_data *md, + const pcre_uchar *this_start_code, + const pcre_uchar *current_subject, + int start_offset, + int *offsets, + int offsetcount, + int *workspace, + int wscount, + int rlevel) +{ +stateblock *active_states, *new_states, *temp_states; +stateblock *next_active_state, *next_new_state; + +const pcre_uint8 *ctypes, *lcc, *fcc; +const pcre_uchar *ptr; +const pcre_uchar *end_code, *first_op; + +dfa_recursion_info new_recursive; + +int active_count, new_count, match_count; + +/* Some fields in the md block are frequently referenced, so we load them into +independent variables in the hope that this will perform better. */ + +const pcre_uchar *start_subject = md->start_subject; +const pcre_uchar *end_subject = md->end_subject; +const pcre_uchar *start_code = md->start_code; + +#ifdef SUPPORT_UTF +BOOL utf = (md->poptions & PCRE_UTF8) != 0; +#else +BOOL utf = FALSE; +#endif + +rlevel++; +offsetcount &= (-2); + +wscount -= 2; +wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) / + (2 * INTS_PER_STATEBLOCK); + +DPRINTF(("\n%.*s---------------------\n" + "%.*sCall to internal_dfa_exec f=%d\n", + rlevel*2-2, SP, rlevel*2-2, SP, rlevel)); + +ctypes = md->tables + ctypes_offset; +lcc = md->tables + lcc_offset; +fcc = md->tables + fcc_offset; + +match_count = PCRE_ERROR_NOMATCH; /* A negative number */ + +active_states = (stateblock *)(workspace + 2); +next_new_state = new_states = active_states + wscount; +new_count = 0; + +first_op = this_start_code + 1 + LINK_SIZE + + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || + *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) + ? IMM2_SIZE:0); + +/* The first thing in any (sub) pattern is a bracket of some sort. Push all +the alternative states onto the list, and find out where the end is. This +makes is possible to use this function recursively, when we want to stop at a +matching internal ket rather than at the end. + +If the first opcode in the first alternative is OP_REVERSE, we are dealing with +a backward assertion. In that case, we have to find out the maximum amount to +move back, and set up each alternative appropriately. */ + +if (*first_op == OP_REVERSE) + { + int max_back = 0; + int gone_back; + + end_code = this_start_code; + do + { + int back = GET(end_code, 2+LINK_SIZE); + if (back > max_back) max_back = back; + end_code += GET(end_code, 1); + } + while (*end_code == OP_ALT); + + /* If we can't go back the amount required for the longest lookbehind + pattern, go back as far as we can; some alternatives may still be viable. */ + +#ifdef SUPPORT_UTF + /* In character mode we have to step back character by character */ + + if (utf) + { + for (gone_back = 0; gone_back < max_back; gone_back++) + { + if (current_subject <= start_subject) break; + current_subject--; + ACROSSCHAR(current_subject > start_subject, *current_subject, current_subject--); + } + } + else +#endif + + /* In byte-mode we can do this quickly. */ + + { + gone_back = (current_subject - max_back < start_subject)? + (int)(current_subject - start_subject) : max_back; + current_subject -= gone_back; + } + + /* Save the earliest consulted character */ + + if (current_subject < md->start_used_ptr) + md->start_used_ptr = current_subject; + + /* Now we can process the individual branches. */ + + end_code = this_start_code; + do + { + int back = GET(end_code, 2+LINK_SIZE); + if (back <= gone_back) + { + int bstate = (int)(end_code - start_code + 2 + 2*LINK_SIZE); + ADD_NEW_DATA(-bstate, 0, gone_back - back); + } + end_code += GET(end_code, 1); + } + while (*end_code == OP_ALT); + } + +/* This is the code for a "normal" subpattern (not a backward assertion). The +start of a whole pattern is always one of these. If we are at the top level, +we may be asked to restart matching from the same point that we reached for a +previous partial match. We still have to scan through the top-level branches to +find the end state. */ + +else + { + end_code = this_start_code; + + /* Restarting */ + + if (rlevel == 1 && (md->moptions & PCRE_DFA_RESTART) != 0) + { + do { end_code += GET(end_code, 1); } while (*end_code == OP_ALT); + new_count = workspace[1]; + if (!workspace[0]) + memcpy(new_states, active_states, new_count * sizeof(stateblock)); + } + + /* Not restarting */ + + else + { + int length = 1 + LINK_SIZE + + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA || + *this_start_code == OP_CBRAPOS || *this_start_code == OP_SCBRAPOS) + ? IMM2_SIZE:0); + do + { + ADD_NEW((int)(end_code - start_code + length), 0); + end_code += GET(end_code, 1); + length = 1 + LINK_SIZE; + } + while (*end_code == OP_ALT); + } + } + +workspace[0] = 0; /* Bit indicating which vector is current */ + +DPRINTF(("%.*sEnd state = %d\n", rlevel*2-2, SP, (int)(end_code - start_code))); + +/* Loop for scanning the subject */ + +ptr = current_subject; +for (;;) + { + int i, j; + int clen, dlen; + unsigned int c, d; + int forced_fail = 0; + BOOL could_continue = FALSE; + + /* Make the new state list into the active state list and empty the + new state list. */ + + temp_states = active_states; + active_states = new_states; + new_states = temp_states; + active_count = new_count; + new_count = 0; + + workspace[0] ^= 1; /* Remember for the restarting feature */ + workspace[1] = active_count; + +#ifdef PCRE_DEBUG + printf("%.*sNext character: rest of subject = \"", rlevel*2-2, SP); + pchars(ptr, STRLEN_UC(ptr), stdout); + printf("\"\n"); + + printf("%.*sActive states: ", rlevel*2-2, SP); + for (i = 0; i < active_count; i++) + printf("%d/%d ", active_states[i].offset, active_states[i].count); + printf("\n"); +#endif + + /* Set the pointers for adding new states */ + + next_active_state = active_states + active_count; + next_new_state = new_states; + + /* Load the current character from the subject outside the loop, as many + different states may want to look at it, and we assume that at least one + will. */ + + if (ptr < end_subject) + { + clen = 1; /* Number of bytes in the character */ +#ifdef SUPPORT_UTF + if (utf) { GETCHARLEN(c, ptr, clen); } else +#endif /* SUPPORT_UTF */ + c = *ptr; + } + else + { + clen = 0; /* This indicates the end of the subject */ + c = NOTACHAR; /* This value should never actually be used */ + } + + /* Scan up the active states and act on each one. The result of an action + may be to add more states to the currently active list (e.g. on hitting a + parenthesis) or it may be to put states on the new list, for considering + when we move the character pointer on. */ + + for (i = 0; i < active_count; i++) + { + stateblock *current_state = active_states + i; + BOOL caseless = FALSE; + const pcre_uchar *code; + int state_offset = current_state->offset; + int count, codevalue, rrc; + +#ifdef PCRE_DEBUG + printf ("%.*sProcessing state %d c=", rlevel*2-2, SP, state_offset); + if (clen == 0) printf("EOL\n"); + else if (c > 32 && c < 127) printf("'%c'\n", c); + else printf("0x%02x\n", c); +#endif + + /* A negative offset is a special case meaning "hold off going to this + (negated) state until the number of characters in the data field have + been skipped". */ + + if (state_offset < 0) + { + if (current_state->data > 0) + { + DPRINTF(("%.*sSkipping this character\n", rlevel*2-2, SP)); + ADD_NEW_DATA(state_offset, current_state->count, + current_state->data - 1); + continue; + } + else + { + current_state->offset = state_offset = -state_offset; + } + } + + /* Check for a duplicate state with the same count, and skip if found. + See the note at the head of this module about the possibility of improving + performance here. */ + + for (j = 0; j < i; j++) + { + if (active_states[j].offset == state_offset && + active_states[j].count == current_state->count) + { + DPRINTF(("%.*sDuplicate state: skipped\n", rlevel*2-2, SP)); + goto NEXT_ACTIVE_STATE; + } + } + + /* The state offset is the offset to the opcode */ + + code = start_code + state_offset; + codevalue = *code; + + /* If this opcode inspects a character, but we are at the end of the + subject, remember the fact for use when testing for a partial match. */ + + if (clen == 0 && poptable[codevalue] != 0) + could_continue = TRUE; + + /* If this opcode is followed by an inline character, load it. It is + tempting to test for the presence of a subject character here, but that + is wrong, because sometimes zero repetitions of the subject are + permitted. + + We also use this mechanism for opcodes such as OP_TYPEPLUS that take an + argument that is not a data character - but is always one byte long. We + have to take special action to deal with \P, \p, \H, \h, \V, \v and \X in + this case. To keep the other cases fast, convert these ones to new opcodes. + */ + + if (coptable[codevalue] > 0) + { + dlen = 1; +#ifdef SUPPORT_UTF + if (utf) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else +#endif /* SUPPORT_UTF */ + d = code[coptable[codevalue]]; + if (codevalue >= OP_TYPESTAR) + { + switch(d) + { + case OP_ANYBYTE: return PCRE_ERROR_DFA_UITEM; + case OP_NOTPROP: + case OP_PROP: codevalue += OP_PROP_EXTRA; break; + case OP_ANYNL: codevalue += OP_ANYNL_EXTRA; break; + case OP_EXTUNI: codevalue += OP_EXTUNI_EXTRA; break; + case OP_NOT_HSPACE: + case OP_HSPACE: codevalue += OP_HSPACE_EXTRA; break; + case OP_NOT_VSPACE: + case OP_VSPACE: codevalue += OP_VSPACE_EXTRA; break; + default: break; + } + } + } + else + { + dlen = 0; /* Not strictly necessary, but compilers moan */ + d = NOTACHAR; /* if these variables are not set. */ + } + + + /* Now process the individual opcodes */ + + switch (codevalue) + { +/* ========================================================================== */ + /* These cases are never obeyed. This is a fudge that causes a compile- + time error if the vectors coptable or poptable, which are indexed by + opcode, are not the correct length. It seems to be the only way to do + such a check at compile time, as the sizeof() operator does not work + in the C preprocessor. */ + + case OP_TABLE_LENGTH: + case OP_TABLE_LENGTH + + ((sizeof(coptable) == OP_TABLE_LENGTH) && + (sizeof(poptable) == OP_TABLE_LENGTH)): + break; + +/* ========================================================================== */ + /* Reached a closing bracket. If not at the end of the pattern, carry + on with the next opcode. For repeating opcodes, also add the repeat + state. Note that KETRPOS will always be encountered at the end of the + subpattern, because the possessive subpattern repeats are always handled + using recursive calls. Thus, it never adds any new states. + + At the end of the (sub)pattern, unless we have an empty string and + PCRE_NOTEMPTY is set, or PCRE_NOTEMPTY_ATSTART is set and we are at the + start of the subject, save the match data, shifting up all previous + matches so we always have the longest first. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + case OP_KETRPOS: + if (code != end_code) + { + ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0); + if (codevalue != OP_KET) + { + ADD_ACTIVE(state_offset - GET(code, 1), 0); + } + } + else + { + if (ptr > current_subject || + ((md->moptions & PCRE_NOTEMPTY) == 0 && + ((md->moptions & PCRE_NOTEMPTY_ATSTART) == 0 || + current_subject > start_subject + md->start_offset))) + { + if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0; + else if (match_count > 0 && ++match_count * 2 > offsetcount) + match_count = 0; + count = ((match_count == 0)? offsetcount : match_count * 2) - 2; + if (count > 0) memmove(offsets + 2, offsets, count * sizeof(int)); + if (offsetcount >= 2) + { + offsets[0] = (int)(current_subject - start_subject); + offsets[1] = (int)(ptr - start_subject); + DPRINTF(("%.*sSet matched string = \"%.*s\"\n", rlevel*2-2, SP, + offsets[1] - offsets[0], current_subject)); + } + if ((md->moptions & PCRE_DFA_SHORTEST) != 0) + { + DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" + "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, + match_count, rlevel*2-2, SP)); + return match_count; + } + } + } + break; + +/* ========================================================================== */ + /* These opcodes add to the current list of states without looking + at the current character. */ + + /*-----------------------------------------------------------------*/ + case OP_ALT: + do { code += GET(code, 1); } while (*code == OP_ALT); + ADD_ACTIVE((int)(code - start_code), 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_BRA: + case OP_SBRA: + do + { + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); + code += GET(code, 1); + } + while (*code == OP_ALT); + break; + + /*-----------------------------------------------------------------*/ + case OP_CBRA: + case OP_SCBRA: + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE + IMM2_SIZE), 0); + code += GET(code, 1); + while (*code == OP_ALT) + { + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); + code += GET(code, 1); + } + break; + + /*-----------------------------------------------------------------*/ + case OP_BRAZERO: + case OP_BRAMINZERO: + ADD_ACTIVE(state_offset + 1, 0); + code += 1 + GET(code, 2); + while (*code == OP_ALT) code += GET(code, 1); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_SKIPZERO: + code += 1 + GET(code, 2); + while (*code == OP_ALT) code += GET(code, 1); + ADD_ACTIVE((int)(code - start_code + 1 + LINK_SIZE), 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_CIRC: + if (ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_CIRCM: + if ((ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) || + (ptr != end_subject && WAS_NEWLINE(ptr))) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_EOD: + if (ptr >= end_subject) + { + if ((md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else { ADD_ACTIVE(state_offset + 1, 0); } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_SOD: + if (ptr == start_subject) { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_SOM: + if (ptr == start_subject + start_offset) { ADD_ACTIVE(state_offset + 1, 0); } + break; + + +/* ========================================================================== */ + /* These opcodes inspect the next subject character, and sometimes + the previous one as well, but do not have an argument. The variable + clen contains the length of the current character and is zero if we are + at the end of the subject. */ + + /*-----------------------------------------------------------------*/ + case OP_ANY: + if (clen > 0 && !IS_NEWLINE(ptr)) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_ALLANY: + if (clen > 0) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_EODN: + if (clen == 0 && (md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - md->nllen)) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_DOLL: + if ((md->moptions & PCRE_NOTEOL) == 0) + { + if (clen == 0 && (md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else if (clen == 0 || + ((md->poptions & PCRE_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr) && + (ptr == end_subject - md->nllen) + )) + { ADD_ACTIVE(state_offset + 1, 0); } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_DOLLM: + if ((md->moptions & PCRE_NOTEOL) == 0) + { + if (clen == 0 && (md->moptions & PCRE_PARTIAL_HARD) != 0) + could_continue = TRUE; + else if (clen == 0 || + ((md->poptions & PCRE_DOLLAR_ENDONLY) == 0 && IS_NEWLINE(ptr))) + { ADD_ACTIVE(state_offset + 1, 0); } + } + else if (IS_NEWLINE(ptr)) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + + case OP_DIGIT: + case OP_WHITESPACE: + case OP_WORDCHAR: + if (clen > 0 && c < 256 && + ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_NOT_DIGIT: + case OP_NOT_WHITESPACE: + case OP_NOT_WORDCHAR: + if (clen > 0 && (c >= 256 || + ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0)) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + { + int left_word, right_word; + + if (ptr > start_subject) + { + const pcre_uchar *temp = ptr - 1; + if (temp < md->start_used_ptr) md->start_used_ptr = temp; +#ifdef SUPPORT_UTF + if (utf) { BACKCHAR(temp); } +#endif + GETCHARTEST(d, temp); +#ifdef SUPPORT_UCP + if ((md->poptions & PCRE_UCP) != 0) + { + if (d == '_') left_word = TRUE; else + { + int cat = UCD_CATEGORY(d); + left_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + left_word = d < 256 && (ctypes[d] & ctype_word) != 0; + } + else left_word = FALSE; + + if (clen > 0) + { +#ifdef SUPPORT_UCP + if ((md->poptions & PCRE_UCP) != 0) + { + if (c == '_') right_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + right_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + right_word = c < 256 && (ctypes[c] & ctype_word) != 0; + } + else right_word = FALSE; + + if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY)) + { ADD_ACTIVE(state_offset + 1, 0); } + } + break; + + + /*-----------------------------------------------------------------*/ + /* Check the next character by Unicode property. We will get here only + if the support is in the binary; otherwise a compile-time error occurs. + */ + +#ifdef SUPPORT_UCP + case OP_PROP: + case OP_NOTPROP: + if (clen > 0) + { + BOOL OK; + const ucd_record * prop = GET_UCD(c); + switch(code[1]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; + break; + + case PT_GC: + OK = PRIV(ucp_gentype)[prop->chartype] == code[2]; + break; + + case PT_PC: + OK = prop->chartype == code[2]; + break; + + case PT_SC: + OK = prop->script == code[2]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (codevalue == OP_PROP)) { ADD_NEW(state_offset + 3, 0); } + } + break; +#endif + + + +/* ========================================================================== */ + /* These opcodes likewise inspect the subject character, but have an + argument that is not a data character. It is one of these opcodes: + OP_ANY, OP_ALLANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE, + OP_WORDCHAR, OP_NOT_WORDCHAR. The value is loaded into d. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || !IS_NEWLINE(ptr)) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (count > 0 && codevalue == OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW(state_offset, count); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || !IS_NEWLINE(ptr)) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (codevalue == OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset + 2, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || !IS_NEWLINE(ptr)) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (codevalue == OP_TYPEPOSSTAR) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPEEXACT: + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || !IS_NEWLINE(ptr)) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + 1 + IMM2_SIZE + 1, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || !IS_NEWLINE(ptr)) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (codevalue == OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + 2 + IMM2_SIZE, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + +/* ========================================================================== */ + /* These are virtual opcodes that are used when something like + OP_TYPEPLUS has OP_PROP, OP_NOTPROP, OP_ANYNL, or OP_EXTUNI as its + argument. It keeps the code above fast for the other cases. The argument + is in the d variable. */ + +#ifdef SUPPORT_UCP + case OP_PROP_EXTRA + OP_TYPEPLUS: + case OP_PROP_EXTRA + OP_TYPEMINPLUS: + case OP_PROP_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 4, 0); } + if (clen > 0) + { + BOOL OK; + const ucd_record * prop = GET_UCD(c); + switch(code[2]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; + break; + + case PT_GC: + OK = PRIV(ucp_gentype)[prop->chartype] == code[3]; + break; + + case PT_PC: + OK = prop->chartype == code[3]; + break; + + case PT_SC: + OK = prop->script == code[3]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (d == OP_PROP)) + { + if (count > 0 && codevalue == OP_PROP_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW(state_offset, count); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXTUNI_EXTRA + OP_TYPEPLUS: + case OP_EXTUNI_EXTRA + OP_TYPEMINPLUS: + case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0 && UCD_CATEGORY(c) != ucp_M) + { + const pcre_uchar *nptr = ptr + clen; + int ncount = 0; + if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + while (nptr < end_subject) + { + int nd; + int ndlen = 1; + GETCHARLEN(nd, nptr, ndlen); + if (UCD_CATEGORY(nd) != ucp_M) break; + ncount++; + nptr += ndlen; + } + count++; + ADD_NEW_DATA(-state_offset, count, ncount); + } + break; +#endif + + /*-----------------------------------------------------------------*/ + case OP_ANYNL_EXTRA + OP_TYPEPLUS: + case OP_ANYNL_EXTRA + OP_TYPEMINPLUS: + case OP_ANYNL_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + int ncount = 0; + switch (c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + goto ANYNL01; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + /* Fall through */ + + ANYNL01: + case 0x000a: + if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW_DATA(-state_offset, count, ncount); + break; + + default: + break; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE_EXTRA + OP_TYPEPLUS: + case OP_VSPACE_EXTRA + OP_TYPEMINPLUS: + case OP_VSPACE_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_VSPACE)) + { + if (count > 0 && codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW_DATA(-state_offset, count, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE_EXTRA + OP_TYPEPLUS: + case OP_HSPACE_EXTRA + OP_TYPEMINPLUS: + case OP_HSPACE_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_HSPACE)) + { + if (count > 0 && codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW_DATA(-state_offset, count, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ +#ifdef SUPPORT_UCP + case OP_PROP_EXTRA + OP_TYPEQUERY: + case OP_PROP_EXTRA + OP_TYPEMINQUERY: + case OP_PROP_EXTRA + OP_TYPEPOSQUERY: + count = 4; + goto QS1; + + case OP_PROP_EXTRA + OP_TYPESTAR: + case OP_PROP_EXTRA + OP_TYPEMINSTAR: + case OP_PROP_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS1: + + ADD_ACTIVE(state_offset + 4, 0); + if (clen > 0) + { + BOOL OK; + const ucd_record * prop = GET_UCD(c); + switch(code[2]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; + break; + + case PT_GC: + OK = PRIV(ucp_gentype)[prop->chartype] == code[3]; + break; + + case PT_PC: + OK = prop->chartype == code[3]; + break; + + case PT_SC: + OK = prop->script == code[3]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (d == OP_PROP)) + { + if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_PROP_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset + count, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXTUNI_EXTRA + OP_TYPEQUERY: + case OP_EXTUNI_EXTRA + OP_TYPEMINQUERY: + case OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS2; + + case OP_EXTUNI_EXTRA + OP_TYPESTAR: + case OP_EXTUNI_EXTRA + OP_TYPEMINSTAR: + case OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS2: + + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0 && UCD_CATEGORY(c) != ucp_M) + { + const pcre_uchar *nptr = ptr + clen; + int ncount = 0; + if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + while (nptr < end_subject) + { + int nd; + int ndlen = 1; + GETCHARLEN(nd, nptr, ndlen); + if (UCD_CATEGORY(nd) != ucp_M) break; + ncount++; + nptr += ndlen; + } + ADD_NEW_DATA(-(state_offset + count), 0, ncount); + } + break; +#endif + + /*-----------------------------------------------------------------*/ + case OP_ANYNL_EXTRA + OP_TYPEQUERY: + case OP_ANYNL_EXTRA + OP_TYPEMINQUERY: + case OP_ANYNL_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS3; + + case OP_ANYNL_EXTRA + OP_TYPESTAR: + case OP_ANYNL_EXTRA + OP_TYPEMINSTAR: + case OP_ANYNL_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS3: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + int ncount = 0; + switch (c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + goto ANYNL02; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + /* Fall through */ + + ANYNL02: + case 0x000a: + if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW_DATA(-(state_offset + count), 0, ncount); + break; + + default: + break; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE_EXTRA + OP_TYPEQUERY: + case OP_VSPACE_EXTRA + OP_TYPEMINQUERY: + case OP_VSPACE_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS4; + + case OP_VSPACE_EXTRA + OP_TYPESTAR: + case OP_VSPACE_EXTRA + OP_TYPEMINSTAR: + case OP_VSPACE_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS4: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + if (OK == (d == OP_VSPACE)) + { + if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW_DATA(-(state_offset + count), 0, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE_EXTRA + OP_TYPEQUERY: + case OP_HSPACE_EXTRA + OP_TYPEMINQUERY: + case OP_HSPACE_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS5; + + case OP_HSPACE_EXTRA + OP_TYPESTAR: + case OP_HSPACE_EXTRA + OP_TYPEMINSTAR: + case OP_HSPACE_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS5: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_HSPACE)) + { + if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW_DATA(-(state_offset + count), 0, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ +#ifdef SUPPORT_UCP + case OP_PROP_EXTRA + OP_TYPEEXACT: + case OP_PROP_EXTRA + OP_TYPEUPTO: + case OP_PROP_EXTRA + OP_TYPEMINUPTO: + case OP_PROP_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 1 + IMM2_SIZE + 3, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + BOOL OK; + const ucd_record * prop = GET_UCD(c); + switch(code[1 + IMM2_SIZE + 1]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt; + break; + + case PT_GC: + OK = PRIV(ucp_gentype)[prop->chartype] == code[1 + IMM2_SIZE + 2]; + break; + + case PT_PC: + OK = prop->chartype == code[1 + IMM2_SIZE + 2]; + break; + + case PT_SC: + OK = prop->script == code[1 + IMM2_SIZE + 2]; + break; + + /* These are specials for combination cases. */ + + case PT_ALNUM: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N; + break; + + case PT_SPACE: /* Perl space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR; + break; + + case PT_PXSPACE: /* POSIX space */ + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR; + break; + + case PT_WORD: + OK = PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (d == OP_PROP)) + { + if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + 1 + IMM2_SIZE + 3, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXTUNI_EXTRA + OP_TYPEEXACT: + case OP_EXTUNI_EXTRA + OP_TYPEUPTO: + case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO: + case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0 && UCD_CATEGORY(c) != ucp_M) + { + const pcre_uchar *nptr = ptr + clen; + int ncount = 0; + if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + while (nptr < end_subject) + { + int nd; + int ndlen = 1; + GETCHARLEN(nd, nptr, ndlen); + if (UCD_CATEGORY(nd) != ucp_M) break; + ncount++; + nptr += ndlen; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); } + else + { ADD_NEW_DATA(-state_offset, count, ncount); } + } + break; +#endif + + /*-----------------------------------------------------------------*/ + case OP_ANYNL_EXTRA + OP_TYPEEXACT: + case OP_ANYNL_EXTRA + OP_TYPEUPTO: + case OP_ANYNL_EXTRA + OP_TYPEMINUPTO: + case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + int ncount = 0; + switch (c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + goto ANYNL03; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + /* Fall through */ + + ANYNL03: + case 0x000a: + if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, ncount); } + else + { ADD_NEW_DATA(-state_offset, count, ncount); } + break; + + default: + break; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE_EXTRA + OP_TYPEEXACT: + case OP_VSPACE_EXTRA + OP_TYPEUPTO: + case OP_VSPACE_EXTRA + OP_TYPEMINUPTO: + case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + OK = TRUE; + break; + + default: + OK = FALSE; + } + + if (OK == (d == OP_VSPACE)) + { + if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); } + else + { ADD_NEW_DATA(-state_offset, count, 0); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE_EXTRA + OP_TYPEEXACT: + case OP_HSPACE_EXTRA + OP_TYPEUPTO: + case OP_HSPACE_EXTRA + OP_TYPEMINUPTO: + case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 2 + IMM2_SIZE, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_HSPACE)) + { + if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 2 + IMM2_SIZE), 0, 0); } + else + { ADD_NEW_DATA(-state_offset, count, 0); } + } + } + break; + +/* ========================================================================== */ + /* These opcodes are followed by a character that is usually compared + to the current subject character; it is loaded into d. We still get + here even if there is no subject character, because in some cases zero + repetitions are permitted. */ + + /*-----------------------------------------------------------------*/ + case OP_CHAR: + if (clen > 0 && c == d) { ADD_NEW(state_offset + dlen + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_CHARI: + if (clen == 0) break; + +#ifdef SUPPORT_UTF + if (utf) + { + if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else + { + unsigned int othercase; + if (c < 128) + othercase = fcc[c]; + else + /* If we have Unicode property support, we can use it to test the + other case of the character. */ +#ifdef SUPPORT_UCP + othercase = UCD_OTHERCASE(c); +#else + othercase = NOTACHAR; +#endif + + if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); } + } + } + else +#endif /* SUPPORT_UTF */ + /* Not UTF mode */ + { + if (TABLE_GET(c, lcc, c) == TABLE_GET(d, lcc, d)) + { ADD_NEW(state_offset + 2, 0); } + } + break; + + +#ifdef SUPPORT_UCP + /*-----------------------------------------------------------------*/ + /* This is a tricky one because it can match more than one character. + Find out how many characters to skip, and then set up a negative state + to wait for them to pass before continuing. */ + + case OP_EXTUNI: + if (clen > 0 && UCD_CATEGORY(c) != ucp_M) + { + const pcre_uchar *nptr = ptr + clen; + int ncount = 0; + while (nptr < end_subject) + { + int nclen = 1; + GETCHARLEN(c, nptr, nclen); + if (UCD_CATEGORY(c) != ucp_M) break; + ncount++; + nptr += nclen; + } + ADD_NEW_DATA(-(state_offset + 1), 0, ncount); + } + break; +#endif + + /*-----------------------------------------------------------------*/ + /* This is a tricky like EXTUNI because it too can match more than one + character (when CR is followed by LF). In this case, set up a negative + state to wait for one character to pass before continuing. */ + + case OP_ANYNL: + if (clen > 0) switch(c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + + case 0x000a: + ADD_NEW(state_offset + 1, 0); + break; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) + { + ADD_NEW_DATA(-(state_offset + 1), 0, 1); + } + else + { + ADD_NEW(state_offset + 1, 0); + } + break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_NOT_VSPACE: + if (clen > 0) switch(c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + break; + + default: + ADD_NEW(state_offset + 1, 0); + break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE: + if (clen > 0) switch(c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + ADD_NEW(state_offset + 1, 0); + break; + + default: break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_NOT_HSPACE: + if (clen > 0) switch(c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + + default: + ADD_NEW(state_offset + 1, 0); + break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE: + if (clen > 0) switch(c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + ADD_NEW(state_offset + 1, 0); + break; + } + break; + + /*-----------------------------------------------------------------*/ + /* Match a negated single character casefully. This is only used for + one-byte characters, that is, we know that d < 256. The character we are + checking (c) can be multibyte. */ + + case OP_NOT: + if (clen > 0 && c != d) { ADD_NEW(state_offset + dlen + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + /* Match a negated single character caselessly. This is only used for + one-byte characters, that is, we know that d < 256. The character we are + checking (c) can be multibyte. */ + + case OP_NOTI: + if (clen > 0 && c != d && c != fcc[d]) + { ADD_NEW(state_offset + dlen + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTPOSPLUSI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + + /* Fall through */ + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); } + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if (caseless) + { +#ifdef SUPPORT_UTF + if (utf && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = UCD_OTHERCASE(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (count > 0 && + (codevalue == OP_POSPLUS || codevalue == OP_NOTPOSPLUS)) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW(state_offset, count); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_QUERYI: + case OP_MINQUERYI: + case OP_POSQUERYI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTPOSQUERYI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTPOSQUERY: + ADD_ACTIVE(state_offset + dlen + 1, 0); + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if (caseless) + { +#ifdef SUPPORT_UTF + if (utf && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = UCD_OTHERCASE(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (codevalue == OP_POSQUERY || codevalue == OP_NOTPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset + dlen + 1, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_STARI: + case OP_MINSTARI: + case OP_POSSTARI: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPOSSTARI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPOSSTAR: + ADD_ACTIVE(state_offset + dlen + 1, 0); + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if (caseless) + { +#ifdef SUPPORT_UTF + if (utf && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = UCD_OTHERCASE(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (codevalue == OP_POSSTAR || codevalue == OP_NOTPOSSTAR) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXACTI: + case OP_NOTEXACTI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ + case OP_EXACT: + case OP_NOTEXACT: + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if (caseless) + { +#ifdef SUPPORT_UTF + if (utf && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = UCD_OTHERCASE(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_UPTOI: + case OP_MINUPTOI: + case OP_POSUPTOI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTPOSUPTOI: + caseless = TRUE; + codevalue -= OP_STARI - OP_STAR; + /* Fall through */ + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTPOSUPTO: + ADD_ACTIVE(state_offset + dlen + 1 + IMM2_SIZE, 0); + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if (caseless) + { +#ifdef SUPPORT_UTF + if (utf && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = UCD_OTHERCASE(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF */ + otherd = TABLE_GET(d, fcc, d); + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (codevalue == OP_POSUPTO || codevalue == OP_NOTPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + dlen + 1 + IMM2_SIZE, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + +/* ========================================================================== */ + /* These are the class-handling opcodes */ + + case OP_CLASS: + case OP_NCLASS: + case OP_XCLASS: + { + BOOL isinclass = FALSE; + int next_state_offset; + const pcre_uchar *ecode; + + /* For a simple class, there is always just a 32-byte table, and we + can set isinclass from it. */ + + if (codevalue != OP_XCLASS) + { + ecode = code + 1 + (32 / sizeof(pcre_uchar)); + if (clen > 0) + { + isinclass = (c > 255)? (codevalue == OP_NCLASS) : + ((((pcre_uint8 *)(code + 1))[c/8] & (1 << (c&7))) != 0); + } + } + + /* An extended class may have a table or a list of single characters, + ranges, or both, and it may be positive or negative. There's a + function that sorts all this out. */ + + else + { + ecode = code + GET(code, 1); + if (clen > 0) isinclass = PRIV(xclass)(c, code + 1 + LINK_SIZE, utf); + } + + /* At this point, isinclass is set for all kinds of class, and ecode + points to the byte after the end of the class. If there is a + quantifier, this is where it will be. */ + + next_state_offset = (int)(ecode - start_code); + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + ADD_ACTIVE(next_state_offset + 1, 0); + if (isinclass) { ADD_NEW(state_offset, 0); } + break; + + case OP_CRPLUS: + case OP_CRMINPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(next_state_offset + 1, 0); } + if (isinclass) { count++; ADD_NEW(state_offset, count); } + break; + + case OP_CRQUERY: + case OP_CRMINQUERY: + ADD_ACTIVE(next_state_offset + 1, 0); + if (isinclass) { ADD_NEW(next_state_offset + 1, 0); } + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + count = current_state->count; /* Already matched */ + if (count >= GET2(ecode, 1)) + { ADD_ACTIVE(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } + if (isinclass) + { + int max = GET2(ecode, 1 + IMM2_SIZE); + if (++count >= max && max != 0) /* Max 0 => no limit */ + { ADD_NEW(next_state_offset + 1 + 2 * IMM2_SIZE, 0); } + else + { ADD_NEW(state_offset, count); } + } + break; + + default: + if (isinclass) { ADD_NEW(next_state_offset, 0); } + break; + } + } + break; + +/* ========================================================================== */ + /* These are the opcodes for fancy brackets of various kinds. We have + to use recursion in order to handle them. The "always failing" assertion + (?!) is optimised to OP_FAIL when compiling, so we have to support that, + though the other "backtracking verbs" are not supported. */ + + case OP_FAIL: + forced_fail++; /* Count FAILs for multiple states */ + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + { + int rc; + int local_offsets[2]; + int local_workspace[1000]; + const pcre_uchar *endasscode = code + GET(code, 1); + + while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); + + rc = internal_dfa_exec( + md, /* static match data */ + code, /* this subexpression's code */ + ptr, /* where we currently are */ + (int)(ptr - start_subject), /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + rlevel); /* function recursion level */ + + if (rc == PCRE_ERROR_DFA_UITEM) return rc; + if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK)) + { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_COND: + case OP_SCOND: + { + int local_offsets[1000]; + int local_workspace[1000]; + int codelink = GET(code, 1); + int condcode; + + /* Because of the way auto-callout works during compile, a callout item + is inserted between OP_COND and an assertion condition. This does not + happen for the other conditions. */ + + if (code[LINK_SIZE+1] == OP_CALLOUT) + { + rrc = 0; + if (PUBL(callout) != NULL) + { + PUBL(callout_block) cb; + cb.version = 1; /* Version 1 of the callout block */ + cb.callout_number = code[LINK_SIZE+2]; + cb.offset_vector = offsets; +#ifdef COMPILE_PCRE8 + cb.subject = (PCRE_SPTR)start_subject; +#else + cb.subject = (PCRE_SPTR16)start_subject; +#endif + cb.subject_length = (int)(end_subject - start_subject); + cb.start_match = (int)(current_subject - start_subject); + cb.current_position = (int)(ptr - start_subject); + cb.pattern_position = GET(code, LINK_SIZE + 3); + cb.next_item_length = GET(code, 3 + 2*LINK_SIZE); + cb.capture_top = 1; + cb.capture_last = -1; + cb.callout_data = md->callout_data; + cb.mark = NULL; /* No (*MARK) support */ + if ((rrc = (*PUBL(callout))(&cb)) < 0) return rrc; /* Abandon */ + } + if (rrc > 0) break; /* Fail this thread */ + code += PRIV(OP_lengths)[OP_CALLOUT]; /* Skip callout data */ + } + + condcode = code[LINK_SIZE+1]; + + /* Back reference conditions are not supported */ + + if (condcode == OP_CREF || condcode == OP_NCREF) + return PCRE_ERROR_DFA_UCOND; + + /* The DEFINE condition is always false */ + + if (condcode == OP_DEF) + { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } + + /* The only supported version of OP_RREF is for the value RREF_ANY, + which means "test if in any recursion". We can't test for specifically + recursed groups. */ + + else if (condcode == OP_RREF || condcode == OP_NRREF) + { + int value = GET2(code, LINK_SIZE + 2); + if (value != RREF_ANY) return PCRE_ERROR_DFA_UCOND; + if (md->recursive != NULL) + { ADD_ACTIVE(state_offset + LINK_SIZE + 2 + IMM2_SIZE, 0); } + else { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } + } + + /* Otherwise, the condition is an assertion */ + + else + { + int rc; + const pcre_uchar *asscode = code + LINK_SIZE + 1; + const pcre_uchar *endasscode = asscode + GET(asscode, 1); + + while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); + + rc = internal_dfa_exec( + md, /* fixed match data */ + asscode, /* this subexpression's code */ + ptr, /* where we currently are */ + (int)(ptr - start_subject), /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + rlevel); /* function recursion level */ + + if (rc == PCRE_ERROR_DFA_UITEM) return rc; + if ((rc >= 0) == + (condcode == OP_ASSERT || condcode == OP_ASSERTBACK)) + { ADD_ACTIVE((int)(endasscode + LINK_SIZE + 1 - start_code), 0); } + else + { ADD_ACTIVE(state_offset + codelink + LINK_SIZE + 1, 0); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_RECURSE: + { + dfa_recursion_info *ri; + int local_offsets[1000]; + int local_workspace[1000]; + const pcre_uchar *callpat = start_code + GET(code, 1); + int recno = (callpat == md->start_code)? 0 : + GET2(callpat, 1 + LINK_SIZE); + int rc; + + DPRINTF(("%.*sStarting regex recursion\n", rlevel*2-2, SP)); + + /* Check for repeating a recursion without advancing the subject + pointer. This should catch convoluted mutual recursions. (Some simple + cases are caught at compile time.) */ + + for (ri = md->recursive; ri != NULL; ri = ri->prevrec) + if (recno == ri->group_num && ptr == ri->subject_position) + return PCRE_ERROR_RECURSELOOP; + + /* Remember this recursion and where we started it so as to + catch infinite loops. */ + + new_recursive.group_num = recno; + new_recursive.subject_position = ptr; + new_recursive.prevrec = md->recursive; + md->recursive = &new_recursive; + + rc = internal_dfa_exec( + md, /* fixed match data */ + callpat, /* this subexpression's code */ + ptr, /* where we currently are */ + (int)(ptr - start_subject), /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + rlevel); /* function recursion level */ + + md->recursive = new_recursive.prevrec; /* Done this recursion */ + + DPRINTF(("%.*sReturn from regex recursion: rc=%d\n", rlevel*2-2, SP, + rc)); + + /* Ran out of internal offsets */ + + if (rc == 0) return PCRE_ERROR_DFA_RECURSE; + + /* For each successful matched substring, set up the next state with a + count of characters to skip before trying it. Note that the count is in + characters, not bytes. */ + + if (rc > 0) + { + for (rc = rc*2 - 2; rc >= 0; rc -= 2) + { + int charcount = local_offsets[rc+1] - local_offsets[rc]; +#ifdef SUPPORT_UTF + const pcre_uchar *p = start_subject + local_offsets[rc]; + const pcre_uchar *pp = start_subject + local_offsets[rc+1]; + while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; +#endif + if (charcount > 0) + { + ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0, (charcount - 1)); + } + else + { + ADD_ACTIVE(state_offset + LINK_SIZE + 1, 0); + } + } + } + else if (rc != PCRE_ERROR_NOMATCH) return rc; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOSZERO: + { + int charcount, matched_count; + const pcre_uchar *local_ptr = ptr; + BOOL allow_zero; + + if (codevalue == OP_BRAPOSZERO) + { + allow_zero = TRUE; + codevalue = *(++code); /* Codevalue will be one of above BRAs */ + } + else allow_zero = FALSE; + + /* Loop to match the subpattern as many times as possible as if it were + a complete pattern. */ + + for (matched_count = 0;; matched_count++) + { + int local_offsets[2]; + int local_workspace[1000]; + + int rc = internal_dfa_exec( + md, /* fixed match data */ + code, /* this subexpression's code */ + local_ptr, /* where we currently are */ + (int)(ptr - start_subject), /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + rlevel); /* function recursion level */ + + /* Failed to match */ + + if (rc < 0) + { + if (rc != PCRE_ERROR_NOMATCH) return rc; + break; + } + + /* Matched: break the loop if zero characters matched. */ + + charcount = local_offsets[1] - local_offsets[0]; + if (charcount == 0) break; + local_ptr += charcount; /* Advance temporary position ptr */ + } + + /* At this point we have matched the subpattern matched_count + times, and local_ptr is pointing to the character after the end of the + last match. */ + + if (matched_count > 0 || allow_zero) + { + const pcre_uchar *end_subpattern = code; + int next_state_offset; + + do { end_subpattern += GET(end_subpattern, 1); } + while (*end_subpattern == OP_ALT); + next_state_offset = + (int)(end_subpattern - start_code + LINK_SIZE + 1); + + /* Optimization: if there are no more active states, and there + are no new states yet set up, then skip over the subject string + right here, to save looping. Otherwise, set up the new state to swing + into action when the end of the matched substring is reached. */ + + if (i + 1 >= active_count && new_count == 0) + { + ptr = local_ptr; + clen = 0; + ADD_NEW(next_state_offset, 0); + } + else + { + const pcre_uchar *p = ptr; + const pcre_uchar *pp = local_ptr; + charcount = (int)(pp - p); +#ifdef SUPPORT_UTF + while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; +#endif + ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); + } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_ONCE: + case OP_ONCE_NC: + { + int local_offsets[2]; + int local_workspace[1000]; + + int rc = internal_dfa_exec( + md, /* fixed match data */ + code, /* this subexpression's code */ + ptr, /* where we currently are */ + (int)(ptr - start_subject), /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + rlevel); /* function recursion level */ + + if (rc >= 0) + { + const pcre_uchar *end_subpattern = code; + int charcount = local_offsets[1] - local_offsets[0]; + int next_state_offset, repeat_state_offset; + + do { end_subpattern += GET(end_subpattern, 1); } + while (*end_subpattern == OP_ALT); + next_state_offset = + (int)(end_subpattern - start_code + LINK_SIZE + 1); + + /* If the end of this subpattern is KETRMAX or KETRMIN, we must + arrange for the repeat state also to be added to the relevant list. + Calculate the offset, or set -1 for no repeat. */ + + repeat_state_offset = (*end_subpattern == OP_KETRMAX || + *end_subpattern == OP_KETRMIN)? + (int)(end_subpattern - start_code - GET(end_subpattern, 1)) : -1; + + /* If we have matched an empty string, add the next state at the + current character pointer. This is important so that the duplicate + checking kicks in, which is what breaks infinite loops that match an + empty string. */ + + if (charcount == 0) + { + ADD_ACTIVE(next_state_offset, 0); + } + + /* Optimization: if there are no more active states, and there + are no new states yet set up, then skip over the subject string + right here, to save looping. Otherwise, set up the new state to swing + into action when the end of the matched substring is reached. */ + + else if (i + 1 >= active_count && new_count == 0) + { + ptr += charcount; + clen = 0; + ADD_NEW(next_state_offset, 0); + + /* If we are adding a repeat state at the new character position, + we must fudge things so that it is the only current state. + Otherwise, it might be a duplicate of one we processed before, and + that would cause it to be skipped. */ + + if (repeat_state_offset >= 0) + { + next_active_state = active_states; + active_count = 0; + i = -1; + ADD_ACTIVE(repeat_state_offset, 0); + } + } + else + { +#ifdef SUPPORT_UTF + const pcre_uchar *p = start_subject + local_offsets[0]; + const pcre_uchar *pp = start_subject + local_offsets[1]; + while (p < pp) if (NOT_FIRSTCHAR(*p++)) charcount--; +#endif + ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); + if (repeat_state_offset >= 0) + { ADD_NEW_DATA(-repeat_state_offset, 0, (charcount - 1)); } + } + } + else if (rc != PCRE_ERROR_NOMATCH) return rc; + } + break; + + +/* ========================================================================== */ + /* Handle callouts */ + + case OP_CALLOUT: + rrc = 0; + if (PUBL(callout) != NULL) + { + PUBL(callout_block) cb; + cb.version = 1; /* Version 1 of the callout block */ + cb.callout_number = code[1]; + cb.offset_vector = offsets; +#ifdef COMPILE_PCRE8 + cb.subject = (PCRE_SPTR)start_subject; +#else + cb.subject = (PCRE_SPTR16)start_subject; +#endif + cb.subject_length = (int)(end_subject - start_subject); + cb.start_match = (int)(current_subject - start_subject); + cb.current_position = (int)(ptr - start_subject); + cb.pattern_position = GET(code, 2); + cb.next_item_length = GET(code, 2 + LINK_SIZE); + cb.capture_top = 1; + cb.capture_last = -1; + cb.callout_data = md->callout_data; + cb.mark = NULL; /* No (*MARK) support */ + if ((rrc = (*PUBL(callout))(&cb)) < 0) return rrc; /* Abandon */ + } + if (rrc == 0) + { ADD_ACTIVE(state_offset + PRIV(OP_lengths)[OP_CALLOUT], 0); } + break; + + +/* ========================================================================== */ + default: /* Unsupported opcode */ + return PCRE_ERROR_DFA_UITEM; + } + + NEXT_ACTIVE_STATE: continue; + + } /* End of loop scanning active states */ + + /* We have finished the processing at the current subject character. If no + new states have been set for the next character, we have found all the + matches that we are going to find. If we are at the top level and partial + matching has been requested, check for appropriate conditions. + + The "forced_ fail" variable counts the number of (*F) encountered for the + character. If it is equal to the original active_count (saved in + workspace[1]) it means that (*F) was found on every active state. In this + case we don't want to give a partial match. + + The "could_continue" variable is true if a state could have continued but + for the fact that the end of the subject was reached. */ + + if (new_count <= 0) + { + if (rlevel == 1 && /* Top level, and */ + could_continue && /* Some could go on */ + forced_fail != workspace[1] && /* Not all forced fail & */ + ( /* either... */ + (md->moptions & PCRE_PARTIAL_HARD) != 0 /* Hard partial */ + || /* or... */ + ((md->moptions & PCRE_PARTIAL_SOFT) != 0 && /* Soft partial and */ + match_count < 0) /* no matches */ + ) && /* And... */ + ptr >= end_subject && /* Reached end of subject */ + ptr > md->start_used_ptr) /* Inspected non-empty string */ + { + if (offsetcount >= 2) + { + offsets[0] = (int)(md->start_used_ptr - start_subject); + offsets[1] = (int)(end_subject - start_subject); + } + match_count = PCRE_ERROR_PARTIAL; + } + + DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" + "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, match_count, + rlevel*2-2, SP)); + break; /* In effect, "return", but see the comment below */ + } + + /* One or more states are active for the next character. */ + + ptr += clen; /* Advance to next subject character */ + } /* Loop to move along the subject string */ + +/* Control gets here from "break" a few lines above. We do it this way because +if we use "return" above, we have compiler trouble. Some compilers warn if +there's nothing here because they think the function doesn't return a value. On +the other hand, if we put a dummy statement here, some more clever compilers +complain that it can't be reached. Sigh. */ + +return match_count; +} + + + + +/************************************************* +* Execute a Regular Expression - DFA engine * +*************************************************/ + +/* This external function applies a compiled re to a subject string using a DFA +engine. This function calls the internal function multiple times if the pattern +is not anchored. + +Arguments: + argument_re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets vector of match offsets + offsetcount size of same + workspace workspace vector + wscount size of same + +Returns: > 0 => number of match offset pairs placed in offsets + = 0 => offsets overflowed; longest matches are present + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_dfa_exec(const pcre *argument_re, const pcre_extra *extra_data, + const char *subject, int length, int start_offset, int options, int *offsets, + int offsetcount, int *workspace, int wscount) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_dfa_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, + PCRE_SPTR16 subject, int length, int start_offset, int options, int *offsets, + int offsetcount, int *workspace, int wscount) +#endif +{ +REAL_PCRE *re = (REAL_PCRE *)argument_re; +dfa_match_data match_block; +dfa_match_data *md = &match_block; +BOOL utf, anchored, startline, firstline; +const pcre_uchar *current_subject, *end_subject; +const pcre_study_data *study = NULL; + +const pcre_uchar *req_char_ptr; +const pcre_uint8 *start_bits = NULL; +BOOL has_first_char = FALSE; +BOOL has_req_char = FALSE; +pcre_uchar first_char = 0; +pcre_uchar first_char2 = 0; +pcre_uchar req_char = 0; +pcre_uchar req_char2 = 0; +int newline; + +/* Plausibility checks */ + +if ((options & ~PUBLIC_DFA_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; +if (re == NULL || subject == NULL || workspace == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (offsetcount < 0) return PCRE_ERROR_BADCOUNT; +if (wscount < 20) return PCRE_ERROR_DFA_WSSIZE; +if (start_offset < 0 || start_offset > length) return PCRE_ERROR_BADOFFSET; + +/* We need to find the pointer to any study data before we test for byte +flipping, so we scan the extra_data block first. This may set two fields in the +match block, so we must initialize them beforehand. However, the other fields +in the match block must not be set until after the byte flipping. */ + +md->tables = re->tables; +md->callout_data = NULL; + +if (extra_data != NULL) + { + unsigned int flags = extra_data->flags; + if ((flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = (const pcre_study_data *)extra_data->study_data; + if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) return PCRE_ERROR_DFA_UMLIMIT; + if ((flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) != 0) + return PCRE_ERROR_DFA_UMLIMIT; + if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0) + md->callout_data = extra_data->callout_data; + if ((flags & PCRE_EXTRA_TABLES) != 0) + md->tables = extra_data->tables; + } + +/* Check that the first field in the block is the magic number. If it is not, +return with PCRE_ERROR_BADMAGIC. However, if the magic number is equal to +REVERSED_MAGIC_NUMBER we return with PCRE_ERROR_BADENDIANNESS, which +means that the pattern is likely compiled with different endianness. */ + +if (re->magic_number != MAGIC_NUMBER) + return re->magic_number == REVERSED_MAGIC_NUMBER? + PCRE_ERROR_BADENDIANNESS:PCRE_ERROR_BADMAGIC; +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +/* Set some local values */ + +current_subject = (const pcre_uchar *)subject + start_offset; +end_subject = (const pcre_uchar *)subject + length; +req_char_ptr = current_subject - 1; + +#ifdef SUPPORT_UTF +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +utf = (re->options & PCRE_UTF8) != 0; +#else +utf = FALSE; +#endif + +anchored = (options & (PCRE_ANCHORED|PCRE_DFA_RESTART)) != 0 || + (re->options & PCRE_ANCHORED) != 0; + +/* The remaining fixed data for passing around. */ + +md->start_code = (const pcre_uchar *)argument_re + + re->name_table_offset + re->name_count * re->name_entry_size; +md->start_subject = (const pcre_uchar *)subject; +md->end_subject = end_subject; +md->start_offset = start_offset; +md->moptions = options; +md->poptions = re->options; + +/* If the BSR option is not set at match time, copy what was set +at compile time. */ + +if ((md->moptions & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) == 0) + { + if ((re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) != 0) + md->moptions |= re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE); +#ifdef BSR_ANYCRLF + else md->moptions |= PCRE_BSR_ANYCRLF; +#endif + } + +/* Handle different types of newline. The three bits give eight cases. If +nothing is set at run time, whatever was used at compile time applies. */ + +switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : (pcre_uint32)options) & + PCRE_NEWLINE_BITS) + { + case 0: newline = NEWLINE; break; /* Compile-time default */ + case PCRE_NEWLINE_CR: newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: newline = CHAR_NL; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break; + case PCRE_NEWLINE_ANY: newline = -1; break; + case PCRE_NEWLINE_ANYCRLF: newline = -2; break; + default: return PCRE_ERROR_BADNEWLINE; + } + +if (newline == -2) + { + md->nltype = NLTYPE_ANYCRLF; + } +else if (newline < 0) + { + md->nltype = NLTYPE_ANY; + } +else + { + md->nltype = NLTYPE_FIXED; + if (newline > 255) + { + md->nllen = 2; + md->nl[0] = (newline >> 8) & 255; + md->nl[1] = newline & 255; + } + else + { + md->nllen = 1; + md->nl[0] = newline; + } + } + +/* Check a UTF-8 string if required. Unfortunately there's no way of passing +back the character offset. */ + +#ifdef SUPPORT_UTF +if (utf && (options & PCRE_NO_UTF8_CHECK) == 0) + { + int erroroffset; + int errorcode = PRIV(valid_utf)((pcre_uchar *)subject, length, &erroroffset); + if (errorcode != 0) + { + if (offsetcount >= 2) + { + offsets[0] = erroroffset; + offsets[1] = errorcode; + } + return (errorcode <= PCRE_UTF8_ERR5 && (options & PCRE_PARTIAL_HARD) != 0)? + PCRE_ERROR_SHORTUTF8 : PCRE_ERROR_BADUTF8; + } + if (start_offset > 0 && start_offset < length && + NOT_FIRSTCHAR(((PCRE_PUCHAR)subject)[start_offset])) + return PCRE_ERROR_BADUTF8_OFFSET; + } +#endif + +/* If the exec call supplied NULL for tables, use the inbuilt ones. This +is a feature that makes it possible to save compiled regex and re-use them +in other programs later. */ + +if (md->tables == NULL) md->tables = PRIV(default_tables); + +/* The "must be at the start of a line" flags are used in a loop when finding +where to start. */ + +startline = (re->flags & PCRE_STARTLINE) != 0; +firstline = (re->options & PCRE_FIRSTLINE) != 0; + +/* Set up the first character to match, if available. The first_byte value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->flags & PCRE_FIRSTSET) != 0) + { + has_first_char = TRUE; + first_char = first_char2 = (pcre_uchar)(re->first_char); + if ((re->flags & PCRE_FCH_CASELESS) != 0) + { + first_char2 = TABLE_GET(first_char, md->tables + fcc_offset, first_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && first_char > 127) + first_char2 = UCD_OTHERCASE(first_char); +#endif + } + } + else + { + if (!startline && study != NULL && + (study->flags & PCRE_STUDY_MAPPED) != 0) + start_bits = study->start_bits; + } + } + +/* For anchored or unanchored matches, there may be a "last known required +character" set. */ + +if ((re->flags & PCRE_REQCHSET) != 0) + { + has_req_char = TRUE; + req_char = req_char2 = (pcre_uchar)(re->req_char); + if ((re->flags & PCRE_RCH_CASELESS) != 0) + { + req_char2 = TABLE_GET(req_char, md->tables + fcc_offset, req_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && req_char > 127) + req_char2 = UCD_OTHERCASE(req_char); +#endif + } + } + +/* Call the main matching function, looping for a non-anchored regex after a +failed match. If not restarting, perform certain optimizations at the start of +a match. */ + +for (;;) + { + int rc; + + if ((options & PCRE_DFA_RESTART) == 0) + { + const pcre_uchar *save_end_subject = end_subject; + + /* If firstline is TRUE, the start of the match is constrained to the first + line of a multiline string. Implement this by temporarily adjusting + end_subject so that we stop scanning at a newline. If the match fails at + the newline, later code breaks this loop. */ + + if (firstline) + { + PCRE_PUCHAR t = current_subject; +#ifdef SUPPORT_UTF + if (utf) + { + while (t < md->end_subject && !IS_NEWLINE(t)) + { + t++; + ACROSSCHAR(t < end_subject, *t, t++); + } + } + else +#endif + while (t < md->end_subject && !IS_NEWLINE(t)) t++; + end_subject = t; + } + + /* There are some optimizations that avoid running the match if a known + starting point is not found. However, there is an option that disables + these, for testing and for ensuring that all callouts do actually occur. + The option can be set in the regex by (*NO_START_OPT) or passed in + match-time options. */ + + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0) + { + /* Advance to a known first char. */ + + if (has_first_char) + { + if (first_char != first_char2) + while (current_subject < end_subject && + *current_subject != first_char && *current_subject != first_char2) + current_subject++; + else + while (current_subject < end_subject && + *current_subject != first_char) + current_subject++; + } + + /* Or to just after a linebreak for a multiline match if possible */ + + else if (startline) + { + if (current_subject > md->start_subject + start_offset) + { +#ifdef SUPPORT_UTF + if (utf) + { + while (current_subject < end_subject && + !WAS_NEWLINE(current_subject)) + { + current_subject++; + ACROSSCHAR(current_subject < end_subject, *current_subject, + current_subject++); + } + } + else +#endif + while (current_subject < end_subject && !WAS_NEWLINE(current_subject)) + current_subject++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one + more character. */ + + if (current_subject[-1] == CHAR_CR && + (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && + current_subject < end_subject && + *current_subject == CHAR_NL) + current_subject++; + } + } + + /* Or to a non-unique first char after study */ + + else if (start_bits != NULL) + { + while (current_subject < end_subject) + { + register unsigned int c = *current_subject; +#ifndef COMPILE_PCRE8 + if (c > 255) c = 255; +#endif + if ((start_bits[c/8] & (1 << (c&7))) == 0) + { + current_subject++; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + /* In non 8-bit mode, the iteration will stop for + characters > 255 at the beginning or not stop at all. */ + if (utf) + ACROSSCHAR(current_subject < end_subject, *current_subject, + current_subject++); +#endif + } + else break; + } + } + } + + /* Restore fudged end_subject */ + + end_subject = save_end_subject; + + /* The following two optimizations are disabled for partial matching or if + disabling is explicitly requested (and of course, by the test above, this + code is not obeyed when restarting after a partial match). */ + + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0 && + (options & (PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT)) == 0) + { + /* If the pattern was studied, a minimum subject length may be set. This + is a lower bound; no actual string of that length may actually match the + pattern. Although the value is, strictly, in characters, we treat it as + bytes to avoid spending too much time in this optimization. */ + + if (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0 && + (pcre_uint32)(end_subject - current_subject) < study->minlength) + return PCRE_ERROR_NOMATCH; + + /* If req_char is set, we know that that character must appear in the + subject for the match to succeed. If the first character is set, req_char + must be later in the subject; otherwise the test starts at the match + point. This optimization can save a huge amount of work in patterns with + nested unlimited repeats that aren't going to match. Writing separate + code for cased/caseless versions makes it go faster, as does using an + autoincrement and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end + can take a long time, and give bad performance on quite ordinary + patterns. This showed up when somebody was matching /^C/ on a 32-megabyte + string... so we don't do this when the string is sufficiently long. */ + + if (has_req_char && end_subject - current_subject < REQ_BYTE_MAX) + { + register PCRE_PUCHAR p = current_subject + (has_first_char? 1:0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_char_ptr) + { + if (req_char != req_char2) + { + while (p < end_subject) + { + register int pp = *p++; + if (pp == req_char || pp == req_char2) { p--; break; } + } + } + else + { + while (p < end_subject) + { + if (*p++ == req_char) { p--; break; } + } + } + + /* If we can't find the required character, break the matching loop, + which will cause a return or PCRE_ERROR_NOMATCH. */ + + if (p >= end_subject) break; + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + req_char_ptr = p; + } + } + } + } /* End of optimizations that are done when not restarting */ + + /* OK, now we can do the business */ + + md->start_used_ptr = current_subject; + md->recursive = NULL; + + rc = internal_dfa_exec( + md, /* fixed match data */ + md->start_code, /* this subexpression's code */ + current_subject, /* where we currently are */ + start_offset, /* start offset in subject */ + offsets, /* offset vector */ + offsetcount, /* size of same */ + workspace, /* workspace vector */ + wscount, /* size of same */ + 0); /* function recurse level */ + + /* Anything other than "no match" means we are done, always; otherwise, carry + on only if not anchored. */ + + if (rc != PCRE_ERROR_NOMATCH || anchored) return rc; + + /* Advance to the next subject character unless we are at the end of a line + and firstline is set. */ + + if (firstline && IS_NEWLINE(current_subject)) break; + current_subject++; +#ifdef SUPPORT_UTF + if (utf) + { + ACROSSCHAR(current_subject < end_subject, *current_subject, + current_subject++); + } +#endif + if (current_subject > end_subject) break; + + /* If we have just passed a CR and we are now at a LF, and the pattern does + not contain any explicit matches for \r or \n, and the newline option is CRLF + or ANY or ANYCRLF, advance the match position by one more character. */ + + if (current_subject[-1] == CHAR_CR && + current_subject < end_subject && + *current_subject == CHAR_NL && + (re->flags & PCRE_HASCRORLF) == 0 && + (md->nltype == NLTYPE_ANY || + md->nltype == NLTYPE_ANYCRLF || + md->nllen == 2)) + current_subject++; + + } /* "Bumpalong" loop */ + +return PCRE_ERROR_NOMATCH; +} + +/* End of pcre_dfa_exec.c */ diff --git a/src/lib/pcre/pcre_exec.c b/src/lib/pcre/pcre_exec.c new file mode 100644 index 0000000..2905808 --- /dev/null +++ b/src/lib/pcre/pcre_exec.c @@ -0,0 +1,6960 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains pcre_exec(), the externally visible function that does +pattern matching using an NFA algorithm, trying to mimic Perl as closely as +possible. There are also some static supporting functions. */ + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK md /* Block containing newline information */ +#define PSSTART start_subject /* Field containing processed string start */ +#define PSEND end_subject /* Field containing processed string end */ + +#include "pcre_internal.h" + +/* Undefine some potentially clashing cpp symbols */ + +#undef min +#undef max + +/* Values for setting in md->match_function_type to indicate two special types +of call to match(). We do it this way to save on using another stack variable, +as stack usage is to be discouraged. */ + +#define MATCH_CONDASSERT 1 /* Called to check a condition assertion */ +#define MATCH_CBEGROUP 2 /* Could-be-empty unlimited repeat group */ + +/* Non-error returns from the match() function. Error returns are externally +defined PCRE_ERROR_xxx codes, which are all negative. */ + +#define MATCH_MATCH 1 +#define MATCH_NOMATCH 0 + +/* Special internal returns from the match() function. Make them sufficiently +negative to avoid the external error codes. */ + +#define MATCH_ACCEPT (-999) +#define MATCH_COMMIT (-998) +#define MATCH_KETRPOS (-997) +#define MATCH_ONCE (-996) +#define MATCH_PRUNE (-995) +#define MATCH_SKIP (-994) +#define MATCH_SKIP_ARG (-993) +#define MATCH_THEN (-992) + +/* Maximum number of ints of offset to save on the stack for recursive calls. +If the offset vector is bigger, malloc is used. This should be a multiple of 3, +because the offset vector is always a multiple of 3 long. */ + +#define REC_STACK_SAVE_MAX 30 + +/* Min and max values for the common repeats; for the maxima, 0 => infinity */ + +static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; +static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; + + + +#ifdef PCRE_DEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + is_subject TRUE if printing from within md->start_subject + md pointer to matching data block, if is_subject is TRUE + +Returns: nothing +*/ + +static void +pchars(const pcre_uchar *p, int length, BOOL is_subject, match_data *md) +{ +unsigned int c; +if (is_subject && length > md->end_subject - p) length = md->end_subject - p; +while (length-- > 0) + if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); +} +#endif + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* Normally, if a back reference hasn't been set, the length that is passed is +negative, so the match always fails. However, in JavaScript compatibility mode, +the length passed is zero. Note that in caseless UTF-8 mode, the number of +subject bytes matched may be different to the number of reference bytes. + +Arguments: + offset index into the offset vector + eptr pointer into the subject + length length of reference to be matched (number of bytes) + md points to match data block + caseless TRUE if caseless + +Returns: < 0 if not matched, otherwise the number of subject bytes matched +*/ + +static int +match_ref(int offset, register PCRE_PUCHAR eptr, int length, match_data *md, + BOOL caseless) +{ +PCRE_PUCHAR eptr_start = eptr; +register PCRE_PUCHAR p = md->start_subject + md->offset_vector[offset]; + +#ifdef PCRE_DEBUG +if (eptr >= md->end_subject) + printf("matching subject "); +else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + } +printf(" against backref "); +pchars(p, length, FALSE, md); +printf("\n"); +#endif + +/* Always fail if reference not set (and not JavaScript compatible). */ + +if (length < 0) return -1; + +/* Separate the caseless case for speed. In UTF-8 mode we can only do this +properly if Unicode properties are supported. Otherwise, we can check only +ASCII characters. */ + +if (caseless) + { +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (md->utf) + { + /* Match characters up to the end of the reference. NOTE: the number of + bytes matched may differ, because there are some characters whose upper and + lower case versions code as different numbers of bytes. For example, U+023A + (2 bytes in UTF-8) is the upper case version of U+2C65 (3 bytes in UTF-8); + a sequence of 3 of the former uses 6 bytes, as does a sequence of two of + the latter. It is important, therefore, to check the length along the + reference, not along the subject (earlier code did this wrong). */ + + PCRE_PUCHAR endptr = p + length; + while (p < endptr) + { + int c, d; + if (eptr >= md->end_subject) return -1; + GETCHARINC(c, eptr); + GETCHARINC(d, p); + if (c != d && c != UCD_OTHERCASE(d)) return -1; + } + } + else +#endif +#endif + + /* The same code works when not in UTF-8 mode and in UTF-8 mode when there + is no UCP support. */ + { + if (eptr + length > md->end_subject) return -1; + while (length-- > 0) + { + if (TABLE_GET(*p, md->lcc, *p) != TABLE_GET(*eptr, md->lcc, *eptr)) return -1; + p++; + eptr++; + } + } + } + +/* In the caseful case, we can just compare the bytes, whether or not we +are in UTF-8 mode. */ + +else + { + if (eptr + length > md->end_subject) return -1; + while (length-- > 0) if (*p++ != *eptr++) return -1; + } + +return (int)(eptr - eptr_start); +} + + + +/*************************************************************************** +**************************************************************************** + RECURSION IN THE match() FUNCTION + +The match() function is highly recursive, though not every recursive call +increases the recursive depth. Nevertheless, some regular expressions can cause +it to recurse to a great depth. I was writing for Unix, so I just let it call +itself recursively. This uses the stack for saving everything that has to be +saved for a recursive call. On Unix, the stack can be large, and this works +fine. + +It turns out that on some non-Unix-like systems there are problems with +programs that use a lot of stack. (This despite the fact that every last chip +has oodles of memory these days, and techniques for extending the stack have +been known for decades.) So.... + +There is a fudge, triggered by defining NO_RECURSE, which avoids recursive +calls by keeping local variables that need to be preserved in blocks of memory +obtained from malloc() instead instead of on the stack. Macros are used to +achieve this so that the actual code doesn't look very different to what it +always used to. + +The original heap-recursive code used longjmp(). However, it seems that this +can be very slow on some operating systems. Following a suggestion from Stan +Switzer, the use of longjmp() has been abolished, at the cost of having to +provide a unique number for each call to RMATCH. There is no way of generating +a sequence of numbers at compile time in C. I have given them names, to make +them stand out more clearly. + +Crude tests on x86 Linux show a small speedup of around 5-8%. However, on +FreeBSD, avoiding longjmp() more than halves the time taken to run the standard +tests. Furthermore, not using longjmp() means that local dynamic variables +don't have indeterminate values; this has meant that the frame size can be +reduced because the result can be "passed back" by straight setting of the +variable instead of being passed in the frame. +**************************************************************************** +***************************************************************************/ + +/* Numbers for RMATCH calls. When this list is changed, the code at HEAP_RETURN +below must be updated in sync. */ + +enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, + RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, + RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, + RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40, + RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50, + RM51, RM52, RM53, RM54, RM55, RM56, RM57, RM58, RM59, RM60, + RM61, RM62, RM63, RM64, RM65, RM66 }; + +/* These versions of the macros use the stack, as normal. There are debugging +versions and production versions. Note that the "rw" argument of RMATCH isn't +actually used in this definition. */ + +#ifndef NO_RECURSE +#define REGISTER register + +#ifdef PCRE_DEBUG +#define RMATCH(ra,rb,rc,rd,re,rw) \ + { \ + printf("match() called in line %d\n", __LINE__); \ + rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1); \ + printf("to line %d\n", __LINE__); \ + } +#define RRETURN(ra) \ + { \ + printf("match() returned %d from line %d ", ra, __LINE__); \ + return ra; \ + } +#else +#define RMATCH(ra,rb,rc,rd,re,rw) \ + rrc = match(ra,rb,mstart,rc,rd,re,rdepth+1) +#define RRETURN(ra) return ra +#endif + +#else + + +/* These versions of the macros manage a private stack on the heap. Note that +the "rd" argument of RMATCH isn't actually used in this definition. It's the md +argument of match(), which never changes. */ + +#define REGISTER + +#define RMATCH(ra,rb,rc,rd,re,rw)\ + {\ + heapframe *newframe = (heapframe *)(PUBL(stack_malloc))(sizeof(heapframe));\ + if (newframe == NULL) RRETURN(PCRE_ERROR_NOMEMORY);\ + frame->Xwhere = rw; \ + newframe->Xeptr = ra;\ + newframe->Xecode = rb;\ + newframe->Xmstart = mstart;\ + newframe->Xoffset_top = rc;\ + newframe->Xeptrb = re;\ + newframe->Xrdepth = frame->Xrdepth + 1;\ + newframe->Xprevframe = frame;\ + frame = newframe;\ + DPRINTF(("restarting from line %d\n", __LINE__));\ + goto HEAP_RECURSE;\ + L_##rw:\ + DPRINTF(("jumped back to line %d\n", __LINE__));\ + } + +#define RRETURN(ra)\ + {\ + heapframe *oldframe = frame;\ + frame = oldframe->Xprevframe;\ + if (oldframe != &frame_zero) (PUBL(stack_free))(oldframe);\ + if (frame != NULL)\ + {\ + rrc = ra;\ + goto HEAP_RETURN;\ + }\ + return ra;\ + } + + +/* Structure for remembering the local variables in a private frame */ + +typedef struct heapframe { + struct heapframe *Xprevframe; + + /* Function arguments that may change */ + + PCRE_PUCHAR Xeptr; + const pcre_uchar *Xecode; + PCRE_PUCHAR Xmstart; + int Xoffset_top; + eptrblock *Xeptrb; + unsigned int Xrdepth; + + /* Function local variables */ + + PCRE_PUCHAR Xcallpat; +#ifdef SUPPORT_UTF + PCRE_PUCHAR Xcharptr; +#endif + PCRE_PUCHAR Xdata; + PCRE_PUCHAR Xnext; + PCRE_PUCHAR Xpp; + PCRE_PUCHAR Xprev; + PCRE_PUCHAR Xsaved_eptr; + + recursion_info Xnew_recursive; + + BOOL Xcur_is_word; + BOOL Xcondition; + BOOL Xprev_is_word; + +#ifdef SUPPORT_UCP + int Xprop_type; + int Xprop_value; + int Xprop_fail_result; + int Xoclength; + pcre_uchar Xocchars[6]; +#endif + + int Xcodelink; + int Xctype; + unsigned int Xfc; + int Xfi; + int Xlength; + int Xmax; + int Xmin; + int Xnumber; + int Xoffset; + int Xop; + int Xsave_capture_last; + int Xsave_offset1, Xsave_offset2, Xsave_offset3; + int Xstacksave[REC_STACK_SAVE_MAX]; + + eptrblock Xnewptrb; + + /* Where to jump back to */ + + int Xwhere; + +} heapframe; + +#endif + + +/*************************************************************************** +***************************************************************************/ + + + +/************************************************* +* Match from current position * +*************************************************/ + +/* This function is called recursively in many circumstances. Whenever it +returns a negative (error) response, the outer incarnation must also return the +same response. */ + +/* These macros pack up tests that are used for partial matching, and which +appear several times in the code. We set the "hit end" flag if the pointer is +at the end of the subject and also past the start of the subject (i.e. +something has been matched). For hard partial matching, we then return +immediately. The second one is used when we already know we are past the end of +the subject. */ + +#define CHECK_PARTIAL()\ + if (md->partial != 0 && eptr >= md->end_subject && \ + eptr > md->start_used_ptr) \ + { \ + md->hitend = TRUE; \ + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); \ + } + +#define SCHECK_PARTIAL()\ + if (md->partial != 0 && eptr > md->start_used_ptr) \ + { \ + md->hitend = TRUE; \ + if (md->partial > 1) RRETURN(PCRE_ERROR_PARTIAL); \ + } + + +/* Performance note: It might be tempting to extract commonly used fields from +the md structure (e.g. utf, end_subject) into individual variables to improve +performance. Tests using gcc on a SPARC disproved this; in the first case, it +made performance worse. + +Arguments: + eptr pointer to current character in subject + ecode pointer to current position in compiled code + mstart pointer to the current match start position (can be modified + by encountering \K) + offset_top current top pointer + md pointer to "static" info for the match + eptrb pointer to chain of blocks containing eptr at start of + brackets - for testing for empty matches + rdepth the recursion depth + +Returns: MATCH_MATCH if matched ) these values are >= 0 + MATCH_NOMATCH if failed to match ) + a negative MATCH_xxx value for PRUNE, SKIP, etc + a negative PCRE_ERROR_xxx value if aborted by an error condition + (e.g. stopped by repeated call or recursion limit) +*/ + +static int +match(REGISTER PCRE_PUCHAR eptr, REGISTER const pcre_uchar *ecode, + PCRE_PUCHAR mstart, int offset_top, match_data *md, eptrblock *eptrb, + unsigned int rdepth) +{ +/* These variables do not need to be preserved over recursion in this function, +so they can be ordinary variables in all cases. Mark some of them with +"register" because they are used a lot in loops. */ + +register int rrc; /* Returns from recursive calls */ +register int i; /* Used for loops not involving calls to RMATCH() */ +register unsigned int c; /* Character values not kept over RMATCH() calls */ +register BOOL utf; /* Local copy of UTF flag for speed */ + +BOOL minimize, possessive; /* Quantifier options */ +BOOL caseless; +int condcode; + +/* When recursion is not being used, all "local" variables that have to be +preserved over calls to RMATCH() are part of a "frame". We set up the top-level +frame on the stack here; subsequent instantiations are obtained from the heap +whenever RMATCH() does a "recursion". See the macro definitions above. Putting +the top-level on the stack rather than malloc-ing them all gives a performance +boost in many cases where there is not much "recursion". */ + +#ifdef NO_RECURSE +heapframe frame_zero; +heapframe *frame = &frame_zero; +frame->Xprevframe = NULL; /* Marks the top level */ + +/* Copy in the original argument variables */ + +frame->Xeptr = eptr; +frame->Xecode = ecode; +frame->Xmstart = mstart; +frame->Xoffset_top = offset_top; +frame->Xeptrb = eptrb; +frame->Xrdepth = rdepth; + +/* This is where control jumps back to to effect "recursion" */ + +HEAP_RECURSE: + +/* Macros make the argument variables come from the current frame */ + +#define eptr frame->Xeptr +#define ecode frame->Xecode +#define mstart frame->Xmstart +#define offset_top frame->Xoffset_top +#define eptrb frame->Xeptrb +#define rdepth frame->Xrdepth + +/* Ditto for the local variables */ + +#ifdef SUPPORT_UTF +#define charptr frame->Xcharptr +#endif +#define callpat frame->Xcallpat +#define codelink frame->Xcodelink +#define data frame->Xdata +#define next frame->Xnext +#define pp frame->Xpp +#define prev frame->Xprev +#define saved_eptr frame->Xsaved_eptr + +#define new_recursive frame->Xnew_recursive + +#define cur_is_word frame->Xcur_is_word +#define condition frame->Xcondition +#define prev_is_word frame->Xprev_is_word + +#ifdef SUPPORT_UCP +#define prop_type frame->Xprop_type +#define prop_value frame->Xprop_value +#define prop_fail_result frame->Xprop_fail_result +#define oclength frame->Xoclength +#define occhars frame->Xocchars +#endif + +#define ctype frame->Xctype +#define fc frame->Xfc +#define fi frame->Xfi +#define length frame->Xlength +#define max frame->Xmax +#define min frame->Xmin +#define number frame->Xnumber +#define offset frame->Xoffset +#define op frame->Xop +#define save_capture_last frame->Xsave_capture_last +#define save_offset1 frame->Xsave_offset1 +#define save_offset2 frame->Xsave_offset2 +#define save_offset3 frame->Xsave_offset3 +#define stacksave frame->Xstacksave + +#define newptrb frame->Xnewptrb + +/* When recursion is being used, local variables are allocated on the stack and +get preserved during recursion in the normal way. In this environment, fi and +i, and fc and c, can be the same variables. */ + +#else /* NO_RECURSE not defined */ +#define fi i +#define fc c + +/* Many of the following variables are used only in small blocks of the code. +My normal style of coding would have declared them within each of those blocks. +However, in order to accommodate the version of this code that uses an external +"stack" implemented on the heap, it is easier to declare them all here, so the +declarations can be cut out in a block. The only declarations within blocks +below are for variables that do not have to be preserved over a recursive call +to RMATCH(). */ + +#ifdef SUPPORT_UTF +const pcre_uchar *charptr; +#endif +const pcre_uchar *callpat; +const pcre_uchar *data; +const pcre_uchar *next; +PCRE_PUCHAR pp; +const pcre_uchar *prev; +PCRE_PUCHAR saved_eptr; + +recursion_info new_recursive; + +BOOL cur_is_word; +BOOL condition; +BOOL prev_is_word; + +#ifdef SUPPORT_UCP +int prop_type; +int prop_value; +int prop_fail_result; +int oclength; +pcre_uchar occhars[6]; +#endif + +int codelink; +int ctype; +int length; +int max; +int min; +int number; +int offset; +int op; +int save_capture_last; +int save_offset1, save_offset2, save_offset3; +int stacksave[REC_STACK_SAVE_MAX]; + +eptrblock newptrb; + +/* There is a special fudge for calling match() in a way that causes it to +measure the size of its basic stack frame when the stack is being used for +recursion. The second argument (ecode) being NULL triggers this behaviour. It +cannot normally ever be NULL. The return is the negated value of the frame +size. */ + +if (ecode == NULL) + { + if (rdepth == 0) + return match((PCRE_PUCHAR)&rdepth, NULL, NULL, 0, NULL, NULL, 1); + else + { + int len = (char *)&rdepth - (char *)eptr; + return (len > 0)? -len : len; + } + } +#endif /* NO_RECURSE */ + +/* To save space on the stack and in the heap frame, I have doubled up on some +of the local variables that are used only in localised parts of the code, but +still need to be preserved over recursive calls of match(). These macros define +the alternative names that are used. */ + +#define allow_zero cur_is_word +#define cbegroup condition +#define code_offset codelink +#define condassert condition +#define matched_once prev_is_word +#define foc number +#define save_mark data + +/* These statements are here to stop the compiler complaining about unitialized +variables. */ + +#ifdef SUPPORT_UCP +prop_value = 0; +prop_fail_result = 0; +#endif + + +/* This label is used for tail recursion, which is used in a few cases even +when NO_RECURSE is not defined, in order to reduce the amount of stack that is +used. Thanks to Ian Taylor for noticing this possibility and sending the +original patch. */ + +TAIL_RECURSE: + +/* OK, now we can get on with the real code of the function. Recursive calls +are specified by the macro RMATCH and RRETURN is used to return. When +NO_RECURSE is *not* defined, these just turn into a recursive call to match() +and a "return", respectively (possibly with some debugging if PCRE_DEBUG is +defined). However, RMATCH isn't like a function call because it's quite a +complicated macro. It has to be used in one particular way. This shouldn't, +however, impact performance when true recursion is being used. */ + +#ifdef SUPPORT_UTF +utf = md->utf; /* Local copy of the flag */ +#else +utf = FALSE; +#endif + +/* First check that we haven't called match() too many times, or that we +haven't exceeded the recursive call limit. */ + +if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT); +if (rdepth >= md->match_limit_recursion) RRETURN(PCRE_ERROR_RECURSIONLIMIT); + +/* At the start of a group with an unlimited repeat that may match an empty +string, the variable md->match_function_type is set to MATCH_CBEGROUP. It is +done this way to save having to use another function argument, which would take +up space on the stack. See also MATCH_CONDASSERT below. + +When MATCH_CBEGROUP is set, add the current subject pointer to the chain of +such remembered pointers, to be checked when we hit the closing ket, in order +to break infinite loops that match no characters. When match() is called in +other circumstances, don't add to the chain. The MATCH_CBEGROUP feature must +NOT be used with tail recursion, because the memory block that is used is on +the stack, so a new one may be required for each match(). */ + +if (md->match_function_type == MATCH_CBEGROUP) + { + newptrb.epb_saved_eptr = eptr; + newptrb.epb_prev = eptrb; + eptrb = &newptrb; + md->match_function_type = 0; + } + +/* Now start processing the opcodes. */ + +for (;;) + { + minimize = possessive = FALSE; + op = *ecode; + + switch(op) + { + case OP_MARK: + md->nomatch_mark = ecode + 2; + md->mark = NULL; /* In case previously set by assertion */ + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md, + eptrb, RM55); + if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && + md->mark == NULL) md->mark = ecode + 2; + + /* A return of MATCH_SKIP_ARG means that matching failed at SKIP with an + argument, and we must check whether that argument matches this MARK's + argument. It is passed back in md->start_match_ptr (an overloading of that + variable). If it does match, we reset that variable to the current subject + position and return MATCH_SKIP. Otherwise, pass back the return code + unaltered. */ + + else if (rrc == MATCH_SKIP_ARG && + STRCMP_UC_UC(ecode + 2, md->start_match_ptr) == 0) + { + md->start_match_ptr = eptr; + RRETURN(MATCH_SKIP); + } + RRETURN(rrc); + + case OP_FAIL: + RRETURN(MATCH_NOMATCH); + + /* COMMIT overrides PRUNE, SKIP, and THEN */ + + case OP_COMMIT: + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM52); + if (rrc != MATCH_NOMATCH && rrc != MATCH_PRUNE && + rrc != MATCH_SKIP && rrc != MATCH_SKIP_ARG && + rrc != MATCH_THEN) + RRETURN(rrc); + RRETURN(MATCH_COMMIT); + + /* PRUNE overrides THEN */ + + case OP_PRUNE: + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM51); + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + RRETURN(MATCH_PRUNE); + + case OP_PRUNE_ARG: + md->nomatch_mark = ecode + 2; + md->mark = NULL; /* In case previously set by assertion */ + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md, + eptrb, RM56); + if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && + md->mark == NULL) md->mark = ecode + 2; + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + RRETURN(MATCH_PRUNE); + + /* SKIP overrides PRUNE and THEN */ + + case OP_SKIP: + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM53); + if (rrc != MATCH_NOMATCH && rrc != MATCH_PRUNE && rrc != MATCH_THEN) + RRETURN(rrc); + md->start_match_ptr = eptr; /* Pass back current position */ + RRETURN(MATCH_SKIP); + + /* Note that, for Perl compatibility, SKIP with an argument does NOT set + nomatch_mark. There is a flag that disables this opcode when re-matching a + pattern that ended with a SKIP for which there was not a matching MARK. */ + + case OP_SKIP_ARG: + if (md->ignore_skip_arg) + { + ecode += PRIV(OP_lengths)[*ecode] + ecode[1]; + break; + } + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, md, + eptrb, RM57); + if (rrc != MATCH_NOMATCH && rrc != MATCH_PRUNE && rrc != MATCH_THEN) + RRETURN(rrc); + + /* Pass back the current skip name by overloading md->start_match_ptr and + returning the special MATCH_SKIP_ARG return code. This will either be + caught by a matching MARK, or get to the top, where it causes a rematch + with the md->ignore_skip_arg flag set. */ + + md->start_match_ptr = ecode + 2; + RRETURN(MATCH_SKIP_ARG); + + /* For THEN (and THEN_ARG) we pass back the address of the opcode, so that + the branch in which it occurs can be determined. Overload the start of + match pointer to do this. */ + + case OP_THEN: + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM54); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->start_match_ptr = ecode; + RRETURN(MATCH_THEN); + + case OP_THEN_ARG: + md->nomatch_mark = ecode + 2; + md->mark = NULL; /* In case previously set by assertion */ + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode] + ecode[1], offset_top, + md, eptrb, RM58); + if ((rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) && + md->mark == NULL) md->mark = ecode + 2; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->start_match_ptr = ecode; + RRETURN(MATCH_THEN); + + /* Handle an atomic group that does not contain any capturing parentheses. + This can be handled like an assertion. Prior to 8.13, all atomic groups + were handled this way. In 8.13, the code was changed as below for ONCE, so + that backups pass through the group and thereby reset captured values. + However, this uses a lot more stack, so in 8.20, atomic groups that do not + contain any captures generate OP_ONCE_NC, which can be handled in the old, + less stack intensive way. + + Check the alternative branches in turn - the matching won't pass the KET + for this kind of subpattern. If any one branch matches, we carry on as at + the end of a normal bracket, leaving the subject pointer, but resetting + the start-of-match value in case it was changed by \K. */ + + case OP_ONCE_NC: + prev = ecode; + saved_eptr = eptr; + save_mark = md->mark; + do + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM64); + if (rrc == MATCH_MATCH) /* Note: _not_ MATCH_ACCEPT */ + { + mstart = md->start_match_ptr; + break; + } + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += GET(ecode,1); + md->mark = save_mark; + } + while (*ecode == OP_ALT); + + /* If hit the end of the group (which could be repeated), fail */ + + if (*ecode != OP_ONCE_NC && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH); + + /* Continue as from after the group, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += GET(ecode, 1); while (*ecode == OP_ALT); + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + ecode += 1+LINK_SIZE; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. The second "call" of match() + uses tail recursion, to avoid using another stack frame. */ + + if (*ecode == OP_KETRMIN) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM65); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode = prev; + goto TAIL_RECURSE; + } + else /* OP_KETRMAX */ + { + md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, prev, offset_top, md, eptrb, RM66); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += 1 + LINK_SIZE; + goto TAIL_RECURSE; + } + /* Control never gets here */ + + /* Handle a capturing bracket, other than those that are possessive with an + unlimited repeat. If there is space in the offset vector, save the current + subject position in the working slot at the top of the vector. We mustn't + change the current values of the data slot, because they may be set from a + previous iteration of this group, and be referred to by a reference inside + the group. A failure to match might occur after the group has succeeded, + if something later on doesn't match. For this reason, we need to restore + the working value and also the values of the final offsets, in case they + were set by a previous iteration of the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were + a non-capturing bracket. Don't worry about setting the flag for the error + case here; that is handled in the code for KET. */ + + case OP_CBRA: + case OP_SCBRA: + number = GET2(ecode, 1+LINK_SIZE); + offset = number << 1; + +#ifdef PCRE_DEBUG + printf("start bracket %d\n", number); + printf("subject="); + pchars(eptr, 16, TRUE, md); + printf("\n"); +#endif + + if (offset < md->offset_max) + { + save_offset1 = md->offset_vector[offset]; + save_offset2 = md->offset_vector[offset+1]; + save_offset3 = md->offset_vector[md->offset_end - number]; + save_capture_last = md->capture_last; + save_mark = md->mark; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + md->offset_vector[md->offset_end - number] = + (int)(eptr - md->start_subject); + + for (;;) + { + if (op >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM1); + if (rrc == MATCH_ONCE) break; /* Backing up through an atomic group */ + + /* If we backed up to a THEN, check whether it is within the current + branch by comparing the address of the THEN that is passed back with + the end of the branch. If it is within the current branch, and the + branch is one of two or more alternatives (it either starts or ends + with OP_ALT), we have reached the limit of THEN's action, so convert + the return code to NOMATCH, which will cause normal backtracking to + happen from now on. Otherwise, THEN is passed back to an outer + alternative. This implements Perl's treatment of parenthesized groups, + where a group not containing | does not affect the current alternative, + that is, (X) is NOT the same as (X|(*F)). */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + /* Anything other than NOMATCH is passed back. */ + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->capture_last = save_capture_last; + ecode += GET(ecode, 1); + md->mark = save_mark; + if (*ecode != OP_ALT) break; + } + + DPRINTF(("bracket %d failed\n", number)); + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; + + /* At this point, rrc will be one of MATCH_ONCE or MATCH_NOMATCH. */ + + RRETURN(rrc); + } + + /* FALL THROUGH ... Insufficient room for saving captured contents. Treat + as a non-capturing bracket. */ + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + DPRINTF(("insufficient capture room: treat as non-capturing\n")); + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + /* Non-capturing or atomic group, except for possessive with unlimited + repeat and ONCE group with no captures. Loop for all the alternatives. + + When we get to the final alternative within the brackets, we used to return + the result of a recursive call to match() whatever happened so it was + possible to reduce stack usage by turning this into a tail recursion, + except in the case of a possibly empty group. However, now that there is + the possiblity of (*THEN) occurring in the final alternative, this + optimization is no longer always possible. + + We can optimize if we know there are no (*THEN)s in the pattern; at present + this is the best that can be done. + + MATCH_ONCE is returned when the end of an atomic group is successfully + reached, but subsequent matching fails. It passes back up the tree (causing + captured values to be reset) until the original atomic group level is + reached. This is tested by comparing md->once_target with the start of the + group. At this point, the return is converted into MATCH_NOMATCH so that + previous backup points can be taken. */ + + case OP_ONCE: + case OP_BRA: + case OP_SBRA: + DPRINTF(("start non-capturing bracket\n")); + + for (;;) + { + if (op >= OP_SBRA || op == OP_ONCE) md->match_function_type = MATCH_CBEGROUP; + + /* If this is not a possibly empty group, and there are no (*THEN)s in + the pattern, and this is the final alternative, optimize as described + above. */ + + else if (!md->hasthen && ecode[GET(ecode, 1)] != OP_ALT) + { + ecode += PRIV(OP_lengths)[*ecode]; + goto TAIL_RECURSE; + } + + /* In all other cases, we have to make another call to match(). */ + + save_mark = md->mark; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, eptrb, + RM2); + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) + { + if (rrc == MATCH_ONCE) + { + const pcre_uchar *scode = ecode; + if (*scode != OP_ONCE) /* If not at start, find it */ + { + while (*scode == OP_ALT) scode += GET(scode, 1); + scode -= GET(scode, 1); + } + if (md->once_target == scode) rrc = MATCH_NOMATCH; + } + RRETURN(rrc); + } + ecode += GET(ecode, 1); + md->mark = save_mark; + if (*ecode != OP_ALT) break; + } + + RRETURN(MATCH_NOMATCH); + + /* Handle possessive capturing brackets with an unlimited repeat. We come + here from BRAZERO with allow_zero set TRUE. The offset_vector values are + handled similarly to the normal case above. However, the matching is + different. The end of these brackets will always be OP_KETRPOS, which + returns MATCH_KETRPOS without going further in the pattern. By this means + we can handle the group by iteration rather than recursion, thereby + reducing the amount of stack needed. */ + + case OP_CBRAPOS: + case OP_SCBRAPOS: + allow_zero = FALSE; + + POSSESSIVE_CAPTURE: + number = GET2(ecode, 1+LINK_SIZE); + offset = number << 1; + +#ifdef PCRE_DEBUG + printf("start possessive bracket %d\n", number); + printf("subject="); + pchars(eptr, 16, TRUE, md); + printf("\n"); +#endif + + if (offset < md->offset_max) + { + matched_once = FALSE; + code_offset = (int)(ecode - md->start_code); + + save_offset1 = md->offset_vector[offset]; + save_offset2 = md->offset_vector[offset+1]; + save_offset3 = md->offset_vector[md->offset_end - number]; + save_capture_last = md->capture_last; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + + /* Each time round the loop, save the current subject position for use + when the group matches. For MATCH_MATCH, the group has matched, so we + restart it with a new subject starting position, remembering that we had + at least one match. For MATCH_NOMATCH, carry on with the alternatives, as + usual. If we haven't matched any alternatives in any iteration, check to + see if a previous iteration matched. If so, the group has matched; + continue from afterwards. Otherwise it has failed; restore the previous + capture values before returning NOMATCH. */ + + for (;;) + { + md->offset_vector[md->offset_end - number] = + (int)(eptr - md->start_subject); + if (op >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM63); + if (rrc == MATCH_KETRPOS) + { + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + ecode = md->start_code + code_offset; + save_capture_last = md->capture_last; + matched_once = TRUE; + continue; + } + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->capture_last = save_capture_last; + ecode += GET(ecode, 1); + if (*ecode != OP_ALT) break; + } + + if (!matched_once) + { + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; + } + + if (allow_zero || matched_once) + { + ecode += 1 + LINK_SIZE; + break; + } + + RRETURN(MATCH_NOMATCH); + } + + /* FALL THROUGH ... Insufficient room for saving captured contents. Treat + as a non-capturing bracket. */ + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + DPRINTF(("insufficient capture room: treat as non-capturing\n")); + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + /* Non-capturing possessive bracket with unlimited repeat. We come here + from BRAZERO with allow_zero = TRUE. The code is similar to the above, + without the capturing complication. It is written out separately for speed + and cleanliness. */ + + case OP_BRAPOS: + case OP_SBRAPOS: + allow_zero = FALSE; + + POSSESSIVE_NON_CAPTURE: + matched_once = FALSE; + code_offset = (int)(ecode - md->start_code); + + for (;;) + { + if (op >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + PRIV(OP_lengths)[*ecode], offset_top, md, + eptrb, RM48); + if (rrc == MATCH_KETRPOS) + { + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + ecode = md->start_code + code_offset; + matched_once = TRUE; + continue; + } + + /* See comment in the code for capturing groups above about handling + THEN. */ + + if (rrc == MATCH_THEN) + { + next = ecode + GET(ecode,1); + if (md->start_match_ptr < next && + (*ecode == OP_ALT || *next == OP_ALT)) + rrc = MATCH_NOMATCH; + } + + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += GET(ecode, 1); + if (*ecode != OP_ALT) break; + } + + if (matched_once || allow_zero) + { + ecode += 1 + LINK_SIZE; + break; + } + RRETURN(MATCH_NOMATCH); + + /* Control never reaches here. */ + + /* Conditional group: compilation checked that there are no more than + two branches. If the condition is false, skipping the first branch takes us + past the end if there is only one branch, but that's OK because that is + exactly what going to the ket would do. */ + + case OP_COND: + case OP_SCOND: + codelink = GET(ecode, 1); + + /* Because of the way auto-callout works during compile, a callout item is + inserted between OP_COND and an assertion condition. */ + + if (ecode[LINK_SIZE+1] == OP_CALLOUT) + { + if (PUBL(callout) != NULL) + { + PUBL(callout_block) cb; + cb.version = 2; /* Version 1 of the callout block */ + cb.callout_number = ecode[LINK_SIZE+2]; + cb.offset_vector = md->offset_vector; +#ifdef COMPILE_PCRE8 + cb.subject = (PCRE_SPTR)md->start_subject; +#else + cb.subject = (PCRE_SPTR16)md->start_subject; +#endif + cb.subject_length = (int)(md->end_subject - md->start_subject); + cb.start_match = (int)(mstart - md->start_subject); + cb.current_position = (int)(eptr - md->start_subject); + cb.pattern_position = GET(ecode, LINK_SIZE + 3); + cb.next_item_length = GET(ecode, 3 + 2*LINK_SIZE); + cb.capture_top = offset_top/2; + cb.capture_last = md->capture_last; + cb.callout_data = md->callout_data; + cb.mark = md->nomatch_mark; + if ((rrc = (*PUBL(callout))(&cb)) > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + } + ecode += PRIV(OP_lengths)[OP_CALLOUT]; + } + + condcode = ecode[LINK_SIZE+1]; + + /* Now see what the actual condition is */ + + if (condcode == OP_RREF || condcode == OP_NRREF) /* Recursion test */ + { + if (md->recursive == NULL) /* Not recursing => FALSE */ + { + condition = FALSE; + ecode += GET(ecode, 1); + } + else + { + int recno = GET2(ecode, LINK_SIZE + 2); /* Recursion group number*/ + condition = (recno == RREF_ANY || recno == md->recursive->group_num); + + /* If the test is for recursion into a specific subpattern, and it is + false, but the test was set up by name, scan the table to see if the + name refers to any other numbers, and test them. The condition is true + if any one is set. */ + + if (!condition && condcode == OP_NRREF) + { + pcre_uchar *slotA = md->name_table; + for (i = 0; i < md->name_count; i++) + { + if (GET2(slotA, 0) == recno) break; + slotA += md->name_entry_size; + } + + /* Found a name for the number - there can be only one; duplicate + names for different numbers are allowed, but not vice versa. First + scan down for duplicates. */ + + if (i < md->name_count) + { + pcre_uchar *slotB = slotA; + while (slotB > md->name_table) + { + slotB -= md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == md->recursive->group_num; + if (condition) break; + } + else break; + } + + /* Scan up for duplicates */ + + if (!condition) + { + slotB = slotA; + for (i++; i < md->name_count; i++) + { + slotB += md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == md->recursive->group_num; + if (condition) break; + } + else break; + } + } + } + } + + /* Chose branch according to the condition */ + + ecode += condition? 1 + IMM2_SIZE : GET(ecode, 1); + } + } + + else if (condcode == OP_CREF || condcode == OP_NCREF) /* Group used test */ + { + offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */ + condition = offset < offset_top && md->offset_vector[offset] >= 0; + + /* If the numbered capture is unset, but the reference was by name, + scan the table to see if the name refers to any other numbers, and test + them. The condition is true if any one is set. This is tediously similar + to the code above, but not close enough to try to amalgamate. */ + + if (!condition && condcode == OP_NCREF) + { + int refno = offset >> 1; + pcre_uchar *slotA = md->name_table; + + for (i = 0; i < md->name_count; i++) + { + if (GET2(slotA, 0) == refno) break; + slotA += md->name_entry_size; + } + + /* Found a name for the number - there can be only one; duplicate names + for different numbers are allowed, but not vice versa. First scan down + for duplicates. */ + + if (i < md->name_count) + { + pcre_uchar *slotB = slotA; + while (slotB > md->name_table) + { + slotB -= md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + offset = GET2(slotB, 0) << 1; + condition = offset < offset_top && + md->offset_vector[offset] >= 0; + if (condition) break; + } + else break; + } + + /* Scan up for duplicates */ + + if (!condition) + { + slotB = slotA; + for (i++; i < md->name_count; i++) + { + slotB += md->name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + offset = GET2(slotB, 0) << 1; + condition = offset < offset_top && + md->offset_vector[offset] >= 0; + if (condition) break; + } + else break; + } + } + } + } + + /* Chose branch according to the condition */ + + ecode += condition? 1 + IMM2_SIZE : GET(ecode, 1); + } + + else if (condcode == OP_DEF) /* DEFINE - always false */ + { + condition = FALSE; + ecode += GET(ecode, 1); + } + + /* The condition is an assertion. Call match() to evaluate it - setting + md->match_function_type to MATCH_CONDASSERT causes it to stop at the end of + an assertion. */ + + else + { + md->match_function_type = MATCH_CONDASSERT; + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, NULL, RM3); + if (rrc == MATCH_MATCH) + { + if (md->end_offset_top > offset_top) + offset_top = md->end_offset_top; /* Captures may have happened */ + condition = TRUE; + ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE + 2); + while (*ecode == OP_ALT) ecode += GET(ecode, 1); + } + + /* PCRE doesn't allow the effect of (*THEN) to escape beyond an + assertion; it is therefore treated as NOMATCH. */ + + else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + { + RRETURN(rrc); /* Need braces because of following else */ + } + else + { + condition = FALSE; + ecode += codelink; + } + } + + /* We are now at the branch that is to be obeyed. As there is only one, can + use tail recursion to avoid using another stack frame, except when there is + unlimited repeat of a possibly empty group. In the latter case, a recursive + call to match() is always required, unless the second alternative doesn't + exist, in which case we can just plough on. Note that, for compatibility + with Perl, the | in a conditional group is NOT treated as creating two + alternatives. If a THEN is encountered in the branch, it propagates out to + the enclosing alternative (unless nested in a deeper set of alternatives, + of course). */ + + if (condition || *ecode == OP_ALT) + { + if (op != OP_SCOND) + { + ecode += 1 + LINK_SIZE; + goto TAIL_RECURSE; + } + + md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM49); + RRETURN(rrc); + } + + /* Condition false & no alternative; continue after the group. */ + + else + { + ecode += 1 + LINK_SIZE; + } + break; + + + /* Before OP_ACCEPT there may be any number of OP_CLOSE opcodes, + to close any currently open capturing brackets. */ + + case OP_CLOSE: + number = GET2(ecode, 1); + offset = number << 1; + +#ifdef PCRE_DEBUG + printf("end bracket %d at *ACCEPT", number); + printf("\n"); +#endif + + md->capture_last = number; + if (offset >= md->offset_max) md->offset_overflow = TRUE; else + { + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = (int)(eptr - md->start_subject); + if (offset_top <= offset) offset_top = offset + 2; + } + ecode += 1 + IMM2_SIZE; + break; + + + /* End of the pattern, either real or forced. */ + + case OP_END: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + + /* If we have matched an empty string, fail if not in an assertion and not + in a recursion if either PCRE_NOTEMPTY is set, or if PCRE_NOTEMPTY_ATSTART + is set and we have matched at the start of the subject. In both cases, + backtracking will then try other alternatives, if any. */ + + if (eptr == mstart && op != OP_ASSERT_ACCEPT && + md->recursive == NULL && + (md->notempty || + (md->notempty_atstart && + mstart == md->start_subject + md->start_offset))) + RRETURN(MATCH_NOMATCH); + + /* Otherwise, we have a match. */ + + md->end_match_ptr = eptr; /* Record where we ended */ + md->end_offset_top = offset_top; /* and how many extracts were taken */ + md->start_match_ptr = mstart; /* and the start (\K can modify) */ + + /* For some reason, the macros don't work properly if an expression is + given as the argument to RRETURN when the heap is in use. */ + + rrc = (op == OP_END)? MATCH_MATCH : MATCH_ACCEPT; + RRETURN(rrc); + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. When the assertion is part + of a condition, we want to return immediately afterwards. The caller of + this incarnation of the match() function will have set MATCH_CONDASSERT in + md->match_function type, and one of these opcodes will be the first opcode + that is processed. We use a local variable that is preserved over calls to + match() to remember this case. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + save_mark = md->mark; + if (md->match_function_type == MATCH_CONDASSERT) + { + condassert = TRUE; + md->match_function_type = 0; + } + else condassert = FALSE; + + do + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, NULL, RM4); + if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) + { + mstart = md->start_match_ptr; /* In case \K reset it */ + break; + } + + /* PCRE does not allow THEN to escape beyond an assertion; it is treated + as NOMATCH. */ + + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + ecode += GET(ecode, 1); + md->mark = save_mark; + } + while (*ecode == OP_ALT); + + if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH); + + /* If checking an assertion for a condition, return MATCH_MATCH. */ + + if (condassert) RRETURN(MATCH_MATCH); + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + ecode += 1 + LINK_SIZE; + offset_top = md->end_offset_top; + continue; + + /* Negative assertion: all branches must fail to match. Encountering SKIP, + PRUNE, or COMMIT means we must assume failure without checking subsequent + branches. */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + save_mark = md->mark; + if (md->match_function_type == MATCH_CONDASSERT) + { + condassert = TRUE; + md->match_function_type = 0; + } + else condassert = FALSE; + + do + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, NULL, RM5); + md->mark = save_mark; + if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) RRETURN(MATCH_NOMATCH); + if (rrc == MATCH_SKIP || rrc == MATCH_PRUNE || rrc == MATCH_COMMIT) + { + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + break; + } + + /* PCRE does not allow THEN to escape beyond an assertion; it is treated + as NOMATCH. */ + + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + ecode += GET(ecode,1); + } + while (*ecode == OP_ALT); + + if (condassert) RRETURN(MATCH_MATCH); /* Condition assertion */ + + ecode += 1 + LINK_SIZE; + continue; + + /* Move the subject pointer back. This occurs only at the start of + each branch of a lookbehind assertion. If we are too close to the start to + move back, this match function fails. When working with UTF-8 we move + back a number of characters, not bytes. */ + + case OP_REVERSE: +#ifdef SUPPORT_UTF + if (utf) + { + i = GET(ecode, 1); + while (i-- > 0) + { + eptr--; + if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH); + BACKCHAR(eptr); + } + } + else +#endif + + /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ + + { + eptr -= GET(ecode, 1); + if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH); + } + + /* Save the earliest consulted character, then skip to next op code */ + + if (eptr < md->start_used_ptr) md->start_used_ptr = eptr; + ecode += 1 + LINK_SIZE; + break; + + /* The callout item calls an external function, if one is provided, passing + details of the match so far. This is mainly for debugging, though the + function is able to force a failure. */ + + case OP_CALLOUT: + if (PUBL(callout) != NULL) + { + PUBL(callout_block) cb; + cb.version = 2; /* Version 1 of the callout block */ + cb.callout_number = ecode[1]; + cb.offset_vector = md->offset_vector; +#ifdef COMPILE_PCRE8 + cb.subject = (PCRE_SPTR)md->start_subject; +#else + cb.subject = (PCRE_SPTR16)md->start_subject; +#endif + cb.subject_length = (int)(md->end_subject - md->start_subject); + cb.start_match = (int)(mstart - md->start_subject); + cb.current_position = (int)(eptr - md->start_subject); + cb.pattern_position = GET(ecode, 2); + cb.next_item_length = GET(ecode, 2 + LINK_SIZE); + cb.capture_top = offset_top/2; + cb.capture_last = md->capture_last; + cb.callout_data = md->callout_data; + cb.mark = md->nomatch_mark; + if ((rrc = (*PUBL(callout))(&cb)) > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + } + ecode += 2 + 2*LINK_SIZE; + break; + + /* Recursion either matches the current regex, or some subexpression. The + offset data is the offset to the starting bracket from the start of the + whole pattern. (This is so that it works from duplicated subpatterns.) + + The state of the capturing groups is preserved over recursion, and + re-instated afterwards. We don't know how many are started and not yet + finished (offset_top records the completed total) so we just have to save + all the potential data. There may be up to 65535 such values, which is too + large to put on the stack, but using malloc for small numbers seems + expensive. As a compromise, the stack is used when there are no more than + REC_STACK_SAVE_MAX values to store; otherwise malloc is used. + + There are also other values that have to be saved. We use a chained + sequence of blocks that actually live on the stack. Thanks to Robin Houston + for the original version of this logic. It has, however, been hacked around + a lot, so he is not to blame for the current way it works. */ + + case OP_RECURSE: + { + recursion_info *ri; + int recno; + + callpat = md->start_code + GET(ecode, 1); + recno = (callpat == md->start_code)? 0 : + GET2(callpat, 1 + LINK_SIZE); + + /* Check for repeating a recursion without advancing the subject pointer. + This should catch convoluted mutual recursions. (Some simple cases are + caught at compile time.) */ + + for (ri = md->recursive; ri != NULL; ri = ri->prevrec) + if (recno == ri->group_num && eptr == ri->subject_position) + RRETURN(PCRE_ERROR_RECURSELOOP); + + /* Add to "recursing stack" */ + + new_recursive.group_num = recno; + new_recursive.subject_position = eptr; + new_recursive.prevrec = md->recursive; + md->recursive = &new_recursive; + + /* Where to continue from afterwards */ + + ecode += 1 + LINK_SIZE; + + /* Now save the offset data */ + + new_recursive.saved_max = md->offset_end; + if (new_recursive.saved_max <= REC_STACK_SAVE_MAX) + new_recursive.offset_save = stacksave; + else + { + new_recursive.offset_save = + (int *)(PUBL(malloc))(new_recursive.saved_max * sizeof(int)); + if (new_recursive.offset_save == NULL) RRETURN(PCRE_ERROR_NOMEMORY); + } + memcpy(new_recursive.offset_save, md->offset_vector, + new_recursive.saved_max * sizeof(int)); + + /* OK, now we can do the recursion. After processing each alternative, + restore the offset data. If there were nested recursions, md->recursive + might be changed, so reset it before looping. */ + + DPRINTF(("Recursing into group %d\n", new_recursive.group_num)); + cbegroup = (*callpat >= OP_SBRA); + do + { + if (cbegroup) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, callpat + PRIV(OP_lengths)[*callpat], offset_top, + md, eptrb, RM6); + memcpy(md->offset_vector, new_recursive.offset_save, + new_recursive.saved_max * sizeof(int)); + md->recursive = new_recursive.prevrec; + if (rrc == MATCH_MATCH || rrc == MATCH_ACCEPT) + { + DPRINTF(("Recursion matched\n")); + if (new_recursive.offset_save != stacksave) + (PUBL(free))(new_recursive.offset_save); + + /* Set where we got to in the subject, and reset the start in case + it was changed by \K. This *is* propagated back out of a recursion, + for Perl compatibility. */ + + eptr = md->end_match_ptr; + mstart = md->start_match_ptr; + goto RECURSION_MATCHED; /* Exit loop; end processing */ + } + + /* PCRE does not allow THEN to escape beyond a recursion; it is treated + as NOMATCH. */ + + else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + { + DPRINTF(("Recursion gave error %d\n", rrc)); + if (new_recursive.offset_save != stacksave) + (PUBL(free))(new_recursive.offset_save); + RRETURN(rrc); + } + + md->recursive = &new_recursive; + callpat += GET(callpat, 1); + } + while (*callpat == OP_ALT); + + DPRINTF(("Recursion didn't match\n")); + md->recursive = new_recursive.prevrec; + if (new_recursive.offset_save != stacksave) + (PUBL(free))(new_recursive.offset_save); + RRETURN(MATCH_NOMATCH); + } + + RECURSION_MATCHED: + break; + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + case OP_ALT: + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + break; + + /* BRAZERO, BRAMINZERO and SKIPZERO occur just before a bracket group, + indicating that it may occur zero times. It may repeat infinitely, or not + at all - i.e. it could be ()* or ()? or even (){0} in the pattern. Brackets + with fixed upper repeat limits are compiled as a number of copies, with the + optional ones preceded by BRAZERO or BRAMINZERO. */ + + case OP_BRAZERO: + next = ecode + 1; + RMATCH(eptr, next, offset_top, md, eptrb, RM10); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + do next += GET(next, 1); while (*next == OP_ALT); + ecode = next + 1 + LINK_SIZE; + break; + + case OP_BRAMINZERO: + next = ecode + 1; + do next += GET(next, 1); while (*next == OP_ALT); + RMATCH(eptr, next + 1+LINK_SIZE, offset_top, md, eptrb, RM11); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode++; + break; + + case OP_SKIPZERO: + next = ecode+1; + do next += GET(next,1); while (*next == OP_ALT); + ecode = next + 1 + LINK_SIZE; + break; + + /* BRAPOSZERO occurs before a possessive bracket group. Don't do anything + here; just jump to the group, with allow_zero set TRUE. */ + + case OP_BRAPOSZERO: + op = *(++ecode); + allow_zero = TRUE; + if (op == OP_CBRAPOS || op == OP_SCBRAPOS) goto POSSESSIVE_CAPTURE; + goto POSSESSIVE_NON_CAPTURE; + + /* End of a group, repeated or non-repeating. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + case OP_KETRPOS: + prev = ecode - GET(ecode, 1); + + /* If this was a group that remembered the subject start, in order to break + infinite repeats of empty string matches, retrieve the subject start from + the chain. Otherwise, set it NULL. */ + + if (*prev >= OP_SBRA || *prev == OP_ONCE) + { + saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */ + eptrb = eptrb->epb_prev; /* Backup to previous group */ + } + else saved_eptr = NULL; + + /* If we are at the end of an assertion group or a non-capturing atomic + group, stop matching and return MATCH_MATCH, but record the current high + water mark for use by positive assertions. We also need to record the match + start in case it was changed by \K. */ + + if ((*prev >= OP_ASSERT && *prev <= OP_ASSERTBACK_NOT) || + *prev == OP_ONCE_NC) + { + md->end_match_ptr = eptr; /* For ONCE_NC */ + md->end_offset_top = offset_top; + md->start_match_ptr = mstart; + RRETURN(MATCH_MATCH); /* Sets md->mark */ + } + + /* For capturing groups we have to check the group number back at the start + and if necessary complete handling an extraction by setting the offsets and + bumping the high water mark. Whole-pattern recursion is coded as a recurse + into group 0, so it won't be picked up here. Instead, we catch it when the + OP_END is reached. Other recursion is handled here. We just have to record + the current subject position and start match pointer and give a MATCH + return. */ + + if (*prev == OP_CBRA || *prev == OP_SCBRA || + *prev == OP_CBRAPOS || *prev == OP_SCBRAPOS) + { + number = GET2(prev, 1+LINK_SIZE); + offset = number << 1; + +#ifdef PCRE_DEBUG + printf("end bracket %d", number); + printf("\n"); +#endif + + /* Handle a recursively called group. */ + + if (md->recursive != NULL && md->recursive->group_num == number) + { + md->end_match_ptr = eptr; + md->start_match_ptr = mstart; + RRETURN(MATCH_MATCH); + } + + /* Deal with capturing */ + + md->capture_last = number; + if (offset >= md->offset_max) md->offset_overflow = TRUE; else + { + /* If offset is greater than offset_top, it means that we are + "skipping" a capturing group, and that group's offsets must be marked + unset. In earlier versions of PCRE, all the offsets were unset at the + start of matching, but this doesn't work because atomic groups and + assertions can cause a value to be set that should later be unset. + Example: matching /(?>(a))b|(a)c/ against "ac". This sets group 1 as + part of the atomic group, but this is not on the final matching path, + so must be unset when 2 is set. (If there is no group 2, there is no + problem, because offset_top will then be 2, indicating no capture.) */ + + if (offset > offset_top) + { + register int *iptr = md->offset_vector + offset_top; + register int *iend = md->offset_vector + offset; + while (iptr < iend) *iptr++ = -1; + } + + /* Now make the extraction */ + + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = (int)(eptr - md->start_subject); + if (offset_top <= offset) offset_top = offset + 2; + } + } + + /* For an ordinary non-repeating ket, just continue at this level. This + also happens for a repeating ket if no characters were matched in the + group. This is the forcible breaking of infinite loops as implemented in + Perl 5.005. For a non-repeating atomic group that includes captures, + establish a backup point by processing the rest of the pattern at a lower + level. If this results in a NOMATCH return, pass MATCH_ONCE back to the + original OP_ONCE level, thereby bypassing intermediate backup points, but + resetting any captures that happened along the way. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + if (*prev == OP_ONCE) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM12); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ + RRETURN(MATCH_ONCE); + } + ecode += 1 + LINK_SIZE; /* Carry on at this level */ + break; + } + + /* OP_KETRPOS is a possessive repeating ket. Remember the current position, + and return the MATCH_KETRPOS. This makes it possible to do the repeats one + at a time from the outer level, thus saving stack. */ + + if (*ecode == OP_KETRPOS) + { + md->end_match_ptr = eptr; + md->end_offset_top = offset_top; + RRETURN(MATCH_KETRPOS); + } + + /* The normal repeating kets try the rest of the pattern or restart from + the preceding bracket, in the appropriate order. In the second case, we can + use tail recursion to avoid using another stack frame, unless we have an + an atomic group or an unlimited repeat of a group that can match an empty + string. */ + + if (*ecode == OP_KETRMIN) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM7); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (*prev == OP_ONCE) + { + RMATCH(eptr, prev, offset_top, md, eptrb, RM8); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->once_target = prev; /* Level at which to change to MATCH_NOMATCH */ + RRETURN(MATCH_ONCE); + } + if (*prev >= OP_SBRA) /* Could match an empty string */ + { + md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, prev, offset_top, md, eptrb, RM50); + RRETURN(rrc); + } + ecode = prev; + goto TAIL_RECURSE; + } + else /* OP_KETRMAX */ + { + if (*prev >= OP_SBRA) md->match_function_type = MATCH_CBEGROUP; + RMATCH(eptr, prev, offset_top, md, eptrb, RM13); + if (rrc == MATCH_ONCE && md->once_target == prev) rrc = MATCH_NOMATCH; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (*prev == OP_ONCE) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, eptrb, RM9); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->once_target = prev; + RRETURN(MATCH_ONCE); + } + ecode += 1 + LINK_SIZE; + goto TAIL_RECURSE; + } + /* Control never gets here */ + + /* Not multiline mode: start of subject assertion, unless notbol. */ + + case OP_CIRC: + if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH); + + /* Start of subject assertion */ + + case OP_SOD: + if (eptr != md->start_subject) RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* Multiline mode: start of subject unless notbol, or after any newline. */ + + case OP_CIRCM: + if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH); + if (eptr != md->start_subject && + (eptr == md->end_subject || !WAS_NEWLINE(eptr))) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* Start of match assertion */ + + case OP_SOM: + if (eptr != md->start_subject + md->start_offset) RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* Reset the start of match point */ + + case OP_SET_SOM: + mstart = eptr; + ecode++; + break; + + /* Multiline mode: assert before any newline, or before end of subject + unless noteol is set. */ + + case OP_DOLLM: + if (eptr < md->end_subject) + { if (!IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); } + else + { + if (md->noteol) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + } + ecode++; + break; + + /* Not multiline mode: assert before a terminating newline or before end of + subject unless noteol is set. */ + + case OP_DOLL: + if (md->noteol) RRETURN(MATCH_NOMATCH); + if (!md->endonly) goto ASSERT_NL_OR_EOS; + + /* ... else fall through for endonly */ + + /* End of subject assertion (\z) */ + + case OP_EOD: + if (eptr < md->end_subject) RRETURN(MATCH_NOMATCH); + SCHECK_PARTIAL(); + ecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + ASSERT_NL_OR_EOS: + if (eptr < md->end_subject && + (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen)) + RRETURN(MATCH_NOMATCH); + + /* Either at end of string or \n before end. */ + + SCHECK_PARTIAL(); + ecode++; + break; + + /* Word boundary assertions */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + { + + /* Find out if the previous and current characters are "word" characters. + It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to + be "non-word" characters. Remember the earliest consulted character for + partial matching. */ + +#ifdef SUPPORT_UTF + if (utf) + { + /* Get status of previous character */ + + if (eptr == md->start_subject) prev_is_word = FALSE; else + { + PCRE_PUCHAR lastptr = eptr - 1; + BACKCHAR(lastptr); + if (lastptr < md->start_used_ptr) md->start_used_ptr = lastptr; + GETCHAR(c, lastptr); +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + if (c == '_') prev_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + prev_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; + } + + /* Get status of next character */ + + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + cur_is_word = FALSE; + } + else + { + GETCHAR(c, eptr); +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + if (c == '_') cur_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + cur_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; + } + } + else +#endif + + /* Not in UTF-8 mode, but we may still have PCRE_UCP set, and for + consistency with the behaviour of \w we do use it in this case. */ + + { + /* Get status of previous character */ + + if (eptr == md->start_subject) prev_is_word = FALSE; else + { + if (eptr <= md->start_used_ptr) md->start_used_ptr = eptr - 1; +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + c = eptr[-1]; + if (c == '_') prev_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + prev_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + prev_is_word = MAX_255(eptr[-1]) + && ((md->ctypes[eptr[-1]] & ctype_word) != 0); + } + + /* Get status of next character */ + + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + cur_is_word = FALSE; + } + else +#ifdef SUPPORT_UCP + if (md->use_ucp) + { + c = *eptr; + if (c == '_') cur_is_word = TRUE; else + { + int cat = UCD_CATEGORY(c); + cur_is_word = (cat == ucp_L || cat == ucp_N); + } + } + else +#endif + cur_is_word = MAX_255(*eptr) + && ((md->ctypes[*eptr] & ctype_word) != 0); + } + + /* Now see if the situation is what we want */ + + if ((*ecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + RRETURN(MATCH_NOMATCH); + } + break; + + /* Match a single character type; inline for speed */ + + case OP_ANY: + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + /* Fall through */ + + case OP_ALLANY: + if (eptr >= md->end_subject) /* DO NOT merge the eptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr++; +#ifdef SUPPORT_UTF + if (utf) ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); +#endif + ecode++; + break; + + /* Match a single byte, even in UTF-8 mode. This opcode really does match + any byte, even newline, independent of the setting of PCRE_DOTALL. */ + + case OP_ANYBYTE: + if (eptr >= md->end_subject) /* DO NOT merge the eptr++ here; it must */ + { /* not be updated before SCHECK_PARTIAL. */ + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr++; + ecode++; + break; + + case OP_NOT_DIGIT: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ( +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c < 256 && +#endif + (md->ctypes[c] & ctype_digit) != 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_DIGIT: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ( +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c > 255 || +#endif + (md->ctypes[c] & ctype_digit) == 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ( +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c < 256 && +#endif + (md->ctypes[c] & ctype_space) != 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_WHITESPACE: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ( +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c > 255 || +#endif + (md->ctypes[c] & ctype_space) == 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_NOT_WORDCHAR: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ( +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c < 256 && +#endif + (md->ctypes[c] & ctype_word) != 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_WORDCHAR: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ( +#if defined SUPPORT_UTF || !(defined COMPILE_PCRE8) + c > 255 || +#endif + (md->ctypes[c] & ctype_word) == 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_ANYNL: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + ecode++; + break; + + case OP_NOT_HSPACE: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + RRETURN(MATCH_NOMATCH); + } + ecode++; + break; + + case OP_HSPACE: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + } + ecode++; + break; + + case OP_NOT_VSPACE: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + RRETURN(MATCH_NOMATCH); + } + ecode++; + break; + + case OP_VSPACE: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + break; + } + ecode++; + break; + +#ifdef SUPPORT_UCP + /* Check the next character by Unicode property. We will get here only + if the support is in the binary; otherwise a compile-time error occurs. */ + + case OP_PROP: + case OP_NOTPROP: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + { + const ucd_record *prop = GET_UCD(c); + + switch(ecode[1]) + { + case PT_ANY: + if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + case PT_LAMP: + if ((prop->chartype == ucp_Lu || + prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_GC: + if ((ecode[2] != PRIV(ucp_gentype)[prop->chartype]) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_PC: + if ((ecode[2] != prop->chartype) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_SC: + if ((ecode[2] != prop->script) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* These are specials */ + + case PT_ALNUM: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_SPACE: /* Perl space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR) + == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_PXSPACE: /* POSIX space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR) + == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_WORD: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || + c == CHAR_UNDERSCORE) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + /* This should never occur */ + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + ecode += 3; + } + break; + + /* Match an extended Unicode sequence. We will get here only if the support + is in the binary; otherwise a compile-time error occurs. */ + + case OP_EXTUNI: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (UCD_CATEGORY(c) == ucp_M) RRETURN(MATCH_NOMATCH); + while (eptr < md->end_subject) + { + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + if (UCD_CATEGORY(c) != ucp_M) break; + eptr += len; + } + ecode++; + break; +#endif + + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + case OP_REF: + case OP_REFI: + caseless = op == OP_REFI; + offset = GET2(ecode, 1) << 1; /* Doubled ref number */ + ecode += 1 + IMM2_SIZE; + + /* If the reference is unset, there are two possibilities: + + (a) In the default, Perl-compatible state, set the length negative; + this ensures that every attempt at a match fails. We can't just fail + here, because of the possibility of quantifiers with zero minima. + + (b) If the JavaScript compatibility flag is set, set the length to zero + so that the back reference matches an empty string. + + Otherwise, set the length to the length of what was matched by the + referenced subpattern. */ + + if (offset >= offset_top || md->offset_vector[offset] < 0) + length = (md->jscript_compat)? 0 : -1; + else + length = md->offset_vector[offset+1] - md->offset_vector[offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 1 + IMM2_SIZE); + if (max == 0) max = INT_MAX; + ecode += 1 + 2 * IMM2_SIZE; + break; + + default: /* No repeat follows */ + if ((length = match_ref(offset, eptr, length, md, caseless)) < 0) + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += length; + continue; /* With the main loop */ + } + + /* Handle repeated back references. If the length of the reference is + zero, just continue with the main loop. If the length is negative, it + means the reference is unset in non-Java-compatible mode. If the minimum is + zero, we can continue at the same level without recursion. For any other + minimum, carrying on will result in NOMATCH. */ + + if (length == 0) continue; + if (length < 0 && min == 0) continue; + + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + + for (i = 1; i <= min; i++) + { + int slength; + if ((slength = match_ref(offset, eptr, length, md, caseless)) < 0) + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += slength; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == max) continue; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) + { + for (fi = min;; fi++) + { + int slength; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM14); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if ((slength = match_ref(offset, eptr, length, md, caseless)) < 0) + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += slength; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else + { + pp = eptr; + for (i = min; i < max; i++) + { + int slength; + if ((slength = match_ref(offset, eptr, length, md, caseless)) < 0) + { + CHECK_PARTIAL(); + break; + } + eptr += slength; + } + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM15); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr -= length; + } + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + /* Match a bit-mapped character class, possibly repeatedly. This op code is + used when all the characters in the class have values in the range 0-255, + and either the matching is caseful, or the characters are in the range + 0-127 when UTF-8 processing is enabled. The only difference between + OP_CLASS and OP_NCLASS occurs when a data character outside the range is + encountered. + + First, look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats - written out + again for speed. */ + + case OP_NCLASS: + case OP_CLASS: + { + /* The data variable is saved across frames, so the byte map needs to + be stored there. */ +#define BYTE_MAP ((pcre_uint8 *)data) + data = ecode + 1; /* Save for matching */ + ecode += 1 + (32 / sizeof(pcre_uchar)); /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 1 + IMM2_SIZE); + if (max == 0) max = INT_MAX; + ecode += 1 + 2 * IMM2_SIZE; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + +#ifdef SUPPORT_UTF + if (utf) + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + c = *eptr++; +#ifndef COMPILE_PCRE8 + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { +#ifdef SUPPORT_UTF + if (utf) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM16); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM17); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + c = *eptr++; +#ifndef COMPILE_PCRE8 + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else +#endif + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + pp = eptr; + +#ifdef SUPPORT_UTF + if (utf) + { + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c > 255) + { + if (op == OP_CLASS) break; + } + else + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; + eptr += len; + } + for (;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM18); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + c = *eptr; +#ifndef COMPILE_PCRE8 + if (c > 255) + { + if (op == OP_CLASS) break; + } + else +#endif + if ((BYTE_MAP[c/8] & (1 << (c&7))) == 0) break; + eptr++; + } + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM19); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } +#undef BYTE_MAP + } + /* Control never gets here */ + + + /* Match an extended character class. This opcode is encountered only + when UTF-8 mode mode is supported. Nevertheless, we may not be in UTF-8 + mode, because Unicode properties are supported in non-UTF-8 mode. */ + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + { + data = ecode + 1 + LINK_SIZE; /* Save for matching */ + ecode += GET(ecode, 1); /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 1 + IMM2_SIZE); + if (max == 0) max = INT_MAX; + ecode += 1 + 2 * IMM2_SIZE; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM20); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (!PRIV(xclass)(c, data, utf)) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + pp = eptr; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } +#ifdef SUPPORT_UTF + GETCHARLENTEST(c, eptr, len); +#else + c = *eptr; +#endif + if (!PRIV(xclass)(c, data, utf)) break; + eptr += len; + } + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM21); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ +#ifdef SUPPORT_UTF + if (utf) BACKCHAR(eptr); +#endif + } + RRETURN(MATCH_NOMATCH); + } + + /* Control never gets here */ + } +#endif /* End of XCLASS */ + + /* Match a single character, casefully */ + + case OP_CHAR: +#ifdef SUPPORT_UTF + if (utf) + { + length = 1; + ecode++; + GETCHARLEN(fc, ecode, length); + if (length > md->end_subject - eptr) + { + CHECK_PARTIAL(); /* Not SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); + } + while (length-- > 0) if (*ecode++ != *eptr++) RRETURN(MATCH_NOMATCH); + } + else +#endif + /* Not UTF mode */ + { + if (md->end_subject - eptr < 1) + { + SCHECK_PARTIAL(); /* This one can use SCHECK_PARTIAL() */ + RRETURN(MATCH_NOMATCH); + } + if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH); + ecode += 2; + } + break; + + /* Match a single character, caselessly. If we are at the end of the + subject, give up immediately. */ + + case OP_CHARI: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + +#ifdef SUPPORT_UTF + if (utf) + { + length = 1; + ecode++; + GETCHARLEN(fc, ecode, length); + + /* If the pattern character's value is < 128, we have only one byte, and + we know that its other case must also be one byte long, so we can use the + fast lookup table. We know that there is at least one byte left in the + subject. */ + + if (fc < 128) + { + if (md->lcc[fc] + != TABLE_GET(*eptr, md->lcc, *eptr)) RRETURN(MATCH_NOMATCH); + ecode++; + eptr++; + } + + /* Otherwise we must pick up the subject character. Note that we cannot + use the value of "length" to check for sufficient bytes left, because the + other case of the character may have more or fewer bytes. */ + + else + { + unsigned int dc; + GETCHARINC(dc, eptr); + ecode += length; + + /* If we have Unicode property support, we can use it to test the other + case of the character, if there is one. */ + + if (fc != dc) + { +#ifdef SUPPORT_UCP + if (dc != UCD_OTHERCASE(fc)) +#endif + RRETURN(MATCH_NOMATCH); + } + } + } + else +#endif /* SUPPORT_UTF */ + + /* Not UTF mode */ + { + if (TABLE_GET(ecode[1], md->lcc, ecode[1]) + != TABLE_GET(*eptr, md->lcc, *eptr)) RRETURN(MATCH_NOMATCH); + eptr++; + ecode += 2; + } + break; + + /* Match a single character repeatedly. */ + + case OP_EXACT: + case OP_EXACTI: + min = max = GET2(ecode, 1); + ecode += 1 + IMM2_SIZE; + goto REPEATCHAR; + + case OP_POSUPTO: + case OP_POSUPTOI: + possessive = TRUE; + /* Fall through */ + + case OP_UPTO: + case OP_UPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_MINUPTO || *ecode == OP_MINUPTOI; + ecode += 1 + IMM2_SIZE; + goto REPEATCHAR; + + case OP_POSSTAR: + case OP_POSSTARI: + possessive = TRUE; + min = 0; + max = INT_MAX; + ecode++; + goto REPEATCHAR; + + case OP_POSPLUS: + case OP_POSPLUSI: + possessive = TRUE; + min = 1; + max = INT_MAX; + ecode++; + goto REPEATCHAR; + + case OP_POSQUERY: + case OP_POSQUERYI: + possessive = TRUE; + min = 0; + max = 1; + ecode++; + goto REPEATCHAR; + + case OP_STAR: + case OP_STARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_QUERY: + case OP_QUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + c = *ecode++ - ((op < OP_STARI)? OP_STAR : OP_STARI); + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. */ + + REPEATCHAR: +#ifdef SUPPORT_UTF + if (utf) + { + length = 1; + charptr = ecode; + GETCHARLEN(fc, ecode, length); + ecode += length; + + /* Handle multibyte character matching specially here. There is + support for caseless matching if UCP support is present. */ + + if (length > 1) + { +#ifdef SUPPORT_UCP + unsigned int othercase; + if (op >= OP_STARI && /* Caseless */ + (othercase = UCD_OTHERCASE(fc)) != fc) + oclength = PRIV(ord2utf)(othercase, occhars); + else oclength = 0; +#endif /* SUPPORT_UCP */ + + for (i = 1; i <= min; i++) + { + if (eptr <= md->end_subject - length && + memcmp(eptr, charptr, IN_UCHARS(length)) == 0) eptr += length; +#ifdef SUPPORT_UCP + else if (oclength > 0 && + eptr <= md->end_subject - oclength && + memcmp(eptr, occhars, IN_UCHARS(oclength)) == 0) eptr += oclength; +#endif /* SUPPORT_UCP */ + else + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + + if (min == max) continue; + + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM22); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr <= md->end_subject - length && + memcmp(eptr, charptr, IN_UCHARS(length)) == 0) eptr += length; +#ifdef SUPPORT_UCP + else if (oclength > 0 && + eptr <= md->end_subject - oclength && + memcmp(eptr, occhars, IN_UCHARS(oclength)) == 0) eptr += oclength; +#endif /* SUPPORT_UCP */ + else + { + CHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + else /* Maximize */ + { + pp = eptr; + for (i = min; i < max; i++) + { + if (eptr <= md->end_subject - length && + memcmp(eptr, charptr, IN_UCHARS(length)) == 0) eptr += length; +#ifdef SUPPORT_UCP + else if (oclength > 0 && + eptr <= md->end_subject - oclength && + memcmp(eptr, occhars, IN_UCHARS(oclength)) == 0) eptr += oclength; +#endif /* SUPPORT_UCP */ + else + { + CHECK_PARTIAL(); + break; + } + } + + if (possessive) continue; + + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM23); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr == pp) { RRETURN(MATCH_NOMATCH); } +#ifdef SUPPORT_UCP + eptr--; + BACKCHAR(eptr); +#else /* without SUPPORT_UCP */ + eptr -= length; +#endif /* SUPPORT_UCP */ + } + } + /* Control never gets here */ + } + + /* If the length of a UTF-8 character is 1, we fall through here, and + obey the code as for non-UTF-8 characters below, though in this case the + value of fc will always be < 128. */ + } + else +#endif /* SUPPORT_UTF */ + /* When not in UTF-8 mode, load a single-byte character. */ + fc = *ecode++; + + /* The value of fc at this point is always one character, though we may + or may not be in UTF mode. The code is duplicated for the caseless and + caseful cases, for speed, since matching characters is likely to be quite + common. First, ensure the minimum number of matches are present. If min = + max, continue at the same level without recursing. Otherwise, if + minimizing, keep trying the rest of the expression and advancing one + matching character if failing, up to the maximum. Alternatively, if + maximizing, find the maximum number of characters and work backwards. */ + + DPRINTF(("matching %c{%d,%d} against subject %.*s\n", fc, min, max, + max, eptr)); + + if (op >= OP_STARI) /* Caseless */ + { +#ifdef COMPILE_PCRE8 + /* fc must be < 128 if UTF is enabled. */ + foc = md->fcc[fc]; +#else +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (utf && fc > 127) + foc = UCD_OTHERCASE(fc); +#else + if (utf && fc > 127) + foc = fc; +#endif /* SUPPORT_UCP */ + else +#endif /* SUPPORT_UTF */ + foc = TABLE_GET(fc, md->fcc, fc); +#endif /* COMPILE_PCRE8 */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc != *eptr && foc != *eptr) RRETURN(MATCH_NOMATCH); + eptr++; + } + if (min == max) continue; + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM24); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc != *eptr && foc != *eptr) RRETURN(MATCH_NOMATCH); + eptr++; + } + /* Control never gets here */ + } + else /* Maximize */ + { + pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc != *eptr && foc != *eptr) break; + eptr++; + } + + if (possessive) continue; + + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM25); + eptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* Caseful comparisons (includes all multi-byte characters) */ + + else + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc != *eptr++) RRETURN(MATCH_NOMATCH); + } + + if (min == max) continue; + + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM26); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc != *eptr++) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + else /* Maximize */ + { + pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc != *eptr) break; + eptr++; + } + if (possessive) continue; + + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM27); + eptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + + /* Match a negated single one-byte character. The character we are + checking can be multibyte. */ + + case OP_NOT: + case OP_NOTI: + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + ecode++; + GETCHARINCTEST(c, eptr); + if (op == OP_NOTI) /* The caseless case */ + { + register unsigned int ch, och; + ch = *ecode++; +#ifdef COMPILE_PCRE8 + /* ch must be < 128 if UTF is enabled. */ + och = md->fcc[ch]; +#else +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (utf && ch > 127) + och = UCD_OTHERCASE(ch); +#else + if (utf && ch > 127) + och = ch; +#endif /* SUPPORT_UCP */ + else +#endif /* SUPPORT_UTF */ + och = TABLE_GET(ch, md->fcc, ch); +#endif /* COMPILE_PCRE8 */ + if (ch == c || och == c) RRETURN(MATCH_NOMATCH); + } + else /* Caseful */ + { + if (*ecode++ == c) RRETURN(MATCH_NOMATCH); + } + break; + + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ + + case OP_NOTEXACT: + case OP_NOTEXACTI: + min = max = GET2(ecode, 1); + ecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_NOTMINUPTO || *ecode == OP_NOTMINUPTOI; + ecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + possessive = TRUE; + min = 0; + max = INT_MAX; + ecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + possessive = TRUE; + min = 1; + max = INT_MAX; + ecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + possessive = TRUE; + min = 0; + max = 1; + ecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + possessive = TRUE; + min = 0; + max = GET2(ecode, 1); + ecode += 1 + IMM2_SIZE; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + c = *ecode++ - ((op >= OP_NOTSTARI)? OP_NOTSTARI: OP_NOTSTAR); + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-byte matches. */ + + REPEATNOTCHAR: + fc = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", fc, min, max, + max, eptr)); + + if (op >= OP_NOTSTARI) /* Caseless */ + { +#ifdef COMPILE_PCRE8 + /* fc must be < 128 if UTF is enabled. */ + foc = md->fcc[fc]; +#else +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + if (utf && fc > 127) + foc = UCD_OTHERCASE(fc); +#else + if (utf && fc > 127) + foc = fc; +#endif /* SUPPORT_UCP */ + else +#endif /* SUPPORT_UTF */ + foc = TABLE_GET(fc, md->fcc, fc); +#endif /* COMPILE_PCRE8 */ + +#ifdef SUPPORT_UTF + if (utf) + { + register unsigned int d; + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, eptr); + if (fc == d || (unsigned int) foc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); + eptr++; + } + } + + if (min == max) continue; + + if (minimize) + { +#ifdef SUPPORT_UTF + if (utf) + { + register unsigned int d; + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM28); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, eptr); + if (fc == d || (unsigned int)foc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM29); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr || foc == *eptr) RRETURN(MATCH_NOMATCH); + eptr++; + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + pp = eptr; + +#ifdef SUPPORT_UTF + if (utf) + { + register unsigned int d; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(d, eptr, len); + if (fc == d || (unsigned int)foc == d) break; + eptr += len; + } + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM30); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc == *eptr || foc == *eptr) break; + eptr++; + } + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM31); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { +#ifdef SUPPORT_UTF + if (utf) + { + register unsigned int d; + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, eptr); + if (fc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr++) RRETURN(MATCH_NOMATCH); + } + } + + if (min == max) continue; + + if (minimize) + { +#ifdef SUPPORT_UTF + if (utf) + { + register unsigned int d; + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM32); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(d, eptr); + if (fc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM33); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (fc == *eptr++) RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + pp = eptr; + +#ifdef SUPPORT_UTF + if (utf) + { + register unsigned int d; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(d, eptr, len); + if (fc == d) break; + eptr += len; + } + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM34); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (fc == *eptr) break; + eptr++; + } + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM35); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + case OP_TYPEEXACT: + min = max = GET2(ecode, 1); + minimize = TRUE; + ecode += 1 + IMM2_SIZE; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_TYPEMINUPTO; + ecode += 1 + IMM2_SIZE; + goto REPEATTYPE; + + case OP_TYPEPOSSTAR: + possessive = TRUE; + min = 0; + max = INT_MAX; + ecode++; + goto REPEATTYPE; + + case OP_TYPEPOSPLUS: + possessive = TRUE; + min = 1; + max = INT_MAX; + ecode++; + goto REPEATTYPE; + + case OP_TYPEPOSQUERY: + possessive = TRUE; + min = 0; + max = 1; + ecode++; + goto REPEATTYPE; + + case OP_TYPEPOSUPTO: + possessive = TRUE; + min = 0; + max = GET2(ecode, 1); + ecode += 1 + IMM2_SIZE; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + c = *ecode++ - OP_TYPESTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single character type matches. Note that + in UTF-8 mode, '.' matches a character of any length, but for the other + character types, the valid characters are all one-byte long. */ + + REPEATTYPE: + ctype = *ecode++; /* Code for the character type */ + +#ifdef SUPPORT_UCP + if (ctype == OP_PROP || ctype == OP_NOTPROP) + { + prop_fail_result = ctype == OP_NOTPROP; + prop_type = *ecode++; + prop_value = *ecode++; + } + else prop_type = -1; +#endif + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Separate the UTF-8 code completely as that + is tidier. Also separate the UCP code, which can be the same for both UTF-8 + and single-bytes. */ + + if (min > 0) + { +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + switch(prop_type) + { + case PT_ANY: + if (prop_fail_result) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + } + break; + + case PT_LAMP: + for (i = 1; i <= min; i++) + { + int chartype; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + chartype = UCD_CHARTYPE(c); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_GC: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_PC: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_SC: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_ALNUM: + for (i = 1; i <= min; i++) + { + int category; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_SPACE: /* Perl space */ + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_PXSPACE: /* POSIX space */ + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_VT || c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_WORD: + for (i = 1; i <= min; i++) + { + int category; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N || c == CHAR_UNDERSCORE) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + /* This should not occur */ + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (UCD_CATEGORY(c) == ucp_M) RRETURN(MATCH_NOMATCH); + while (eptr < md->end_subject) + { + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + if (UCD_CATEGORY(c) != ucp_M) break; + eptr += len; + } + } + } + + else +#endif /* SUPPORT_UCP */ + +/* Handle all other cases when the coding is UTF-8 */ + +#ifdef SUPPORT_UTF + if (utf) switch(ctype) + { + case OP_ANY: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + break; + + case OP_ALLANY: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + break; + + case OP_ANYBYTE: + if (eptr > md->end_subject - min) RRETURN(MATCH_NOMATCH); + eptr += min; + break; + + case OP_ANYNL: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + } + } + break; + + case OP_NOT_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + break; + } + } + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINC(c, eptr); + if (c < 128 && (md->ctypes[c] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (*eptr >= 128 || (md->ctypes[*eptr] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (*eptr < 128 && (md->ctypes[*eptr] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (*eptr >= 128 || (md->ctypes[*eptr] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (*eptr < 128 && (md->ctypes[*eptr] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (*eptr >= 128 || (md->ctypes[*eptr] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } /* End switch(ctype) */ + + else +#endif /* SUPPORT_UTF */ + + /* Code for the non-UTF-8 case for minimum matching of operators other + than OP_PROP and OP_NOTPROP. */ + + switch(ctype) + { + case OP_ANY: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + case OP_ALLANY: + if (eptr > md->end_subject - min) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += min; + break; + + case OP_ANYBYTE: + if (eptr > md->end_subject - min) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + eptr += min; + break; + + case OP_ANYNL: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*eptr++) + { + default: RRETURN(MATCH_NOMATCH); + + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: +#ifdef COMPILE_PCRE16 + case 0x2028: + case 0x2029: +#endif + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*eptr++) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ +#ifdef COMPILE_PCRE16 + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ +#endif + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*eptr++) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ +#ifdef COMPILE_PCRE16 + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ +#endif + break; + } + } + break; + + case OP_NOT_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*eptr++) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ +#ifdef COMPILE_PCRE16 + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ +#endif + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + switch(*eptr++) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ +#ifdef COMPILE_PCRE16 + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ +#endif + break; + } + } + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + eptr++; + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + + /* If min = max, continue at the same level without recursing */ + + if (min == max) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. Again, separate the UTF-8 case for speed, and also + separate the UCP cases. */ + + if (minimize) + { +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + switch(prop_type) + { + case PT_ANY: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM36); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (prop_fail_result) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_LAMP: + for (fi = min;; fi++) + { + int chartype; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM37); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + chartype = UCD_CHARTYPE(c); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_GC: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM38); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_PC: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM39); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_SC: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM40); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_ALNUM: + for (fi = min;; fi++) + { + int category; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM59); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_SPACE: /* Perl space */ + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM60); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_PXSPACE: /* POSIX space */ + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM61); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_VT || c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_WORD: + for (fi = min;; fi++) + { + int category; + RMATCH(eptr, ecode, offset_top, md, eptrb, RM62); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + category = UCD_CATEGORY(c); + if ((category == ucp_L || + category == ucp_N || + c == CHAR_UNDERSCORE) + == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + /* This should never occur */ + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM41); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + GETCHARINCTEST(c, eptr); + if (UCD_CATEGORY(c) == ucp_M) RRETURN(MATCH_NOMATCH); + while (eptr < md->end_subject) + { + int len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + if (UCD_CATEGORY(c) != ucp_M) break; + eptr += len; + } + } + } + else +#endif /* SUPPORT_UCP */ + +#ifdef SUPPORT_UTF + if (utf) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM42); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (ctype == OP_ANY && IS_NEWLINE(eptr)) + RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + switch(ctype) + { + case OP_ANY: /* This is the non-NL case */ + case OP_ALLANY: + case OP_ANYBYTE: + break; + + case OP_ANYNL: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + break; + + case OP_NOT_HSPACE: + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_HSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + } + break; + + case OP_NOT_VSPACE: + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_VSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + break; + } + break; + + case OP_NOT_DIGIT: + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + if (c < 256 && (md->ctypes[c] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + if (c >= 256 || (md->ctypes[c] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + if (c < 256 && (md->ctypes[c] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + } + else +#endif + /* Not UTF mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM43); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max) RRETURN(MATCH_NOMATCH); + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + RRETURN(MATCH_NOMATCH); + } + if (ctype == OP_ANY && IS_NEWLINE(eptr)) + RRETURN(MATCH_NOMATCH); + c = *eptr++; + switch(ctype) + { + case OP_ANY: /* This is the non-NL case */ + case OP_ALLANY: + case OP_ANYBYTE: + break; + + case OP_ANYNL: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: +#ifdef COMPILE_PCRE16 + case 0x2028: + case 0x2029: +#endif + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + break; + + case OP_NOT_HSPACE: + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ +#ifdef COMPILE_PCRE16 + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ +#endif + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_HSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ +#ifdef COMPILE_PCRE16 + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ +#endif + break; + } + break; + + case OP_NOT_VSPACE: + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ +#ifdef COMPILE_PCRE16 + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ +#endif + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_VSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ +#ifdef COMPILE_PCRE16 + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ +#endif + break; + } + break; + + case OP_NOT_DIGIT: + if (MAX_255(c) && (md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + if (!MAX_255(c) || (md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + if (MAX_255(c) && (md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + if (!MAX_255(c) || (md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + if (MAX_255(c) && (md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if (!MAX_255(c) || (md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + } + /* Control never gets here */ + } + + /* If maximizing, it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). Again, keep the + UTF-8 and UCP stuff separate. */ + + else + { + pp = eptr; /* Remember where we started */ + +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + switch(prop_type) + { + case PT_ANY: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if (prop_fail_result) break; + eptr+= len; + } + break; + + case PT_LAMP: + for (i = min; i < max; i++) + { + int chartype; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + chartype = UCD_CHARTYPE(c); + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_GC: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CATEGORY(c) == prop_value) == prop_fail_result) break; + eptr+= len; + } + break; + + case PT_PC: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CHARTYPE(c) == prop_value) == prop_fail_result) break; + eptr+= len; + } + break; + + case PT_SC: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_SCRIPT(c) == prop_value) == prop_fail_result) break; + eptr+= len; + } + break; + + case PT_ALNUM: + for (i = min; i < max; i++) + { + int category; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N) == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_SPACE: /* Perl space */ + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_PXSPACE: /* POSIX space */ + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + if ((UCD_CATEGORY(c) == ucp_Z || c == CHAR_HT || c == CHAR_NL || + c == CHAR_VT || c == CHAR_FF || c == CHAR_CR) + == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_WORD: + for (i = min; i < max; i++) + { + int category; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLENTEST(c, eptr, len); + category = UCD_CATEGORY(c); + if ((category == ucp_L || category == ucp_N || + c == CHAR_UNDERSCORE) == prop_fail_result) + break; + eptr+= len; + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM44); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + if (utf) BACKCHAR(eptr); + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) + { + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + if (UCD_CATEGORY(c) == ucp_M) break; + eptr += len; + while (eptr < md->end_subject) + { + len = 1; + if (!utf) c = *eptr; else { GETCHARLEN(c, eptr, len); } + if (UCD_CATEGORY(c) != ucp_M) break; + eptr += len; + } + } + + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; + + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM45); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + for (;;) /* Move back over one extended */ + { + if (!utf) c = *eptr; else + { + BACKCHAR(eptr); + GETCHAR(c, eptr); + } + if (UCD_CATEGORY(c) != ucp_M) break; + eptr--; + } + } + } + + else +#endif /* SUPPORT_UCP */ + +#ifdef SUPPORT_UTF + if (utf) + { + switch(ctype) + { + case OP_ANY: + if (max < INT_MAX) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(eptr)) break; + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + } + + /* Handle unlimited UTF-8 repeat */ + + else + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(eptr)) break; + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + } + break; + + case OP_ALLANY: + if (max < INT_MAX) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + eptr++; + ACROSSCHAR(eptr < md->end_subject, *eptr, eptr++); + } + } + else + { + eptr = md->end_subject; /* Unlimited UTF-8 repeat */ + SCHECK_PARTIAL(); + } + break; + + /* The byte case is the same as non-UTF8 */ + + case OP_ANYBYTE: + c = max - min; + if (c > (unsigned int)(md->end_subject - eptr)) + { + eptr = md->end_subject; + SCHECK_PARTIAL(); + } + else eptr += c; + break; + + case OP_ANYNL: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c == 0x000d) + { + if (++eptr >= md->end_subject) break; + if (*eptr == 0x000a) eptr++; + } + else + { + if (c != 0x000a && + (md->bsr_anycrlf || + (c != 0x000b && c != 0x000c && + c != 0x0085 && c != 0x2028 && c != 0x2029))) + break; + eptr += len; + } + } + break; + + case OP_NOT_HSPACE: + case OP_HSPACE: + for (i = min; i < max; i++) + { + BOOL gotspace; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + switch(c) + { + default: gotspace = FALSE; break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + gotspace = TRUE; + break; + } + if (gotspace == (ctype == OP_NOT_HSPACE)) break; + eptr += len; + } + break; + + case OP_NOT_VSPACE: + case OP_VSPACE: + for (i = min; i < max; i++) + { + BOOL gotspace; + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + switch(c) + { + default: gotspace = FALSE; break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + gotspace = TRUE; + break; + } + if (gotspace == (ctype == OP_NOT_VSPACE)) break; + eptr += len; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break; + eptr+= len; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break; + eptr+= len; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break; + eptr+= len; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break; + eptr+= len; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break; + eptr+= len; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + GETCHARLEN(c, eptr, len); + if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break; + eptr+= len; + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + /* eptr is now past the end of the maximum run. If possessive, we are + done (no backing up). Otherwise, match at this position; anything other + than no match is immediately returned. For nomatch, back up one + character, unless we are matching \R and the last thing matched was + \r\n, in which case, back up two bytes. */ + + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM46); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + if (ctype == OP_ANYNL && eptr > pp && *eptr == '\n' && + eptr[-1] == '\r') eptr--; + } + } + else +#endif /* SUPPORT_UTF */ + /* Not UTF mode */ + { + switch(ctype) + { + case OP_ANY: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (IS_NEWLINE(eptr)) break; + eptr++; + } + break; + + case OP_ALLANY: + case OP_ANYBYTE: + c = max - min; + if (c > (unsigned int)(md->end_subject - eptr)) + { + eptr = md->end_subject; + SCHECK_PARTIAL(); + } + else eptr += c; + break; + + case OP_ANYNL: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + c = *eptr; + if (c == 0x000d) + { + if (++eptr >= md->end_subject) break; + if (*eptr == 0x000a) eptr++; + } + else + { + if (c != 0x000a && (md->bsr_anycrlf || + (c != 0x000b && c != 0x000c && c != 0x0085 +#ifdef COMPILE_PCRE16 + && c != 0x2028 && c != 0x2029 +#endif + ))) break; + eptr++; + } + } + break; + + case OP_NOT_HSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + c = *eptr; + if (c == 0x09 || c == 0x20 || c == 0xa0 +#ifdef COMPILE_PCRE16 + || c == 0x1680 || c == 0x180e || (c >= 0x2000 && c <= 0x200A) + || c == 0x202f || c == 0x205f || c == 0x3000 +#endif + ) break; + eptr++; + } + break; + + case OP_HSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + c = *eptr; + if (c != 0x09 && c != 0x20 && c != 0xa0 +#ifdef COMPILE_PCRE16 + && c != 0x1680 && c != 0x180e && (c < 0x2000 || c > 0x200A) + && c != 0x202f && c != 0x205f && c != 0x3000 +#endif + ) break; + eptr++; + } + break; + + case OP_NOT_VSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + c = *eptr; + if (c == 0x0a || c == 0x0b || c == 0x0c || c == 0x0d || c == 0x85 +#ifdef COMPILE_PCRE16 + || c == 0x2028 || c == 0x2029 +#endif + ) break; + eptr++; + } + break; + + case OP_VSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + c = *eptr; + if (c != 0x0a && c != 0x0b && c != 0x0c && c != 0x0d && c != 0x85 +#ifdef COMPILE_PCRE16 + && c != 0x2028 && c != 0x2029 +#endif + ) break; + eptr++; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_digit) != 0) break; + eptr++; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_digit) == 0) break; + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_space) != 0) break; + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_space) == 0) break; + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (MAX_255(*eptr) && (md->ctypes[*eptr] & ctype_word) != 0) break; + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) + { + SCHECK_PARTIAL(); + break; + } + if (!MAX_255(*eptr) || (md->ctypes[*eptr] & ctype_word) == 0) break; + eptr++; + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + /* eptr is now past the end of the maximum run. If possessive, we are + done (no backing up). Otherwise, match at this position; anything other + than no match is immediately returned. For nomatch, back up one + character (byte), unless we are matching \R and the last thing matched + was \r\n, in which case, back up two bytes. */ + + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, eptrb, RM47); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + if (ctype == OP_ANYNL && eptr > pp && *eptr == '\n' && + eptr[-1] == '\r') eptr--; + } + } + + /* Get here if we can't make it match with any permitted repetitions */ + + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + /* There's been some horrible disaster. Arrival here can only mean there is + something seriously wrong in the code above or the OP_xxx definitions. */ + + default: + DPRINTF(("Unknown opcode %d\n", *ecode)); + RRETURN(PCRE_ERROR_UNKNOWN_OPCODE); + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ + + +/* When compiling to use the heap rather than the stack for recursive calls to +match(), the RRETURN() macro jumps here. The number that is saved in +frame->Xwhere indicates which label we actually want to return to. */ + +#ifdef NO_RECURSE +#define LBL(val) case val: goto L_RM##val; +HEAP_RETURN: +switch (frame->Xwhere) + { + LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8) + LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17) + LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33) + LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52) + LBL(53) LBL(54) LBL(55) LBL(56) LBL(57) LBL(58) LBL(63) LBL(64) + LBL(65) LBL(66) +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + LBL(21) +#endif +#ifdef SUPPORT_UTF + LBL(16) LBL(18) LBL(20) + LBL(22) LBL(23) LBL(28) LBL(30) + LBL(32) LBL(34) LBL(42) LBL(46) +#ifdef SUPPORT_UCP + LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45) + LBL(59) LBL(60) LBL(61) LBL(62) +#endif /* SUPPORT_UCP */ +#endif /* SUPPORT_UTF */ + default: + DPRINTF(("jump error in pcre match: label %d non-existent\n", frame->Xwhere)); + +printf("+++jump error in pcre match: label %d non-existent\n", frame->Xwhere); + + return PCRE_ERROR_INTERNAL; + } +#undef LBL +#endif /* NO_RECURSE */ +} + + +/*************************************************************************** +**************************************************************************** + RECURSION IN THE match() FUNCTION + +Undefine all the macros that were defined above to handle this. */ + +#ifdef NO_RECURSE +#undef eptr +#undef ecode +#undef mstart +#undef offset_top +#undef eptrb +#undef flags + +#undef callpat +#undef charptr +#undef data +#undef next +#undef pp +#undef prev +#undef saved_eptr + +#undef new_recursive + +#undef cur_is_word +#undef condition +#undef prev_is_word + +#undef ctype +#undef length +#undef max +#undef min +#undef number +#undef offset +#undef op +#undef save_capture_last +#undef save_offset1 +#undef save_offset2 +#undef save_offset3 +#undef stacksave + +#undef newptrb + +#endif + +/* These two are defined as macros in both cases */ + +#undef fc +#undef fi + +/*************************************************************************** +***************************************************************************/ + + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + argument_re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_exec(const pcre *argument_re, const pcre_extra *extra_data, + PCRE_SPTR subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_exec(const pcre16 *argument_re, const pcre16_extra *extra_data, + PCRE_SPTR16 subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +#endif +{ +int rc, ocount, arg_offset_max; +int newline; +BOOL using_temporary_offsets = FALSE; +BOOL anchored; +BOOL startline; +BOOL firstline; +BOOL utf; +BOOL has_first_char = FALSE; +BOOL has_req_char = FALSE; +pcre_uchar first_char = 0; +pcre_uchar first_char2 = 0; +pcre_uchar req_char = 0; +pcre_uchar req_char2 = 0; +match_data match_block; +match_data *md = &match_block; +const pcre_uint8 *tables; +const pcre_uint8 *start_bits = NULL; +PCRE_PUCHAR start_match = (PCRE_PUCHAR)subject + start_offset; +PCRE_PUCHAR end_subject; +PCRE_PUCHAR start_partial = NULL; +PCRE_PUCHAR req_char_ptr = start_match - 1; + +const pcre_study_data *study; +const REAL_PCRE *re = (const REAL_PCRE *)argument_re; + +/* Check for the special magic call that measures the size of the stack used +per recursive call of match(). */ + +if (re == NULL && extra_data == NULL && subject == NULL && length == -999 && + start_offset == -999) +#ifdef NO_RECURSE + return -sizeof(heapframe); +#else + return match(NULL, NULL, NULL, 0, NULL, NULL, 0); +#endif + +/* Plausibility checks */ + +if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; +if (re == NULL || subject == NULL || (offsets == NULL && offsetcount > 0)) + return PCRE_ERROR_NULL; +if (offsetcount < 0) return PCRE_ERROR_BADCOUNT; +if (start_offset < 0 || start_offset > length) return PCRE_ERROR_BADOFFSET; + +/* Check that the first field in the block is the magic number. If it is not, +return with PCRE_ERROR_BADMAGIC. However, if the magic number is equal to +REVERSED_MAGIC_NUMBER we return with PCRE_ERROR_BADENDIANNESS, which +means that the pattern is likely compiled with different endianness. */ + +if (re->magic_number != MAGIC_NUMBER) + return re->magic_number == REVERSED_MAGIC_NUMBER? + PCRE_ERROR_BADENDIANNESS:PCRE_ERROR_BADMAGIC; +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +/* These two settings are used in the code for checking a UTF-8 string that +follows immediately afterwards. Other values in the md block are used only +during "normal" pcre_exec() processing, not when the JIT support is in use, +so they are set up later. */ + +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +utf = md->utf = (re->options & PCRE_UTF8) != 0; +md->partial = ((options & PCRE_PARTIAL_HARD) != 0)? 2 : + ((options & PCRE_PARTIAL_SOFT) != 0)? 1 : 0; + +/* Check a UTF-8 string if required. Pass back the character offset and error +code for an invalid string if a results vector is available. */ + +#ifdef SUPPORT_UTF +if (utf && (options & PCRE_NO_UTF8_CHECK) == 0) + { + int erroroffset; + int errorcode = PRIV(valid_utf)((PCRE_PUCHAR)subject, length, &erroroffset); + if (errorcode != 0) + { + if (offsetcount >= 2) + { + offsets[0] = erroroffset; + offsets[1] = errorcode; + } +#ifdef COMPILE_PCRE16 + return (errorcode <= PCRE_UTF16_ERR1 && md->partial > 1)? + PCRE_ERROR_SHORTUTF16 : PCRE_ERROR_BADUTF16; +#else + return (errorcode <= PCRE_UTF8_ERR5 && md->partial > 1)? + PCRE_ERROR_SHORTUTF8 : PCRE_ERROR_BADUTF8; +#endif + } + + /* Check that a start_offset points to the start of a UTF character. */ + if (start_offset > 0 && start_offset < length && + NOT_FIRSTCHAR(((PCRE_PUCHAR)subject)[start_offset])) + return PCRE_ERROR_BADUTF8_OFFSET; + } +#endif + +/* If the pattern was successfully studied with JIT support, run the JIT +executable instead of the rest of this function. Most options must be set at +compile time for the JIT code to be usable. Fallback to the normal code path if +an unsupported flag is set. In particular, JIT does not support partial +matching. */ + +#ifdef SUPPORT_JIT +if (extra_data != NULL + && (extra_data->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 + && extra_data->executable_jit != NULL + && (extra_data->flags & PCRE_EXTRA_TABLES) == 0 + && (options & ~(PCRE_NO_UTF8_CHECK | PCRE_NOTBOL | PCRE_NOTEOL | + PCRE_NOTEMPTY | PCRE_NOTEMPTY_ATSTART)) == 0) + return PRIV(jit_exec)(re, extra_data->executable_jit, + (const pcre_uchar *)subject, length, start_offset, options, + ((extra_data->flags & PCRE_EXTRA_MATCH_LIMIT) == 0) + ? MATCH_LIMIT : extra_data->match_limit, offsets, offsetcount); +#endif + +/* Carry on with non-JIT matching. This information is for finding all the +numbers associated with a given name, for condition testing. */ + +md->name_table = (pcre_uchar *)re + re->name_table_offset; +md->name_count = re->name_count; +md->name_entry_size = re->name_entry_size; + +/* Fish out the optional data from the extra_data structure, first setting +the default values. */ + +study = NULL; +md->match_limit = MATCH_LIMIT; +md->match_limit_recursion = MATCH_LIMIT_RECURSION; +md->callout_data = NULL; + +/* The table pointer is always in native byte order. */ + +tables = re->tables; + +if (extra_data != NULL) + { + register unsigned int flags = extra_data->flags; + if ((flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = (const pcre_study_data *)extra_data->study_data; + if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) + md->match_limit = extra_data->match_limit; + if ((flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) != 0) + md->match_limit_recursion = extra_data->match_limit_recursion; + if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0) + md->callout_data = extra_data->callout_data; + if ((flags & PCRE_EXTRA_TABLES) != 0) tables = extra_data->tables; + } + +/* If the exec call supplied NULL for tables, use the inbuilt ones. This +is a feature that makes it possible to save compiled regex and re-use them +in other programs later. */ + +if (tables == NULL) tables = PRIV(default_tables); + +/* Set up other data */ + +anchored = ((re->options | options) & PCRE_ANCHORED) != 0; +startline = (re->flags & PCRE_STARTLINE) != 0; +firstline = (re->options & PCRE_FIRSTLINE) != 0; + +/* The code starts after the real_pcre block and the capture name table. */ + +md->start_code = (const pcre_uchar *)re + re->name_table_offset + + re->name_count * re->name_entry_size; + +md->start_subject = (PCRE_PUCHAR)subject; +md->start_offset = start_offset; +md->end_subject = md->start_subject + length; +end_subject = md->end_subject; + +md->endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; +md->use_ucp = (re->options & PCRE_UCP) != 0; +md->jscript_compat = (re->options & PCRE_JAVASCRIPT_COMPAT) != 0; +md->ignore_skip_arg = FALSE; + +/* Some options are unpacked into BOOL variables in the hope that testing +them will be faster than individual option bits. */ + +md->notbol = (options & PCRE_NOTBOL) != 0; +md->noteol = (options & PCRE_NOTEOL) != 0; +md->notempty = (options & PCRE_NOTEMPTY) != 0; +md->notempty_atstart = (options & PCRE_NOTEMPTY_ATSTART) != 0; + +md->hitend = FALSE; +md->mark = md->nomatch_mark = NULL; /* In case never set */ + +md->recursive = NULL; /* No recursion at top level */ +md->hasthen = (re->flags & PCRE_HASTHEN) != 0; + +md->lcc = tables + lcc_offset; +md->fcc = tables + fcc_offset; +md->ctypes = tables + ctypes_offset; + +/* Handle different \R options. */ + +switch (options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) + { + case 0: + if ((re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) != 0) + md->bsr_anycrlf = (re->options & PCRE_BSR_ANYCRLF) != 0; + else +#ifdef BSR_ANYCRLF + md->bsr_anycrlf = TRUE; +#else + md->bsr_anycrlf = FALSE; +#endif + break; + + case PCRE_BSR_ANYCRLF: + md->bsr_anycrlf = TRUE; + break; + + case PCRE_BSR_UNICODE: + md->bsr_anycrlf = FALSE; + break; + + default: return PCRE_ERROR_BADNEWLINE; + } + +/* Handle different types of newline. The three bits give eight cases. If +nothing is set at run time, whatever was used at compile time applies. */ + +switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : + (pcre_uint32)options) & PCRE_NEWLINE_BITS) + { + case 0: newline = NEWLINE; break; /* Compile-time default */ + case PCRE_NEWLINE_CR: newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: newline = CHAR_NL; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: newline = (CHAR_CR << 8) | CHAR_NL; break; + case PCRE_NEWLINE_ANY: newline = -1; break; + case PCRE_NEWLINE_ANYCRLF: newline = -2; break; + default: return PCRE_ERROR_BADNEWLINE; + } + +if (newline == -2) + { + md->nltype = NLTYPE_ANYCRLF; + } +else if (newline < 0) + { + md->nltype = NLTYPE_ANY; + } +else + { + md->nltype = NLTYPE_FIXED; + if (newline > 255) + { + md->nllen = 2; + md->nl[0] = (newline >> 8) & 255; + md->nl[1] = newline & 255; + } + else + { + md->nllen = 1; + md->nl[0] = newline; + } + } + +/* Partial matching was originally supported only for a restricted set of +regexes; from release 8.00 there are no restrictions, but the bits are still +defined (though never set). So there's no harm in leaving this code. */ + +if (md->partial && (re->flags & PCRE_NOPARTIAL) != 0) + return PCRE_ERROR_BADPARTIAL; + +/* If the expression has got more back references than the offsets supplied can +hold, we get a temporary chunk of working store to use during the matching. +Otherwise, we can use the vector supplied, rounding down its size to a multiple +of 3. */ + +ocount = offsetcount - (offsetcount % 3); +arg_offset_max = (2*ocount)/3; + +if (re->top_backref > 0 && re->top_backref >= ocount/3) + { + ocount = re->top_backref * 3 + 3; + md->offset_vector = (int *)(PUBL(malloc))(ocount * sizeof(int)); + if (md->offset_vector == NULL) return PCRE_ERROR_NOMEMORY; + using_temporary_offsets = TRUE; + DPRINTF(("Got memory to hold back references\n")); + } +else md->offset_vector = offsets; + +md->offset_end = ocount; +md->offset_max = (2*ocount)/3; +md->offset_overflow = FALSE; +md->capture_last = -1; + +/* Reset the working variable associated with each extraction. These should +never be used unless previously set, but they get saved and restored, and so we +initialize them to avoid reading uninitialized locations. Also, unset the +offsets for the matched string. This is really just for tidiness with callouts, +in case they inspect these fields. */ + +if (md->offset_vector != NULL) + { + register int *iptr = md->offset_vector + ocount; + register int *iend = iptr - re->top_bracket; + if (iend < md->offset_vector + 2) iend = md->offset_vector + 2; + while (--iptr >= iend) *iptr = -1; + md->offset_vector[0] = md->offset_vector[1] = -1; + } + +/* Set up the first character to match, if available. The first_char value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->flags & PCRE_FIRSTSET) != 0) + { + has_first_char = TRUE; + first_char = first_char2 = (pcre_uchar)(re->first_char); + if ((re->flags & PCRE_FCH_CASELESS) != 0) + { + first_char2 = TABLE_GET(first_char, md->fcc, first_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && first_char > 127) + first_char2 = UCD_OTHERCASE(first_char); +#endif + } + } + else + if (!startline && study != NULL && + (study->flags & PCRE_STUDY_MAPPED) != 0) + start_bits = study->start_bits; + } + +/* For anchored or unanchored matches, there may be a "last known required +character" set. */ + +if ((re->flags & PCRE_REQCHSET) != 0) + { + has_req_char = TRUE; + req_char = req_char2 = (pcre_uchar)(re->req_char); + if ((re->flags & PCRE_RCH_CASELESS) != 0) + { + req_char2 = TABLE_GET(req_char, md->fcc, req_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (utf && req_char > 127) + req_char2 = UCD_OTHERCASE(req_char); +#endif + } + } + + +/* ==========================================================================*/ + +/* Loop for handling unanchored repeated matching attempts; for anchored regexs +the loop runs just once. */ + +for(;;) + { + PCRE_PUCHAR save_end_subject = end_subject; + PCRE_PUCHAR new_start_match; + + /* If firstline is TRUE, the start of the match is constrained to the first + line of a multiline string. That is, the match must be before or at the first + newline. Implement this by temporarily adjusting end_subject so that we stop + scanning at a newline. If the match fails at the newline, later code breaks + this loop. */ + + if (firstline) + { + PCRE_PUCHAR t = start_match; +#ifdef SUPPORT_UTF + if (utf) + { + while (t < md->end_subject && !IS_NEWLINE(t)) + { + t++; + ACROSSCHAR(t < end_subject, *t, t++); + } + } + else +#endif + while (t < md->end_subject && !IS_NEWLINE(t)) t++; + end_subject = t; + } + + /* There are some optimizations that avoid running the match if a known + starting point is not found, or if a known later character is not present. + However, there is an option that disables these, for testing and for ensuring + that all callouts do actually occur. The option can be set in the regex by + (*NO_START_OPT) or passed in match-time options. */ + + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0) + { + /* Advance to a unique first char if there is one. */ + + if (has_first_char) + { + if (first_char != first_char2) + while (start_match < end_subject && + *start_match != first_char && *start_match != first_char2) + start_match++; + else + while (start_match < end_subject && *start_match != first_char) + start_match++; + } + + /* Or to just after a linebreak for a multiline match */ + + else if (startline) + { + if (start_match > md->start_subject + start_offset) + { +#ifdef SUPPORT_UTF + if (utf) + { + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + { + start_match++; + ACROSSCHAR(start_match < end_subject, *start_match, + start_match++); + } + } + else +#endif + while (start_match < end_subject && !WAS_NEWLINE(start_match)) + start_match++; + + /* If we have just passed a CR and the newline option is ANY or ANYCRLF, + and we are now at a LF, advance the match position by one more character. + */ + + if (start_match[-1] == CHAR_CR && + (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + *start_match == CHAR_NL) + start_match++; + } + } + + /* Or to a non-unique first byte after study */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + register unsigned int c = *start_match; +#ifndef COMPILE_PCRE8 + if (c > 255) c = 255; +#endif + if ((start_bits[c/8] & (1 << (c&7))) == 0) + { + start_match++; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + /* In non 8-bit mode, the iteration will stop for + characters > 255 at the beginning or not stop at all. */ + if (utf) + ACROSSCHAR(start_match < end_subject, *start_match, + start_match++); +#endif + } + else break; + } + } + } /* Starting optimizations */ + + /* Restore fudged end_subject */ + + end_subject = save_end_subject; + + /* The following two optimizations are disabled for partial matching or if + disabling is explicitly requested. */ + + if (((options | re->options) & PCRE_NO_START_OPTIMIZE) == 0 && !md->partial) + { + /* If the pattern was studied, a minimum subject length may be set. This is + a lower bound; no actual string of that length may actually match the + pattern. Although the value is, strictly, in characters, we treat it as + bytes to avoid spending too much time in this optimization. */ + + if (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0 && + (pcre_uint32)(end_subject - start_match) < study->minlength) + { + rc = MATCH_NOMATCH; + break; + } + + /* If req_char is set, we know that that character must appear in the + subject for the match to succeed. If the first character is set, req_char + must be later in the subject; otherwise the test starts at the match point. + This optimization can save a huge amount of backtracking in patterns with + nested unlimited repeats that aren't going to match. Writing separate code + for cased/caseless versions makes it go faster, as does using an + autoincrement and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end + can take a long time, and give bad performance on quite ordinary patterns. + This showed up when somebody was matching something like /^\d+C/ on a + 32-megabyte string... so we don't do this when the string is sufficiently + long. */ + + if (has_req_char && end_subject - start_match < REQ_BYTE_MAX) + { + register PCRE_PUCHAR p = start_match + (has_first_char? 1:0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_char_ptr) + { + if (req_char != req_char2) + { + while (p < end_subject) + { + register int pp = *p++; + if (pp == req_char || pp == req_char2) { p--; break; } + } + } + else + { + while (p < end_subject) + { + if (*p++ == req_char) { p--; break; } + } + } + + /* If we can't find the required character, break the matching loop, + forcing a match failure. */ + + if (p >= end_subject) + { + rc = MATCH_NOMATCH; + break; + } + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + req_char_ptr = p; + } + } + } + +#ifdef PCRE_DEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, md); + printf("\n"); +#endif + + /* OK, we can now run the match. If "hitend" is set afterwards, remember the + first starting point for which a partial match was found. */ + + md->start_match_ptr = start_match; + md->start_used_ptr = start_match; + md->match_call_count = 0; + md->match_function_type = 0; + md->end_offset_top = 0; + rc = match(start_match, md->start_code, start_match, 2, md, NULL, 0); + if (md->hitend && start_partial == NULL) start_partial = md->start_used_ptr; + + switch(rc) + { + /* If MATCH_SKIP_ARG reaches this level it means that a MARK that matched + the SKIP's arg was not found. In this circumstance, Perl ignores the SKIP + entirely. The only way we can do that is to re-do the match at the same + point, with a flag to force SKIP with an argument to be ignored. Just + treating this case as NOMATCH does not work because it does not check other + alternatives in patterns such as A(*SKIP:A)B|AC when the subject is AC. */ + + case MATCH_SKIP_ARG: + new_start_match = start_match; + md->ignore_skip_arg = TRUE; + break; + + /* SKIP passes back the next starting point explicitly, but if it is the + same as the match we have just done, treat it as NOMATCH. */ + + case MATCH_SKIP: + if (md->start_match_ptr != start_match) + { + new_start_match = md->start_match_ptr; + break; + } + /* Fall through */ + + /* NOMATCH and PRUNE advance by one character. THEN at this level acts + exactly like PRUNE. Unset the ignore SKIP-with-argument flag. */ + + case MATCH_NOMATCH: + case MATCH_PRUNE: + case MATCH_THEN: + md->ignore_skip_arg = FALSE; + new_start_match = start_match + 1; +#ifdef SUPPORT_UTF + if (utf) + ACROSSCHAR(new_start_match < end_subject, *new_start_match, + new_start_match++); +#endif + break; + + /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */ + + case MATCH_COMMIT: + rc = MATCH_NOMATCH; + goto ENDLOOP; + + /* Any other return is either a match, or some kind of error. */ + + default: + goto ENDLOOP; + } + + /* Control reaches here for the various types of "no match at this point" + result. Reset the code to MATCH_NOMATCH for subsequent checking. */ + + rc = MATCH_NOMATCH; + + /* If PCRE_FIRSTLINE is set, the match must happen before or at the first + newline in the subject (though it may continue over the newline). Therefore, + if we have just failed to match, starting at a newline, do not continue. */ + + if (firstline && IS_NEWLINE(start_match)) break; + + /* Advance to new matching position */ + + start_match = new_start_match; + + /* Break the loop if the pattern is anchored or if we have passed the end of + the subject. */ + + if (anchored || start_match > end_subject) break; + + /* If we have just passed a CR and we are now at a LF, and the pattern does + not contain any explicit matches for \r or \n, and the newline option is CRLF + or ANY or ANYCRLF, advance the match position by one more character. In + normal matching start_match will aways be greater than the first position at + this stage, but a failed *SKIP can cause a return at the same point, which is + why the first test exists. */ + + if (start_match > (PCRE_PUCHAR)subject + start_offset && + start_match[-1] == CHAR_CR && + start_match < end_subject && + *start_match == CHAR_NL && + (re->flags & PCRE_HASCRORLF) == 0 && + (md->nltype == NLTYPE_ANY || + md->nltype == NLTYPE_ANYCRLF || + md->nllen == 2)) + start_match++; + + md->mark = NULL; /* Reset for start of next match attempt */ + } /* End of for(;;) "bumpalong" loop */ + +/* ==========================================================================*/ + +/* We reach here when rc is not MATCH_NOMATCH, or if one of the stopping +conditions is true: + +(1) The pattern is anchored or the match was failed by (*COMMIT); + +(2) We are past the end of the subject; + +(3) PCRE_FIRSTLINE is set and we have failed to match at a newline, because + this option requests that a match occur at or before the first newline in + the subject. + +When we have a match and the offset vector is big enough to deal with any +backreferences, captured substring offsets will already be set up. In the case +where we had to get some local store to hold offsets for backreference +processing, copy those that we can. In this case there need not be overflow if +certain parts of the pattern were not used, even though there are more +capturing parentheses than vector slots. */ + +ENDLOOP: + +if (rc == MATCH_MATCH || rc == MATCH_ACCEPT) + { + if (using_temporary_offsets) + { + if (arg_offset_max >= 4) + { + memcpy(offsets + 2, md->offset_vector + 2, + (arg_offset_max - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (md->end_offset_top > arg_offset_max) md->offset_overflow = TRUE; + DPRINTF(("Freeing temporary memory\n")); + (PUBL(free))(md->offset_vector); + } + + /* Set the return code to the number of captured strings, or 0 if there were + too many to fit into the vector. */ + + rc = (md->offset_overflow && md->end_offset_top >= arg_offset_max)? + 0 : md->end_offset_top/2; + + /* If there is space in the offset vector, set any unused pairs at the end of + the pattern to -1 for backwards compatibility. It is documented that this + happens. In earlier versions, the whole set of potential capturing offsets + was set to -1 each time round the loop, but this is handled differently now. + "Gaps" are set to -1 dynamically instead (this fixes a bug). Thus, it is only + those at the end that need unsetting here. We can't just unset them all at + the start of the whole thing because they may get set in one branch that is + not the final matching branch. */ + + if (md->end_offset_top/2 <= re->top_bracket && offsets != NULL) + { + register int *iptr, *iend; + int resetcount = 2 + re->top_bracket * 2; + if (resetcount > offsetcount) resetcount = ocount; + iptr = offsets + md->end_offset_top; + iend = offsets + resetcount; + while (iptr < iend) *iptr++ = -1; + } + + /* If there is space, set up the whole thing as substring 0. The value of + md->start_match_ptr might be modified if \K was encountered on the success + matching path. */ + + if (offsetcount < 2) rc = 0; else + { + offsets[0] = (int)(md->start_match_ptr - md->start_subject); + offsets[1] = (int)(md->end_match_ptr - md->start_subject); + } + + /* Return MARK data if requested */ + + if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_MARK) != 0) + *(extra_data->mark) = (pcre_uchar *)md->mark; + DPRINTF((">>>> returning %d\n", rc)); + return rc; + } + +/* Control gets here if there has been an error, or if the overall match +attempt has failed at all permitted starting positions. */ + +if (using_temporary_offsets) + { + DPRINTF(("Freeing temporary memory\n")); + (PUBL(free))(md->offset_vector); + } + +/* For anything other than nomatch or partial match, just return the code. */ + +if (rc != MATCH_NOMATCH && rc != PCRE_ERROR_PARTIAL) + { + DPRINTF((">>>> error: returning %d\n", rc)); + return rc; + } + +/* Handle partial matches - disable any mark data */ + +if (start_partial != NULL) + { + DPRINTF((">>>> returning PCRE_ERROR_PARTIAL\n")); + md->mark = NULL; + if (offsetcount > 1) + { + offsets[0] = (int)(start_partial - (PCRE_PUCHAR)subject); + offsets[1] = (int)(end_subject - (PCRE_PUCHAR)subject); + } + rc = PCRE_ERROR_PARTIAL; + } + +/* This is the classic nomatch case */ + +else + { + DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); + rc = PCRE_ERROR_NOMATCH; + } + +/* Return the MARK data if it has been requested. */ + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_MARK) != 0) + *(extra_data->mark) = (pcre_uchar *)md->nomatch_mark; +return rc; +} + +/* End of pcre_exec.c */ diff --git a/src/lib/pcre/pcre_fullinfo.c b/src/lib/pcre/pcre_fullinfo.c new file mode 100644 index 0000000..e5e68f3 --- /dev/null +++ b/src/lib/pcre/pcre_fullinfo.c @@ -0,0 +1,202 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_fullinfo(), which returns +information about a compiled pattern. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Return info about compiled pattern * +*************************************************/ + +/* This is a newer "info" function which has an extensible interface so +that additional items can be added compatibly. + +Arguments: + argument_re points to compiled code + extra_data points extra data, or NULL + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, + int what, void *where) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_fullinfo(const pcre16 *argument_re, const pcre16_extra *extra_data, + int what, void *where) +#endif +{ +const REAL_PCRE *re = (const REAL_PCRE *)argument_re; +const pcre_study_data *study = NULL; + +if (re == NULL || where == NULL) return PCRE_ERROR_NULL; + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = (const pcre_study_data *)extra_data->study_data; + +/* Check that the first field in the block is the magic number. If it is not, +return with PCRE_ERROR_BADMAGIC. However, if the magic number is equal to +REVERSED_MAGIC_NUMBER we return with PCRE_ERROR_BADENDIANNESS, which +means that the pattern is likely compiled with different endianness. */ + +if (re->magic_number != MAGIC_NUMBER) + return re->magic_number == REVERSED_MAGIC_NUMBER? + PCRE_ERROR_BADENDIANNESS:PCRE_ERROR_BADMAGIC; + +/* Check that this pattern was compiled in the correct bit mode */ + +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; + +switch (what) + { + case PCRE_INFO_OPTIONS: + *((unsigned long int *)where) = re->options & PUBLIC_COMPILE_OPTIONS; + break; + + case PCRE_INFO_SIZE: + *((size_t *)where) = re->size; + break; + + case PCRE_INFO_STUDYSIZE: + *((size_t *)where) = (study == NULL)? 0 : study->size; + break; + + case PCRE_INFO_JITSIZE: +#ifdef SUPPORT_JIT + *((size_t *)where) = + (extra_data != NULL && + (extra_data->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra_data->executable_jit != NULL)? + PRIV(jit_get_size)(extra_data->executable_jit) : 0; +#else + *((size_t *)where) = 0; +#endif + break; + + case PCRE_INFO_CAPTURECOUNT: + *((int *)where) = re->top_bracket; + break; + + case PCRE_INFO_BACKREFMAX: + *((int *)where) = re->top_backref; + break; + + case PCRE_INFO_FIRSTBYTE: + *((int *)where) = + ((re->flags & PCRE_FIRSTSET) != 0)? re->first_char : + ((re->flags & PCRE_STARTLINE) != 0)? -1 : -2; + break; + + /* Make sure we pass back the pointer to the bit vector in the external + block, not the internal copy (with flipped integer fields). */ + + case PCRE_INFO_FIRSTTABLE: + *((const pcre_uint8 **)where) = + (study != NULL && (study->flags & PCRE_STUDY_MAPPED) != 0)? + ((const pcre_study_data *)extra_data->study_data)->start_bits : NULL; + break; + + case PCRE_INFO_MINLENGTH: + *((int *)where) = + (study != NULL && (study->flags & PCRE_STUDY_MINLEN) != 0)? + (int)(study->minlength) : -1; + break; + + case PCRE_INFO_JIT: + *((int *)where) = extra_data != NULL && + (extra_data->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra_data->executable_jit != NULL; + break; + + case PCRE_INFO_LASTLITERAL: + *((int *)where) = + ((re->flags & PCRE_REQCHSET) != 0)? re->req_char : -1; + break; + + case PCRE_INFO_NAMEENTRYSIZE: + *((int *)where) = re->name_entry_size; + break; + + case PCRE_INFO_NAMECOUNT: + *((int *)where) = re->name_count; + break; + + case PCRE_INFO_NAMETABLE: + *((const pcre_uchar **)where) = (const pcre_uchar *)re + re->name_table_offset; + break; + + case PCRE_INFO_DEFAULT_TABLES: + *((const pcre_uint8 **)where) = (const pcre_uint8 *)(PRIV(default_tables)); + break; + + /* From release 8.00 this will always return TRUE because NOPARTIAL is + no longer ever set (the restrictions have been removed). */ + + case PCRE_INFO_OKPARTIAL: + *((int *)where) = (re->flags & PCRE_NOPARTIAL) == 0; + break; + + case PCRE_INFO_JCHANGED: + *((int *)where) = (re->flags & PCRE_JCHANGED) != 0; + break; + + case PCRE_INFO_HASCRORLF: + *((int *)where) = (re->flags & PCRE_HASCRORLF) != 0; + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + +/* End of pcre_fullinfo.c */ diff --git a/src/lib/pcre/pcre_get.c b/src/lib/pcre/pcre_get.c new file mode 100644 index 0000000..daecb69 --- /dev/null +++ b/src/lib/pcre/pcre_get.c @@ -0,0 +1,587 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains some convenience functions for extracting substrings +from the subject string after a regex match has succeeded. The original idea +for these functions came from Scott Wimer. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Find number for named string * +*************************************************/ + +/* This function is used by the get_first_set() function below, as well +as being generally available. It assumes that names are unique. + +Arguments: + code the compiled regex + stringname the name whose number is required + +Returns: the number of the named parentheses, or a negative number + (PCRE_ERROR_NOSUBSTRING) if not found +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_stringnumber(const pcre *code, const char *stringname) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_stringnumber(const pcre16 *code, PCRE_SPTR16 stringname) +#endif +{ +int rc; +int entrysize; +int top, bot; +pcre_uchar *nametable; + +#ifdef COMPILE_PCRE8 +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif +#ifdef COMPILE_PCRE16 +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif + +bot = 0; +while (top > bot) + { + int mid = (top + bot) / 2; + pcre_uchar *entry = nametable + entrysize*mid; + int c = STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(entry + IMM2_SIZE)); + if (c == 0) return GET2(entry, 0); + if (c > 0) bot = mid + 1; else top = mid; + } + +return PCRE_ERROR_NOSUBSTRING; +} + + + +/************************************************* +* Find (multiple) entries for named string * +*************************************************/ + +/* This is used by the get_first_set() function below, as well as being +generally available. It is used when duplicated names are permitted. + +Arguments: + code the compiled regex + stringname the name whose entries required + firstptr where to put the pointer to the first entry + lastptr where to put the pointer to the last entry + +Returns: the length of each entry, or a negative number + (PCRE_ERROR_NOSUBSTRING) if not found +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_stringtable_entries(const pcre *code, const char *stringname, + char **firstptr, char **lastptr) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_stringtable_entries(const pcre16 *code, PCRE_SPTR16 stringname, + PCRE_UCHAR16 **firstptr, PCRE_UCHAR16 **lastptr) +#endif +{ +int rc; +int entrysize; +int top, bot; +pcre_uchar *nametable, *lastentry; + +#ifdef COMPILE_PCRE8 +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif +#ifdef COMPILE_PCRE16 +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre16_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; +#endif + +lastentry = nametable + entrysize * (top - 1); +bot = 0; +while (top > bot) + { + int mid = (top + bot) / 2; + pcre_uchar *entry = nametable + entrysize*mid; + int c = STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(entry + IMM2_SIZE)); + if (c == 0) + { + pcre_uchar *first = entry; + pcre_uchar *last = entry; + while (first > nametable) + { + if (STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(first - entrysize + IMM2_SIZE)) != 0) break; + first -= entrysize; + } + while (last < lastentry) + { + if (STRCMP_UC_UC((pcre_uchar *)stringname, + (pcre_uchar *)(last + entrysize + IMM2_SIZE)) != 0) break; + last += entrysize; + } +#ifdef COMPILE_PCRE8 + *firstptr = (char *)first; + *lastptr = (char *)last; +#else + *firstptr = (PCRE_UCHAR16 *)first; + *lastptr = (PCRE_UCHAR16 *)last; +#endif + return entrysize; + } + if (c > 0) bot = mid + 1; else top = mid; + } + +return PCRE_ERROR_NOSUBSTRING; +} + + + +/************************************************* +* Find first set of multiple named strings * +*************************************************/ + +/* This function allows for duplicate names in the table of named substrings. +It returns the number of the first one that was set in a pattern match. + +Arguments: + code the compiled regex + stringname the name of the capturing substring + ovector the vector of matched substrings + +Returns: the number of the first that is set, + or the number of the last one if none are set, + or a negative number on error +*/ + +#ifdef COMPILE_PCRE8 +static int +get_first_set(const pcre *code, const char *stringname, int *ovector) +#else +static int +get_first_set(const pcre16 *code, PCRE_SPTR16 stringname, int *ovector) +#endif +{ +const REAL_PCRE *re = (const REAL_PCRE *)code; +int entrysize; +pcre_uchar *entry; +#ifdef COMPILE_PCRE8 +char *first, *last; +#else +PCRE_UCHAR16 *first, *last; +#endif + +#ifdef COMPILE_PCRE8 +if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) + return pcre_get_stringnumber(code, stringname); +entrysize = pcre_get_stringtable_entries(code, stringname, &first, &last); +#else +if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) + return pcre16_get_stringnumber(code, stringname); +entrysize = pcre16_get_stringtable_entries(code, stringname, &first, &last); +#endif +if (entrysize <= 0) return entrysize; +for (entry = (pcre_uchar *)first; entry <= (pcre_uchar *)last; entry += entrysize) + { + int n = GET2(entry, 0); + if (ovector[n*2] >= 0) return n; + } +return GET2(entry, 0); +} + + + + +/************************************************* +* Copy captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer. +Note that we use memcpy() rather than strncpy() in case there are binary zeros +in the string. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_copy_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, char *buffer, int size) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_copy_substring(PCRE_SPTR16 subject, int *ovector, int stringcount, + int stringnumber, PCRE_UCHAR16 *buffer, int size) +#endif +{ +int yield; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +if (size < yield + 1) return PCRE_ERROR_NOMEMORY; +memcpy(buffer, subject + ovector[stringnumber], IN_UCHARS(yield)); +buffer[yield] = 0; +return yield; +} + + + +/************************************************* +* Copy named captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer, +identifying it by name. If the regex permits duplicate names, the first +substring that is set is chosen. + +Arguments: + code the compiled regex + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringname the name of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_copy_named_substring(const pcre *code, const char *subject, + int *ovector, int stringcount, const char *stringname, + char *buffer, int size) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_copy_named_substring(const pcre16 *code, PCRE_SPTR16 subject, + int *ovector, int stringcount, PCRE_SPTR16 stringname, + PCRE_UCHAR16 *buffer, int size) +#endif +{ +int n = get_first_set(code, stringname, ovector); +if (n <= 0) return n; +#ifdef COMPILE_PCRE8 +return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +#else +return pcre16_copy_substring(subject, ovector, stringcount, n, buffer, size); +#endif +} + + + +/************************************************* +* Copy all captured strings to new store * +*************************************************/ + +/* This function gets one chunk of store and builds a list of pointers and all +of the captured substrings in it. A NULL pointer is put on the end of the list. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + listptr set to point to the list of pointers + +Returns: if successful: 0 + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_substring_list(const char *subject, int *ovector, int stringcount, + const char ***listptr) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_substring_list(PCRE_SPTR16 subject, int *ovector, int stringcount, + PCRE_SPTR16 **listptr) +#endif +{ +int i; +int size = sizeof(pcre_uchar *); +int double_count = stringcount * 2; +pcre_uchar **stringlist; +pcre_uchar *p; + +for (i = 0; i < double_count; i += 2) + size += sizeof(pcre_uchar *) + IN_UCHARS(ovector[i+1] - ovector[i] + 1); + +stringlist = (pcre_uchar **)(PUBL(malloc))(size); +if (stringlist == NULL) return PCRE_ERROR_NOMEMORY; + +#ifdef COMPILE_PCRE8 +*listptr = (const char **)stringlist; +#else +*listptr = (PCRE_SPTR16 *)stringlist; +#endif +p = (pcre_uchar *)(stringlist + stringcount + 1); + +for (i = 0; i < double_count; i += 2) + { + int len = ovector[i+1] - ovector[i]; + memcpy(p, subject + ovector[i], IN_UCHARS(len)); + *stringlist++ = p; + p += len; + *p++ = 0; + } + +*stringlist = NULL; +return 0; +} + + + +/************************************************* +* Free store obtained by get_substring_list * +*************************************************/ + +/* This function exists for the benefit of people calling PCRE from non-C +programs that can call its functions, but not free() or (PUBL(free))() +directly. + +Argument: the result of a previous pcre_get_substring_list() +Returns: nothing +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre_free_substring_list(const char **pointer) +#else +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre16_free_substring_list(PCRE_SPTR16 *pointer) +#endif +{ +(PUBL(free))((void *)pointer); +} + + + +/************************************************* +* Copy captured string to new store * +*************************************************/ + +/* This function copies a single captured substring into a piece of new +store + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + stringptr where to put a pointer to the substring + +Returns: if successful: + the length of the string, not including the zero that + is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store + PCRE_ERROR_NOSUBSTRING (-7) substring not present +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, const char **stringptr) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_substring(PCRE_SPTR16 subject, int *ovector, int stringcount, + int stringnumber, PCRE_SPTR16 *stringptr) +#endif +{ +int yield; +pcre_uchar *substring; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +substring = (pcre_uchar *)(PUBL(malloc))(IN_UCHARS(yield + 1)); +if (substring == NULL) return PCRE_ERROR_NOMEMORY; +memcpy(substring, subject + ovector[stringnumber], IN_UCHARS(yield)); +substring[yield] = 0; +#ifdef COMPILE_PCRE8 +*stringptr = (const char *)substring; +#else +*stringptr = (PCRE_SPTR16)substring; +#endif +return yield; +} + + + +/************************************************* +* Copy named captured string to new store * +*************************************************/ + +/* This function copies a single captured substring, identified by name, into +new store. If the regex permits duplicate names, the first substring that is +set is chosen. + +Arguments: + code the compiled regex + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringname the name of the required substring + stringptr where to put the pointer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) couldn't get memory + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_get_named_substring(const pcre *code, const char *subject, + int *ovector, int stringcount, const char *stringname, + const char **stringptr) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_get_named_substring(const pcre16 *code, PCRE_SPTR16 subject, + int *ovector, int stringcount, PCRE_SPTR16 stringname, + PCRE_SPTR16 *stringptr) +#endif +{ +int n = get_first_set(code, stringname, ovector); +if (n <= 0) return n; +#ifdef COMPILE_PCRE8 +return pcre_get_substring(subject, ovector, stringcount, n, stringptr); +#else +return pcre16_get_substring(subject, ovector, stringcount, n, stringptr); +#endif +} + + + + +/************************************************* +* Free store obtained by get_substring * +*************************************************/ + +/* This function exists for the benefit of people calling PCRE from non-C +programs that can call its functions, but not free() or (PUBL(free))() +directly. + +Argument: the result of a previous pcre_get_substring() +Returns: nothing +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre_free_substring(const char *pointer) +#else +PCRE_EXP_DEFN void PCRE_CALL_CONVENTION +pcre16_free_substring(PCRE_SPTR16 pointer) +#endif +{ +(PUBL(free))((void *)pointer); +} + +/* End of pcre_get.c */ diff --git a/src/lib/pcre/pcre_globals.c b/src/lib/pcre/pcre_globals.c new file mode 100644 index 0000000..d5b6286 --- /dev/null +++ b/src/lib/pcre/pcre_globals.c @@ -0,0 +1,84 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains global variables that are exported by the PCRE library. +PCRE is thread-clean and doesn't use any global variables in the normal sense. +However, it calls memory allocation and freeing functions via the four +indirections below, and it can optionally do callouts, using the fifth +indirection. These values can be changed by the caller, but are shared between +all threads. + +For MS Visual Studio and Symbian OS, there are problems in initializing these +variables to non-local functions. In these cases, therefore, an indirection via +a local function is used. + +Also, when compiling for Virtual Pascal, things are done differently, and +global variables are not used. */ + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#if defined _MSC_VER || defined __SYMBIAN32__ +static void* LocalPcreMalloc(size_t aSize) + { + return malloc(aSize); + } +static void LocalPcreFree(void* aPtr) + { + free(aPtr); + } +PCRE_EXP_DATA_DEFN void *(*PUBL(malloc))(size_t) = LocalPcreMalloc; +PCRE_EXP_DATA_DEFN void (*PUBL(free))(void *) = LocalPcreFree; +PCRE_EXP_DATA_DEFN void *(*PUBL(stack_malloc))(size_t) = LocalPcreMalloc; +PCRE_EXP_DATA_DEFN void (*PUBL(stack_free))(void *) = LocalPcreFree; +PCRE_EXP_DATA_DEFN int (*PUBL(callout))(PUBL(callout_block) *) = NULL; + +#elif !defined VPCOMPAT +PCRE_EXP_DATA_DEFN void *(*PUBL(malloc))(size_t) = malloc; +PCRE_EXP_DATA_DEFN void (*PUBL(free))(void *) = free; +PCRE_EXP_DATA_DEFN void *(*PUBL(stack_malloc))(size_t) = malloc; +PCRE_EXP_DATA_DEFN void (*PUBL(stack_free))(void *) = free; +PCRE_EXP_DATA_DEFN int (*PUBL(callout))(PUBL(callout_block) *) = NULL; +#endif + +/* End of pcre_globals.c */ diff --git a/src/lib/pcre/pcre_internal.h b/src/lib/pcre/pcre_internal.h new file mode 100644 index 0000000..e5a4b6a --- /dev/null +++ b/src/lib/pcre/pcre_internal.h @@ -0,0 +1,2332 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the exported API. This includes some +functions whose names all begin with "_pcre_" or "_pcre16_" depending on +the PRIV macro. */ + +#ifndef PCRE_INTERNAL_H +#define PCRE_INTERNAL_H + +/* Define PCRE_DEBUG to get debugging output on stdout. */ + +#if 0 +#define PCRE_DEBUG +#endif + +/* PCRE is compiled as an 8 bit library if it is not requested otherwise. */ +#ifndef COMPILE_PCRE16 +#define COMPILE_PCRE8 +#endif + +/* If SUPPORT_UCP is defined, SUPPORT_UTF must also be defined. The +"configure" script ensures this, but not everybody uses "configure". */ + +#if defined SUPPORT_UCP && !(defined SUPPORT_UTF) +#define SUPPORT_UTF 1 +#endif + +/* We define SUPPORT_UTF if SUPPORT_UTF8 is enabled for compatibility +reasons with existing code. */ + +#if defined SUPPORT_UTF8 && !(defined SUPPORT_UTF) +#define SUPPORT_UTF 1 +#endif + +/* Fixme: SUPPORT_UTF8 should be eventually disappear from the code. +Until then we define it if SUPPORT_UTF is defined. */ + +#if defined SUPPORT_UTF && !(defined SUPPORT_UTF8) +#define SUPPORT_UTF8 1 +#endif + +/* We do not support both EBCDIC and UTF-8/16 at the same time. The "configure" +script prevents both being selected, but not everybody uses "configure". */ + +#if defined EBCDIC && defined SUPPORT_UTF +#error The use of both EBCDIC and SUPPORT_UTF8/16 is not supported. +#endif + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements, or at least there were when I first wrote this. After +all, it had only been about 10 years then... + +It turns out that the Mac Debugging.h header also defines the macro DPRINTF, so +be absolutely sure we get our version. */ + +#undef DPRINTF +#ifdef PCRE_DEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /* Nothing */ +#endif + + +/* Standard C headers plus the external interface definition. The only time +setjmp and stdarg are used is when NO_RECURSE is set. */ + +#include +#include +#include +#include +#include +#include + +/* When compiling a DLL for Windows, the exported symbols have to be declared +using some MS magic. I found some useful information on this web page: +http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the +information there, using __declspec(dllexport) without "extern" we have a +definition; with "extern" we have a declaration. The settings here override the +setting in pcre.h (which is included below); it defines only PCRE_EXP_DECL, +which is all that is needed for applications (they just import the symbols). We +use: + + PCRE_EXP_DECL for declarations + PCRE_EXP_DEFN for definitions of exported functions + PCRE_EXP_DATA_DEFN for definitions of exported variables + +The reason for the two DEFN macros is that in non-Windows environments, one +does not want to have "extern" before variable definitions because it leads to +compiler warnings. So we distinguish between functions and variables. In +Windows, the two should always be the same. + +The reason for wrapping this in #ifndef PCRE_EXP_DECL is so that pcretest, +which is an application, but needs to import this file in order to "peek" at +internals, can #include pcre.h first to get an application's-eye view. + +In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon, +special-purpose environments) might want to stick other stuff in front of +exported symbols. That's why, in the non-Windows case, we set PCRE_EXP_DEFN and +PCRE_EXP_DATA_DEFN only if they are not already set. */ + +#ifndef PCRE_EXP_DECL +# ifdef _WIN32 +# ifndef PCRE_STATIC +# define PCRE_EXP_DECL extern __declspec(dllexport) +# define PCRE_EXP_DEFN __declspec(dllexport) +# define PCRE_EXP_DATA_DEFN __declspec(dllexport) +# else +# define PCRE_EXP_DECL extern +# define PCRE_EXP_DEFN +# define PCRE_EXP_DATA_DEFN +# endif +# else +# ifdef __cplusplus +# define PCRE_EXP_DECL extern "C" +# else +# define PCRE_EXP_DECL extern +# endif +# ifndef PCRE_EXP_DEFN +# define PCRE_EXP_DEFN PCRE_EXP_DECL +# endif +# ifndef PCRE_EXP_DATA_DEFN +# define PCRE_EXP_DATA_DEFN +# endif +# endif +#endif + +/* When compiling with the MSVC compiler, it is sometimes necessary to include +a "calling convention" before exported function names. (This is secondhand +information; I know nothing about MSVC myself). For example, something like + + void __cdecl function(....) + +might be needed. In order so make this easy, all the exported functions have +PCRE_CALL_CONVENTION just before their names. It is rarely needed; if not +set, we ensure here that it has no effect. */ + +#ifndef PCRE_CALL_CONVENTION +#define PCRE_CALL_CONVENTION +#endif + +/* We need to have types that specify unsigned 8, 16 and 32-bit integers. We +cannot determine these outside the compilation (e.g. by running a program as +part of "configure") because PCRE is often cross-compiled for use on other +systems. Instead we make use of the maximum sizes that are available at +preprocessor time in standard C environments. */ + +typedef unsigned char pcre_uint8; + +#if USHRT_MAX == 65535 + typedef unsigned short pcre_uint16; + typedef short pcre_int16; +#elif UINT_MAX == 65535 + typedef unsigned int pcre_uint16; + typedef int pcre_int16; +#else + #error Cannot determine a type for 16-bit unsigned integers +#endif + +#if UINT_MAX == 4294967295 + typedef unsigned int pcre_uint32; + typedef int pcre_int32; +#elif ULONG_MAX == 4294967295 + typedef unsigned long int pcre_uint32; + typedef long int pcre_int32; +#else + #error Cannot determine a type for 32-bit unsigned integers +#endif + +/* When checking for integer overflow in pcre_compile(), we need to handle +large integers. If a 64-bit integer type is available, we can use that. +Otherwise we have to cast to double, which of course requires floating point +arithmetic. Handle this by defining a macro for the appropriate type. If +stdint.h is available, include it; it may define INT64_MAX. Systems that do not +have stdint.h (e.g. Solaris) may have inttypes.h. The macro int64_t may be set +by "configure". */ + +#if HAVE_STDINT_H +#include +#elif HAVE_INTTYPES_H +#include +#endif + +#if defined INT64_MAX || defined int64_t +#define INT64_OR_DOUBLE int64_t +#else +#define INT64_OR_DOUBLE double +#endif + +/* All character handling must be done as unsigned characters. Otherwise there +are problems with top-bit-set characters and functions such as isspace(). +However, we leave the interface to the outside world as char * or short *, +because that should make things easier for callers. This character type is +called pcre_uchar. + +The IN_UCHARS macro multiply its argument with the byte size of the current +pcre_uchar type. Useful for memcpy and such operations, whose require the +byte size of their input/output buffers. + +The MAX_255 macro checks whether its pcre_uchar input is less than 256. + +The TABLE_GET macro is designed for accessing elements of tables whose contain +exactly 256 items. When the character is able to contain more than 256 +items, some check is needed before accessing these tables. +*/ + +#ifdef COMPILE_PCRE8 + +typedef unsigned char pcre_uchar; +#define IN_UCHARS(x) (x) +#define MAX_255(c) 1 +#define TABLE_GET(c, table, default) ((table)[c]) + +#else + +#ifdef COMPILE_PCRE16 +#if USHRT_MAX != 65535 +/* This is a warning message. Change PCRE_UCHAR16 to a 16 bit data type in +pcre.h(.in) and disable (comment out) this message. */ +#error Warning: PCRE_UCHAR16 is not a 16 bit data type. +#endif + +typedef pcre_uint16 pcre_uchar; +#define IN_UCHARS(x) ((x) << 1) +#define MAX_255(c) ((c) <= 255u) +#define TABLE_GET(c, table, default) (MAX_255(c)? ((table)[c]):(default)) + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE16 */ + +#endif /* COMPILE_PCRE8 */ + +/* This is an unsigned int value that no character can ever have. UTF-8 +characters only go up to 0x7fffffff (though Unicode doesn't go beyond +0x0010ffff). */ + +#define NOTACHAR 0xffffffff + +/* PCRE is able to support several different kinds of newline (CR, LF, CRLF, +"any" and "anycrlf" at present). The following macros are used to package up +testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various +modules to indicate in which datablock the parameters exist, and what the +start/end of string field names are. */ + +#define NLTYPE_FIXED 0 /* Newline is a fixed length string */ +#define NLTYPE_ANY 1 /* Newline is any Unicode line ending */ +#define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */ + +/* This macro checks for a newline at the given position */ + +#define IS_NEWLINE(p) \ + ((NLBLOCK->nltype != NLTYPE_FIXED)? \ + ((p) < NLBLOCK->PSEND && \ + PRIV(is_newline)((p), NLBLOCK->nltype, NLBLOCK->PSEND, \ + &(NLBLOCK->nllen), utf)) \ + : \ + ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \ + (p)[0] == NLBLOCK->nl[0] && \ + (NLBLOCK->nllen == 1 || (p)[1] == NLBLOCK->nl[1]) \ + ) \ + ) + +/* This macro checks for a newline immediately preceding the given position */ + +#define WAS_NEWLINE(p) \ + ((NLBLOCK->nltype != NLTYPE_FIXED)? \ + ((p) > NLBLOCK->PSSTART && \ + PRIV(was_newline)((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ + &(NLBLOCK->nllen), utf)) \ + : \ + ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \ + (p)[-NLBLOCK->nllen] == NLBLOCK->nl[0] && \ + (NLBLOCK->nllen == 1 || (p)[-NLBLOCK->nllen+1] == NLBLOCK->nl[1]) \ + ) \ + ) + +/* When PCRE is compiled as a C++ library, the subject pointer can be replaced +with a custom type. This makes it possible, for example, to allow pcre_exec() +to process subject strings that are discontinuous by using a smart pointer +class. It must always be possible to inspect all of the subject string in +pcre_exec() because of the way it backtracks. Two macros are required in the +normal case, for sign-unspecified and unsigned char pointers. The former is +used for the external interface and appears in pcre.h, which is why its name +must begin with PCRE_. */ + +#ifdef CUSTOM_SUBJECT_PTR +#define PCRE_PUCHAR CUSTOM_SUBJECT_PTR +#else +#define PCRE_PUCHAR const pcre_uchar * +#endif + +/* Include the public PCRE header and the definitions of UCP character property +values. */ + +#include "pcre.h" +#include "ucp.h" + +/* When compiling for use with the Virtual Pascal compiler, these functions +need to have their names changed. PCRE must be compiled with the -DVPCOMPAT +option on the command line. */ + +#ifdef VPCOMPAT +#define strlen(s) _strlen(s) +#define strncmp(s1,s2,m) _strncmp(s1,s2,m) +#define memcmp(s,c,n) _memcmp(s,c,n) +#define memcpy(d,s,n) _memcpy(d,s,n) +#define memmove(d,s,n) _memmove(d,s,n) +#define memset(s,c,n) _memset(s,c,n) +#else /* VPCOMPAT */ + +/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), +define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY +is set. Otherwise, include an emulating function for those systems that have +neither (there some non-Unix environments where this is the case). */ + +#ifndef HAVE_MEMMOVE +#undef memmove /* some systems may have a macro */ +#ifdef HAVE_BCOPY +#define memmove(a, b, c) bcopy(b, a, c) +#else /* HAVE_BCOPY */ +static void * +pcre_memmove(void *d, const void *s, size_t n) +{ +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +} +#define memmove(a, b, c) pcre_memmove(a, b, c) +#endif /* not HAVE_BCOPY */ +#endif /* not HAVE_MEMMOVE */ +#endif /* not VPCOMPAT */ + + +/* PCRE keeps offsets in its compiled code as 2-byte quantities (always stored +in big-endian order) by default. These are used, for example, to link from the +start of a subpattern to its alternatives and its end. The use of 2 bytes per +offset limits the size of the compiled regex to around 64K, which is big enough +for almost everybody. However, I received a request for an even bigger limit. +For this reason, and also to make the code easier to maintain, the storing and +loading of offsets from the byte string is now handled by the macros that are +defined here. + +The macros are controlled by the value of LINK_SIZE. This defaults to 2 in +the config.h file, but can be overridden by using -D on the command line. This +is automated on Unix systems via the "configure" command. */ + +#ifdef COMPILE_PCRE8 + +#if LINK_SIZE == 2 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 8), \ + (a[(n)+1] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + +#define MAX_PATTERN_SIZE (1 << 16) + + +#elif LINK_SIZE == 3 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 16), \ + (a[(n)+1] = (d) >> 8), \ + (a[(n)+2] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2]) + +#define MAX_PATTERN_SIZE (1 << 24) + + +#elif LINK_SIZE == 4 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 24), \ + (a[(n)+1] = (d) >> 16), \ + (a[(n)+2] = (d) >> 8), \ + (a[(n)+3] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) + +/* Keep it positive */ +#define MAX_PATTERN_SIZE (1 << 30) + +#else +#error LINK_SIZE must be either 2, 3, or 4 +#endif + +#else /* COMPILE_PCRE8 */ + +#ifdef COMPILE_PCRE16 + +#if LINK_SIZE == 2 + +#undef LINK_SIZE +#define LINK_SIZE 1 + +#define PUT(a,n,d) \ + (a[n] = (d)) + +#define GET(a,n) \ + (a[n]) + +#define MAX_PATTERN_SIZE (1 << 16) + +#elif LINK_SIZE == 3 || LINK_SIZE == 4 + +#undef LINK_SIZE +#define LINK_SIZE 2 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 16), \ + (a[(n)+1] = (d) & 65535) + +#define GET(a,n) \ + (((a)[n] << 16) | (a)[(n)+1]) + +/* Keep it positive */ +#define MAX_PATTERN_SIZE (1 << 30) + +#else +#error LINK_SIZE must be either 2, 3, or 4 +#endif + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE16 */ + +#endif /* COMPILE_PCRE8 */ + +/* Convenience macro defined in terms of the others */ + +#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE + + +/* PCRE uses some other 2-byte quantities that do not change when the size of +offsets changes. There are used for repeat counts and for other things such as +capturing parenthesis numbers in back references. */ + +#ifdef COMPILE_PCRE8 + +#define IMM2_SIZE 2 + +#define PUT2(a,n,d) \ + a[n] = (d) >> 8; \ + a[(n)+1] = (d) & 255 + +#define GET2(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + +#else /* COMPILE_PCRE8 */ + +#ifdef COMPILE_PCRE16 + +#define IMM2_SIZE 1 + +#define PUT2(a,n,d) \ + a[n] = d + +#define GET2(a,n) \ + a[n] + +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE16 */ + +#endif /* COMPILE_PCRE8 */ + +#define PUT2INC(a,n,d) PUT2(a,n,d), a += IMM2_SIZE + +/* When UTF encoding is being used, a character is no longer just a single +character. The macros for character handling generate simple sequences when +used in character-mode, and more complicated ones for UTF characters. +GETCHARLENTEST and other macros are not used when UTF is not supported, +so they are not defined. To make sure they can never even appear when +UTF support is omitted, we don't even define them. */ + +#ifndef SUPPORT_UTF + +/* #define MAX_VALUE_FOR_SINGLE_CHAR */ +/* #define HAS_EXTRALEN(c) */ +/* #define GET_EXTRALEN(c) */ +/* #define NOT_FIRSTCHAR(c) */ +#define GETCHAR(c, eptr) c = *eptr; +#define GETCHARTEST(c, eptr) c = *eptr; +#define GETCHARINC(c, eptr) c = *eptr++; +#define GETCHARINCTEST(c, eptr) c = *eptr++; +#define GETCHARLEN(c, eptr, len) c = *eptr; +/* #define GETCHARLENTEST(c, eptr, len) */ +/* #define BACKCHAR(eptr) */ +/* #define FORWARDCHAR(eptr) */ +/* #define ACROSSCHAR(condition, eptr, action) */ + +#else /* SUPPORT_UTF */ + +#ifdef COMPILE_PCRE8 + +/* These macros were originally written in the form of loops that used data +from the tables whose names start with PRIV(utf8_table). They were rewritten by +a user so as not to use loops, because in some environments this gives a +significant performance advantage, and it seems never to do any harm. */ + +/* Tells the biggest code point which can be encoded as a single character. */ + +#define MAX_VALUE_FOR_SINGLE_CHAR 127 + +/* Tests whether the code point needs extra characters to decode. */ + +#define HAS_EXTRALEN(c) ((c) >= 0xc0) + +/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE. +Otherwise it has an undefined behaviour. */ + +#define GET_EXTRALEN(c) (PRIV(utf8_table4)[(c) & 0x3f]) + +/* Returns TRUE, if the given character is not the first character +of a UTF sequence. */ + +#define NOT_FIRSTCHAR(c) (((c) & 0xc0) == 0x80) + +/* Base macro to pick up the remaining bytes of a UTF-8 character, not +advancing the pointer. */ + +#define GETUTF8(c, eptr) \ + { \ + if ((c & 0x20) == 0) \ + c = ((c & 0x1f) << 6) | (eptr[1] & 0x3f); \ + else if ((c & 0x10) == 0) \ + c = ((c & 0x0f) << 12) | ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ + else if ((c & 0x08) == 0) \ + c = ((c & 0x07) << 18) | ((eptr[1] & 0x3f) << 12) | \ + ((eptr[2] & 0x3f) << 6) | (eptr[3] & 0x3f); \ + else if ((c & 0x04) == 0) \ + c = ((c & 0x03) << 24) | ((eptr[1] & 0x3f) << 18) | \ + ((eptr[2] & 0x3f) << 12) | ((eptr[3] & 0x3f) << 6) | \ + (eptr[4] & 0x3f); \ + else \ + c = ((c & 0x01) << 30) | ((eptr[1] & 0x3f) << 24) | \ + ((eptr[2] & 0x3f) << 18) | ((eptr[3] & 0x3f) << 12) | \ + ((eptr[4] & 0x3f) << 6) | (eptr[5] & 0x3f); \ + } + +/* Get the next UTF-8 character, not advancing the pointer. This is called when +we know we are in UTF-8 mode. */ + +#define GETCHAR(c, eptr) \ + c = *eptr; \ + if (c >= 0xc0) GETUTF8(c, eptr); + +/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the +pointer. */ + +#define GETCHARTEST(c, eptr) \ + c = *eptr; \ + if (utf && c >= 0xc0) GETUTF8(c, eptr); + +/* Base macro to pick up the remaining bytes of a UTF-8 character, advancing +the pointer. */ + +#define GETUTF8INC(c, eptr) \ + { \ + if ((c & 0x20) == 0) \ + c = ((c & 0x1f) << 6) | (*eptr++ & 0x3f); \ + else if ((c & 0x10) == 0) \ + { \ + c = ((c & 0x0f) << 12) | ((*eptr & 0x3f) << 6) | (eptr[1] & 0x3f); \ + eptr += 2; \ + } \ + else if ((c & 0x08) == 0) \ + { \ + c = ((c & 0x07) << 18) | ((*eptr & 0x3f) << 12) | \ + ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ + eptr += 3; \ + } \ + else if ((c & 0x04) == 0) \ + { \ + c = ((c & 0x03) << 24) | ((*eptr & 0x3f) << 18) | \ + ((eptr[1] & 0x3f) << 12) | ((eptr[2] & 0x3f) << 6) | \ + (eptr[3] & 0x3f); \ + eptr += 4; \ + } \ + else \ + { \ + c = ((c & 0x01) << 30) | ((*eptr & 0x3f) << 24) | \ + ((eptr[1] & 0x3f) << 18) | ((eptr[2] & 0x3f) << 12) | \ + ((eptr[3] & 0x3f) << 6) | (eptr[4] & 0x3f); \ + eptr += 5; \ + } \ + } + +/* Get the next UTF-8 character, advancing the pointer. This is called when we +know we are in UTF-8 mode. */ + +#define GETCHARINC(c, eptr) \ + c = *eptr++; \ + if (c >= 0xc0) GETUTF8INC(c, eptr); + +/* Get the next character, testing for UTF-8 mode, and advancing the pointer. +This is called when we don't know if we are in UTF-8 mode. */ + +#define GETCHARINCTEST(c, eptr) \ + c = *eptr++; \ + if (utf && c >= 0xc0) GETUTF8INC(c, eptr); + +/* Base macro to pick up the remaining bytes of a UTF-8 character, not +advancing the pointer, incrementing the length. */ + +#define GETUTF8LEN(c, eptr, len) \ + { \ + if ((c & 0x20) == 0) \ + { \ + c = ((c & 0x1f) << 6) | (eptr[1] & 0x3f); \ + len++; \ + } \ + else if ((c & 0x10) == 0) \ + { \ + c = ((c & 0x0f) << 12) | ((eptr[1] & 0x3f) << 6) | (eptr[2] & 0x3f); \ + len += 2; \ + } \ + else if ((c & 0x08) == 0) \ + {\ + c = ((c & 0x07) << 18) | ((eptr[1] & 0x3f) << 12) | \ + ((eptr[2] & 0x3f) << 6) | (eptr[3] & 0x3f); \ + len += 3; \ + } \ + else if ((c & 0x04) == 0) \ + { \ + c = ((c & 0x03) << 24) | ((eptr[1] & 0x3f) << 18) | \ + ((eptr[2] & 0x3f) << 12) | ((eptr[3] & 0x3f) << 6) | \ + (eptr[4] & 0x3f); \ + len += 4; \ + } \ + else \ + {\ + c = ((c & 0x01) << 30) | ((eptr[1] & 0x3f) << 24) | \ + ((eptr[2] & 0x3f) << 18) | ((eptr[3] & 0x3f) << 12) | \ + ((eptr[4] & 0x3f) << 6) | (eptr[5] & 0x3f); \ + len += 5; \ + } \ + } + +/* Get the next UTF-8 character, not advancing the pointer, incrementing length +if there are extra bytes. This is called when we know we are in UTF-8 mode. */ + +#define GETCHARLEN(c, eptr, len) \ + c = *eptr; \ + if (c >= 0xc0) GETUTF8LEN(c, eptr, len); + +/* Get the next UTF-8 character, testing for UTF-8 mode, not advancing the +pointer, incrementing length if there are extra bytes. This is called when we +do not know if we are in UTF-8 mode. */ + +#define GETCHARLENTEST(c, eptr, len) \ + c = *eptr; \ + if (utf && c >= 0xc0) GETUTF8LEN(c, eptr, len); + +/* If the pointer is not at the start of a character, move it back until +it is. This is called only in UTF-8 mode - we don't put a test within the macro +because almost all calls are already within a block of UTF-8 only code. */ + +#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr-- + +/* Same as above, just in the other direction. */ +#define FORWARDCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr++ + +/* Same as above, but it allows a fully customizable form. */ +#define ACROSSCHAR(condition, eptr, action) \ + while((condition) && ((eptr) & 0xc0) == 0x80) action + +#else /* COMPILE_PCRE8 */ + +#ifdef COMPILE_PCRE16 + +/* Tells the biggest code point which can be encoded as a single character. */ + +#define MAX_VALUE_FOR_SINGLE_CHAR 65535 + +/* Tests whether the code point needs extra characters to decode. */ + +#define HAS_EXTRALEN(c) (((c) & 0xfc00) == 0xd800) + +/* Returns with the additional number of characters if IS_MULTICHAR(c) is TRUE. +Otherwise it has an undefined behaviour. */ + +#define GET_EXTRALEN(c) 1 + +/* Returns TRUE, if the given character is not the first character +of a UTF sequence. */ + +#define NOT_FIRSTCHAR(c) (((c) & 0xfc00) == 0xdc00) + +/* Base macro to pick up the low surrogate of a UTF-16 character, not +advancing the pointer. */ + +#define GETUTF16(c, eptr) \ + { c = (((c & 0x3ff) << 10) | (eptr[1] & 0x3ff)) + 0x10000; } + +/* Get the next UTF-16 character, not advancing the pointer. This is called when +we know we are in UTF-16 mode. */ + +#define GETCHAR(c, eptr) \ + c = *eptr; \ + if ((c & 0xfc00) == 0xd800) GETUTF16(c, eptr); + +/* Get the next UTF-16 character, testing for UTF-16 mode, and not advancing the +pointer. */ + +#define GETCHARTEST(c, eptr) \ + c = *eptr; \ + if (utf && (c & 0xfc00) == 0xd800) GETUTF16(c, eptr); + +/* Base macro to pick up the low surrogate of a UTF-16 character, advancing +the pointer. */ + +#define GETUTF16INC(c, eptr) \ + { c = (((c & 0x3ff) << 10) | (*eptr++ & 0x3ff)) + 0x10000; } + +/* Get the next UTF-16 character, advancing the pointer. This is called when we +know we are in UTF-16 mode. */ + +#define GETCHARINC(c, eptr) \ + c = *eptr++; \ + if ((c & 0xfc00) == 0xd800) GETUTF16INC(c, eptr); + +/* Get the next character, testing for UTF-16 mode, and advancing the pointer. +This is called when we don't know if we are in UTF-16 mode. */ + +#define GETCHARINCTEST(c, eptr) \ + c = *eptr++; \ + if (utf && (c & 0xfc00) == 0xd800) GETUTF16INC(c, eptr); + +/* Base macro to pick up the low surrogate of a UTF-16 character, not +advancing the pointer, incrementing the length. */ + +#define GETUTF16LEN(c, eptr, len) \ + { c = (((c & 0x3ff) << 10) | (eptr[1] & 0x3ff)) + 0x10000; len++; } + +/* Get the next UTF-16 character, not advancing the pointer, incrementing +length if there is a low surrogate. This is called when we know we are in +UTF-16 mode. */ + +#define GETCHARLEN(c, eptr, len) \ + c = *eptr; \ + if ((c & 0xfc00) == 0xd800) GETUTF16LEN(c, eptr, len); + +/* Get the next UTF-816character, testing for UTF-16 mode, not advancing the +pointer, incrementing length if there is a low surrogate. This is called when +we do not know if we are in UTF-16 mode. */ + +#define GETCHARLENTEST(c, eptr, len) \ + c = *eptr; \ + if (utf && (c & 0xfc00) == 0xd800) GETUTF16LEN(c, eptr, len); + +/* If the pointer is not at the start of a character, move it back until +it is. This is called only in UTF-16 mode - we don't put a test within the +macro because almost all calls are already within a block of UTF-16 only +code. */ + +#define BACKCHAR(eptr) if ((*eptr & 0xfc00) == 0xdc00) eptr-- + +/* Same as above, just in the other direction. */ +#define FORWARDCHAR(eptr) if ((*eptr & 0xfc00) == 0xdc00) eptr++ + +/* Same as above, but it allows a fully customizable form. */ +#define ACROSSCHAR(condition, eptr, action) \ + if ((condition) && ((eptr) & 0xfc00) == 0xdc00) action + +#endif + +#endif /* COMPILE_PCRE8 */ + +#endif /* SUPPORT_UTF */ + + +/* In case there is no definition of offsetof() provided - though any proper +Standard C system should have one. */ + +#ifndef offsetof +#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +#endif + + +/* Private flags containing information about the compiled regex. They used to +live at the top end of the options word, but that got almost full, so now they +are in a 16-bit flags word. From release 8.00, PCRE_NOPARTIAL is unused, as +the restrictions on partial matching have been lifted. It remains for backwards +compatibility. */ + +#ifdef COMPILE_PCRE8 +#define PCRE_MODE 0x0001 /* compiled in 8 bit mode */ +#endif +#ifdef COMPILE_PCRE16 +#define PCRE_MODE 0x0002 /* compiled in 16 bit mode */ +#endif +#define PCRE_FIRSTSET 0x0010 /* first_char is set */ +#define PCRE_FCH_CASELESS 0x0020 /* caseless first char */ +#define PCRE_REQCHSET 0x0040 /* req_byte is set */ +#define PCRE_RCH_CASELESS 0x0080 /* caseless requested char */ +#define PCRE_STARTLINE 0x0100 /* start after \n for multiline */ +#define PCRE_NOPARTIAL 0x0200 /* can't use partial with this regex */ +#define PCRE_JCHANGED 0x0400 /* j option used in regex */ +#define PCRE_HASCRORLF 0x0800 /* explicit \r or \n in pattern */ +#define PCRE_HASTHEN 0x1000 /* pattern contains (*THEN) */ + +/* Flags for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_MAPPED 0x0001 /* a map of starting chars exists */ +#define PCRE_STUDY_MINLEN 0x0002 /* a minimum length field exists */ + +/* Masks for identifying the public options that are permitted at compile +time, run time, or study time, respectively. */ + +#define PCRE_NEWLINE_BITS (PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|PCRE_NEWLINE_ANY| \ + PCRE_NEWLINE_ANYCRLF) + +#define PUBLIC_COMPILE_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ + PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT|PCRE_FIRSTLINE| \ + PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \ + PCRE_JAVASCRIPT_COMPAT|PCRE_UCP|PCRE_NO_START_OPTIMIZE) + +#define PUBLIC_EXEC_OPTIONS \ + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTART| \ + PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_NEWLINE_BITS| \ + PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE|PCRE_NO_START_OPTIMIZE) + +#define PUBLIC_DFA_EXEC_OPTIONS \ + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NOTEMPTY_ATSTART| \ + PCRE_NO_UTF8_CHECK|PCRE_PARTIAL_HARD|PCRE_PARTIAL_SOFT|PCRE_DFA_SHORTEST| \ + PCRE_DFA_RESTART|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE| \ + PCRE_NO_START_OPTIMIZE) + +#define PUBLIC_STUDY_OPTIONS \ + PCRE_STUDY_JIT_COMPILE + +/* Magic number to provide a small check against being handed junk. */ + +#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ + +/* This variable is used to detect a loaded regular expression +in different endianness. */ + +#define REVERSED_MAGIC_NUMBER 0x45524350UL /* 'ERCP' */ + +/* Negative values for the firstchar and reqchar variables */ + +#define REQ_UNSET (-2) +#define REQ_NONE (-1) + +/* The maximum remaining length of subject we are prepared to search for a +req_byte match. */ + +#define REQ_BYTE_MAX 1000 + +/* Miscellaneous definitions. The #ifndef is to pacify compiler warnings in +environments where these macros are defined elsewhere. Unfortunately, there +is no way to do the same for the typedef. */ + +typedef int BOOL; + +#ifndef FALSE +#define FALSE 0 +#define TRUE 1 +#endif + +/* If PCRE is to support UTF-8 on EBCDIC platforms, we cannot use normal +character constants like '*' because the compiler would emit their EBCDIC code, +which is different from their ASCII/UTF-8 code. Instead we define macros for +the characters so that they always use the ASCII/UTF-8 code when UTF-8 support +is enabled. When UTF-8 support is not enabled, the definitions use character +literals. Both character and string versions of each character are needed, and +there are some longer strings as well. + +This means that, on EBCDIC platforms, the PCRE library can handle either +EBCDIC, or UTF-8, but not both. To support both in the same compiled library +would need different lookups depending on whether PCRE_UTF8 was set or not. +This would make it impossible to use characters in switch/case statements, +which would reduce performance. For a theoretical use (which nobody has asked +for) in a minority area (EBCDIC platforms), this is not sensible. Any +application that did need both could compile two versions of the library, using +macros to give the functions distinct names. */ + +#ifndef SUPPORT_UTF + +/* UTF-8 support is not enabled; use the platform-dependent character literals +so that PCRE works on both ASCII and EBCDIC platforms, in non-UTF-mode only. */ + +#define CHAR_HT '\t' +#define CHAR_VT '\v' +#define CHAR_FF '\f' +#define CHAR_CR '\r' +#define CHAR_NL '\n' +#define CHAR_BS '\b' +#define CHAR_BEL '\a' +#ifdef EBCDIC +#define CHAR_ESC '\047' +#define CHAR_DEL '\007' +#else +#define CHAR_ESC '\033' +#define CHAR_DEL '\177' +#endif + +#define CHAR_SPACE ' ' +#define CHAR_EXCLAMATION_MARK '!' +#define CHAR_QUOTATION_MARK '"' +#define CHAR_NUMBER_SIGN '#' +#define CHAR_DOLLAR_SIGN '$' +#define CHAR_PERCENT_SIGN '%' +#define CHAR_AMPERSAND '&' +#define CHAR_APOSTROPHE '\'' +#define CHAR_LEFT_PARENTHESIS '(' +#define CHAR_RIGHT_PARENTHESIS ')' +#define CHAR_ASTERISK '*' +#define CHAR_PLUS '+' +#define CHAR_COMMA ',' +#define CHAR_MINUS '-' +#define CHAR_DOT '.' +#define CHAR_SLASH '/' +#define CHAR_0 '0' +#define CHAR_1 '1' +#define CHAR_2 '2' +#define CHAR_3 '3' +#define CHAR_4 '4' +#define CHAR_5 '5' +#define CHAR_6 '6' +#define CHAR_7 '7' +#define CHAR_8 '8' +#define CHAR_9 '9' +#define CHAR_COLON ':' +#define CHAR_SEMICOLON ';' +#define CHAR_LESS_THAN_SIGN '<' +#define CHAR_EQUALS_SIGN '=' +#define CHAR_GREATER_THAN_SIGN '>' +#define CHAR_QUESTION_MARK '?' +#define CHAR_COMMERCIAL_AT '@' +#define CHAR_A 'A' +#define CHAR_B 'B' +#define CHAR_C 'C' +#define CHAR_D 'D' +#define CHAR_E 'E' +#define CHAR_F 'F' +#define CHAR_G 'G' +#define CHAR_H 'H' +#define CHAR_I 'I' +#define CHAR_J 'J' +#define CHAR_K 'K' +#define CHAR_L 'L' +#define CHAR_M 'M' +#define CHAR_N 'N' +#define CHAR_O 'O' +#define CHAR_P 'P' +#define CHAR_Q 'Q' +#define CHAR_R 'R' +#define CHAR_S 'S' +#define CHAR_T 'T' +#define CHAR_U 'U' +#define CHAR_V 'V' +#define CHAR_W 'W' +#define CHAR_X 'X' +#define CHAR_Y 'Y' +#define CHAR_Z 'Z' +#define CHAR_LEFT_SQUARE_BRACKET '[' +#define CHAR_BACKSLASH '\\' +#define CHAR_RIGHT_SQUARE_BRACKET ']' +#define CHAR_CIRCUMFLEX_ACCENT '^' +#define CHAR_UNDERSCORE '_' +#define CHAR_GRAVE_ACCENT '`' +#define CHAR_a 'a' +#define CHAR_b 'b' +#define CHAR_c 'c' +#define CHAR_d 'd' +#define CHAR_e 'e' +#define CHAR_f 'f' +#define CHAR_g 'g' +#define CHAR_h 'h' +#define CHAR_i 'i' +#define CHAR_j 'j' +#define CHAR_k 'k' +#define CHAR_l 'l' +#define CHAR_m 'm' +#define CHAR_n 'n' +#define CHAR_o 'o' +#define CHAR_p 'p' +#define CHAR_q 'q' +#define CHAR_r 'r' +#define CHAR_s 's' +#define CHAR_t 't' +#define CHAR_u 'u' +#define CHAR_v 'v' +#define CHAR_w 'w' +#define CHAR_x 'x' +#define CHAR_y 'y' +#define CHAR_z 'z' +#define CHAR_LEFT_CURLY_BRACKET '{' +#define CHAR_VERTICAL_LINE '|' +#define CHAR_RIGHT_CURLY_BRACKET '}' +#define CHAR_TILDE '~' + +#define STR_HT "\t" +#define STR_VT "\v" +#define STR_FF "\f" +#define STR_CR "\r" +#define STR_NL "\n" +#define STR_BS "\b" +#define STR_BEL "\a" +#ifdef EBCDIC +#define STR_ESC "\047" +#define STR_DEL "\007" +#else +#define STR_ESC "\033" +#define STR_DEL "\177" +#endif + +#define STR_SPACE " " +#define STR_EXCLAMATION_MARK "!" +#define STR_QUOTATION_MARK "\"" +#define STR_NUMBER_SIGN "#" +#define STR_DOLLAR_SIGN "$" +#define STR_PERCENT_SIGN "%" +#define STR_AMPERSAND "&" +#define STR_APOSTROPHE "'" +#define STR_LEFT_PARENTHESIS "(" +#define STR_RIGHT_PARENTHESIS ")" +#define STR_ASTERISK "*" +#define STR_PLUS "+" +#define STR_COMMA "," +#define STR_MINUS "-" +#define STR_DOT "." +#define STR_SLASH "/" +#define STR_0 "0" +#define STR_1 "1" +#define STR_2 "2" +#define STR_3 "3" +#define STR_4 "4" +#define STR_5 "5" +#define STR_6 "6" +#define STR_7 "7" +#define STR_8 "8" +#define STR_9 "9" +#define STR_COLON ":" +#define STR_SEMICOLON ";" +#define STR_LESS_THAN_SIGN "<" +#define STR_EQUALS_SIGN "=" +#define STR_GREATER_THAN_SIGN ">" +#define STR_QUESTION_MARK "?" +#define STR_COMMERCIAL_AT "@" +#define STR_A "A" +#define STR_B "B" +#define STR_C "C" +#define STR_D "D" +#define STR_E "E" +#define STR_F "F" +#define STR_G "G" +#define STR_H "H" +#define STR_I "I" +#define STR_J "J" +#define STR_K "K" +#define STR_L "L" +#define STR_M "M" +#define STR_N "N" +#define STR_O "O" +#define STR_P "P" +#define STR_Q "Q" +#define STR_R "R" +#define STR_S "S" +#define STR_T "T" +#define STR_U "U" +#define STR_V "V" +#define STR_W "W" +#define STR_X "X" +#define STR_Y "Y" +#define STR_Z "Z" +#define STR_LEFT_SQUARE_BRACKET "[" +#define STR_BACKSLASH "\\" +#define STR_RIGHT_SQUARE_BRACKET "]" +#define STR_CIRCUMFLEX_ACCENT "^" +#define STR_UNDERSCORE "_" +#define STR_GRAVE_ACCENT "`" +#define STR_a "a" +#define STR_b "b" +#define STR_c "c" +#define STR_d "d" +#define STR_e "e" +#define STR_f "f" +#define STR_g "g" +#define STR_h "h" +#define STR_i "i" +#define STR_j "j" +#define STR_k "k" +#define STR_l "l" +#define STR_m "m" +#define STR_n "n" +#define STR_o "o" +#define STR_p "p" +#define STR_q "q" +#define STR_r "r" +#define STR_s "s" +#define STR_t "t" +#define STR_u "u" +#define STR_v "v" +#define STR_w "w" +#define STR_x "x" +#define STR_y "y" +#define STR_z "z" +#define STR_LEFT_CURLY_BRACKET "{" +#define STR_VERTICAL_LINE "|" +#define STR_RIGHT_CURLY_BRACKET "}" +#define STR_TILDE "~" + +#define STRING_ACCEPT0 "ACCEPT\0" +#define STRING_COMMIT0 "COMMIT\0" +#define STRING_F0 "F\0" +#define STRING_FAIL0 "FAIL\0" +#define STRING_MARK0 "MARK\0" +#define STRING_PRUNE0 "PRUNE\0" +#define STRING_SKIP0 "SKIP\0" +#define STRING_THEN "THEN" + +#define STRING_alpha0 "alpha\0" +#define STRING_lower0 "lower\0" +#define STRING_upper0 "upper\0" +#define STRING_alnum0 "alnum\0" +#define STRING_ascii0 "ascii\0" +#define STRING_blank0 "blank\0" +#define STRING_cntrl0 "cntrl\0" +#define STRING_digit0 "digit\0" +#define STRING_graph0 "graph\0" +#define STRING_print0 "print\0" +#define STRING_punct0 "punct\0" +#define STRING_space0 "space\0" +#define STRING_word0 "word\0" +#define STRING_xdigit "xdigit" + +#define STRING_DEFINE "DEFINE" + +#define STRING_CR_RIGHTPAR "CR)" +#define STRING_LF_RIGHTPAR "LF)" +#define STRING_CRLF_RIGHTPAR "CRLF)" +#define STRING_ANY_RIGHTPAR "ANY)" +#define STRING_ANYCRLF_RIGHTPAR "ANYCRLF)" +#define STRING_BSR_ANYCRLF_RIGHTPAR "BSR_ANYCRLF)" +#define STRING_BSR_UNICODE_RIGHTPAR "BSR_UNICODE)" +#ifdef COMPILE_PCRE8 +#define STRING_UTF_RIGHTPAR "UTF8)" +#endif +#ifdef COMPILE_PCRE16 +#define STRING_UTF_RIGHTPAR "UTF16)" +#endif +#define STRING_UCP_RIGHTPAR "UCP)" +#define STRING_NO_START_OPT_RIGHTPAR "NO_START_OPT)" + +#else /* SUPPORT_UTF */ + +/* UTF-8 support is enabled; always use UTF-8 (=ASCII) character codes. This +works in both modes non-EBCDIC platforms, and on EBCDIC platforms in UTF-8 mode +only. */ + +#define CHAR_HT '\011' +#define CHAR_VT '\013' +#define CHAR_FF '\014' +#define CHAR_CR '\015' +#define CHAR_NL '\012' +#define CHAR_BS '\010' +#define CHAR_BEL '\007' +#define CHAR_ESC '\033' +#define CHAR_DEL '\177' + +#define CHAR_SPACE '\040' +#define CHAR_EXCLAMATION_MARK '\041' +#define CHAR_QUOTATION_MARK '\042' +#define CHAR_NUMBER_SIGN '\043' +#define CHAR_DOLLAR_SIGN '\044' +#define CHAR_PERCENT_SIGN '\045' +#define CHAR_AMPERSAND '\046' +#define CHAR_APOSTROPHE '\047' +#define CHAR_LEFT_PARENTHESIS '\050' +#define CHAR_RIGHT_PARENTHESIS '\051' +#define CHAR_ASTERISK '\052' +#define CHAR_PLUS '\053' +#define CHAR_COMMA '\054' +#define CHAR_MINUS '\055' +#define CHAR_DOT '\056' +#define CHAR_SLASH '\057' +#define CHAR_0 '\060' +#define CHAR_1 '\061' +#define CHAR_2 '\062' +#define CHAR_3 '\063' +#define CHAR_4 '\064' +#define CHAR_5 '\065' +#define CHAR_6 '\066' +#define CHAR_7 '\067' +#define CHAR_8 '\070' +#define CHAR_9 '\071' +#define CHAR_COLON '\072' +#define CHAR_SEMICOLON '\073' +#define CHAR_LESS_THAN_SIGN '\074' +#define CHAR_EQUALS_SIGN '\075' +#define CHAR_GREATER_THAN_SIGN '\076' +#define CHAR_QUESTION_MARK '\077' +#define CHAR_COMMERCIAL_AT '\100' +#define CHAR_A '\101' +#define CHAR_B '\102' +#define CHAR_C '\103' +#define CHAR_D '\104' +#define CHAR_E '\105' +#define CHAR_F '\106' +#define CHAR_G '\107' +#define CHAR_H '\110' +#define CHAR_I '\111' +#define CHAR_J '\112' +#define CHAR_K '\113' +#define CHAR_L '\114' +#define CHAR_M '\115' +#define CHAR_N '\116' +#define CHAR_O '\117' +#define CHAR_P '\120' +#define CHAR_Q '\121' +#define CHAR_R '\122' +#define CHAR_S '\123' +#define CHAR_T '\124' +#define CHAR_U '\125' +#define CHAR_V '\126' +#define CHAR_W '\127' +#define CHAR_X '\130' +#define CHAR_Y '\131' +#define CHAR_Z '\132' +#define CHAR_LEFT_SQUARE_BRACKET '\133' +#define CHAR_BACKSLASH '\134' +#define CHAR_RIGHT_SQUARE_BRACKET '\135' +#define CHAR_CIRCUMFLEX_ACCENT '\136' +#define CHAR_UNDERSCORE '\137' +#define CHAR_GRAVE_ACCENT '\140' +#define CHAR_a '\141' +#define CHAR_b '\142' +#define CHAR_c '\143' +#define CHAR_d '\144' +#define CHAR_e '\145' +#define CHAR_f '\146' +#define CHAR_g '\147' +#define CHAR_h '\150' +#define CHAR_i '\151' +#define CHAR_j '\152' +#define CHAR_k '\153' +#define CHAR_l '\154' +#define CHAR_m '\155' +#define CHAR_n '\156' +#define CHAR_o '\157' +#define CHAR_p '\160' +#define CHAR_q '\161' +#define CHAR_r '\162' +#define CHAR_s '\163' +#define CHAR_t '\164' +#define CHAR_u '\165' +#define CHAR_v '\166' +#define CHAR_w '\167' +#define CHAR_x '\170' +#define CHAR_y '\171' +#define CHAR_z '\172' +#define CHAR_LEFT_CURLY_BRACKET '\173' +#define CHAR_VERTICAL_LINE '\174' +#define CHAR_RIGHT_CURLY_BRACKET '\175' +#define CHAR_TILDE '\176' + +#define STR_HT "\011" +#define STR_VT "\013" +#define STR_FF "\014" +#define STR_CR "\015" +#define STR_NL "\012" +#define STR_BS "\010" +#define STR_BEL "\007" +#define STR_ESC "\033" +#define STR_DEL "\177" + +#define STR_SPACE "\040" +#define STR_EXCLAMATION_MARK "\041" +#define STR_QUOTATION_MARK "\042" +#define STR_NUMBER_SIGN "\043" +#define STR_DOLLAR_SIGN "\044" +#define STR_PERCENT_SIGN "\045" +#define STR_AMPERSAND "\046" +#define STR_APOSTROPHE "\047" +#define STR_LEFT_PARENTHESIS "\050" +#define STR_RIGHT_PARENTHESIS "\051" +#define STR_ASTERISK "\052" +#define STR_PLUS "\053" +#define STR_COMMA "\054" +#define STR_MINUS "\055" +#define STR_DOT "\056" +#define STR_SLASH "\057" +#define STR_0 "\060" +#define STR_1 "\061" +#define STR_2 "\062" +#define STR_3 "\063" +#define STR_4 "\064" +#define STR_5 "\065" +#define STR_6 "\066" +#define STR_7 "\067" +#define STR_8 "\070" +#define STR_9 "\071" +#define STR_COLON "\072" +#define STR_SEMICOLON "\073" +#define STR_LESS_THAN_SIGN "\074" +#define STR_EQUALS_SIGN "\075" +#define STR_GREATER_THAN_SIGN "\076" +#define STR_QUESTION_MARK "\077" +#define STR_COMMERCIAL_AT "\100" +#define STR_A "\101" +#define STR_B "\102" +#define STR_C "\103" +#define STR_D "\104" +#define STR_E "\105" +#define STR_F "\106" +#define STR_G "\107" +#define STR_H "\110" +#define STR_I "\111" +#define STR_J "\112" +#define STR_K "\113" +#define STR_L "\114" +#define STR_M "\115" +#define STR_N "\116" +#define STR_O "\117" +#define STR_P "\120" +#define STR_Q "\121" +#define STR_R "\122" +#define STR_S "\123" +#define STR_T "\124" +#define STR_U "\125" +#define STR_V "\126" +#define STR_W "\127" +#define STR_X "\130" +#define STR_Y "\131" +#define STR_Z "\132" +#define STR_LEFT_SQUARE_BRACKET "\133" +#define STR_BACKSLASH "\134" +#define STR_RIGHT_SQUARE_BRACKET "\135" +#define STR_CIRCUMFLEX_ACCENT "\136" +#define STR_UNDERSCORE "\137" +#define STR_GRAVE_ACCENT "\140" +#define STR_a "\141" +#define STR_b "\142" +#define STR_c "\143" +#define STR_d "\144" +#define STR_e "\145" +#define STR_f "\146" +#define STR_g "\147" +#define STR_h "\150" +#define STR_i "\151" +#define STR_j "\152" +#define STR_k "\153" +#define STR_l "\154" +#define STR_m "\155" +#define STR_n "\156" +#define STR_o "\157" +#define STR_p "\160" +#define STR_q "\161" +#define STR_r "\162" +#define STR_s "\163" +#define STR_t "\164" +#define STR_u "\165" +#define STR_v "\166" +#define STR_w "\167" +#define STR_x "\170" +#define STR_y "\171" +#define STR_z "\172" +#define STR_LEFT_CURLY_BRACKET "\173" +#define STR_VERTICAL_LINE "\174" +#define STR_RIGHT_CURLY_BRACKET "\175" +#define STR_TILDE "\176" + +#define STRING_ACCEPT0 STR_A STR_C STR_C STR_E STR_P STR_T "\0" +#define STRING_COMMIT0 STR_C STR_O STR_M STR_M STR_I STR_T "\0" +#define STRING_F0 STR_F "\0" +#define STRING_FAIL0 STR_F STR_A STR_I STR_L "\0" +#define STRING_MARK0 STR_M STR_A STR_R STR_K "\0" +#define STRING_PRUNE0 STR_P STR_R STR_U STR_N STR_E "\0" +#define STRING_SKIP0 STR_S STR_K STR_I STR_P "\0" +#define STRING_THEN STR_T STR_H STR_E STR_N + +#define STRING_alpha0 STR_a STR_l STR_p STR_h STR_a "\0" +#define STRING_lower0 STR_l STR_o STR_w STR_e STR_r "\0" +#define STRING_upper0 STR_u STR_p STR_p STR_e STR_r "\0" +#define STRING_alnum0 STR_a STR_l STR_n STR_u STR_m "\0" +#define STRING_ascii0 STR_a STR_s STR_c STR_i STR_i "\0" +#define STRING_blank0 STR_b STR_l STR_a STR_n STR_k "\0" +#define STRING_cntrl0 STR_c STR_n STR_t STR_r STR_l "\0" +#define STRING_digit0 STR_d STR_i STR_g STR_i STR_t "\0" +#define STRING_graph0 STR_g STR_r STR_a STR_p STR_h "\0" +#define STRING_print0 STR_p STR_r STR_i STR_n STR_t "\0" +#define STRING_punct0 STR_p STR_u STR_n STR_c STR_t "\0" +#define STRING_space0 STR_s STR_p STR_a STR_c STR_e "\0" +#define STRING_word0 STR_w STR_o STR_r STR_d "\0" +#define STRING_xdigit STR_x STR_d STR_i STR_g STR_i STR_t + +#define STRING_DEFINE STR_D STR_E STR_F STR_I STR_N STR_E + +#define STRING_CR_RIGHTPAR STR_C STR_R STR_RIGHT_PARENTHESIS +#define STRING_LF_RIGHTPAR STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_CRLF_RIGHTPAR STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_ANY_RIGHTPAR STR_A STR_N STR_Y STR_RIGHT_PARENTHESIS +#define STRING_ANYCRLF_RIGHTPAR STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_BSR_ANYCRLF_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_A STR_N STR_Y STR_C STR_R STR_L STR_F STR_RIGHT_PARENTHESIS +#define STRING_BSR_UNICODE_RIGHTPAR STR_B STR_S STR_R STR_UNDERSCORE STR_U STR_N STR_I STR_C STR_O STR_D STR_E STR_RIGHT_PARENTHESIS +#ifdef COMPILE_PCRE8 +#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_8 STR_RIGHT_PARENTHESIS +#endif +#ifdef COMPILE_PCRE16 +#define STRING_UTF_RIGHTPAR STR_U STR_T STR_F STR_1 STR_6 STR_RIGHT_PARENTHESIS +#endif +#define STRING_UCP_RIGHTPAR STR_U STR_C STR_P STR_RIGHT_PARENTHESIS +#define STRING_NO_START_OPT_RIGHTPAR STR_N STR_O STR_UNDERSCORE STR_S STR_T STR_A STR_R STR_T STR_UNDERSCORE STR_O STR_P STR_T STR_RIGHT_PARENTHESIS + +#endif /* SUPPORT_UTF */ + +/* Escape items that are just an encoding of a particular data value. */ + +#ifndef ESC_e +#define ESC_e CHAR_ESC +#endif + +#ifndef ESC_f +#define ESC_f CHAR_FF +#endif + +#ifndef ESC_n +#define ESC_n CHAR_NL +#endif + +#ifndef ESC_r +#define ESC_r CHAR_CR +#endif + +/* We can't officially use ESC_t because it is a POSIX reserved identifier +(presumably because of all the others like size_t). */ + +#ifndef ESC_tee +#define ESC_tee CHAR_HT +#endif + +/* Codes for different types of Unicode property */ + +#define PT_ANY 0 /* Any property - matches all chars */ +#define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */ +#define PT_GC 2 /* Specified general characteristic (e.g. L) */ +#define PT_PC 3 /* Specified particular characteristic (e.g. Lu) */ +#define PT_SC 4 /* Script (e.g. Han) */ +#define PT_ALNUM 5 /* Alphanumeric - the union of L and N */ +#define PT_SPACE 6 /* Perl space - Z plus 9,10,12,13 */ +#define PT_PXSPACE 7 /* POSIX space - Z plus 9,10,11,12,13 */ +#define PT_WORD 8 /* Word - L plus N plus underscore */ + +/* Flag bits and data types for the extended class (OP_XCLASS) for classes that +contain characters with values greater than 255. */ + +#define XCL_NOT 0x01 /* Flag: this is a negative class */ +#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ + +#define XCL_END 0 /* Marks end of individual items */ +#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ +#define XCL_RANGE 2 /* A range (two multibyte chars) follows */ +#define XCL_PROP 3 /* Unicode property (2-byte property code follows) */ +#define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_z. There's a dummy for OP_ALLANY because it +corresponds to "." in DOTALL mode rather than an escape sequence. It is also +used for [^] in JavaScript compatibility mode, and for \C in non-utf mode. In +non-DOTALL mode, "." behaves like \N. + +The special values ESC_DU, ESC_du, etc. are used instead of ESC_D, ESC_d, etc. +when PCRE_UCP is set, when replacement of \d etc by \p sequences is required. +They must be contiguous, and remain in order so that the replacements can be +looked up from a table. + +The final escape must be ESC_REF as subsequent values are used for +backreferences (\1, \2, \3, etc). There are two tests in the code for an escape +greater than ESC_b and less than ESC_Z to detect the types that may be +repeated. These are the types that consume characters. If any new escapes are +put in between that don't consume a character, that code will have to change. +*/ + +enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, + ESC_W, ESC_w, ESC_N, ESC_dum, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, + ESC_h, ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, + ESC_E, ESC_Q, ESC_g, ESC_k, + ESC_DU, ESC_du, ESC_SU, ESC_su, ESC_WU, ESC_wu, + ESC_REF }; + +/* Opcode table: Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. + +*** NOTE NOTE NOTE *** Whenever this list is updated, the two macro definitions +that follow must also be updated to match. There are also tables called +"coptable" and "poptable" in pcre_dfa_exec.c that must be updated. */ + +enum { + OP_END, /* 0 End of pattern */ + + /* Values corresponding to backslashed metacharacters */ + + OP_SOD, /* 1 Start of data: \A */ + OP_SOM, /* 2 Start of match (subject + offset): \G */ + OP_SET_SOM, /* 3 Set start of match (\K) */ + OP_NOT_WORD_BOUNDARY, /* 4 \B */ + OP_WORD_BOUNDARY, /* 5 \b */ + OP_NOT_DIGIT, /* 6 \D */ + OP_DIGIT, /* 7 \d */ + OP_NOT_WHITESPACE, /* 8 \S */ + OP_WHITESPACE, /* 9 \s */ + OP_NOT_WORDCHAR, /* 10 \W */ + OP_WORDCHAR, /* 11 \w */ + + OP_ANY, /* 12 Match any character except newline */ + OP_ALLANY, /* 13 Match any character */ + OP_ANYBYTE, /* 14 Match any byte (\C); different to OP_ANY for UTF-8 */ + OP_NOTPROP, /* 15 \P (not Unicode property) */ + OP_PROP, /* 16 \p (Unicode property) */ + OP_ANYNL, /* 17 \R (any newline sequence) */ + OP_NOT_HSPACE, /* 18 \H (not horizontal whitespace) */ + OP_HSPACE, /* 19 \h (horizontal whitespace) */ + OP_NOT_VSPACE, /* 20 \V (not vertical whitespace) */ + OP_VSPACE, /* 21 \v (vertical whitespace) */ + OP_EXTUNI, /* 22 \X (extended Unicode sequence */ + OP_EODN, /* 23 End of data or \n at end of data: \Z. */ + OP_EOD, /* 24 End of data: \z */ + + OP_CIRC, /* 25 Start of line - not multiline */ + OP_CIRCM, /* 26 Start of line - multiline */ + OP_DOLL, /* 27 End of line - not multiline */ + OP_DOLLM, /* 28 End of line - multiline */ + OP_CHAR, /* 29 Match one character, casefully */ + OP_CHARI, /* 30 Match one character, caselessly */ + OP_NOT, /* 31 Match one character, not the given one, casefully */ + OP_NOTI, /* 32 Match one character, not the given one, caselessly */ + + /* The following sets of 13 opcodes must always be kept in step because + the offset from the first one is used to generate the others. */ + + /**** Single characters, caseful, must precede the caseless ones ****/ + + OP_STAR, /* 33 The maximizing and minimizing versions of */ + OP_MINSTAR, /* 34 these six opcodes must come in pairs, with */ + OP_PLUS, /* 35 the minimizing one second. */ + OP_MINPLUS, /* 36 */ + OP_QUERY, /* 37 */ + OP_MINQUERY, /* 38 */ + + OP_UPTO, /* 39 From 0 to n matches of one character, caseful*/ + OP_MINUPTO, /* 40 */ + OP_EXACT, /* 41 Exactly n matches */ + + OP_POSSTAR, /* 42 Possessified star, caseful */ + OP_POSPLUS, /* 43 Possessified plus, caseful */ + OP_POSQUERY, /* 44 Posesssified query, caseful */ + OP_POSUPTO, /* 45 Possessified upto, caseful */ + + /**** Single characters, caseless, must follow the caseful ones */ + + OP_STARI, /* 46 */ + OP_MINSTARI, /* 47 */ + OP_PLUSI, /* 48 */ + OP_MINPLUSI, /* 49 */ + OP_QUERYI, /* 50 */ + OP_MINQUERYI, /* 51 */ + + OP_UPTOI, /* 52 From 0 to n matches of one character, caseless */ + OP_MINUPTOI, /* 53 */ + OP_EXACTI, /* 54 */ + + OP_POSSTARI, /* 55 Possessified star, caseless */ + OP_POSPLUSI, /* 56 Possessified plus, caseless */ + OP_POSQUERYI, /* 57 Posesssified query, caseless */ + OP_POSUPTOI, /* 58 Possessified upto, caseless */ + + /**** The negated ones must follow the non-negated ones, and match them ****/ + /**** Negated single character, caseful; must precede the caseless ones ****/ + + OP_NOTSTAR, /* 59 The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* 60 these six opcodes must come in pairs, with */ + OP_NOTPLUS, /* 61 the minimizing one second. They must be in */ + OP_NOTMINPLUS, /* 62 exactly the same order as those above. */ + OP_NOTQUERY, /* 63 */ + OP_NOTMINQUERY, /* 64 */ + + OP_NOTUPTO, /* 65 From 0 to n matches, caseful */ + OP_NOTMINUPTO, /* 66 */ + OP_NOTEXACT, /* 67 Exactly n matches */ + + OP_NOTPOSSTAR, /* 68 Possessified versions, caseful */ + OP_NOTPOSPLUS, /* 69 */ + OP_NOTPOSQUERY, /* 70 */ + OP_NOTPOSUPTO, /* 71 */ + + /**** Negated single character, caseless; must follow the caseful ones ****/ + + OP_NOTSTARI, /* 72 */ + OP_NOTMINSTARI, /* 73 */ + OP_NOTPLUSI, /* 74 */ + OP_NOTMINPLUSI, /* 75 */ + OP_NOTQUERYI, /* 76 */ + OP_NOTMINQUERYI, /* 77 */ + + OP_NOTUPTOI, /* 78 From 0 to n matches, caseless */ + OP_NOTMINUPTOI, /* 79 */ + OP_NOTEXACTI, /* 80 Exactly n matches */ + + OP_NOTPOSSTARI, /* 81 Possessified versions, caseless */ + OP_NOTPOSPLUSI, /* 82 */ + OP_NOTPOSQUERYI, /* 83 */ + OP_NOTPOSUPTOI, /* 84 */ + + /**** Character types ****/ + + OP_TYPESTAR, /* 85 The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* 86 these six opcodes must come in pairs, with */ + OP_TYPEPLUS, /* 87 the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* 88 be in exactly the same order as those above. */ + OP_TYPEQUERY, /* 89 */ + OP_TYPEMINQUERY, /* 90 */ + + OP_TYPEUPTO, /* 91 From 0 to n matches */ + OP_TYPEMINUPTO, /* 92 */ + OP_TYPEEXACT, /* 93 Exactly n matches */ + + OP_TYPEPOSSTAR, /* 94 Possessified versions */ + OP_TYPEPOSPLUS, /* 95 */ + OP_TYPEPOSQUERY, /* 96 */ + OP_TYPEPOSUPTO, /* 97 */ + + /* These are used for character classes and back references; only the + first six are the same as the sets above. */ + + OP_CRSTAR, /* 98 The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* 99 all these opcodes must come in pairs, with */ + OP_CRPLUS, /* 100 the minimizing one second. These codes must */ + OP_CRMINPLUS, /* 101 be in exactly the same order as those above. */ + OP_CRQUERY, /* 102 */ + OP_CRMINQUERY, /* 103 */ + + OP_CRRANGE, /* 104 These are different to the three sets above. */ + OP_CRMINRANGE, /* 105 */ + + /* End of quantifier opcodes */ + + OP_CLASS, /* 106 Match a character class, chars < 256 only */ + OP_NCLASS, /* 107 Same, but the bitmap was created from a negative + class - the difference is relevant only when a + character > 255 is encountered. */ + OP_XCLASS, /* 108 Extended class for handling > 255 chars within the + class. This does both positive and negative. */ + OP_REF, /* 109 Match a back reference, casefully */ + OP_REFI, /* 110 Match a back reference, caselessly */ + OP_RECURSE, /* 111 Match a numbered subpattern (possibly recursive) */ + OP_CALLOUT, /* 112 Call out to external function if provided */ + + OP_ALT, /* 113 Start of alternation */ + OP_KET, /* 114 End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* 115 These two must remain together and in this */ + OP_KETRMIN, /* 116 order. They are for groups the repeat for ever. */ + OP_KETRPOS, /* 117 Possessive unlimited repeat. */ + + /* The assertions must come before BRA, CBRA, ONCE, and COND, and the four + asserts must remain in order. */ + + OP_REVERSE, /* 118 Move pointer back - used in lookbehind assertions */ + OP_ASSERT, /* 119 Positive lookahead */ + OP_ASSERT_NOT, /* 120 Negative lookahead */ + OP_ASSERTBACK, /* 121 Positive lookbehind */ + OP_ASSERTBACK_NOT, /* 122 Negative lookbehind */ + + /* ONCE, ONCE_NC, BRA, BRAPOS, CBRA, CBRAPOS, and COND must come immediately + after the assertions, with ONCE first, as there's a test for >= ONCE for a + subpattern that isn't an assertion. The POS versions must immediately follow + the non-POS versions in each case. */ + + OP_ONCE, /* 123 Atomic group, contains captures */ + OP_ONCE_NC, /* 124 Atomic group containing no captures */ + OP_BRA, /* 125 Start of non-capturing bracket */ + OP_BRAPOS, /* 126 Ditto, with unlimited, possessive repeat */ + OP_CBRA, /* 127 Start of capturing bracket */ + OP_CBRAPOS, /* 128 Ditto, with unlimited, possessive repeat */ + OP_COND, /* 129 Conditional group */ + + /* These five must follow the previous five, in the same order. There's a + check for >= SBRA to distinguish the two sets. */ + + OP_SBRA, /* 130 Start of non-capturing bracket, check empty */ + OP_SBRAPOS, /* 131 Ditto, with unlimited, possessive repeat */ + OP_SCBRA, /* 132 Start of capturing bracket, check empty */ + OP_SCBRAPOS, /* 133 Ditto, with unlimited, possessive repeat */ + OP_SCOND, /* 134 Conditional group, check empty */ + + /* The next two pairs must (respectively) be kept together. */ + + OP_CREF, /* 135 Used to hold a capture number as condition */ + OP_NCREF, /* 136 Same, but generated by a name reference*/ + OP_RREF, /* 137 Used to hold a recursion number as condition */ + OP_NRREF, /* 138 Same, but generated by a name reference*/ + OP_DEF, /* 139 The DEFINE condition */ + + OP_BRAZERO, /* 140 These two must remain together and in this */ + OP_BRAMINZERO, /* 141 order. */ + OP_BRAPOSZERO, /* 142 */ + + /* These are backtracking control verbs */ + + OP_MARK, /* 143 always has an argument */ + OP_PRUNE, /* 144 */ + OP_PRUNE_ARG, /* 145 same, but with argument */ + OP_SKIP, /* 146 */ + OP_SKIP_ARG, /* 147 same, but with argument */ + OP_THEN, /* 148 */ + OP_THEN_ARG, /* 149 same, but with argument */ + OP_COMMIT, /* 150 */ + + /* These are forced failure and success verbs */ + + OP_FAIL, /* 151 */ + OP_ACCEPT, /* 152 */ + OP_ASSERT_ACCEPT, /* 153 Used inside assertions */ + OP_CLOSE, /* 154 Used before OP_ACCEPT to close open captures */ + + /* This is used to skip a subpattern with a {0} quantifier */ + + OP_SKIPZERO, /* 155 */ + + /* This is not an opcode, but is used to check that tables indexed by opcode + are the correct length, in order to catch updating errors - there have been + some in the past. */ + + OP_TABLE_LENGTH +}; + +/* *** NOTE NOTE NOTE *** Whenever the list above is updated, the two macro +definitions that follow must also be updated to match. There are also tables +called "coptable" and "poptable" in pcre_dfa_exec.c that must be updated. */ + + +/* This macro defines textual names for all the opcodes. These are used only +for debugging, and some of them are only partial names. The macro is referenced +only in pcre_printint.c, which fills out the full names in many cases (and in +some cases doesn't actually use these names at all). */ + +#define OP_NAME_LIST \ + "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \ + "\\S", "\\s", "\\W", "\\w", "Any", "AllAny", "Anybyte", \ + "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \ + "extuni", "\\Z", "\\z", \ + "^", "^", "$", "$", "char", "chari", "not", "noti", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", \ + "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", \ + "class", "nclass", "xclass", "Ref", "Refi", \ + "Recurse", "Callout", \ + "Alt", "Ket", "KetRmax", "KetRmin", "KetRpos", \ + "Reverse", "Assert", "Assert not", "AssertB", "AssertB not", \ + "Once", "Once_NC", \ + "Bra", "BraPos", "CBra", "CBraPos", \ + "Cond", \ + "SBra", "SBraPos", "SCBra", "SCBraPos", \ + "SCond", \ + "Cond ref", "Cond nref", "Cond rec", "Cond nrec", "Cond def", \ + "Brazero", "Braminzero", "Braposzero", \ + "*MARK", "*PRUNE", "*PRUNE", "*SKIP", "*SKIP", \ + "*THEN", "*THEN", "*COMMIT", "*FAIL", \ + "*ACCEPT", "*ASSERT_ACCEPT", \ + "Close", "Skip zero" + + +/* This macro defines the length of fixed length operations in the compiled +regex. The lengths are used when searching for specific things, and also in the +debugging printing of a compiled regex. We use a macro so that it can be +defined close to the definitions of the opcodes themselves. + +As things have been extended, some of these are no longer fixed lenths, but are +minima instead. For example, the length of a single-character repeat may vary +in UTF-8 mode. The code that uses this table must know about such things. */ + +#define OP_LENGTHS \ + 1, /* End */ \ + 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \ + 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \ + 1, 1, 1, /* Any, AllAny, Anybyte */ \ + 3, 3, /* \P, \p */ \ + 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \ + 1, /* \X */ \ + 1, 1, 1, 1, 1, 1, /* \Z, \z, ^, ^M, $, $M */ \ + 2, /* Char - the minimum length */ \ + 2, /* Chari - the minimum length */ \ + 2, /* not */ \ + 2, /* noti */ \ + /* Positive single-char repeats ** These are */ \ + 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto, minupto ** mode */ \ + 2+IMM2_SIZE, /* exact */ \ + 2, 2, 2, 2+IMM2_SIZE, /* *+, ++, ?+, upto+ */ \ + 2, 2, 2, 2, 2, 2, /* *I, *?I, +I, +?I, ?I, ??I ** UTF-8 */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* upto I, minupto I */ \ + 2+IMM2_SIZE, /* exact I */ \ + 2, 2, 2, 2+IMM2_SIZE, /* *+I, ++I, ?+I, upto+I */ \ + /* Negative single-char repeats - only for chars < 256 */ \ + 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto, minupto */ \ + 2+IMM2_SIZE, /* NOT exact */ \ + 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *, +, ?, upto */ \ + 2, 2, 2, 2, 2, 2, /* NOT *I, *?I, +I, +?I, ?I, ??I */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* NOT upto I, minupto I */ \ + 2+IMM2_SIZE, /* NOT exact I */ \ + 2, 2, 2, 2+IMM2_SIZE, /* Possessive NOT *I, +I, ?I, upto I */ \ + /* Positive type repeats */ \ + 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ + 2+IMM2_SIZE, 2+IMM2_SIZE, /* Type upto, minupto */ \ + 2+IMM2_SIZE, /* Type exact */ \ + 2, 2, 2, 2+IMM2_SIZE, /* Possessive *+, ++, ?+, upto+ */ \ + /* Character class & ref repeats */ \ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ + 1+2*IMM2_SIZE, 1+2*IMM2_SIZE, /* CRRANGE, CRMINRANGE */ \ + 1+(32/sizeof(pcre_uchar)), /* CLASS */ \ + 1+(32/sizeof(pcre_uchar)), /* NCLASS */ \ + 0, /* XCLASS - variable length */ \ + 1+IMM2_SIZE, /* REF */ \ + 1+IMM2_SIZE, /* REFI */ \ + 1+LINK_SIZE, /* RECURSE */ \ + 2+2*LINK_SIZE, /* CALLOUT */ \ + 1+LINK_SIZE, /* Alt */ \ + 1+LINK_SIZE, /* Ket */ \ + 1+LINK_SIZE, /* KetRmax */ \ + 1+LINK_SIZE, /* KetRmin */ \ + 1+LINK_SIZE, /* KetRpos */ \ + 1+LINK_SIZE, /* Reverse */ \ + 1+LINK_SIZE, /* Assert */ \ + 1+LINK_SIZE, /* Assert not */ \ + 1+LINK_SIZE, /* Assert behind */ \ + 1+LINK_SIZE, /* Assert behind not */ \ + 1+LINK_SIZE, /* ONCE */ \ + 1+LINK_SIZE, /* ONCE_NC */ \ + 1+LINK_SIZE, /* BRA */ \ + 1+LINK_SIZE, /* BRAPOS */ \ + 1+LINK_SIZE+IMM2_SIZE, /* CBRA */ \ + 1+LINK_SIZE+IMM2_SIZE, /* CBRAPOS */ \ + 1+LINK_SIZE, /* COND */ \ + 1+LINK_SIZE, /* SBRA */ \ + 1+LINK_SIZE, /* SBRAPOS */ \ + 1+LINK_SIZE+IMM2_SIZE, /* SCBRA */ \ + 1+LINK_SIZE+IMM2_SIZE, /* SCBRAPOS */ \ + 1+LINK_SIZE, /* SCOND */ \ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* CREF, NCREF */ \ + 1+IMM2_SIZE, 1+IMM2_SIZE, /* RREF, NRREF */ \ + 1, /* DEF */ \ + 1, 1, 1, /* BRAZERO, BRAMINZERO, BRAPOSZERO */ \ + 3, 1, 3, /* MARK, PRUNE, PRUNE_ARG */ \ + 1, 3, /* SKIP, SKIP_ARG */ \ + 1, 3, /* THEN, THEN_ARG */ \ + 1, 1, 1, 1, /* COMMIT, FAIL, ACCEPT, ASSERT_ACCEPT */ \ + 1+IMM2_SIZE, 1 /* CLOSE, SKIPZERO */ + +/* A magic value for OP_RREF and OP_NRREF to indicate the "any recursion" +condition. */ + +#define RREF_ANY 0xffff + +/* Compile time error code numbers. They are given names so that they can more +easily be tracked. When a new number is added, the table called eint in +pcreposix.c must be updated. */ + +enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, + ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, + ERR20, ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, + ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, + ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, + ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, + ERR60, ERR61, ERR62, ERR63, ERR64, ERR65, ERR66, ERR67, ERR68, ERR69, + ERR70, ERR71, ERR72, ERR73, ERR74, ERRCOUNT }; + +/* The real format of the start of the pcre block; the index of names and the +code vector run on as long as necessary after the end. We store an explicit +offset to the name table so that if a regex is compiled on one host, saved, and +then run on another where the size of pointers is different, all might still +be well. For the case of compiled-on-4 and run-on-8, we include an extra +pointer that is always NULL. For future-proofing, a few dummy fields were +originally included - even though you can never get this planning right - but +there is only one left now. + +NOTE NOTE NOTE: +Because people can now save and re-use compiled patterns, any additions to this +structure should be made at the end, and something earlier (e.g. a new +flag in the options or one of the dummy fields) should indicate that the new +fields are present. Currently PCRE always sets the dummy fields to zero. +NOTE NOTE NOTE +*/ + +#ifdef COMPILE_PCRE8 +#define REAL_PCRE real_pcre +#else +#define REAL_PCRE real_pcre16 +#endif + +typedef struct REAL_PCRE { + pcre_uint32 magic_number; + pcre_uint32 size; /* Total that was malloced */ + pcre_uint32 options; /* Public options */ + pcre_uint16 flags; /* Private flags */ + pcre_uint16 dummy1; /* For future use */ + pcre_uint16 top_bracket; + pcre_uint16 top_backref; + pcre_uint16 first_char; /* Starting character */ + pcre_uint16 req_char; /* This character must be seen */ + pcre_uint16 name_table_offset; /* Offset to name table that follows */ + pcre_uint16 name_entry_size; /* Size of any name items */ + pcre_uint16 name_count; /* Number of name items */ + pcre_uint16 ref_count; /* Reference count */ + + const pcre_uint8 *tables; /* Pointer to tables or NULL for std */ + const pcre_uint8 *nullpad; /* NULL padding */ +} REAL_PCRE; + +/* The format of the block used to store data from pcre_study(). The same +remark (see NOTE above) about extending this structure applies. */ + +typedef struct pcre_study_data { + pcre_uint32 size; /* Total that was malloced */ + pcre_uint32 flags; /* Private flags */ + pcre_uint8 start_bits[32]; /* Starting char bits */ + pcre_uint32 minlength; /* Minimum subject length */ +} pcre_study_data; + +/* Structure for building a chain of open capturing subpatterns during +compiling, so that instructions to close them can be compiled when (*ACCEPT) is +encountered. This is also used to identify subpatterns that contain recursive +back references to themselves, so that they can be made atomic. */ + +typedef struct open_capitem { + struct open_capitem *next; /* Chain link */ + pcre_uint16 number; /* Capture number */ + pcre_uint16 flag; /* Set TRUE if recursive back ref */ +} open_capitem; + +/* Structure for passing "static" information around between the functions +doing the compiling, so that they are thread-safe. */ + +typedef struct compile_data { + const pcre_uint8 *lcc; /* Points to lower casing table */ + const pcre_uint8 *fcc; /* Points to case-flipping table */ + const pcre_uint8 *cbits; /* Points to character type table */ + const pcre_uint8 *ctypes; /* Points to table of type maps */ + const pcre_uchar *start_workspace;/* The start of working space */ + const pcre_uchar *start_code; /* The start of the compiled code */ + const pcre_uchar *start_pattern; /* The start of the pattern */ + const pcre_uchar *end_pattern; /* The end of the pattern */ + open_capitem *open_caps; /* Chain of open capture items */ + pcre_uchar *hwm; /* High watermark of workspace */ + pcre_uchar *name_table; /* The name/number table */ + int names_found; /* Number of entries so far */ + int name_entry_size; /* Size of each entry */ + int workspace_size; /* Size of workspace */ + int bracount; /* Count of capturing parens as we compile */ + int final_bracount; /* Saved value after first pass */ + int top_backref; /* Maximum back reference */ + unsigned int backref_map; /* Bitmap of low back refs */ + int assert_depth; /* Depth of nested assertions */ + int external_options; /* External (initial) options */ + int external_flags; /* External flag bits to be set */ + int req_varyopt; /* "After variable item" flag for reqbyte */ + BOOL had_accept; /* (*ACCEPT) encountered */ + BOOL check_lookbehind; /* Lookbehinds need later checking */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + pcre_uchar nl[4]; /* Newline string when fixed length */ +} compile_data; + +/* Structure for maintaining a chain of pointers to the currently incomplete +branches, for testing for left recursion while compiling. */ + +typedef struct branch_chain { + struct branch_chain *outer; + pcre_uchar *current_branch; +} branch_chain; + +/* Structure for items in a linked list that represents an explicit recursive +call within the pattern; used by pcre_exec(). */ + +typedef struct recursion_info { + struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ + int group_num; /* Number of group that was called */ + int *offset_save; /* Pointer to start of saved offsets */ + int saved_max; /* Number of saved offsets */ + PCRE_PUCHAR subject_position; /* Position at start of recursion */ +} recursion_info; + +/* A similar structure for pcre_dfa_exec(). */ + +typedef struct dfa_recursion_info { + struct dfa_recursion_info *prevrec; + int group_num; + PCRE_PUCHAR subject_position; +} dfa_recursion_info; + +/* Structure for building a chain of data for holding the values of the subject +pointer at the start of each subpattern, so as to detect when an empty string +has been matched by a subpattern - to break infinite loops; used by +pcre_exec(). */ + +typedef struct eptrblock { + struct eptrblock *epb_prev; + PCRE_PUCHAR epb_saved_eptr; +} eptrblock; + + +/* Structure for passing "static" information around between the functions +doing traditional NFA matching, so that they are thread-safe. */ + +typedef struct match_data { + unsigned long int match_call_count; /* As it says */ + unsigned long int match_limit; /* As it says */ + unsigned long int match_limit_recursion; /* As it says */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + int offset_max; /* The maximum usable for return data */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + int name_count; /* Number of names in name table */ + int name_entry_size; /* Size of entry in names table */ + pcre_uchar *name_table; /* Table of names */ + pcre_uchar nl[4]; /* Newline string when fixed */ + const pcre_uint8 *lcc; /* Points to lower casing table */ + const pcre_uint8 *fcc; /* Points to case-flipping table */ + const pcre_uint8 *ctypes; /* Points to table of type maps */ + BOOL offset_overflow; /* Set if too many extractions */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL utf; /* UTF-8 / UTF-16 flag */ + BOOL jscript_compat; /* JAVASCRIPT_COMPAT flag */ + BOOL use_ucp; /* PCRE_UCP flag */ + BOOL endonly; /* Dollar not before final \n */ + BOOL notempty; /* Empty string match not wanted */ + BOOL notempty_atstart; /* Empty string match at start not wanted */ + BOOL hitend; /* Hit the end of the subject at some point */ + BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode */ + BOOL hasthen; /* Pattern contains (*THEN) */ + BOOL ignore_skip_arg; /* For re-run when SKIP name not found */ + const pcre_uchar *start_code; /* For use when recursing */ + PCRE_PUCHAR start_subject; /* Start of the subject string */ + PCRE_PUCHAR end_subject; /* End of the subject string */ + PCRE_PUCHAR start_match_ptr; /* Start of matched string */ + PCRE_PUCHAR end_match_ptr; /* Subject position at end match */ + PCRE_PUCHAR start_used_ptr; /* Earliest consulted character */ + int partial; /* PARTIAL options */ + int end_offset_top; /* Highwater mark at end of match */ + int capture_last; /* Most recent capture number */ + int start_offset; /* The start offset value */ + int match_function_type; /* Set for certain special calls of MATCH() */ + eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */ + int eptrn; /* Next free eptrblock */ + recursion_info *recursive; /* Linked list of recursion data */ + void *callout_data; /* To pass back to callouts */ + const pcre_uchar *mark; /* Mark pointer to pass back on success */ + const pcre_uchar *nomatch_mark;/* Mark pointer to pass back on failure */ + const pcre_uchar *once_target; /* Where to back up to for atomic groups */ +} match_data; + +/* A similar structure is used for the same purpose by the DFA matching +functions. */ + +typedef struct dfa_match_data { + const pcre_uchar *start_code; /* Start of the compiled pattern */ + const pcre_uchar *start_subject ; /* Start of the subject string */ + const pcre_uchar *end_subject; /* End of subject string */ + const pcre_uchar *start_used_ptr; /* Earliest consulted character */ + const pcre_uint8 *tables; /* Character tables */ + int start_offset; /* The start offset value */ + int moptions; /* Match options */ + int poptions; /* Pattern options */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + pcre_uchar nl[4]; /* Newline string when fixed */ + void *callout_data; /* To pass back to callouts */ + dfa_recursion_info *recursive; /* Linked list of recursion data */ +} dfa_match_data; + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_letter 0x02 +#define ctype_digit 0x04 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphanumeric or '_' */ +#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. Some classes are built by combining these tables. */ + +#define cbit_space 0 /* [:space:] or \s */ +#define cbit_xdigit 32 /* [:xdigit:] */ +#define cbit_digit 64 /* [:digit:] or \d */ +#define cbit_upper 96 /* [:upper:] */ +#define cbit_lower 128 /* [:lower:] */ +#define cbit_word 160 /* [:word:] or \w */ +#define cbit_graph 192 /* [:graph:] */ +#define cbit_print 224 /* [:print:] */ +#define cbit_punct 256 /* [:punct:] */ +#define cbit_cntrl 288 /* [:cntrl:] */ +#define cbit_length 320 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 256 +#define cbits_offset 512 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 256) + +/* Internal function prefix */ + +#ifdef COMPILE_PCRE8 +#ifndef PUBL +#define PUBL(name) pcre_##name +#endif +#ifndef PRIV +#define PRIV(name) _pcre_##name +#endif +#else /* COMPILE_PCRE8 */ +#ifdef COMPILE_PCRE16 +#ifndef PUBL +#define PUBL(name) pcre16_##name +#endif +#ifndef PRIV +#define PRIV(name) _pcre16_##name +#endif +#else +#error Unsupported compiling mode +#endif /* COMPILE_PCRE16 */ +#endif /* COMPILE_PCRE8 */ + +/* Layout of the UCP type table that translates property names into types and +codes. Each entry used to point directly to a name, but to reduce the number of +relocations in shared libraries, it now has an offset into a single string +instead. */ + +typedef struct { + pcre_uint16 name_offset; + pcre_uint16 type; + pcre_uint16 value; +} ucp_type_table; + + +/* Internal shared data tables. These are tables that are used by more than one +of the exported public functions. They have to be "external" in the C sense, +but are not part of the PCRE public API. The data for these tables is in the +pcre_tables.c module. */ + +#ifdef COMPILE_PCRE8 + +extern const int PRIV(utf8_table1)[]; +extern const int PRIV(utf8_table1_size); +extern const int PRIV(utf8_table2)[]; +extern const int PRIV(utf8_table3)[]; +extern const pcre_uint8 PRIV(utf8_table4)[]; + +#endif /* COMPILE_PCRE8 */ + +extern const char PRIV(utt_names)[]; +extern const ucp_type_table PRIV(utt)[]; +extern const int PRIV(utt_size); + +extern const pcre_uint8 PRIV(default_tables)[]; + +extern const pcre_uint8 PRIV(OP_lengths)[]; + + +/* Internal shared functions. These are functions that are used by more than +one of the exported public functions. They have to be "external" in the C +sense, but are not part of the PCRE public API. */ + +/* String comparison functions. */ +#ifdef COMPILE_PCRE8 + +#define STRCMP_UC_UC(str1, str2) \ + strcmp((char *)(str1), (char *)(str2)) +#define STRCMP_UC_C8(str1, str2) \ + strcmp((char *)(str1), (str2)) +#define STRNCMP_UC_UC(str1, str2, num) \ + strncmp((char *)(str1), (char *)(str2), (num)) +#define STRNCMP_UC_C8(str1, str2, num) \ + strncmp((char *)(str1), (str2), (num)) +#define STRLEN_UC(str) strlen((const char *)str) + +#else + +extern int PRIV(strcmp_uc_uc)(const pcre_uchar *, + const pcre_uchar *); +extern int PRIV(strcmp_uc_c8)(const pcre_uchar *, + const char *); +extern int PRIV(strncmp_uc_uc)(const pcre_uchar *, + const pcre_uchar *, unsigned int num); +extern int PRIV(strncmp_uc_c8)(const pcre_uchar *, + const char *, unsigned int num); +extern unsigned int PRIV(strlen_uc)(const pcre_uchar *str); + +#define STRCMP_UC_UC(str1, str2) \ + PRIV(strcmp_uc_uc)((str1), (str2)) +#define STRCMP_UC_C8(str1, str2) \ + PRIV(strcmp_uc_c8)((str1), (str2)) +#define STRNCMP_UC_UC(str1, str2, num) \ + PRIV(strncmp_uc_uc)((str1), (str2), (num)) +#define STRNCMP_UC_C8(str1, str2, num) \ + PRIV(strncmp_uc_c8)((str1), (str2), (num)) +#define STRLEN_UC(str) PRIV(strlen_uc)(str) + +#endif /* COMPILE_PCRE8 */ + +extern const pcre_uchar *PRIV(find_bracket)(const pcre_uchar *, BOOL, int); +extern BOOL PRIV(is_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, + int *, BOOL); +extern int PRIV(ord2utf)(pcre_uint32, pcre_uchar *); +extern int PRIV(valid_utf)(PCRE_PUCHAR, int, int *); +extern BOOL PRIV(was_newline)(PCRE_PUCHAR, int, PCRE_PUCHAR, + int *, BOOL); +extern BOOL PRIV(xclass)(int, const pcre_uchar *, BOOL); + +#ifdef SUPPORT_JIT +extern void PRIV(jit_compile)(const REAL_PCRE *, PUBL(extra) *); +extern int PRIV(jit_exec)(const REAL_PCRE *, void *, + const pcre_uchar *, int, int, int, int, int *, int); +extern void PRIV(jit_free)(void *); +extern int PRIV(jit_get_size)(void *); +extern const char* PRIV(jit_get_target)(void); +#endif + +/* Unicode character database (UCD) */ + +typedef struct { + pcre_uint8 script; + pcre_uint8 chartype; + pcre_int32 other_case; +} ucd_record; + +extern const ucd_record PRIV(ucd_records)[]; +extern const pcre_uint8 PRIV(ucd_stage1)[]; +extern const pcre_uint16 PRIV(ucd_stage2)[]; +extern const int PRIV(ucp_gentype)[]; +#ifdef SUPPORT_JIT +extern const int PRIV(ucp_typerange)[]; +#endif + +#ifdef SUPPORT_UCP +/* UCD access macros */ + +#define UCD_BLOCK_SIZE 128 +#define GET_UCD(ch) (PRIV(ucd_records) + \ + PRIV(ucd_stage2)[PRIV(ucd_stage1)[(ch) / UCD_BLOCK_SIZE] * \ + UCD_BLOCK_SIZE + (ch) % UCD_BLOCK_SIZE]) + +#define UCD_CHARTYPE(ch) GET_UCD(ch)->chartype +#define UCD_SCRIPT(ch) GET_UCD(ch)->script +#define UCD_CATEGORY(ch) PRIV(ucp_gentype)[UCD_CHARTYPE(ch)] +#define UCD_OTHERCASE(ch) (ch + GET_UCD(ch)->other_case) + +#endif /* SUPPORT_UCP */ + +#endif + +/* End of pcre_internal.h */ diff --git a/src/lib/pcre/pcre_jit_compile.c b/src/lib/pcre/pcre_jit_compile.c new file mode 100644 index 0000000..f3d240d --- /dev/null +++ b/src/lib/pcre/pcre_jit_compile.c @@ -0,0 +1,6915 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + + The machine code generator part (this module) was written by Zoltan Herczeg + Copyright (c) 2010-2012 + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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. +----------------------------------------------------------------------------- +*/ + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#ifdef SUPPORT_JIT + +/* All-in-one: Since we use the JIT compiler only from here, +we just include it. This way we don't need to touch the build +system files. */ + +#define SLJIT_MALLOC(size) (PUBL(malloc))(size) +#define SLJIT_FREE(ptr) (PUBL(free))(ptr) +#define SLJIT_CONFIG_AUTO 1 +#define SLJIT_CONFIG_STATIC 1 +#define SLJIT_VERBOSE 0 +#define SLJIT_DEBUG 0 + +#include "sljit/sljitLir.c" + +#if defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED +#error Unsupported architecture +#endif + +/* Allocate memory on the stack. Fast, but limited size. */ +#define LOCAL_SPACE_SIZE 32768 + +#define STACK_GROWTH_RATE 8192 + +/* Enable to check that the allocation could destroy temporaries. */ +#if defined SLJIT_DEBUG && SLJIT_DEBUG +#define DESTROY_REGISTERS 1 +#endif + +/* +Short summary about the backtracking mechanism empolyed by the jit code generator: + +The code generator follows the recursive nature of the PERL compatible regular +expressions. The basic blocks of regular expressions are condition checkers +whose execute different commands depending on the result of the condition check. +The relationship between the operators can be horizontal (concatenation) and +vertical (sub-expression) (See struct fallback_common for more details). + + 'ab' - 'a' and 'b' regexps are concatenated + 'a+' - 'a' is the sub-expression of the '+' operator + +The condition checkers are boolean (true/false) checkers. Machine code is generated +for the checker itself and for the actions depending on the result of the checker. +The 'true' case is called as the hot path (expected path), and the other is called as +the 'fallback' path. Branch instructions are expesive for all CPUs, so we avoid taken +branches on the hot path. + + Greedy star operator (*) : + Hot path: match happens. + Fallback path: match failed. + Non-greedy star operator (*?) : + Hot path: no need to perform a match. + Fallback path: match is required. + +The following example shows how the code generated for a capturing bracket +with two alternatives. Let A, B, C, D are arbirary regular expressions, and +we have the following regular expression: + + A(B|C)D + +The generated code will be the following: + + A hot path + '(' hot path (pushing arguments to the stack) + B hot path + ')' hot path (pushing arguments to the stack) + D hot path + return with successful match + + D fallback path + ')' fallback path (If we arrived from "C" jump to the fallback of "C") + B fallback path + C expected path + jump to D hot path + C fallback path + A fallback path + + Notice, that the order of fallback code paths are the opposite of the fast + code paths. In this way the topmost value on the stack is always belong + to the current fallback code path. The fallback code path must check + whether there is a next alternative. If so, it needs to jump back to + the hot path eventually. Otherwise it needs to clear out its own stack + frame and continue the execution on the fallback code paths. +*/ + +/* +Saved stack frames: + +Atomic blocks and asserts require reloading the values of local variables +when the fallback mechanism performed. Because of OP_RECURSE, the locals +are not necessarly known in compile time, thus we need a dynamic restore +mechanism. + +The stack frames are stored in a chain list, and have the following format: +([ capturing bracket offset ][ start value ][ end value ])+ ... [ 0 ] [ previous head ] + +Thus we can restore the locals to a particular point in the stack. +*/ + +typedef struct jit_arguments { + /* Pointers first. */ + struct sljit_stack *stack; + const pcre_uchar *str; + const pcre_uchar *begin; + const pcre_uchar *end; + int *offsets; + pcre_uchar *ptr; + /* Everything else after. */ + int offsetcount; + int calllimit; + pcre_uint8 notbol; + pcre_uint8 noteol; + pcre_uint8 notempty; + pcre_uint8 notempty_atstart; +} jit_arguments; + +typedef struct executable_function { + void *executable_func; + PUBL(jit_callback) callback; + void *userdata; + sljit_uw executable_size; +} executable_function; + +typedef struct jump_list { + struct sljit_jump *jump; + struct jump_list *next; +} jump_list; + +enum stub_types { stack_alloc }; + +typedef struct stub_list { + enum stub_types type; + int data; + struct sljit_jump *start; + struct sljit_label *leave; + struct stub_list *next; +} stub_list; + +typedef int (SLJIT_CALL *jit_function)(jit_arguments *args); + +/* The following structure is the key data type for the recursive +code generator. It is allocated by compile_hotpath, and contains +the aguments for compile_fallbackpath. Must be the first member +of its descendants. */ +typedef struct fallback_common { + /* Concatenation stack. */ + struct fallback_common *prev; + jump_list *nextfallbacks; + /* Internal stack (for component operators). */ + struct fallback_common *top; + jump_list *topfallbacks; + /* Opcode pointer. */ + pcre_uchar *cc; +} fallback_common; + +typedef struct assert_fallback { + fallback_common common; + jump_list *condfailed; + /* Less than 0 (-1) if a frame is not needed. */ + int framesize; + /* Points to our private memory word on the stack. */ + int localptr; + /* For iterators. */ + struct sljit_label *hotpath; +} assert_fallback; + +typedef struct bracket_fallback { + fallback_common common; + /* Where to coninue if an alternative is successfully matched. */ + struct sljit_label *althotpath; + /* For rmin and rmax iterators. */ + struct sljit_label *recursivehotpath; + /* For greedy ? operator. */ + struct sljit_label *zerohotpath; + /* Contains the branches of a failed condition. */ + union { + /* Both for OP_COND, OP_SCOND. */ + jump_list *condfailed; + assert_fallback *assert; + /* For OP_ONCE. -1 if not needed. */ + int framesize; + } u; + /* Points to our private memory word on the stack. */ + int localptr; +} bracket_fallback; + +typedef struct bracketpos_fallback { + fallback_common common; + /* Points to our private memory word on the stack. */ + int localptr; + /* Reverting stack is needed. */ + int framesize; + /* Allocated stack size. */ + int stacksize; +} bracketpos_fallback; + +typedef struct braminzero_fallback { + fallback_common common; + struct sljit_label *hotpath; +} braminzero_fallback; + +typedef struct iterator_fallback { + fallback_common common; + /* Next iteration. */ + struct sljit_label *hotpath; +} iterator_fallback; + +typedef struct recurse_entry { + struct recurse_entry *next; + /* Contains the function entry. */ + struct sljit_label *entry; + /* Collects the calls until the function is not created. */ + jump_list *calls; + /* Points to the starting opcode. */ + int start; +} recurse_entry; + +typedef struct recurse_fallback { + fallback_common common; +} recurse_fallback; + +typedef struct compiler_common { + struct sljit_compiler *compiler; + pcre_uchar *start; + int localsize; + int *localptrs; + const pcre_uint8 *fcc; + sljit_w lcc; + int cbraptr; + int nltype; + int newline; + int bsr_nltype; + int endonly; + sljit_w ctypes; + sljit_uw name_table; + sljit_w name_count; + sljit_w name_entry_size; + struct sljit_label *acceptlabel; + stub_list *stubs; + recurse_entry *entries; + recurse_entry *currententry; + jump_list *accept; + jump_list *calllimit; + jump_list *stackalloc; + jump_list *revertframes; + jump_list *wordboundary; + jump_list *anynewline; + jump_list *hspace; + jump_list *vspace; + jump_list *casefulcmp; + jump_list *caselesscmp; + BOOL jscript_compat; +#ifdef SUPPORT_UTF + BOOL utf; +#ifdef SUPPORT_UCP + BOOL use_ucp; +#endif + jump_list *utfreadchar; +#ifdef COMPILE_PCRE8 + jump_list *utfreadtype8; +#endif +#endif /* SUPPORT_UTF */ +#ifdef SUPPORT_UCP + jump_list *getucd; +#endif +} compiler_common; + +/* For byte_sequence_compare. */ + +typedef struct compare_context { + int length; + int sourcereg; +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + int ucharptr; + union { + sljit_i asint; + sljit_uh asushort; +#ifdef COMPILE_PCRE8 + sljit_ub asbyte; + sljit_ub asuchars[4]; +#else +#ifdef COMPILE_PCRE16 + sljit_uh asuchars[2]; +#endif +#endif + } c; + union { + sljit_i asint; + sljit_uh asushort; +#ifdef COMPILE_PCRE8 + sljit_ub asbyte; + sljit_ub asuchars[4]; +#else +#ifdef COMPILE_PCRE16 + sljit_uh asuchars[2]; +#endif +#endif + } oc; +#endif +} compare_context; + +enum { + frame_end = 0, + frame_setstrbegin = -1 +}; + +/* Undefine sljit macros. */ +#undef CMP + +/* Used for accessing the elements of the stack. */ +#define STACK(i) ((-(i) - 1) * (int)sizeof(sljit_w)) + +#define TMP1 SLJIT_TEMPORARY_REG1 +#define TMP2 SLJIT_TEMPORARY_REG3 +#define TMP3 SLJIT_TEMPORARY_EREG2 +#define STR_PTR SLJIT_SAVED_REG1 +#define STR_END SLJIT_SAVED_REG2 +#define STACK_TOP SLJIT_TEMPORARY_REG2 +#define STACK_LIMIT SLJIT_SAVED_REG3 +#define ARGUMENTS SLJIT_SAVED_EREG1 +#define CALL_COUNT SLJIT_SAVED_EREG2 +#define RETURN_ADDR SLJIT_TEMPORARY_EREG1 + +/* Locals layout. */ +/* These two locals can be used by the current opcode. */ +#define LOCALS0 (0 * sizeof(sljit_w)) +#define LOCALS1 (1 * sizeof(sljit_w)) +/* Two local variables for possessive quantifiers (char1 cannot use them). */ +#define POSSESSIVE0 (2 * sizeof(sljit_w)) +#define POSSESSIVE1 (3 * sizeof(sljit_w)) +/* Head of the last recursion. */ +#define RECURSIVE_HEAD (4 * sizeof(sljit_w)) +/* Max limit of recursions. */ +#define CALL_LIMIT (5 * sizeof(sljit_w)) +/* Last known position of the requested byte. */ +#define REQ_CHAR_PTR (6 * sizeof(sljit_w)) +/* End pointer of the first line. */ +#define FIRSTLINE_END (7 * sizeof(sljit_w)) +/* The output vector is stored on the stack, and contains pointers +to characters. The vector data is divided into two groups: the first +group contains the start / end character pointers, and the second is +the start pointers when the end of the capturing group has not yet reached. */ +#define OVECTOR_START (8 * sizeof(sljit_w)) +#define OVECTOR(i) (OVECTOR_START + (i) * sizeof(sljit_w)) +#define OVECTOR_PRIV(i) (common->cbraptr + (i) * sizeof(sljit_w)) +#define PRIV_DATA(cc) (common->localptrs[(cc) - common->start]) + +#ifdef COMPILE_PCRE8 +#define MOV_UCHAR SLJIT_MOV_UB +#define MOVU_UCHAR SLJIT_MOVU_UB +#else +#ifdef COMPILE_PCRE16 +#define MOV_UCHAR SLJIT_MOV_UH +#define MOVU_UCHAR SLJIT_MOVU_UH +#else +#error Unsupported compiling mode +#endif +#endif + +/* Shortcuts. */ +#define DEFINE_COMPILER \ + struct sljit_compiler *compiler = common->compiler +#define OP1(op, dst, dstw, src, srcw) \ + sljit_emit_op1(compiler, (op), (dst), (dstw), (src), (srcw)) +#define OP2(op, dst, dstw, src1, src1w, src2, src2w) \ + sljit_emit_op2(compiler, (op), (dst), (dstw), (src1), (src1w), (src2), (src2w)) +#define LABEL() \ + sljit_emit_label(compiler) +#define JUMP(type) \ + sljit_emit_jump(compiler, (type)) +#define JUMPTO(type, label) \ + sljit_set_label(sljit_emit_jump(compiler, (type)), (label)) +#define JUMPHERE(jump) \ + sljit_set_label((jump), sljit_emit_label(compiler)) +#define CMP(type, src1, src1w, src2, src2w) \ + sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)) +#define CMPTO(type, src1, src1w, src2, src2w, label) \ + sljit_set_label(sljit_emit_cmp(compiler, (type), (src1), (src1w), (src2), (src2w)), (label)) +#define COND_VALUE(op, dst, dstw, type) \ + sljit_emit_cond_value(compiler, (op), (dst), (dstw), (type)) + +static pcre_uchar* bracketend(pcre_uchar* cc) +{ +SLJIT_ASSERT((*cc >= OP_ASSERT && *cc <= OP_ASSERTBACK_NOT) || (*cc >= OP_ONCE && *cc <= OP_SCOND)); +do cc += GET(cc, 1); while (*cc == OP_ALT); +SLJIT_ASSERT(*cc >= OP_KET && *cc <= OP_KETRPOS); +cc += 1 + LINK_SIZE; +return cc; +} + +/* Functions whose might need modification for all new supported opcodes: + next_opcode + get_localspace + set_localptrs + get_framesize + init_frame + get_localsize + copy_locals + compile_hotpath + compile_fallbackpath +*/ + +static pcre_uchar *next_opcode(compiler_common *common, pcre_uchar *cc) +{ +SLJIT_UNUSED_ARG(common); +switch(*cc) + { + case OP_SOD: + case OP_SOM: + case OP_SET_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_ANYNL: + case OP_NOT_HSPACE: + case OP_HSPACE: + case OP_NOT_VSPACE: + case OP_VSPACE: + case OP_EXTUNI: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + case OP_DEF: + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + case OP_FAIL: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + case OP_SKIPZERO: + return cc + 1; + + case OP_ANYBYTE: +#ifdef SUPPORT_UTF + if (common->utf) return NULL; +#endif + return cc + 1; + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + return cc; + + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSUPTO: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSUPTOI: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSUPTO: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSUPTOI: + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + return cc; + + case OP_NOTPROP: + case OP_PROP: + return cc + 1 + 2; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSUPTO: + case OP_REF: + case OP_REFI: + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_CLOSE: + cc += 1 + IMM2_SIZE; + return cc; + + case OP_CRRANGE: + case OP_CRMINRANGE: + return cc + 1 + 2 * IMM2_SIZE; + + case OP_CLASS: + case OP_NCLASS: + return cc + 1 + 32 / sizeof(pcre_uchar); + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + return cc + GET(cc, 1); +#endif + + case OP_RECURSE: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_REVERSE: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRA: + case OP_BRAPOS: + case OP_COND: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + return cc + 1 + LINK_SIZE; + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + return cc + 1 + LINK_SIZE + IMM2_SIZE; + + default: + return NULL; + } +} + +static int get_localspace(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend) +{ +int localspace = 0; +pcre_uchar *alternative; +/* Calculate important variables (like stack size) and checks whether all opcodes are supported. */ +while (cc < ccend) + { + switch(*cc) + { + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + localspace += sizeof(sljit_w); + cc += 1 + LINK_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + localspace += sizeof(sljit_w); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + localspace += sizeof(sljit_w); + cc += 1 + LINK_SIZE; + break; + + default: + cc = next_opcode(common, cc); + if (cc == NULL) + return -1; + break; + } + } +return localspace; +} + +static void set_localptrs(compiler_common *common, int localptr, pcre_uchar *ccend) +{ +pcre_uchar *cc = common->start; +pcre_uchar *alternative; +while (cc < ccend) + { + switch(*cc) + { + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + common->localptrs[cc - common->start] = localptr; + localptr += sizeof(sljit_w); + cc += 1 + LINK_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + common->localptrs[cc - common->start] = localptr; + localptr += sizeof(sljit_w); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + { + common->localptrs[cc - common->start] = localptr; + localptr += sizeof(sljit_w); + } + cc += 1 + LINK_SIZE; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + } +} + +/* Returns with -1 if no need for frame. */ +static int get_framesize(compiler_common *common, pcre_uchar *cc, BOOL recursive) +{ +pcre_uchar *ccend = bracketend(cc); +int length = 0; +BOOL possessive = FALSE; +BOOL setsom_found = FALSE; + +if (!recursive && (*cc == OP_CBRAPOS || *cc == OP_SCBRAPOS)) + { + length = 3; + possessive = TRUE; + } + +cc = next_opcode(common, cc); +SLJIT_ASSERT(cc != NULL); +while (cc < ccend) + switch(*cc) + { + case OP_SET_SOM: + case OP_RECURSE: + if (!setsom_found) + { + length += 2; + setsom_found = TRUE; + } + cc += (*cc == OP_SET_SOM) ? 1 : 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + length += 3; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + +/* Possessive quantifiers can use a special case. */ +if (SLJIT_UNLIKELY(possessive) && length == 3) + return -1; + +if (length > 0) + return length + 1; +return -1; +} + +static void init_frame(compiler_common *common, pcre_uchar *cc, int stackpos, int stacktop, BOOL recursive) +{ +DEFINE_COMPILER; +pcre_uchar *ccend = bracketend(cc); +BOOL setsom_found = FALSE; +int offset; + +/* >= 1 + shortest item size (2) */ +SLJIT_UNUSED_ARG(stacktop); +SLJIT_ASSERT(stackpos >= stacktop + 2); + +stackpos = STACK(stackpos); +if (recursive || (*cc != OP_CBRAPOS && *cc != OP_SCBRAPOS)) + cc = next_opcode(common, cc); +SLJIT_ASSERT(cc != NULL); +while (cc < ccend) + switch(*cc) + { + case OP_SET_SOM: + case OP_RECURSE: + if (!setsom_found) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, frame_setstrbegin); + stackpos += (int)sizeof(sljit_w); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_w); + setsom_found = TRUE; + } + cc += (*cc == OP_SET_SOM) ? 1 : 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_CBRAPOS: + case OP_SCBRA: + case OP_SCBRAPOS: + offset = (GET2(cc, 1 + LINK_SIZE)) << 1; + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, OVECTOR(offset)); + stackpos += (int)sizeof(sljit_w); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP1, 0); + stackpos += (int)sizeof(sljit_w); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, TMP2, 0); + stackpos += (int)sizeof(sljit_w); + + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackpos, SLJIT_IMM, frame_end); +SLJIT_ASSERT(stackpos == STACK(stacktop)); +} + +static SLJIT_INLINE int get_localsize(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend) +{ +int localsize = 2; +pcre_uchar *alternative; +/* Calculate the sum of the local variables. */ +while (cc < ccend) + { + switch(*cc) + { + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + localsize++; + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + localsize++; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + localsize += 2; + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + localsize++; + cc += 1 + LINK_SIZE; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + } +SLJIT_ASSERT(cc == ccend); +return localsize; +} + +static void copy_locals(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, + BOOL save, int stackptr, int stacktop) +{ +DEFINE_COMPILER; +int srcw[2]; +int count; +BOOL tmp1next = TRUE; +BOOL tmp1empty = TRUE; +BOOL tmp2empty = TRUE; +pcre_uchar *alternative; +enum { + start, + loop, + end +} status; + +status = save ? start : loop; +stackptr = STACK(stackptr - 2); +stacktop = STACK(stacktop - 1); + +if (!save) + { + stackptr += sizeof(sljit_w); + if (stackptr < stacktop) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_w); + tmp1empty = FALSE; + } + if (stackptr < stacktop) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_w); + tmp2empty = FALSE; + } + /* The tmp1next must be TRUE in either way. */ + } + +while (status != end) + { + count = 0; + switch(status) + { + case start: + SLJIT_ASSERT(save); + count = 1; + srcw[0] = RECURSIVE_HEAD; + status = loop; + break; + + case loop: + if (cc >= ccend) + { + status = end; + break; + } + + switch(*cc) + { + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRAPOS: + case OP_SBRA: + case OP_SBRAPOS: + case OP_SCOND: + count = 1; + srcw[0] = PRIV_DATA(cc); + SLJIT_ASSERT(srcw[0] != 0); + cc += 1 + LINK_SIZE; + break; + + case OP_CBRA: + case OP_SCBRA: + count = 1; + srcw[0] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + count = 2; + srcw[1] = OVECTOR_PRIV(GET2(cc, 1 + LINK_SIZE)); + srcw[0] = PRIV_DATA(cc); + SLJIT_ASSERT(srcw[0] != 0); + cc += 1 + LINK_SIZE + IMM2_SIZE; + break; + + case OP_COND: + /* Might be a hidden SCOND. */ + alternative = cc + GET(cc, 1); + if (*alternative == OP_KETRMAX || *alternative == OP_KETRMIN) + { + count = 1; + srcw[0] = PRIV_DATA(cc); + SLJIT_ASSERT(srcw[0] != 0); + } + cc += 1 + LINK_SIZE; + break; + + default: + cc = next_opcode(common, cc); + SLJIT_ASSERT(cc != NULL); + break; + } + break; + + case end: + SLJIT_ASSERT_STOP(); + break; + } + + while (count > 0) + { + count--; + if (save) + { + if (tmp1next) + { + if (!tmp1empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + stackptr += sizeof(sljit_w); + } + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count]); + tmp1empty = FALSE; + tmp1next = FALSE; + } + else + { + if (!tmp2empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + stackptr += sizeof(sljit_w); + } + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count]); + tmp2empty = FALSE; + tmp1next = TRUE; + } + } + else + { + if (tmp1next) + { + SLJIT_ASSERT(!tmp1empty); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count], TMP1, 0); + tmp1empty = stackptr >= stacktop; + if (!tmp1empty) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_w); + } + tmp1next = FALSE; + } + else + { + SLJIT_ASSERT(!tmp2empty); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), srcw[count], TMP2, 0); + tmp2empty = stackptr >= stacktop; + if (!tmp2empty) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), stackptr); + stackptr += sizeof(sljit_w); + } + tmp1next = TRUE; + } + } + } + } + +if (save) + { + if (tmp1next) + { + if (!tmp1empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + stackptr += sizeof(sljit_w); + } + if (!tmp2empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + stackptr += sizeof(sljit_w); + } + } + else + { + if (!tmp2empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP2, 0); + stackptr += sizeof(sljit_w); + } + if (!tmp1empty) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), stackptr, TMP1, 0); + stackptr += sizeof(sljit_w); + } + } + } +SLJIT_ASSERT(cc == ccend && stackptr == stacktop && (save || (tmp1empty && tmp2empty))); +} + +static SLJIT_INLINE BOOL ispowerof2(unsigned int value) +{ +return (value & (value - 1)) == 0; +} + +static SLJIT_INLINE void set_jumps(jump_list *list, struct sljit_label *label) +{ +while (list) + { + /* sljit_set_label is clever enough to do nothing + if either the jump or the label is NULL */ + sljit_set_label(list->jump, label); + list = list->next; + } +} + +static SLJIT_INLINE void add_jump(struct sljit_compiler *compiler, jump_list **list, struct sljit_jump* jump) +{ +jump_list *list_item = sljit_alloc_memory(compiler, sizeof(jump_list)); +if (list_item) + { + list_item->next = *list; + list_item->jump = jump; + *list = list_item; + } +} + +static void add_stub(compiler_common *common, enum stub_types type, int data, struct sljit_jump *start) +{ +DEFINE_COMPILER; +stub_list* list_item = sljit_alloc_memory(compiler, sizeof(stub_list)); + +if (list_item) + { + list_item->type = type; + list_item->data = data; + list_item->start = start; + list_item->leave = LABEL(); + list_item->next = common->stubs; + common->stubs = list_item; + } +} + +static void flush_stubs(compiler_common *common) +{ +DEFINE_COMPILER; +stub_list* list_item = common->stubs; + +while (list_item) + { + JUMPHERE(list_item->start); + switch(list_item->type) + { + case stack_alloc: + add_jump(compiler, &common->stackalloc, JUMP(SLJIT_FAST_CALL)); + break; + } + JUMPTO(SLJIT_JUMP, list_item->leave); + list_item = list_item->next; + } +common->stubs = NULL; +} + +static SLJIT_INLINE void decrease_call_count(compiler_common *common) +{ +DEFINE_COMPILER; + +OP2(SLJIT_SUB | SLJIT_SET_E, CALL_COUNT, 0, CALL_COUNT, 0, SLJIT_IMM, 1); +add_jump(compiler, &common->calllimit, JUMP(SLJIT_C_ZERO)); +} + +static SLJIT_INLINE void allocate_stack(compiler_common *common, int size) +{ +/* May destroy all locals and registers except TMP2. */ +DEFINE_COMPILER; + +OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_w)); +#ifdef DESTROY_REGISTERS +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 12345); +OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); +OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, TMP1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP1, 0); +#endif +add_stub(common, stack_alloc, 0, CMP(SLJIT_C_GREATER, STACK_TOP, 0, STACK_LIMIT, 0)); +} + +static SLJIT_INLINE void free_stack(compiler_common *common, int size) +{ +DEFINE_COMPILER; +OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, size * sizeof(sljit_w)); +} + +static SLJIT_INLINE void reset_ovector(compiler_common *common, int length) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +int i; +/* At this point we can freely use all temporary registers. */ +/* TMP1 returns with begin - 1. */ +OP2(SLJIT_SUB, SLJIT_TEMPORARY_REG1, 0, SLJIT_MEM1(SLJIT_SAVED_REG1), SLJIT_OFFSETOF(jit_arguments, begin), SLJIT_IMM, IN_UCHARS(1)); +if (length < 8) + { + for (i = 0; i < length; i++) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(i), SLJIT_TEMPORARY_REG1, 0); + } +else + { + OP2(SLJIT_ADD, SLJIT_TEMPORARY_REG2, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, OVECTOR_START - sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG3, 0, SLJIT_IMM, length); + loop = LABEL(); + OP1(SLJIT_MOVU, SLJIT_MEM1(SLJIT_TEMPORARY_REG2), sizeof(sljit_w), SLJIT_TEMPORARY_REG1, 0); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_TEMPORARY_REG3, 0, SLJIT_TEMPORARY_REG3, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, loop); + } +} + +static SLJIT_INLINE void copy_ovector(compiler_common *common, int topbracket) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +struct sljit_jump *earlyexit; + +/* At this point we can freely use all registers. */ +OP1(SLJIT_MOV, SLJIT_SAVED_REG3, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1), STR_PTR, 0); + +OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV_SI, SLJIT_TEMPORARY_REG2, 0, SLJIT_MEM1(SLJIT_TEMPORARY_REG1), SLJIT_OFFSETOF(jit_arguments, offsetcount)); +OP2(SLJIT_SUB, SLJIT_TEMPORARY_REG3, 0, SLJIT_MEM1(SLJIT_TEMPORARY_REG1), SLJIT_OFFSETOF(jit_arguments, offsets), SLJIT_IMM, sizeof(int)); +OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG1, 0, SLJIT_MEM1(SLJIT_TEMPORARY_REG1), SLJIT_OFFSETOF(jit_arguments, begin)); +OP2(SLJIT_ADD, SLJIT_SAVED_REG1, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, OVECTOR_START); +/* Unlikely, but possible */ +earlyexit = CMP(SLJIT_C_EQUAL, SLJIT_TEMPORARY_REG2, 0, SLJIT_IMM, 0); +loop = LABEL(); +OP2(SLJIT_SUB, SLJIT_SAVED_REG2, 0, SLJIT_MEM1(SLJIT_SAVED_REG1), 0, SLJIT_TEMPORARY_REG1, 0); +OP2(SLJIT_ADD, SLJIT_SAVED_REG1, 0, SLJIT_SAVED_REG1, 0, SLJIT_IMM, sizeof(sljit_w)); +/* Copy the integer value to the output buffer */ +#ifdef COMPILE_PCRE16 +OP2(SLJIT_ASHR, SLJIT_SAVED_REG2, 0, SLJIT_SAVED_REG2, 0, SLJIT_IMM, 1); +#endif +OP1(SLJIT_MOVU_SI, SLJIT_MEM1(SLJIT_TEMPORARY_REG3), sizeof(int), SLJIT_SAVED_REG2, 0); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_TEMPORARY_REG2, 0, SLJIT_TEMPORARY_REG2, 0, SLJIT_IMM, 1); +JUMPTO(SLJIT_C_NOT_ZERO, loop); +JUMPHERE(earlyexit); + +/* Calculate the return value, which is the maximum ovector value. */ +if (topbracket > 1) + { + OP2(SLJIT_ADD, SLJIT_TEMPORARY_REG1, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, OVECTOR_START + topbracket * 2 * sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG2, 0, SLJIT_IMM, topbracket + 1); + + /* OVECTOR(0) is never equal to SLJIT_SAVED_REG3. */ + loop = LABEL(); + OP1(SLJIT_MOVU, SLJIT_TEMPORARY_REG3, 0, SLJIT_MEM1(SLJIT_TEMPORARY_REG1), -(2 * (sljit_w)sizeof(sljit_w))); + OP2(SLJIT_SUB, SLJIT_TEMPORARY_REG2, 0, SLJIT_TEMPORARY_REG2, 0, SLJIT_IMM, 1); + CMPTO(SLJIT_C_EQUAL, SLJIT_TEMPORARY_REG3, 0, SLJIT_SAVED_REG3, 0, loop); + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_TEMPORARY_REG2, 0); + } +else + OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, 1); +} + +static SLJIT_INLINE BOOL char_has_othercase(compiler_common *common, pcre_uchar* cc) +{ +/* Detects if the character has an othercase. */ +unsigned int c; + +#ifdef SUPPORT_UTF +if (common->utf) + { + GETCHAR(c, cc); + if (c > 127) + { +#ifdef SUPPORT_UCP + return c != UCD_OTHERCASE(c); +#else + return FALSE; +#endif + } +#ifndef COMPILE_PCRE8 + return common->fcc[c] != c; +#endif + } +else +#endif + c = *cc; +return MAX_255(c) ? common->fcc[c] != c : FALSE; +} + +static SLJIT_INLINE unsigned int char_othercase(compiler_common *common, unsigned int c) +{ +/* Returns with the othercase. */ +#ifdef SUPPORT_UTF +if (common->utf && c > 127) + { +#ifdef SUPPORT_UCP + return UCD_OTHERCASE(c); +#else + return c; +#endif + } +#endif +return TABLE_GET(c, common->fcc, c); +} + +static unsigned int char_get_othercase_bit(compiler_common *common, pcre_uchar* cc) +{ +/* Detects if the character and its othercase has only 1 bit difference. */ +unsigned int c, oc, bit; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +int n; +#endif + +#ifdef SUPPORT_UTF +if (common->utf) + { + GETCHAR(c, cc); + if (c <= 127) + oc = common->fcc[c]; + else + { +#ifdef SUPPORT_UCP + oc = UCD_OTHERCASE(c); +#else + oc = c; +#endif + } + } +else + { + c = *cc; + oc = TABLE_GET(c, common->fcc, c); + } +#else +c = *cc; +oc = TABLE_GET(c, common->fcc, c); +#endif + +SLJIT_ASSERT(c != oc); + +bit = c ^ oc; +/* Optimized for English alphabet. */ +if (c <= 127 && bit == 0x20) + return (0 << 8) | 0x20; + +/* Since c != oc, they must have at least 1 bit difference. */ +if (!ispowerof2(bit)) + return 0; + +#ifdef COMPILE_PCRE8 + +#ifdef SUPPORT_UTF +if (common->utf && c > 127) + { + n = GET_EXTRALEN(*cc); + while ((bit & 0x3f) == 0) + { + n--; + bit >>= 6; + } + return (n << 8) | bit; + } +#endif /* SUPPORT_UTF */ +return (0 << 8) | bit; + +#else /* COMPILE_PCRE8 */ + +#ifdef COMPILE_PCRE16 +#ifdef SUPPORT_UTF +if (common->utf && c > 65535) + { + if (bit >= (1 << 10)) + bit >>= 10; + else + return (bit < 256) ? ((2 << 8) | bit) : ((3 << 8) | (bit >> 8)); + } +#endif /* SUPPORT_UTF */ +return (bit < 256) ? ((0 << 8) | bit) : ((1 << 8) | (bit >> 8)); +#endif /* COMPILE_PCRE16 */ + +#endif /* COMPILE_PCRE8 */ +} + +static SLJIT_INLINE void check_input_end(compiler_common *common, jump_list **fallbacks) +{ +DEFINE_COMPILER; +add_jump(compiler, fallbacks, CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0)); +} + +static void read_char(compiler_common *common) +{ +/* Reads the character into TMP1, updates STR_PTR. +Does not check STR_END. TMP2 Destroyed. */ +DEFINE_COMPILER; +#ifdef SUPPORT_UTF +struct sljit_jump *jump; +#endif + +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +#ifdef SUPPORT_UTF +if (common->utf) + { +#ifdef COMPILE_PCRE8 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); +#else +#ifdef COMPILE_PCRE16 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); +#endif +#endif /* COMPILE_PCRE8 */ + add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL)); + JUMPHERE(jump); + } +#endif +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +} + +static void peek_char(compiler_common *common) +{ +/* Reads the character into TMP1, keeps STR_PTR. +Does not check STR_END. TMP2 Destroyed. */ +DEFINE_COMPILER; +#ifdef SUPPORT_UTF +struct sljit_jump *jump; +#endif + +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +#ifdef SUPPORT_UTF +if (common->utf) + { +#ifdef COMPILE_PCRE8 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); +#else +#ifdef COMPILE_PCRE16 + jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); +#endif +#endif /* COMPILE_PCRE8 */ + add_jump(compiler, &common->utfreadchar, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + JUMPHERE(jump); + } +#endif +} + +static void read_char8_type(compiler_common *common) +{ +/* Reads the character type into TMP1, updates STR_PTR. Does not check STR_END. */ +DEFINE_COMPILER; +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 +struct sljit_jump *jump; +#endif + +#ifdef SUPPORT_UTF +if (common->utf) + { + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#ifdef COMPILE_PCRE8 + /* This can be an extra read in some situations, but hopefully + it is needed in most cases. */ + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); + jump = CMP(SLJIT_C_LESS, TMP2, 0, SLJIT_IMM, 0xc0); + add_jump(compiler, &common->utfreadtype8, JUMP(SLJIT_FAST_CALL)); + JUMPHERE(jump); +#else +#ifdef COMPILE_PCRE16 + OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); + JUMPHERE(jump); + /* Skip low surrogate if necessary. */ + OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0xd800); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +#endif +#endif /* COMPILE_PCRE8 */ + return; + } +#endif +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), 0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#ifdef COMPILE_PCRE16 +/* The ctypes array contains only 256 values. */ +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +jump = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); +#endif +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); +#ifdef COMPILE_PCRE16 +JUMPHERE(jump); +#endif +} + +static void skip_char_back(compiler_common *common) +{ +/* Goes one character back. Affects STR_PTR and TMP1. Does not check begin. */ +DEFINE_COMPILER; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +struct sljit_label *label; + +if (common->utf) + { + label = LABEL(); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xc0); + CMPTO(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0x80, label); + return; + } +#endif +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 +if (common->utf) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), -IN_UCHARS(1)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + /* Skip low surrogate if necessary. */ + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xdc00); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + return; + } +#endif +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +} + +static void check_newlinechar(compiler_common *common, int nltype, jump_list **fallbacks, BOOL jumpiftrue) +{ +/* Character comes in TMP1. Checks if it is a newline. TMP2 may be destroyed. */ +DEFINE_COMPILER; + +if (nltype == NLTYPE_ANY) + { + add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, fallbacks, JUMP(jumpiftrue ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + } +else if (nltype == NLTYPE_ANYCRLF) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_CR); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + add_jump(compiler, fallbacks, JUMP(jumpiftrue ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + } +else + { + SLJIT_ASSERT(nltype == NLTYPE_FIXED && common->newline < 256); + add_jump(compiler, fallbacks, CMP(jumpiftrue ? SLJIT_C_EQUAL : SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline)); + } +} + +#ifdef SUPPORT_UTF + +#ifdef COMPILE_PCRE8 +static void do_utfreadchar(compiler_common *common) +{ +/* Fast decoding a UTF-8 character. TMP1 contains the first byte +of the character (>= 0xc0). Return char value in TMP1, length - 1 in TMP2. */ +DEFINE_COMPILER; +struct sljit_jump *jump; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); +/* Searching for the first zero. */ +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); +jump = JUMP(SLJIT_C_NOT_ZERO); +/* Two byte sequence. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1f); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 6); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +JUMPHERE(jump); + +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x10); +jump = JUMP(SLJIT_C_NOT_ZERO); +/* Three byte sequence. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0f); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 12); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(2)); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +JUMPHERE(jump); + +/* Four byte sequence. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x07); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 18); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 12); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(2)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(3)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(3)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(3)); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +static void do_utfreadtype8(compiler_common *common) +{ +/* Fast decoding a UTF-8 character type. TMP2 contains the first byte +of the character (>= 0xc0). Return value in TMP1. */ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_jump *compare; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); + +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_IMM, 0x20); +jump = JUMP(SLJIT_C_NOT_ZERO); +/* Two byte sequence. */ +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x1f); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 6); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3f); +OP2(SLJIT_OR, TMP2, 0, TMP2, 0, TMP1, 0); +compare = CMP(SLJIT_C_GREATER, TMP2, 0, SLJIT_IMM, 255); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), common->ctypes); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); + +JUMPHERE(compare); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +JUMPHERE(jump); + +/* We only have types for characters less than 256. */ +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP2), (sljit_w)PRIV(utf8_table4) - 0xc0); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 0); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#else /* COMPILE_PCRE8 */ + +#ifdef COMPILE_PCRE16 +static void do_utfreadchar(compiler_common *common) +{ +/* Fast decoding a UTF-16 character. TMP1 contains the first 16 bit char +of the character (>= 0xd800). Return char value in TMP1, length - 1 in TMP2. */ +DEFINE_COMPILER; +struct sljit_jump *jump; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); +jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xdc00); +/* Do nothing, only return. */ +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); + +JUMPHERE(jump); +/* Combine two 16 bit characters. */ +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x3ff); +OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 10); +OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 0x3ff); +OP2(SLJIT_OR, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x10000); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} +#endif /* COMPILE_PCRE16 */ + +#endif /* COMPILE_PCRE8 */ + +#endif /* SUPPORT_UTF */ + +#ifdef SUPPORT_UCP + +/* UCD_BLOCK_SIZE must be 128 (see the assert below). */ +#define UCD_BLOCK_MASK 127 +#define UCD_BLOCK_SHIFT 7 + +static void do_getucd(compiler_common *common) +{ +/* Search the UCD record for the character comes in TMP1. +Returns chartype in TMP1 and UCD offset in TMP2. */ +DEFINE_COMPILER; + +SLJIT_ASSERT(UCD_BLOCK_SIZE == 128 && sizeof(ucd_record) == 8); + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); +OP2(SLJIT_LSHR, TMP2, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), (sljit_w)PRIV(ucd_stage1)); +OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, UCD_BLOCK_MASK); +OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, UCD_BLOCK_SHIFT); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, (sljit_w)PRIV(ucd_stage2)); +OP1(SLJIT_MOV_UH, TMP2, 0, SLJIT_MEM2(TMP2, TMP1), 1); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, (sljit_w)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, chartype)); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM2(TMP1, TMP2), 3); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} +#endif + +static SLJIT_INLINE struct sljit_label *mainloop_entry(compiler_common *common, BOOL hascrorlf, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *mainloop; +struct sljit_label *newlinelabel = NULL; +struct sljit_jump *start; +struct sljit_jump *end = NULL; +struct sljit_jump *nl = NULL; +#ifdef SUPPORT_UTF +struct sljit_jump *singlechar; +#endif +jump_list *newline = NULL; +BOOL newlinecheck = FALSE; +BOOL readuchar = FALSE; + +if (!(hascrorlf || firstline) && (common->nltype == NLTYPE_ANY || + common->nltype == NLTYPE_ANYCRLF || common->newline > 255)) + newlinecheck = TRUE; + +if (firstline) + { + /* Search for the end of the first line. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END, STR_END, 0); + + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + mainloop = LABEL(); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + end = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, mainloop); + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, mainloop); + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + } + else + { + end = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + mainloop = LABEL(); + /* Continual stores does not cause data dependency. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END, STR_PTR, 0); + read_char(common); + check_newlinechar(common, common->nltype, &newline, TRUE); + CMPTO(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0, mainloop); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END, STR_PTR, 0); + set_jumps(newline, LABEL()); + } + + JUMPHERE(end); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + } + +start = JUMP(SLJIT_JUMP); + +if (newlinecheck) + { + newlinelabel = LABEL(); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + end = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, common->newline & 0xff); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); +#ifdef COMPILE_PCRE16 + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); +#endif + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + nl = JUMP(SLJIT_JUMP); + } + +mainloop = LABEL(); + +/* Increasing the STR_PTR here requires one less jump in the most common case. */ +#ifdef SUPPORT_UTF +if (common->utf) readuchar = TRUE; +#endif +if (newlinecheck) readuchar = TRUE; + +if (readuchar) + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + +if (newlinecheck) + CMPTO(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, newlinelabel); + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (common->utf) + { + singlechar = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(singlechar); + } +#endif +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 +if (common->utf) + { + singlechar = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(singlechar); + } +#endif +JUMPHERE(start); + +if (newlinecheck) + { + JUMPHERE(end); + JUMPHERE(nl); + } + +return mainloop; +} + +static SLJIT_INLINE void fast_forward_first_char(compiler_common *common, pcre_uchar first_char, BOOL caseless, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *leave; +struct sljit_jump *found; +pcre_uchar oc, bit; + +if (firstline) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END); + } + +start = LABEL(); +leave = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + +oc = first_char; +if (caseless) + { + oc = TABLE_GET(first_char, common->fcc, first_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (first_char > 127 && common->utf) + oc = UCD_OTHERCASE(first_char); +#endif + } +if (first_char == oc) + found = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, first_char); +else + { + bit = first_char ^ oc; + if (ispowerof2(bit)) + { + OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, bit); + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, first_char | bit); + } + else + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, first_char); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, oc); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + found = JUMP(SLJIT_C_NOT_ZERO); + } + } + +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (common->utf) + { + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + } +#endif +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 +if (common->utf) + { + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + } +#endif +JUMPTO(SLJIT_JUMP, start); +JUMPHERE(found); +JUMPHERE(leave); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); +} + +static SLJIT_INLINE void fast_forward_newline(compiler_common *common, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +struct sljit_jump *lastchar; +struct sljit_jump *firstchar; +struct sljit_jump *leave; +struct sljit_jump *foundcr = NULL; +struct sljit_jump *notfoundnl; +jump_list *newline = NULL; + +if (firstline) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END); + } + +if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + lastchar = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + firstchar = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP2, 0); + + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(2)); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, TMP1, 0); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_GREATER_EQUAL); +#ifdef COMPILE_PCRE16 + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); +#endif + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + + loop = LABEL(); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + leave = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff, loop); + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff, loop); + + JUMPHERE(leave); + JUMPHERE(firstchar); + JUMPHERE(lastchar); + + if (firstline) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + return; + } + +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +firstchar = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP2, 0); +skip_char_back(common); + +loop = LABEL(); +read_char(common); +lastchar = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) + foundcr = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); +check_newlinechar(common, common->nltype, &newline, FALSE); +set_jumps(newline, loop); + +if (common->nltype == NLTYPE_ANY || common->nltype == NLTYPE_ANYCRLF) + { + leave = JUMP(SLJIT_JUMP); + JUMPHERE(foundcr); + notfoundnl = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_NL); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); +#ifdef COMPILE_PCRE16 + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); +#endif + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(notfoundnl); + JUMPHERE(leave); + } +JUMPHERE(lastchar); +JUMPHERE(firstchar); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); +} + +static SLJIT_INLINE void fast_forward_start_bits(compiler_common *common, sljit_uw start_bits, BOOL firstline) +{ +DEFINE_COMPILER; +struct sljit_label *start; +struct sljit_jump *leave; +struct sljit_jump *found; +#ifndef COMPILE_PCRE8 +struct sljit_jump *jump; +#endif + +if (firstline) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, STR_END, 0); + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END); + } + +start = LABEL(); +leave = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); +#ifdef SUPPORT_UTF +if (common->utf) + OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); +#endif +#ifndef COMPILE_PCRE8 +jump = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 255); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_IMM, 255); +JUMPHERE(jump); +#endif +OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); +OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); +OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), start_bits); +OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); +OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); +found = JUMP(SLJIT_C_NOT_ZERO); + +#ifdef SUPPORT_UTF +if (common->utf) + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); +#endif +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (common->utf) + { + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0, start); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + } +#endif +#if defined SUPPORT_UTF && defined COMPILE_PCRE16 +if (common->utf) + { + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800, start); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + } +#endif +JUMPTO(SLJIT_JUMP, start); +JUMPHERE(found); +JUMPHERE(leave); + +if (firstline) + OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); +} + +static SLJIT_INLINE struct sljit_jump *search_requested_char(compiler_common *common, pcre_uchar req_char, BOOL caseless, BOOL has_firstchar) +{ +DEFINE_COMPILER; +struct sljit_label *loop; +struct sljit_jump *toolong; +struct sljit_jump *alreadyfound; +struct sljit_jump *found; +struct sljit_jump *foundoc = NULL; +struct sljit_jump *notfound; +pcre_uchar oc, bit; + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), REQ_CHAR_PTR); +OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, REQ_BYTE_MAX); +toolong = CMP(SLJIT_C_LESS, TMP1, 0, STR_END, 0); +alreadyfound = CMP(SLJIT_C_LESS, STR_PTR, 0, TMP2, 0); + +if (has_firstchar) + OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +else + OP1(SLJIT_MOV, TMP1, 0, STR_PTR, 0); + +loop = LABEL(); +notfound = CMP(SLJIT_C_GREATER_EQUAL, TMP1, 0, STR_END, 0); + +OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(TMP1), 0); +oc = req_char; +if (caseless) + { + oc = TABLE_GET(req_char, common->fcc, req_char); +#if defined SUPPORT_UCP && !(defined COMPILE_PCRE8) + if (req_char > 127 && common->utf) + oc = UCD_OTHERCASE(req_char); +#endif + } +if (req_char == oc) + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, req_char); +else + { + bit = req_char ^ oc; + if (ispowerof2(bit)) + { + OP2(SLJIT_OR, TMP2, 0, TMP2, 0, SLJIT_IMM, bit); + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, req_char | bit); + } + else + { + found = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, req_char); + foundoc = CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, oc); + } + } +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_JUMP, loop); + +JUMPHERE(found); +if (foundoc) + JUMPHERE(foundoc); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), REQ_CHAR_PTR, TMP1, 0); +JUMPHERE(alreadyfound); +JUMPHERE(toolong); +return notfound; +} + +static void do_revertframes(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *mainloop; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); +OP1(SLJIT_MOV, TMP1, 0, STACK_TOP, 0); + +/* Drop frames until we reach STACK_TOP. */ +mainloop = LABEL(); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), 0); +jump = CMP(SLJIT_C_SIG_LESS_EQUAL, TMP2, 0, SLJIT_IMM, frame_end); +OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_LOCALS_REG, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), 0, SLJIT_MEM1(TMP1), sizeof(sljit_w)); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), sizeof(sljit_w), SLJIT_MEM1(TMP1), 2 * sizeof(sljit_w)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 3 * sizeof(sljit_w)); +JUMPTO(SLJIT_JUMP, mainloop); + +JUMPHERE(jump); +jump = CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, frame_end); +/* End of dropping frames. */ +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); + +JUMPHERE(jump); +jump = CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, frame_setstrbegin); +/* Set string begin. */ +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), sizeof(sljit_w)); +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_w)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), TMP2, 0); +JUMPTO(SLJIT_JUMP, mainloop); + +JUMPHERE(jump); +/* Unknown command. */ +OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 2 * sizeof(sljit_w)); +JUMPTO(SLJIT_JUMP, mainloop); +} + +static void check_wordboundary(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *beginend; +#if !(defined COMPILE_PCRE8) || defined SUPPORT_UTF +struct sljit_jump *jump; +#endif + +SLJIT_COMPILE_ASSERT(ctype_word == 0x10, ctype_word_must_be_16); + +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, 1, 5, 5, common->localsize); +/* Get type of the previous char, and put it to LOCALS1. */ +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, SLJIT_IMM, 0); +beginend = CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP1, 0); +skip_char_back(common); +read_char(common); + +/* Testing char type. */ +#ifdef SUPPORT_UCP +if (common->use_ucp) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1); + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_LESS_EQUAL); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_LESS_EQUAL); + JUMPHERE(jump); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP2, 0); + } +else +#endif + { +#ifndef COMPILE_PCRE8 + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#elif defined SUPPORT_UTF + /* Here LOCALS1 has already been zeroed. */ + jump = NULL; + if (common->utf) + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#endif /* COMPILE_PCRE8 */ + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), common->ctypes); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 4 /* ctype_word */); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP1, 0); +#ifndef COMPILE_PCRE8 + JUMPHERE(jump); +#elif defined SUPPORT_UTF + if (jump != NULL) + JUMPHERE(jump); +#endif /* COMPILE_PCRE8 */ + } +JUMPHERE(beginend); + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); +beginend = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); +peek_char(common); + +/* Testing char type. This is a code duplication. */ +#ifdef SUPPORT_UCP +if (common->use_ucp) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 1); + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_LESS_EQUAL); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Nd - ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ucp_No - ucp_Nd); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_LESS_EQUAL); + JUMPHERE(jump); + } +else +#endif + { +#ifndef COMPILE_PCRE8 + /* TMP2 may be destroyed by peek_char. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#elif defined SUPPORT_UTF + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + jump = NULL; + if (common->utf) + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#endif + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), common->ctypes); + OP2(SLJIT_LSHR, TMP2, 0, TMP2, 0, SLJIT_IMM, 4 /* ctype_word */); + OP2(SLJIT_AND, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); +#ifndef COMPILE_PCRE8 + JUMPHERE(jump); +#elif defined SUPPORT_UTF + if (jump != NULL) + JUMPHERE(jump); +#endif /* COMPILE_PCRE8 */ + } +JUMPHERE(beginend); + +OP2(SLJIT_XOR | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); +sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +} + +static void check_anynewline(compiler_common *common) +{ +/* Check whether TMP1 contains a newline character. TMP2 destroyed. */ +DEFINE_COMPILER; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); + +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 +#ifdef COMPILE_PCRE8 +if (common->utf) + { +#endif + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); +#ifdef COMPILE_PCRE8 + } +#endif +#endif /* SUPPORT_UTF || COMPILE_PCRE16 */ +COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +static void check_hspace(compiler_common *common) +{ +/* Check whether TMP1 contains a newline character. TMP2 destroyed. */ +DEFINE_COMPILER; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); + +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x09); +COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x20); +COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xa0); +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 +#ifdef COMPILE_PCRE8 +if (common->utf) + { +#endif + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x1680); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x180e); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x2000); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x200A - 0x2000); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_LESS_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x202f - 0x2000); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x205f - 0x2000); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x3000 - 0x2000); +#ifdef COMPILE_PCRE8 + } +#endif +#endif /* SUPPORT_UTF || COMPILE_PCRE16 */ +COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +static void check_vspace(compiler_common *common) +{ +/* Check whether TMP1 contains a newline character. TMP2 destroyed. */ +DEFINE_COMPILER; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); + +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x0a); +OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x0d - 0x0a); +COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_LESS_EQUAL); +OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x85 - 0x0a); +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 +#ifdef COMPILE_PCRE8 +if (common->utf) + { +#endif + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, 0x1); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0x2029 - 0x0a); +#ifdef COMPILE_PCRE8 + } +#endif +#endif /* SUPPORT_UTF || COMPILE_PCRE16 */ +COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#define CHAR1 STR_END +#define CHAR2 STACK_TOP + +static void do_casefulcmp(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *label; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); +OP1(SLJIT_MOV, TMP3, 0, CHAR1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, CHAR2, 0); +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +label = LABEL(); +OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +jump = CMP(SLJIT_C_NOT_EQUAL, CHAR1, 0, CHAR2, 0); +OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_C_NOT_ZERO, label); + +JUMPHERE(jump); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP1(SLJIT_MOV, CHAR1, 0, TMP3, 0); +OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#define LCC_TABLE STACK_LIMIT + +static void do_caselesscmp(compiler_common *common) +{ +DEFINE_COMPILER; +struct sljit_jump *jump; +struct sljit_label *label; + +sljit_emit_fast_enter(compiler, RETURN_ADDR, 0, 1, 5, 5, common->localsize); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + +OP1(SLJIT_MOV, TMP3, 0, LCC_TABLE, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, CHAR1, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, CHAR2, 0); +OP1(SLJIT_MOV, LCC_TABLE, 0, SLJIT_IMM, common->lcc); +OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, IN_UCHARS(1)); +OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + +label = LABEL(); +OP1(MOVU_UCHAR, CHAR1, 0, SLJIT_MEM1(TMP1), IN_UCHARS(1)); +OP1(MOVU_UCHAR, CHAR2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); +#ifndef COMPILE_PCRE8 +jump = CMP(SLJIT_C_GREATER, CHAR1, 0, SLJIT_IMM, 255); +#endif +OP1(SLJIT_MOV_UB, CHAR1, 0, SLJIT_MEM2(LCC_TABLE, CHAR1), 0); +#ifndef COMPILE_PCRE8 +JUMPHERE(jump); +jump = CMP(SLJIT_C_GREATER, CHAR2, 0, SLJIT_IMM, 255); +#endif +OP1(SLJIT_MOV_UB, CHAR2, 0, SLJIT_MEM2(LCC_TABLE, CHAR2), 0); +#ifndef COMPILE_PCRE8 +JUMPHERE(jump); +#endif +jump = CMP(SLJIT_C_NOT_EQUAL, CHAR1, 0, CHAR2, 0); +OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, IN_UCHARS(1)); +JUMPTO(SLJIT_C_NOT_ZERO, label); + +JUMPHERE(jump); +OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +OP1(SLJIT_MOV, LCC_TABLE, 0, TMP3, 0); +OP1(SLJIT_MOV, CHAR1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); +OP1(SLJIT_MOV, CHAR2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); +sljit_emit_fast_return(compiler, RETURN_ADDR, 0); +} + +#undef LCC_TABLE +#undef CHAR1 +#undef CHAR2 + +#if defined SUPPORT_UTF && defined SUPPORT_UCP + +static const pcre_uchar *SLJIT_CALL do_utf_caselesscmp(pcre_uchar *src1, jit_arguments *args, pcre_uchar *end1) +{ +/* This function would be ineffective to do in JIT level. */ +int c1, c2; +const pcre_uchar *src2 = args->ptr; +const pcre_uchar *end2 = args->end; + +while (src1 < end1) + { + if (src2 >= end2) + return 0; + GETCHARINC(c1, src1); + GETCHARINC(c2, src2); + if (c1 != c2 && c1 != UCD_OTHERCASE(c2)) return 0; + } +return src2; +} + +#endif /* SUPPORT_UTF && SUPPORT_UCP */ + +static pcre_uchar *byte_sequence_compare(compiler_common *common, BOOL caseless, pcre_uchar *cc, + compare_context* context, jump_list **fallbacks) +{ +DEFINE_COMPILER; +unsigned int othercasebit = 0; +pcre_uchar *othercasechar = NULL; +#ifdef SUPPORT_UTF +int utflength; +#endif + +if (caseless && char_has_othercase(common, cc)) + { + othercasebit = char_get_othercase_bit(common, cc); + SLJIT_ASSERT(othercasebit); + /* Extracting bit difference info. */ +#ifdef COMPILE_PCRE8 + othercasechar = cc + (othercasebit >> 8); + othercasebit &= 0xff; +#else +#ifdef COMPILE_PCRE16 + othercasechar = cc + (othercasebit >> 9); + if ((othercasebit & 0x100) != 0) + othercasebit = (othercasebit & 0xff) << 8; + else + othercasebit &= 0xff; +#endif +#endif + } + +if (context->sourcereg == -1) + { +#ifdef COMPILE_PCRE8 +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + if (context->length >= 4) + OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); + else if (context->length >= 2) + OP1(SLJIT_MOV_UH, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); + else +#endif + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); +#else +#ifdef COMPILE_PCRE16 +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + if (context->length >= 4) + OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); + else +#endif + OP1(SLJIT_MOV_UH, TMP1, 0, SLJIT_MEM1(STR_PTR), -context->length); +#endif +#endif /* COMPILE_PCRE8 */ + context->sourcereg = TMP2; + } + +#ifdef SUPPORT_UTF +utflength = 1; +if (common->utf && HAS_EXTRALEN(*cc)) + utflength += GET_EXTRALEN(*cc); + +do + { +#endif + + context->length -= IN_UCHARS(1); +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + + /* Unaligned read is supported. */ + if (othercasebit != 0 && othercasechar == cc) + { + context->c.asuchars[context->ucharptr] = *cc | othercasebit; + context->oc.asuchars[context->ucharptr] = othercasebit; + } + else + { + context->c.asuchars[context->ucharptr] = *cc; + context->oc.asuchars[context->ucharptr] = 0; + } + context->ucharptr++; + +#ifdef COMPILE_PCRE8 + if (context->ucharptr >= 4 || context->length == 0 || (context->ucharptr == 2 && context->length == 1)) +#else + if (context->ucharptr >= 2 || context->length == 0) +#endif + { + if (context->length >= 4) + OP1(SLJIT_MOV_SI, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#ifdef COMPILE_PCRE8 + else if (context->length >= 2) + OP1(SLJIT_MOV_UH, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); + else if (context->length >= 1) + OP1(SLJIT_MOV_UB, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#else + else if (context->length >= 2) + OP1(SLJIT_MOV_UH, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#endif + context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1; + + switch(context->ucharptr) + { + case 4 / sizeof(pcre_uchar): + if (context->oc.asint != 0) + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asint); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asint | context->oc.asint)); + break; + + case 2 / sizeof(pcre_uchar): + if (context->oc.asushort != 0) + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asushort); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asushort | context->oc.asushort)); + break; + +#ifdef COMPILE_PCRE8 + case 1: + if (context->oc.asbyte != 0) + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, context->oc.asbyte); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, context->c.asbyte | context->oc.asbyte)); + break; +#endif + + default: + SLJIT_ASSERT_STOP(); + break; + } + context->ucharptr = 0; + } + +#else + + /* Unaligned read is unsupported. */ +#ifdef COMPILE_PCRE8 + if (context->length > 0) + OP1(SLJIT_MOV_UB, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#else + if (context->length > 0) + OP1(SLJIT_MOV_UH, context->sourcereg, 0, SLJIT_MEM1(STR_PTR), -context->length); +#endif + context->sourcereg = context->sourcereg == TMP1 ? TMP2 : TMP1; + + if (othercasebit != 0 && othercasechar == cc) + { + OP2(SLJIT_OR, context->sourcereg, 0, context->sourcereg, 0, SLJIT_IMM, othercasebit); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc | othercasebit)); + } + else + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, context->sourcereg, 0, SLJIT_IMM, *cc)); + +#endif + + cc++; +#ifdef SUPPORT_UTF + utflength--; + } +while (utflength > 0); +#endif + +return cc; +} + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + +#define SET_TYPE_OFFSET(value) \ + if ((value) != typeoffset) \ + { \ + if ((value) > typeoffset) \ + OP2(SLJIT_SUB, typereg, 0, typereg, 0, SLJIT_IMM, (value) - typeoffset); \ + else \ + OP2(SLJIT_ADD, typereg, 0, typereg, 0, SLJIT_IMM, typeoffset - (value)); \ + } \ + typeoffset = (value); + +#define SET_CHAR_OFFSET(value) \ + if ((value) != charoffset) \ + { \ + if ((value) > charoffset) \ + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, (value) - charoffset); \ + else \ + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, charoffset - (value)); \ + } \ + charoffset = (value); + +static void compile_xclass_hotpath(compiler_common *common, pcre_uchar *cc, jump_list **fallbacks) +{ +DEFINE_COMPILER; +jump_list *found = NULL; +jump_list **list = (*cc & XCL_NOT) == 0 ? &found : fallbacks; +unsigned int c; +int compares; +struct sljit_jump *jump = NULL; +pcre_uchar *ccbegin; +#ifdef SUPPORT_UCP +BOOL needstype = FALSE, needsscript = FALSE, needschar = FALSE; +BOOL charsaved = FALSE; +int typereg = TMP1, scriptreg = TMP1; +unsigned int typeoffset; +#endif +int invertcmp, numberofcmps; +unsigned int charoffset; + +/* Although SUPPORT_UTF must be defined, we are not necessary in utf mode. */ +check_input_end(common, fallbacks); +read_char(common); + +if ((*cc++ & XCL_MAP) != 0) + { + OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); +#ifndef COMPILE_PCRE8 + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#elif defined SUPPORT_UTF + if (common->utf) + jump = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); +#endif + + OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)cc); + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + add_jump(compiler, list, JUMP(SLJIT_C_NOT_ZERO)); + +#ifndef COMPILE_PCRE8 + JUMPHERE(jump); +#elif defined SUPPORT_UTF + if (common->utf) + JUMPHERE(jump); +#endif + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); +#ifdef SUPPORT_UCP + charsaved = TRUE; +#endif + cc += 32 / sizeof(pcre_uchar); + } + +/* Scanning the necessary info. */ +ccbegin = cc; +compares = 0; +while (*cc != XCL_END) + { + compares++; + if (*cc == XCL_SINGLE) + { + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif +#ifdef SUPPORT_UCP + needschar = TRUE; +#endif + } + else if (*cc == XCL_RANGE) + { + cc += 2; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + cc++; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif +#ifdef SUPPORT_UCP + needschar = TRUE; +#endif + } +#ifdef SUPPORT_UCP + else + { + SLJIT_ASSERT(*cc == XCL_PROP || *cc == XCL_NOTPROP); + cc++; + switch(*cc) + { + case PT_ANY: + break; + + case PT_LAMP: + case PT_GC: + case PT_PC: + case PT_ALNUM: + needstype = TRUE; + break; + + case PT_SC: + needsscript = TRUE; + break; + + case PT_SPACE: + case PT_PXSPACE: + case PT_WORD: + needstype = TRUE; + needschar = TRUE; + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + cc += 2; + } +#endif + } + +#ifdef SUPPORT_UCP +/* Simple register allocation. TMP1 is preferred if possible. */ +if (needstype || needsscript) + { + if (needschar && !charsaved) + OP1(SLJIT_MOV, TMP3, 0, TMP1, 0); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + if (needschar) + { + if (needstype) + { + OP1(SLJIT_MOV, RETURN_ADDR, 0, TMP1, 0); + typereg = RETURN_ADDR; + } + + if (needsscript) + scriptreg = TMP3; + OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); + } + else if (needstype && needsscript) + scriptreg = TMP3; + /* In all other cases only one of them was specified, and that can goes to TMP1. */ + + if (needsscript) + { + if (scriptreg == TMP1) + { + OP1(SLJIT_MOV, scriptreg, 0, SLJIT_IMM, (sljit_w)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script)); + OP1(SLJIT_MOV_UB, scriptreg, 0, SLJIT_MEM2(scriptreg, TMP2), 3); + } + else + { + OP2(SLJIT_SHL, TMP2, 0, TMP2, 0, SLJIT_IMM, 3); + OP2(SLJIT_ADD, TMP2, 0, TMP2, 0, SLJIT_IMM, (sljit_w)PRIV(ucd_records) + SLJIT_OFFSETOF(ucd_record, script)); + OP1(SLJIT_MOV_UB, scriptreg, 0, SLJIT_MEM1(TMP2), 0); + } + } + } +#endif + +/* Generating code. */ +cc = ccbegin; +charoffset = 0; +numberofcmps = 0; +#ifdef SUPPORT_UCP +typeoffset = 0; +#endif + +while (*cc != XCL_END) + { + compares--; + invertcmp = (compares == 0 && list != fallbacks); + jump = NULL; + + if (*cc == XCL_SINGLE) + { + cc ++; +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHARINC(c, cc); + } + else +#endif + c = *cc++; + + if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + COND_VALUE(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + numberofcmps++; + } + else if (numberofcmps > 0) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + numberofcmps = 0; + } + else + { + jump = CMP(SLJIT_C_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, c - charoffset); + numberofcmps = 0; + } + } + else if (*cc == XCL_RANGE) + { + cc ++; +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHARINC(c, cc); + } + else +#endif + c = *cc++; + SET_CHAR_OFFSET(c); +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHARINC(c, cc); + } + else +#endif + c = *cc++; + if (numberofcmps < 3 && (*cc == XCL_SINGLE || *cc == XCL_RANGE)) + { + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + COND_VALUE(numberofcmps == 0 ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_C_LESS_EQUAL); + numberofcmps++; + } + else if (numberofcmps > 0) + { + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c - charoffset); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_LESS_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + numberofcmps = 0; + } + else + { + jump = CMP(SLJIT_C_LESS_EQUAL ^ invertcmp, TMP1, 0, SLJIT_IMM, c - charoffset); + numberofcmps = 0; + } + } +#ifdef SUPPORT_UCP + else + { + if (*cc == XCL_NOTPROP) + invertcmp ^= 0x1; + cc++; + switch(*cc) + { + case PT_ANY: + if (list != fallbacks) + { + if ((cc[-1] == XCL_NOTPROP && compares > 0) || (cc[-1] == XCL_PROP && compares == 0)) + continue; + } + else if (cc[-1] == XCL_NOTPROP) + continue; + jump = JUMP(SLJIT_JUMP); + break; + + case PT_LAMP: + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - typeoffset); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Ll - typeoffset); + COND_VALUE(SLJIT_OR, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lt - typeoffset); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + + case PT_GC: + c = PRIV(ucp_typerange)[(int)cc[1] * 2]; + SET_TYPE_OFFSET(c); + jump = CMP(SLJIT_C_LESS_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, PRIV(ucp_typerange)[(int)cc[1] * 2 + 1] - c); + break; + + case PT_PC: + jump = CMP(SLJIT_C_EQUAL ^ invertcmp, typereg, 0, SLJIT_IMM, (int)cc[1] - typeoffset); + break; + + case PT_SC: + jump = CMP(SLJIT_C_EQUAL ^ invertcmp, scriptreg, 0, SLJIT_IMM, (int)cc[1]); + break; + + case PT_SPACE: + case PT_PXSPACE: + if (*cc == PT_SPACE) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, 0); + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 11 - charoffset); + } + SET_CHAR_OFFSET(9); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 13 - 9); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_LESS_EQUAL); + if (*cc == PT_SPACE) + JUMPHERE(jump); + + SET_TYPE_OFFSET(ucp_Zl); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Zs - ucp_Zl); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_LESS_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + + case PT_WORD: + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, CHAR_UNDERSCORE - charoffset); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + /* ... fall through */ + + case PT_ALNUM: + SET_TYPE_OFFSET(ucp_Ll); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_Lu - ucp_Ll); + COND_VALUE((*cc == PT_ALNUM) ? SLJIT_MOV : SLJIT_OR, TMP2, 0, SLJIT_C_LESS_EQUAL); + SET_TYPE_OFFSET(ucp_Nd); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, typereg, 0, SLJIT_IMM, ucp_No - ucp_Nd); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_LESS_EQUAL); + jump = JUMP(SLJIT_C_NOT_ZERO ^ invertcmp); + break; + } + cc += 2; + } +#endif + + if (jump != NULL) + add_jump(compiler, compares > 0 ? list : fallbacks, jump); + } + +if (found != NULL) + set_jumps(found, LABEL()); +} + +#undef SET_TYPE_OFFSET +#undef SET_CHAR_OFFSET + +#endif + +static pcre_uchar *compile_char1_hotpath(compiler_common *common, pcre_uchar type, pcre_uchar *cc, jump_list **fallbacks) +{ +DEFINE_COMPILER; +int length; +unsigned int c, oc, bit; +compare_context context; +struct sljit_jump *jump[4]; +#ifdef SUPPORT_UTF +struct sljit_label *label; +#ifdef SUPPORT_UCP +pcre_uchar propdata[5]; +#endif +#endif + +switch(type) + { + case OP_SOD: + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, TMP1, 0)); + return cc; + + case OP_SOM: + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, TMP1, 0)); + return cc; + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + add_jump(compiler, &common->wordboundary, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, fallbacks, JUMP(type == OP_NOT_WORD_BOUNDARY ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + return cc; + + case OP_NOT_DIGIT: + case OP_DIGIT: + check_input_end(common, fallbacks); + read_char8_type(common); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_digit); + add_jump(compiler, fallbacks, JUMP(type == OP_DIGIT ? SLJIT_C_ZERO : SLJIT_C_NOT_ZERO)); + return cc; + + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + check_input_end(common, fallbacks); + read_char8_type(common); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_space); + add_jump(compiler, fallbacks, JUMP(type == OP_WHITESPACE ? SLJIT_C_ZERO : SLJIT_C_NOT_ZERO)); + return cc; + + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + check_input_end(common, fallbacks); + read_char8_type(common); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, ctype_word); + add_jump(compiler, fallbacks, JUMP(type == OP_WORDCHAR ? SLJIT_C_ZERO : SLJIT_C_NOT_ZERO)); + return cc; + + case OP_ANY: + check_input_end(common, fallbacks); + read_char(common); + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + jump[0] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff); + jump[1] = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, common->newline & 0xff)); + JUMPHERE(jump[1]); + JUMPHERE(jump[0]); + } + else + check_newlinechar(common, common->nltype, fallbacks, TRUE); + return cc; + + case OP_ALLANY: + check_input_end(common, fallbacks); +#ifdef SUPPORT_UTF + if (common->utf) + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); +#ifdef COMPILE_PCRE8 + jump[0] = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +#else /* COMPILE_PCRE8 */ +#ifdef COMPILE_PCRE16 + jump[0] = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xd800); + OP2(SLJIT_AND, TMP1, 0, TMP1, 0, SLJIT_IMM, 0xfc00); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, 0xd800); + COND_VALUE(SLJIT_MOV, TMP1, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SHL, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); +#endif /* COMPILE_PCRE16 */ +#endif /* COMPILE_PCRE8 */ + JUMPHERE(jump[0]); + return cc; + } +#endif + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + return cc; + + case OP_ANYBYTE: + check_input_end(common, fallbacks); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + return cc; + +#ifdef SUPPORT_UTF +#ifdef SUPPORT_UCP + case OP_NOTPROP: + case OP_PROP: + propdata[0] = 0; + propdata[1] = type == OP_NOTPROP ? XCL_NOTPROP : XCL_PROP; + propdata[2] = cc[0]; + propdata[3] = cc[1]; + propdata[4] = XCL_END; + compile_xclass_hotpath(common, propdata, fallbacks); + return cc + 2; +#endif +#endif + + case OP_ANYNL: + check_input_end(common, fallbacks); + read_char(common); + jump[0] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); + jump[1] = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + jump[2] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + jump[3] = JUMP(SLJIT_JUMP); + JUMPHERE(jump[0]); + check_newlinechar(common, common->bsr_nltype, fallbacks, FALSE); + JUMPHERE(jump[1]); + JUMPHERE(jump[2]); + JUMPHERE(jump[3]); + return cc; + + case OP_NOT_HSPACE: + case OP_HSPACE: + check_input_end(common, fallbacks); + read_char(common); + add_jump(compiler, &common->hspace, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, fallbacks, JUMP(type == OP_NOT_HSPACE ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + return cc; + + case OP_NOT_VSPACE: + case OP_VSPACE: + check_input_end(common, fallbacks); + read_char(common); + add_jump(compiler, &common->vspace, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, fallbacks, JUMP(type == OP_NOT_VSPACE ? SLJIT_C_NOT_ZERO : SLJIT_C_ZERO)); + return cc; + +#ifdef SUPPORT_UCP + case OP_EXTUNI: + check_input_end(common, fallbacks); + read_char(common); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Mc); + add_jump(compiler, fallbacks, CMP(SLJIT_C_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_Mn - ucp_Mc)); + + label = LABEL(); + jump[0] = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP3, 0, STR_PTR, 0); + read_char(common); + add_jump(compiler, &common->getucd, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_SUB, TMP1, 0, TMP1, 0, SLJIT_IMM, ucp_Mc); + CMPTO(SLJIT_C_LESS_EQUAL, TMP1, 0, SLJIT_IMM, ucp_Mn - ucp_Mc, label); + + OP1(SLJIT_MOV, STR_PTR, 0, TMP3, 0); + JUMPHERE(jump[0]); + return cc; +#endif + + case OP_EODN: + jump[0] = CMP(SLJIT_C_GREATER_EQUAL, STR_PTR, 0, STR_END, 0); + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_END, 0)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); + } + else if (common->nltype == NLTYPE_FIXED) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_END, 0)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, common->newline)); + } + else + { + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + jump[1] = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_CR); + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP2, 0, STR_END, 0); + jump[2] = JUMP(SLJIT_C_GREATER); + add_jump(compiler, fallbacks, JUMP(SLJIT_C_LESS)); + /* Equal. */ + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + jump[3] = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL); + add_jump(compiler, fallbacks, JUMP(SLJIT_JUMP)); + + JUMPHERE(jump[1]); + if (common->nltype == NLTYPE_ANYCRLF) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_LESS, TMP2, 0, STR_END, 0)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, CHAR_NL)); + } + else + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, STR_PTR, 0); + read_char(common); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); + add_jump(compiler, &common->anynewline, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, fallbacks, JUMP(SLJIT_C_ZERO)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); + } + JUMPHERE(jump[2]); + JUMPHERE(jump[3]); + } + JUMPHERE(jump[0]); + return cc; + + case OP_EOD: + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, STR_END, 0)); + return cc; + + case OP_CIRC: + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_GREATER, STR_PTR, 0, TMP1, 0)); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + return cc; + + case OP_CIRCM: + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, begin)); + jump[1] = CMP(SLJIT_C_GREATER, STR_PTR, 0, TMP1, 0); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, notbol)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + jump[0] = JUMP(SLJIT_JUMP); + JUMPHERE(jump[1]); + + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, STR_PTR, 0, STR_END, 0)); + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + OP2(SLJIT_SUB, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_LESS, TMP2, 0, TMP1, 0)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-2)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(-1)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); + } + else + { + skip_char_back(common); + read_char(common); + check_newlinechar(common, common->nltype, fallbacks, FALSE); + } + JUMPHERE(jump[0]); + return cc; + + case OP_DOLL: + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + + if (!common->endonly) + compile_char1_hotpath(common, OP_EODN, cc, fallbacks); + else + add_jump(compiler, fallbacks, CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0)); + return cc; + + case OP_DOLLM: + jump[1] = CMP(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0); + OP1(SLJIT_MOV, TMP2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(jit_arguments, noteol)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + jump[0] = JUMP(SLJIT_JUMP); + JUMPHERE(jump[1]); + + if (common->nltype == NLTYPE_FIXED && common->newline > 255) + { + OP2(SLJIT_ADD, TMP2, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(2)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_GREATER, TMP2, 0, STR_END, 0)); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(0)); + OP1(MOV_UCHAR, TMP2, 0, SLJIT_MEM1(STR_PTR), IN_UCHARS(1)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, (common->newline >> 8) & 0xff)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, common->newline & 0xff)); + } + else + { + peek_char(common); + check_newlinechar(common, common->nltype, fallbacks, FALSE); + } + JUMPHERE(jump[0]); + return cc; + + case OP_CHAR: + case OP_CHARI: + length = 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(*cc)) length += GET_EXTRALEN(*cc); +#endif + if (type == OP_CHAR || !char_has_othercase(common, cc) || char_get_othercase_bit(common, cc) != 0) + { + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_GREATER, STR_PTR, 0, STR_END, 0)); + + context.length = IN_UCHARS(length); + context.sourcereg = -1; +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + context.ucharptr = 0; +#endif + return byte_sequence_compare(common, type == OP_CHARI, cc, &context, fallbacks); + } + check_input_end(common, fallbacks); + read_char(common); +#ifdef SUPPORT_UTF + if (common->utf) + { + GETCHAR(c, cc); + } + else +#endif + c = *cc; + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, c); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_IMM, char_othercase(common, c)); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + add_jump(compiler, fallbacks, JUMP(SLJIT_C_ZERO)); + return cc + length; + + case OP_NOT: + case OP_NOTI: + check_input_end(common, fallbacks); + length = 1; +#ifdef SUPPORT_UTF + if (common->utf) + { +#ifdef COMPILE_PCRE8 + c = *cc; + if (c < 128) + { + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(STR_PTR), 0); + if (type == OP_NOT || !char_has_othercase(common, cc)) + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c)); + else + { + /* Since UTF8 code page is fixed, we know that c is in [a-z] or [A-Z] range. */ + OP2(SLJIT_OR, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x20); + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, c | 0x20)); + } + /* Skip the variable-length character. */ + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(1)); + jump[0] = CMP(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, 0xc0); + OP1(MOV_UCHAR, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)PRIV(utf8_table4) - 0xc0); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP1, 0); + JUMPHERE(jump[0]); + return cc + 1; + } + else +#endif /* COMPILE_PCRE8 */ + { + GETCHARLEN(c, cc, length); + read_char(common); + } + } + else +#endif /* SUPPORT_UTF */ + { + read_char(common); + c = *cc; + } + + if (type == OP_NOT || !char_has_othercase(common, cc)) + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c)); + else + { + oc = char_othercase(common, c); + bit = c ^ oc; + if (ispowerof2(bit)) + { + OP2(SLJIT_OR, TMP1, 0, TMP1, 0, SLJIT_IMM, bit); + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c | bit)); + } + else + { + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, c)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, oc)); + } + } + return cc + 1; + + case OP_CLASS: + case OP_NCLASS: + check_input_end(common, fallbacks); + read_char(common); +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + jump[0] = NULL; +#ifdef COMPILE_PCRE8 + /* This check only affects 8 bit mode. In other modes, we + always need to compare the value with 255. */ + if (common->utf) +#endif /* COMPILE_PCRE8 */ + { + jump[0] = CMP(SLJIT_C_GREATER, TMP1, 0, SLJIT_IMM, 255); + if (type == OP_CLASS) + { + add_jump(compiler, fallbacks, jump[0]); + jump[0] = NULL; + } + } +#endif /* SUPPORT_UTF || !COMPILE_PCRE8 */ + OP2(SLJIT_AND, TMP2, 0, TMP1, 0, SLJIT_IMM, 0x7); + OP2(SLJIT_LSHR, TMP1, 0, TMP1, 0, SLJIT_IMM, 3); + OP1(SLJIT_MOV_UB, TMP1, 0, SLJIT_MEM1(TMP1), (sljit_w)cc); + OP2(SLJIT_SHL, TMP2, 0, SLJIT_IMM, 1, TMP2, 0); + OP2(SLJIT_AND | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, TMP2, 0); + add_jump(compiler, fallbacks, JUMP(SLJIT_C_ZERO)); +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + if (jump[0] != NULL) + JUMPHERE(jump[0]); +#endif /* SUPPORT_UTF || !COMPILE_PCRE8 */ + return cc + 32 / sizeof(pcre_uchar); + +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 + case OP_XCLASS: + compile_xclass_hotpath(common, cc + LINK_SIZE, fallbacks); + return cc + GET(cc, 0) - 1; +#endif + + case OP_REVERSE: + length = GET(cc, 0); + SLJIT_ASSERT(length > 0); + OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +#ifdef SUPPORT_UTF + if (common->utf) + { + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_IMM, length); + label = LABEL(); + add_jump(compiler, fallbacks, CMP(SLJIT_C_LESS_EQUAL, STR_PTR, 0, TMP3, 0)); + skip_char_back(common); + OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, TMP2, 0, SLJIT_IMM, 1); + JUMPTO(SLJIT_C_NOT_ZERO, label); + return cc + LINK_SIZE; + } +#endif + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, begin)); + OP2(SLJIT_SUB, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(length)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_LESS, STR_PTR, 0, TMP1, 0)); + return cc + LINK_SIZE; + } +SLJIT_ASSERT_STOP(); +return cc; +} + +static SLJIT_INLINE pcre_uchar *compile_charn_hotpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, jump_list **fallbacks) +{ +/* This function consumes at least one input character. */ +/* To decrease the number of length checks, we try to concatenate the fixed length character sequences. */ +DEFINE_COMPILER; +pcre_uchar *ccbegin = cc; +compare_context context; +int size; + +context.length = 0; +do + { + if (cc >= ccend) + break; + + if (*cc == OP_CHAR) + { + size = 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(cc[1])) + size += GET_EXTRALEN(cc[1]); +#endif + } + else if (*cc == OP_CHARI) + { + size = 1; +#ifdef SUPPORT_UTF + if (common->utf) + { + if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0) + size = 0; + else if (HAS_EXTRALEN(cc[1])) + size += GET_EXTRALEN(cc[1]); + } + else +#endif + if (char_has_othercase(common, cc + 1) && char_get_othercase_bit(common, cc + 1) == 0) + size = 0; + } + else + size = 0; + + cc += 1 + size; + context.length += IN_UCHARS(size); + } +while (size > 0 && context.length <= 128); + +cc = ccbegin; +if (context.length > 0) + { + /* We have a fixed-length byte sequence. */ + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, SLJIT_IMM, context.length); + add_jump(compiler, fallbacks, CMP(SLJIT_C_GREATER, STR_PTR, 0, STR_END, 0)); + + context.sourcereg = -1; +#if defined SLJIT_UNALIGNED && SLJIT_UNALIGNED + context.ucharptr = 0; +#endif + do cc = byte_sequence_compare(common, *cc == OP_CHARI, cc + 1, &context, fallbacks); while (context.length > 0); + return cc; + } + +/* A non-fixed length character will be checked if length == 0. */ +return compile_char1_hotpath(common, *cc, cc + 1, fallbacks); +} + +static struct sljit_jump *compile_ref_checks(compiler_common *common, pcre_uchar *cc, jump_list **fallbacks) +{ +DEFINE_COMPILER; +int offset = GET2(cc, 1) << 1; + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); +if (!common->jscript_compat) + { + if (fallbacks == NULL) + { + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_EQUAL); + OP2(SLJIT_SUB | SLJIT_SET_E, SLJIT_UNUSED, 0, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_EQUAL); + return JUMP(SLJIT_C_NOT_ZERO); + } + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1))); + } +return CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); +} + +/* Forward definitions. */ +static void compile_hotpath(compiler_common *, pcre_uchar *, pcre_uchar *, fallback_common *); +static void compile_fallbackpath(compiler_common *, struct fallback_common *); + +#define PUSH_FALLBACK(size, ccstart, error) \ + do \ + { \ + fallback = sljit_alloc_memory(compiler, (size)); \ + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ + return error; \ + memset(fallback, 0, size); \ + fallback->prev = parent->top; \ + fallback->cc = (ccstart); \ + parent->top = fallback; \ + } \ + while (0) + +#define PUSH_FALLBACK_NOVALUE(size, ccstart) \ + do \ + { \ + fallback = sljit_alloc_memory(compiler, (size)); \ + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ + return; \ + memset(fallback, 0, size); \ + fallback->prev = parent->top; \ + fallback->cc = (ccstart); \ + parent->top = fallback; \ + } \ + while (0) + +#define FALLBACK_AS(type) ((type*)fallback) + +static pcre_uchar *compile_ref_hotpath(compiler_common *common, pcre_uchar *cc, jump_list **fallbacks, BOOL withchecks, BOOL emptyfail) +{ +DEFINE_COMPILER; +int offset = GET2(cc, 1) << 1; +struct sljit_jump *jump = NULL; + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); +if (withchecks && !common->jscript_compat) + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1))); + +#if defined SUPPORT_UTF && defined SUPPORT_UCP +if (common->utf && *cc == OP_REFI) + { + SLJIT_ASSERT(TMP1 == SLJIT_TEMPORARY_REG1 && STACK_TOP == SLJIT_TEMPORARY_REG2 && TMP2 == SLJIT_TEMPORARY_REG3); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + if (withchecks) + jump = CMP(SLJIT_C_EQUAL, TMP1, 0, TMP2, 0); + + /* Needed to save important temporary registers. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG2, 0, ARGUMENTS, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_TEMPORARY_REG2), SLJIT_OFFSETOF(jit_arguments, ptr), STR_PTR, 0); + sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_utf_caselesscmp)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + add_jump(compiler, fallbacks, CMP(SLJIT_C_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_RETURN_REG, 0); + } +else +#endif /* SUPPORT_UTF && SUPPORT_UCP */ + { + OP2(SLJIT_SUB | SLJIT_SET_E, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP1, 0); + if (withchecks) + jump = JUMP(SLJIT_C_ZERO); + OP2(SLJIT_ADD, STR_PTR, 0, STR_PTR, 0, TMP2, 0); + + add_jump(compiler, fallbacks, CMP(SLJIT_C_GREATER, STR_PTR, 0, STR_END, 0)); + add_jump(compiler, *cc == OP_REF ? &common->casefulcmp : &common->caselesscmp, JUMP(SLJIT_FAST_CALL)); + add_jump(compiler, fallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); + } + +if (jump != NULL) + { + if (emptyfail) + add_jump(compiler, fallbacks, jump); + else + JUMPHERE(jump); + } +return cc + 1 + IMM2_SIZE; +} + +static SLJIT_INLINE pcre_uchar *compile_ref_iterator_hotpath(compiler_common *common, pcre_uchar *cc, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; +pcre_uchar type; +struct sljit_label *label; +struct sljit_jump *zerolength; +struct sljit_jump *jump = NULL; +pcre_uchar *ccbegin = cc; +int min = 0, max = 0; +BOOL minimize; + +PUSH_FALLBACK(sizeof(iterator_fallback), cc, NULL); + +type = cc[1 + IMM2_SIZE]; +minimize = (type & 0x1) != 0; +switch(type) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + min = 0; + max = 0; + cc += 1 + IMM2_SIZE + 1; + break; + case OP_CRPLUS: + case OP_CRMINPLUS: + min = 1; + max = 0; + cc += 1 + IMM2_SIZE + 1; + break; + case OP_CRQUERY: + case OP_CRMINQUERY: + min = 0; + max = 1; + cc += 1 + IMM2_SIZE + 1; + break; + case OP_CRRANGE: + case OP_CRMINRANGE: + min = GET2(cc, 1 + IMM2_SIZE + 1); + max = GET2(cc, 1 + IMM2_SIZE + 1 + IMM2_SIZE); + cc += 1 + IMM2_SIZE + 1 + 2 * IMM2_SIZE; + break; + default: + SLJIT_ASSERT_STOP(); + break; + } + +if (!minimize) + { + if (min == 0) + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); + /* Temporary release of STR_PTR. */ + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + zerolength = compile_ref_checks(common, ccbegin, NULL); + /* Restore if not zero length. */ + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + } + else + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + zerolength = compile_ref_checks(common, ccbegin, &fallback->topfallbacks); + } + + if (min > 1 || max > 1) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 0); + + label = LABEL(); + compile_ref_hotpath(common, ccbegin, &fallback->topfallbacks, FALSE, FALSE); + + if (min > 1 || max > 1) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, TMP1, 0); + if (min > 1) + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, min, label); + if (max > 1) + { + jump = CMP(SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, max); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, label); + JUMPHERE(jump); + } + } + + if (max == 0) + { + /* Includes min > 1 case as well. */ + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, label); + } + + JUMPHERE(zerolength); + FALLBACK_AS(iterator_fallback)->hotpath = LABEL(); + + decrease_call_count(common); + return cc; + } + +allocate_stack(common, 2); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); +if (type != OP_CRMINSTAR) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); + +if (min == 0) + { + zerolength = compile_ref_checks(common, ccbegin, NULL); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + jump = JUMP(SLJIT_JUMP); + } +else + zerolength = compile_ref_checks(common, ccbegin, &fallback->topfallbacks); + +FALLBACK_AS(iterator_fallback)->hotpath = LABEL(); +if (max > 0) + add_jump(compiler, &fallback->topfallbacks, CMP(SLJIT_C_GREATER_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, max)); + +compile_ref_hotpath(common, ccbegin, &fallback->topfallbacks, TRUE, TRUE); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + +if (min > 1) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, min, FALLBACK_AS(iterator_fallback)->hotpath); + } +else if (max > 0) + OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); + +if (jump != NULL) + JUMPHERE(jump); +JUMPHERE(zerolength); + +decrease_call_count(common); +return cc; +} + +static SLJIT_INLINE pcre_uchar *compile_recurse_hotpath(compiler_common *common, pcre_uchar *cc, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; +recurse_entry *entry = common->entries; +recurse_entry *prev = NULL; +int start = GET(cc, 1); + +PUSH_FALLBACK(sizeof(recurse_fallback), cc, NULL); +while (entry != NULL) + { + if (entry->start == start) + break; + prev = entry; + entry = entry->next; + } + +if (entry == NULL) + { + entry = sljit_alloc_memory(compiler, sizeof(recurse_entry)); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + entry->next = NULL; + entry->entry = NULL; + entry->calls = NULL; + entry->start = start; + + if (prev != NULL) + prev->next = entry; + else + common->entries = entry; + } + +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); +allocate_stack(common, 1); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + +if (entry->entry == NULL) + add_jump(compiler, &entry->calls, JUMP(SLJIT_FAST_CALL)); +else + JUMPTO(SLJIT_FAST_CALL, entry->entry); +/* Leave if the match is failed. */ +add_jump(compiler, &fallback->topfallbacks, CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0)); +return cc + 1 + LINK_SIZE; +} + +static pcre_uchar *compile_assert_hotpath(compiler_common *common, pcre_uchar *cc, assert_fallback *fallback, BOOL conditional) +{ +DEFINE_COMPILER; +int framesize; +int localptr; +fallback_common altfallback; +pcre_uchar *ccbegin; +pcre_uchar opcode; +pcre_uchar bra = OP_BRA; +jump_list *tmp = NULL; +jump_list **target = (conditional) ? &fallback->condfailed : &fallback->common.topfallbacks; +jump_list **found; +/* Saving previous accept variables. */ +struct sljit_label *save_acceptlabel = common->acceptlabel; +struct sljit_jump *jump; +struct sljit_jump *brajump = NULL; +jump_list *save_accept = common->accept; + +if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) + { + SLJIT_ASSERT(!conditional); + bra = *cc; + cc++; + } +localptr = PRIV_DATA(cc); +SLJIT_ASSERT(localptr != 0); +framesize = get_framesize(common, cc, FALSE); +fallback->framesize = framesize; +fallback->localptr = localptr; +opcode = *cc; +SLJIT_ASSERT(opcode >= OP_ASSERT && opcode <= OP_ASSERTBACK_NOT); +found = (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) ? &tmp : target; +ccbegin = cc; +cc += GET(cc, 1); + +if (bra == OP_BRAMINZERO) + { + /* This is a braminzero fallback path. */ + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + brajump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + } + +if (framesize < 0) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STACK_TOP, 0); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } +else + { + allocate_stack(common, framesize + 2); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, -STACK(framesize + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + init_frame(common, ccbegin, framesize + 1, 2, FALSE); + } + +memset(&altfallback, 0, sizeof(fallback_common)); +while (1) + { + common->acceptlabel = NULL; + common->accept = NULL; + altfallback.top = NULL; + altfallback.topfallbacks = NULL; + + if (*ccbegin == OP_ALT) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + altfallback.cc = ccbegin; + compile_hotpath(common, ccbegin + 1 + LINK_SIZE, cc, &altfallback); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + common->acceptlabel = save_acceptlabel; + common->accept = save_accept; + return NULL; + } + common->acceptlabel = LABEL(); + if (common->accept != NULL) + set_jumps(common->accept, common->acceptlabel); + + /* Reset stack. */ + if (framesize < 0) + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + else { + if ((opcode != OP_ASSERT_NOT && opcode != OP_ASSERTBACK_NOT) || conditional) + { + /* We don't need to keep the STR_PTR, only the previous localptr. */ + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_w)); + } + else + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + } + } + + if (opcode == OP_ASSERT_NOT || opcode == OP_ASSERTBACK_NOT) + { + /* We know that STR_PTR was stored on the top of the stack. */ + if (conditional) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), 0); + else if (bra == OP_BRAZERO) + { + if (framesize < 0) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), 0); + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_w)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), (framesize + 1) * sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP1, 0); + } + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else if (framesize >= 0) + { + /* For OP_BRA and OP_BRAMINZERO. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_w)); + } + } + add_jump(compiler, found, JUMP(SLJIT_JUMP)); + + compile_fallbackpath(common, altfallback.top); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + common->acceptlabel = save_acceptlabel; + common->accept = save_accept; + return NULL; + } + set_jumps(altfallback.topfallbacks, LABEL()); + + if (*cc != OP_ALT) + break; + + ccbegin = cc; + cc += GET(cc, 1); + } +/* None of them matched. */ + +if (opcode == OP_ASSERT || opcode == OP_ASSERTBACK) + { + /* Assert is failed. */ + if (conditional || bra == OP_BRAZERO) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + if (framesize < 0) + { + /* The topmost item should be 0. */ + if (bra == OP_BRAZERO) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + else + free_stack(common, 1); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + /* The topmost item should be 0. */ + if (bra == OP_BRAZERO) + { + free_stack(common, framesize + 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + free_stack(common, framesize + 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP1, 0); + } + jump = JUMP(SLJIT_JUMP); + if (bra != OP_BRAZERO) + add_jump(compiler, target, jump); + + /* Assert is successful. */ + set_jumps(tmp, LABEL()); + if (framesize < 0) + { + /* We know that STR_PTR was stored on the top of the stack. */ + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), 0); + /* Keep the STR_PTR on the top of the stack. */ + if (bra == OP_BRAZERO) + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + else if (bra == OP_BRAMINZERO) + { + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + } + else + { + if (bra == OP_BRA) + { + /* We don't need to keep the STR_PTR, only the previous localptr. */ + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_IMM, (framesize + 1) * sizeof(sljit_w)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), 0); + } + else + { + /* We don't need to keep the STR_PTR, only the previous localptr. */ + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_IMM, (framesize + 2) * sizeof(sljit_w)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), bra == OP_BRAZERO ? STR_PTR : SLJIT_IMM, 0); + } + } + + if (bra == OP_BRAZERO) + { + fallback->hotpath = LABEL(); + sljit_set_label(jump, fallback->hotpath); + } + else if (bra == OP_BRAMINZERO) + { + JUMPTO(SLJIT_JUMP, fallback->hotpath); + JUMPHERE(brajump); + if (framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), framesize * sizeof(sljit_w)); + } + set_jumps(fallback->common.topfallbacks, LABEL()); + } + } +else + { + /* AssertNot is successful. */ + if (framesize < 0) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (bra != OP_BRA) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + else + free_stack(common, 1); + } + else + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + /* The topmost item should be 0. */ + if (bra != OP_BRA) + { + free_stack(common, framesize + 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + else + free_stack(common, framesize + 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP1, 0); + } + + if (bra == OP_BRAZERO) + fallback->hotpath = LABEL(); + else if (bra == OP_BRAMINZERO) + { + JUMPTO(SLJIT_JUMP, fallback->hotpath); + JUMPHERE(brajump); + } + + if (bra != OP_BRA) + { + SLJIT_ASSERT(found == &fallback->common.topfallbacks); + set_jumps(fallback->common.topfallbacks, LABEL()); + fallback->common.topfallbacks = NULL; + } + } + +common->acceptlabel = save_acceptlabel; +common->accept = save_accept; +return cc + 1 + LINK_SIZE; +} + +static sljit_w SLJIT_CALL do_searchovector(sljit_w refno, sljit_w* locals, pcre_uchar *name_table) +{ +int condition = FALSE; +pcre_uchar *slotA = name_table; +pcre_uchar *slotB; +sljit_w name_count = locals[LOCALS0 / sizeof(sljit_w)]; +sljit_w name_entry_size = locals[LOCALS1 / sizeof(sljit_w)]; +sljit_w no_capture; +int i; + +locals += OVECTOR_START / sizeof(sljit_w); +no_capture = locals[1]; + +for (i = 0; i < name_count; i++) + { + if (GET2(slotA, 0) == refno) break; + slotA += name_entry_size; + } + +if (i < name_count) + { + /* Found a name for the number - there can be only one; duplicate names + for different numbers are allowed, but not vice versa. First scan down + for duplicates. */ + + slotB = slotA; + while (slotB > name_table) + { + slotB -= name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = locals[GET2(slotB, 0) << 1] != no_capture; + if (condition) break; + } + else break; + } + + /* Scan up for duplicates */ + if (!condition) + { + slotB = slotA; + for (i++; i < name_count; i++) + { + slotB += name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = locals[GET2(slotB, 0) << 1] != no_capture; + if (condition) break; + } + else break; + } + } + } +return condition; +} + +static sljit_w SLJIT_CALL do_searchgroups(sljit_w recno, sljit_w* locals, pcre_uchar *name_table) +{ +int condition = FALSE; +pcre_uchar *slotA = name_table; +pcre_uchar *slotB; +sljit_w name_count = locals[LOCALS0 / sizeof(sljit_w)]; +sljit_w name_entry_size = locals[LOCALS1 / sizeof(sljit_w)]; +sljit_w group_num = locals[POSSESSIVE0 / sizeof(sljit_w)]; +int i; + +for (i = 0; i < name_count; i++) + { + if (GET2(slotA, 0) == recno) break; + slotA += name_entry_size; + } + +if (i < name_count) + { + /* Found a name for the number - there can be only one; duplicate + names for different numbers are allowed, but not vice versa. First + scan down for duplicates. */ + + slotB = slotA; + while (slotB > name_table) + { + slotB -= name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == group_num; + if (condition) break; + } + else break; + } + + /* Scan up for duplicates */ + if (!condition) + { + slotB = slotA; + for (i++; i < name_count; i++) + { + slotB += name_entry_size; + if (STRCMP_UC_UC(slotA + IMM2_SIZE, slotB + IMM2_SIZE) == 0) + { + condition = GET2(slotB, 0) == group_num; + if (condition) break; + } + else break; + } + } + } +return condition; +} + +/* + Handling bracketed expressions is probably the most complex part. + + Stack layout naming characters: + S - Push the current STR_PTR + 0 - Push a 0 (NULL) + A - Push the current STR_PTR. Needed for restoring the STR_PTR + before the next alternative. Not pushed if there are no alternatives. + M - Any values pushed by the current alternative. Can be empty, or anything. + C - Push the previous OVECTOR(i), OVECTOR(i+1) and OVECTOR_PRIV(i) to the stack. + L - Push the previous local (pointed by localptr) to the stack + () - opional values stored on the stack + ()* - optonal, can be stored multiple times + + The following list shows the regular expression templates, their PCRE byte codes + and stack layout supported by pcre-sljit. + + (?:) OP_BRA | OP_KET A M + () OP_CBRA | OP_KET C M + (?:)+ OP_BRA | OP_KETRMAX 0 A M S ( A M S )* + OP_SBRA | OP_KETRMAX 0 L M S ( L M S )* + (?:)+? OP_BRA | OP_KETRMIN 0 A M S ( A M S )* + OP_SBRA | OP_KETRMIN 0 L M S ( L M S )* + ()+ OP_CBRA | OP_KETRMAX 0 C M S ( C M S )* + OP_SCBRA | OP_KETRMAX 0 C M S ( C M S )* + ()+? OP_CBRA | OP_KETRMIN 0 C M S ( C M S )* + OP_SCBRA | OP_KETRMIN 0 C M S ( C M S )* + (?:)? OP_BRAZERO | OP_BRA | OP_KET S ( A M 0 ) + (?:)?? OP_BRAMINZERO | OP_BRA | OP_KET S ( A M 0 ) + ()? OP_BRAZERO | OP_CBRA | OP_KET S ( C M 0 ) + ()?? OP_BRAMINZERO | OP_CBRA | OP_KET S ( C M 0 ) + (?:)* OP_BRAZERO | OP_BRA | OP_KETRMAX S 0 ( A M S )* + OP_BRAZERO | OP_SBRA | OP_KETRMAX S 0 ( L M S )* + (?:)*? OP_BRAMINZERO | OP_BRA | OP_KETRMIN S 0 ( A M S )* + OP_BRAMINZERO | OP_SBRA | OP_KETRMIN S 0 ( L M S )* + ()* OP_BRAZERO | OP_CBRA | OP_KETRMAX S 0 ( C M S )* + OP_BRAZERO | OP_SCBRA | OP_KETRMAX S 0 ( C M S )* + ()*? OP_BRAMINZERO | OP_CBRA | OP_KETRMIN S 0 ( C M S )* + OP_BRAMINZERO | OP_SCBRA | OP_KETRMIN S 0 ( C M S )* + + + Stack layout naming characters: + A - Push the alternative index (starting from 0) on the stack. + Not pushed if there is no alternatives. + M - Any values pushed by the current alternative. Can be empty, or anything. + + The next list shows the possible content of a bracket: + (|) OP_*BRA | OP_ALT ... M A + (?()|) OP_*COND | OP_ALT M A + (?>|) OP_ONCE | OP_ALT ... [stack trace] M A + (?>|) OP_ONCE_NC | OP_ALT ... [stack trace] M A + Or nothing, if trace is unnecessary +*/ + +static pcre_uchar *compile_bracket_hotpath(compiler_common *common, pcre_uchar *cc, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; +pcre_uchar opcode; +int localptr = 0; +int offset = 0; +int stacksize; +pcre_uchar *ccbegin; +pcre_uchar *hotpath; +pcre_uchar bra = OP_BRA; +pcre_uchar ket; +assert_fallback *assert; +BOOL has_alternatives; +struct sljit_jump *jump; +struct sljit_jump *skip; +struct sljit_label *rmaxlabel = NULL; +struct sljit_jump *braminzerojump = NULL; + +PUSH_FALLBACK(sizeof(bracket_fallback), cc, NULL); + +if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) + { + bra = *cc; + cc++; + opcode = *cc; + } + +opcode = *cc; +ccbegin = cc; +hotpath = ccbegin + 1 + LINK_SIZE; + +if ((opcode == OP_COND || opcode == OP_SCOND) && cc[1 + LINK_SIZE] == OP_DEF) + { + /* Drop this bracket_fallback. */ + parent->top = fallback->prev; + return bracketend(cc); + } + +ket = *(bracketend(cc) - 1 - LINK_SIZE); +SLJIT_ASSERT(ket == OP_KET || ket == OP_KETRMAX || ket == OP_KETRMIN); +SLJIT_ASSERT(!((bra == OP_BRAZERO && ket == OP_KETRMIN) || (bra == OP_BRAMINZERO && ket == OP_KETRMAX))); +cc += GET(cc, 1); + +has_alternatives = *cc == OP_ALT; +if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + { + has_alternatives = (*hotpath == OP_RREF) ? FALSE : TRUE; + if (*hotpath == OP_NRREF) + { + stacksize = GET2(hotpath, 1); + if (common->currententry == NULL || stacksize == RREF_ANY) + has_alternatives = FALSE; + else if (common->currententry->start == 0) + has_alternatives = stacksize != 0; + else + has_alternatives = stacksize != GET2(common->start, common->currententry->start + 1 + LINK_SIZE); + } + } + +if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) + opcode = OP_SCOND; +if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) + opcode = OP_ONCE; + +if (opcode == OP_CBRA || opcode == OP_SCBRA) + { + /* Capturing brackets has a pre-allocated space. */ + offset = GET2(ccbegin, 1 + LINK_SIZE); + localptr = OVECTOR_PRIV(offset); + offset <<= 1; + FALLBACK_AS(bracket_fallback)->localptr = localptr; + hotpath += IMM2_SIZE; + } +else if (opcode == OP_ONCE || opcode == OP_SBRA || opcode == OP_SCOND) + { + /* Other brackets simply allocate the next entry. */ + localptr = PRIV_DATA(ccbegin); + SLJIT_ASSERT(localptr != 0); + FALLBACK_AS(bracket_fallback)->localptr = localptr; + if (opcode == OP_ONCE) + FALLBACK_AS(bracket_fallback)->u.framesize = get_framesize(common, ccbegin, FALSE); + } + +/* Instructions before the first alternative. */ +stacksize = 0; +if ((ket == OP_KETRMAX) || (ket == OP_KETRMIN && bra != OP_BRAMINZERO)) + stacksize++; +if (bra == OP_BRAZERO) + stacksize++; + +if (stacksize > 0) + allocate_stack(common, stacksize); + +stacksize = 0; +if ((ket == OP_KETRMAX) || (ket == OP_KETRMIN && bra != OP_BRAMINZERO)) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + stacksize++; + } + +if (bra == OP_BRAZERO) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + +if (bra == OP_BRAMINZERO) + { + /* This is a fallback path! (Since the hot-path of OP_BRAMINZERO matches to the empty string) */ + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (ket != OP_KETRMIN) + { + free_stack(common, 1); + braminzerojump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + } + else + { + if (opcode == OP_ONCE || opcode >= OP_SBRA) + { + jump = CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + /* Nothing stored during the first run. */ + skip = JUMP(SLJIT_JUMP); + JUMPHERE(jump); + /* Checking zero-length iteration. */ + if (opcode != OP_ONCE || FALLBACK_AS(bracket_fallback)->u.framesize < 0) + { + /* When we come from outside, localptr contains the previous STR_PTR. */ + braminzerojump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + } + else + { + /* Except when the whole stack frame must be saved. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + braminzerojump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (FALLBACK_AS(bracket_fallback)->u.framesize + 1) * sizeof(sljit_w)); + } + JUMPHERE(skip); + } + else + { + jump = CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + JUMPHERE(jump); + } + } + } + +if (ket == OP_KETRMIN) + FALLBACK_AS(bracket_fallback)->recursivehotpath = LABEL(); + +if (ket == OP_KETRMAX) + { + rmaxlabel = LABEL(); + if (has_alternatives && opcode != OP_ONCE && opcode < OP_SBRA) + FALLBACK_AS(bracket_fallback)->althotpath = rmaxlabel; + } + +/* Handling capturing brackets and alternatives. */ +if (opcode == OP_ONCE) + { + if (FALLBACK_AS(bracket_fallback)->u.framesize < 0) + { + /* Neither capturing brackets nor recursions are not found in the block. */ + if (ket == OP_KETRMIN) + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); + OP2(SLJIT_SUB, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + } + else if (ket == OP_KETRMAX || has_alternatives) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STACK_TOP, 0); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + else + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STACK_TOP, 0); + } + else + { + if (ket == OP_KETRMIN || ket == OP_KETRMAX || has_alternatives) + { + allocate_stack(common, FALLBACK_AS(bracket_fallback)->u.framesize + 2); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, -STACK(FALLBACK_AS(bracket_fallback)->u.framesize + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + init_frame(common, ccbegin, FALLBACK_AS(bracket_fallback)->u.framesize + 1, 2, FALSE); + } + else + { + allocate_stack(common, FALLBACK_AS(bracket_fallback)->u.framesize + 1); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, -STACK(FALLBACK_AS(bracket_fallback)->u.framesize)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + init_frame(common, ccbegin, FALLBACK_AS(bracket_fallback)->u.framesize, 1, FALSE); + } + } + } +else if (opcode == OP_CBRA || opcode == OP_SCBRA) + { + /* Saving the previous values. */ + allocate_stack(common, 3); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(2), TMP1, 0); + } +else if (opcode == OP_SBRA || opcode == OP_SCOND) + { + /* Saving the previous value. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP2, 0); + } +else if (has_alternatives) + { + /* Pushing the starting string pointer. */ + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + +/* Generating code for the first alternative. */ +if (opcode == OP_COND || opcode == OP_SCOND) + { + if (*hotpath == OP_CREF) + { + SLJIT_ASSERT(has_alternatives); + add_jump(compiler, &(FALLBACK_AS(bracket_fallback)->u.condfailed), + CMP(SLJIT_C_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(GET2(hotpath, 1) << 1), SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1))); + hotpath += 1 + IMM2_SIZE; + } + else if (*hotpath == OP_NCREF) + { + SLJIT_ASSERT(has_alternatives); + stacksize = GET2(hotpath, 1); + jump = CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(stacksize << 1), SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); + + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, SLJIT_IMM, common->name_count); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, SLJIT_IMM, common->name_entry_size); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG1, 0, SLJIT_IMM, stacksize); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG2, 0, SLJIT_LOCALS_REG, 0); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG3, 0, SLJIT_IMM, common->name_table); + sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_searchovector)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1); + add_jump(compiler, &(FALLBACK_AS(bracket_fallback)->u.condfailed), CMP(SLJIT_C_EQUAL, SLJIT_TEMPORARY_REG1, 0, SLJIT_IMM, 0)); + + JUMPHERE(jump); + hotpath += 1 + IMM2_SIZE; + } + else if (*hotpath == OP_RREF || *hotpath == OP_NRREF) + { + /* Never has other case. */ + FALLBACK_AS(bracket_fallback)->u.condfailed = NULL; + + stacksize = GET2(hotpath, 1); + if (common->currententry == NULL) + stacksize = 0; + else if (stacksize == RREF_ANY) + stacksize = 1; + else if (common->currententry->start == 0) + stacksize = stacksize == 0; + else + stacksize = stacksize == GET2(common->start, common->currententry->start + 1 + LINK_SIZE); + + if (*hotpath == OP_RREF || stacksize || common->currententry == NULL) + { + SLJIT_ASSERT(!has_alternatives); + if (stacksize != 0) + hotpath += 1 + IMM2_SIZE; + else + { + if (*cc == OP_ALT) + { + hotpath = cc + 1 + LINK_SIZE; + cc += GET(cc, 1); + } + else + hotpath = cc; + } + } + else + { + SLJIT_ASSERT(has_alternatives); + + stacksize = GET2(hotpath, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STACK_TOP, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, SLJIT_IMM, common->name_count); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, SLJIT_IMM, common->name_entry_size); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, GET2(common->start, common->currententry->start + 1 + LINK_SIZE)); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG1, 0, SLJIT_IMM, stacksize); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG2, 0, SLJIT_LOCALS_REG, 0); + OP1(SLJIT_MOV, SLJIT_TEMPORARY_REG3, 0, SLJIT_IMM, common->name_table); + sljit_emit_ijump(compiler, SLJIT_CALL3, SLJIT_IMM, SLJIT_FUNC_OFFSET(do_searchgroups)); + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1); + add_jump(compiler, &(FALLBACK_AS(bracket_fallback)->u.condfailed), CMP(SLJIT_C_EQUAL, SLJIT_TEMPORARY_REG1, 0, SLJIT_IMM, 0)); + hotpath += 1 + IMM2_SIZE; + } + } + else + { + SLJIT_ASSERT(has_alternatives && *hotpath >= OP_ASSERT && *hotpath <= OP_ASSERTBACK_NOT); + /* Similar code as PUSH_FALLBACK macro. */ + assert = sljit_alloc_memory(compiler, sizeof(assert_fallback)); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + memset(assert, 0, sizeof(assert_fallback)); + assert->common.cc = hotpath; + FALLBACK_AS(bracket_fallback)->u.assert = assert; + hotpath = compile_assert_hotpath(common, hotpath, assert, TRUE); + } + } + +compile_hotpath(common, hotpath, cc, fallback); +if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + +if (opcode == OP_ONCE) + { + if (FALLBACK_AS(bracket_fallback)->u.framesize < 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + /* TMP2 which is set here used by OP_KETRMAX below. */ + if (ket == OP_KETRMAX) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0); + else if (ket == OP_KETRMIN) + { + /* Move the STR_PTR to the localptr. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), 0); + } + } + else + { + stacksize = (ket == OP_KETRMIN || ket == OP_KETRMAX || has_alternatives) ? 2 : 1; + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_IMM, (FALLBACK_AS(bracket_fallback)->u.framesize + stacksize) * sizeof(sljit_w)); + if (ket == OP_KETRMAX) + { + /* TMP2 which is set here used by OP_KETRMAX below. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + } + } + +stacksize = 0; +if (ket != OP_KET || bra != OP_BRA) + stacksize++; +if (has_alternatives && opcode != OP_ONCE) + stacksize++; + +if (stacksize > 0) + allocate_stack(common, stacksize); + +stacksize = 0; +if (ket != OP_KET) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + stacksize++; + } +else if (bra != OP_BRA) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + stacksize++; + } + +if (has_alternatives) + { + if (opcode != OP_ONCE) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + if (ket != OP_KETRMAX) + FALLBACK_AS(bracket_fallback)->althotpath = LABEL(); + } + +/* Must be after the hotpath label. */ +if (offset != 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 0), TMP1, 0); + } + +if (ket == OP_KETRMAX) + { + if (opcode == OP_ONCE || opcode >= OP_SBRA) + { + if (has_alternatives) + FALLBACK_AS(bracket_fallback)->althotpath = LABEL(); + /* Checking zero-length iteration. */ + if (opcode != OP_ONCE) + CMPTO(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STR_PTR, 0, rmaxlabel); + else + /* TMP2 must contain the starting STR_PTR. */ + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0, rmaxlabel); + } + else + JUMPTO(SLJIT_JUMP, rmaxlabel); + FALLBACK_AS(bracket_fallback)->recursivehotpath = LABEL(); + } + +if (bra == OP_BRAZERO) + FALLBACK_AS(bracket_fallback)->zerohotpath = LABEL(); + +if (bra == OP_BRAMINZERO) + { + /* This is a fallback path! (From the viewpoint of OP_BRAMINZERO) */ + JUMPTO(SLJIT_JUMP, ((braminzero_fallback*)parent)->hotpath); + if (braminzerojump != NULL) + { + JUMPHERE(braminzerojump); + /* We need to release the end pointer to perform the + fallback for the zero-length iteration. When + framesize is < 0, OP_ONCE will do the release itself. */ + if (opcode == OP_ONCE && FALLBACK_AS(bracket_fallback)->u.framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + } + else if (ket == OP_KETRMIN && opcode != OP_ONCE) + free_stack(common, 1); + } + /* Continue to the normal fallback. */ + } + +if ((ket != OP_KET && bra != OP_BRAMINZERO) || bra == OP_BRAZERO) + decrease_call_count(common); + +/* Skip the other alternatives. */ +while (*cc == OP_ALT) + cc += GET(cc, 1); +cc += 1 + LINK_SIZE; +return cc; +} + +static pcre_uchar *compile_bracketpos_hotpath(compiler_common *common, pcre_uchar *cc, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; +pcre_uchar opcode; +int localptr; +int cbraprivptr = 0; +int framesize; +int stacksize; +int offset = 0; +BOOL zero = FALSE; +pcre_uchar *ccbegin = NULL; +int stack; +struct sljit_label *loop = NULL; +struct jump_list *emptymatch = NULL; + +PUSH_FALLBACK(sizeof(bracketpos_fallback), cc, NULL); +if (*cc == OP_BRAPOSZERO) + { + zero = TRUE; + cc++; + } + +opcode = *cc; +localptr = PRIV_DATA(cc); +SLJIT_ASSERT(localptr != 0); +FALLBACK_AS(bracketpos_fallback)->localptr = localptr; +switch(opcode) + { + case OP_BRAPOS: + case OP_SBRAPOS: + ccbegin = cc + 1 + LINK_SIZE; + break; + + case OP_CBRAPOS: + case OP_SCBRAPOS: + offset = GET2(cc, 1 + LINK_SIZE); + cbraprivptr = OVECTOR_PRIV(offset); + offset <<= 1; + ccbegin = cc + 1 + LINK_SIZE + IMM2_SIZE; + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + +framesize = get_framesize(common, cc, FALSE); +FALLBACK_AS(bracketpos_fallback)->framesize = framesize; +if (framesize < 0) + { + stacksize = (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) ? 2 : 1; + if (!zero) + stacksize++; + FALLBACK_AS(bracketpos_fallback)->stacksize = stacksize; + allocate_stack(common, stacksize); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, STACK_TOP, 0); + + if (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP2, 0); + } + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + + if (!zero) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 1); + } +else + { + stacksize = framesize + 1; + if (!zero) + stacksize++; + if (opcode == OP_BRAPOS || opcode == OP_SBRAPOS) + stacksize++; + FALLBACK_AS(bracketpos_fallback)->stacksize = stacksize; + allocate_stack(common, stacksize); + + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP2(SLJIT_SUB, TMP2, 0, STACK_TOP, 0, SLJIT_IMM, -STACK(stacksize - 1)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP2, 0); + stack = 0; + if (!zero) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 1); + stack++; + } + if (opcode == OP_BRAPOS || opcode == OP_SBRAPOS) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), STR_PTR, 0); + stack++; + } + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stack), TMP1, 0); + init_frame(common, cc, stacksize - 1, stacksize - framesize, FALSE); + } + +if (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr, STR_PTR, 0); + +loop = LABEL(); +while (*cc != OP_KETRPOS) + { + fallback->top = NULL; + fallback->topfallbacks = NULL; + cc += GET(cc, 1); + + compile_hotpath(common, ccbegin, cc, fallback); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + + if (framesize < 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + + if (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + } + else + { + if (opcode == OP_SBRAPOS) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + + if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) + add_jump(compiler, &emptymatch, CMP(SLJIT_C_EQUAL, TMP1, 0, STR_PTR, 0)); + + if (!zero) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0); + } + else + { + if (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) + { + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_IMM, stacksize * sizeof(sljit_w)); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr, STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + } + else + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP2(SLJIT_ADD, STACK_TOP, 0, TMP2, 0, SLJIT_IMM, stacksize * sizeof(sljit_w)); + if (opcode == OP_SBRAPOS) + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_w), STR_PTR, 0); + } + + if (opcode == OP_SBRAPOS || opcode == OP_SCBRAPOS) + add_jump(compiler, &emptymatch, CMP(SLJIT_C_EQUAL, TMP1, 0, STR_PTR, 0)); + + if (!zero) + { + if (framesize < 0) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0); + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + } + JUMPTO(SLJIT_JUMP, loop); + flush_stubs(common); + + compile_fallbackpath(common, fallback->top); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return NULL; + set_jumps(fallback->topfallbacks, LABEL()); + + if (framesize < 0) + { + if (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + else + { + if (opcode == OP_CBRAPOS || opcode == OP_SCBRAPOS) + { + /* Last alternative. */ + if (*cc == OP_KETRPOS) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), cbraprivptr); + } + else + { + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP2), (framesize + 1) * sizeof(sljit_w)); + } + } + + if (*cc == OP_KETRPOS) + break; + ccbegin = cc + 1 + LINK_SIZE; + } + +fallback->topfallbacks = NULL; +if (!zero) + { + if (framesize < 0) + add_jump(compiler, &fallback->topfallbacks, CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(stacksize - 1), SLJIT_IMM, 0)); + else /* TMP2 is set to [localptr] above. */ + add_jump(compiler, &fallback->topfallbacks, CMP(SLJIT_C_NOT_EQUAL, SLJIT_MEM1(TMP2), (stacksize - 1) * sizeof(sljit_w), SLJIT_IMM, 0)); + } + +/* None of them matched. */ +set_jumps(emptymatch, LABEL()); +decrease_call_count(common); +return cc + 1 + LINK_SIZE; +} + +static SLJIT_INLINE pcre_uchar *get_iterator_parameters(compiler_common *common, pcre_uchar *cc, pcre_uchar *opcode, pcre_uchar *type, int *arg1, int *arg2, pcre_uchar **end) +{ +int class_len; + +*opcode = *cc; +if (*opcode >= OP_STAR && *opcode <= OP_POSUPTO) + { + cc++; + *type = OP_CHAR; + } +else if (*opcode >= OP_STARI && *opcode <= OP_POSUPTOI) + { + cc++; + *type = OP_CHARI; + *opcode -= OP_STARI - OP_STAR; + } +else if (*opcode >= OP_NOTSTAR && *opcode <= OP_NOTPOSUPTO) + { + cc++; + *type = OP_NOT; + *opcode -= OP_NOTSTAR - OP_STAR; + } +else if (*opcode >= OP_NOTSTARI && *opcode <= OP_NOTPOSUPTOI) + { + cc++; + *type = OP_NOTI; + *opcode -= OP_NOTSTARI - OP_STAR; + } +else if (*opcode >= OP_TYPESTAR && *opcode <= OP_TYPEPOSUPTO) + { + cc++; + *opcode -= OP_TYPESTAR - OP_STAR; + *type = 0; + } +else + { + SLJIT_ASSERT(*opcode >= OP_CLASS || *opcode <= OP_XCLASS); + *type = *opcode; + cc++; + class_len = (*type < OP_XCLASS) ? (int)(1 + (32 / sizeof(pcre_uchar))) : GET(cc, 0); + *opcode = cc[class_len - 1]; + if (*opcode >= OP_CRSTAR && *opcode <= OP_CRMINQUERY) + { + *opcode -= OP_CRSTAR - OP_STAR; + if (end != NULL) + *end = cc + class_len; + } + else + { + SLJIT_ASSERT(*opcode == OP_CRRANGE || *opcode == OP_CRMINRANGE); + *arg1 = GET2(cc, (class_len + IMM2_SIZE)); + *arg2 = GET2(cc, class_len); + + if (*arg2 == 0) + { + SLJIT_ASSERT(*arg1 != 0); + *opcode = (*opcode == OP_CRRANGE) ? OP_UPTO : OP_MINUPTO; + } + if (*arg1 == *arg2) + *opcode = OP_EXACT; + + if (end != NULL) + *end = cc + class_len + 2 * IMM2_SIZE; + } + return cc; + } + +if (*opcode == OP_UPTO || *opcode == OP_MINUPTO || *opcode == OP_EXACT || *opcode == OP_POSUPTO) + { + *arg1 = GET2(cc, 0); + cc += IMM2_SIZE; + } + +if (*type == 0) + { + *type = *cc; + if (end != NULL) + *end = next_opcode(common, cc); + cc++; + return cc; + } + +if (end != NULL) + { + *end = cc + 1; +#ifdef SUPPORT_UTF + if (common->utf && HAS_EXTRALEN(*cc)) *end += GET_EXTRALEN(*cc); +#endif + } +return cc; +} + +static pcre_uchar *compile_iterator_hotpath(compiler_common *common, pcre_uchar *cc, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; +pcre_uchar opcode; +pcre_uchar type; +int arg1 = -1, arg2 = -1; +pcre_uchar* end; +jump_list *nomatch = NULL; +struct sljit_jump *jump = NULL; +struct sljit_label *label; + +PUSH_FALLBACK(sizeof(iterator_fallback), cc, NULL); + +cc = get_iterator_parameters(common, cc, &opcode, &type, &arg1, &arg2, &end); + +switch(opcode) + { + case OP_STAR: + case OP_PLUS: + case OP_UPTO: + case OP_CRRANGE: + if (type == OP_ANYNL || type == OP_EXTUNI) + { + if (opcode == OP_STAR || opcode == OP_UPTO) + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 0); + } + else + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + } + if (opcode == OP_UPTO || opcode == OP_CRRANGE) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 0); + + label = LABEL(); + compile_char1_hotpath(common, type, cc, &fallback->topfallbacks); + if (opcode == OP_UPTO || opcode == OP_CRRANGE) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + if (opcode == OP_CRRANGE && arg2 > 0) + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg2, label); + if (opcode == OP_UPTO || (opcode == OP_CRRANGE && arg1 > 0)) + jump = CMP(SLJIT_C_GREATER_EQUAL, TMP1, 0, SLJIT_IMM, arg1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, TMP1, 0); + } + + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, label); + if (jump != NULL) + JUMPHERE(jump); + } + else + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); + label = LABEL(); + compile_char1_hotpath(common, type, cc, &nomatch); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + if (opcode <= OP_PLUS || (opcode == OP_CRRANGE && arg1 == 0)) + { + OP2(SLJIT_ADD, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); + JUMPTO(SLJIT_JUMP, label); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg1 + 1, label); + } + set_jumps(nomatch, LABEL()); + if (opcode == OP_PLUS || opcode == OP_CRRANGE) + add_jump(compiler, &fallback->topfallbacks, + CMP(SLJIT_C_LESS, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, opcode == OP_PLUS ? 2 : arg2 + 1)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + FALLBACK_AS(iterator_fallback)->hotpath = LABEL(); + break; + + case OP_MINSTAR: + case OP_MINPLUS: + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + if (opcode == OP_MINPLUS) + add_jump(compiler, &fallback->topfallbacks, JUMP(SLJIT_JUMP)); + FALLBACK_AS(iterator_fallback)->hotpath = LABEL(); + break; + + case OP_MINUPTO: + case OP_CRMINRANGE: + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); + if (opcode == OP_CRMINRANGE) + add_jump(compiler, &fallback->topfallbacks, JUMP(SLJIT_JUMP)); + FALLBACK_AS(iterator_fallback)->hotpath = LABEL(); + break; + + case OP_QUERY: + case OP_MINQUERY: + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + if (opcode == OP_QUERY) + compile_char1_hotpath(common, type, cc, &fallback->topfallbacks); + FALLBACK_AS(iterator_fallback)->hotpath = LABEL(); + break; + + case OP_EXACT: + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 1); + label = LABEL(); + compile_char1_hotpath(common, type, cc, &fallback->topfallbacks); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, TMP1, 0); + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg1 + 1, label); + break; + + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSUPTO: + if (opcode != OP_POSSTAR) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STR_PTR, 0); + label = LABEL(); + compile_char1_hotpath(common, type, cc, &nomatch); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STR_PTR, 0); + if (opcode != OP_POSUPTO) + { + if (opcode == OP_POSPLUS) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 2); + JUMPTO(SLJIT_JUMP, label); + } + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, TMP1, 0); + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg1 + 1, label); + } + set_jumps(nomatch, LABEL()); + if (opcode == OP_POSPLUS) + add_jump(compiler, &fallback->topfallbacks, CMP(SLJIT_C_LESS, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE0, SLJIT_IMM, 2)); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1); + break; + + case OP_POSQUERY: + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STR_PTR, 0); + compile_char1_hotpath(common, type, cc, &nomatch); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1, STR_PTR, 0); + set_jumps(nomatch, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), POSSESSIVE1); + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + +decrease_call_count(common); +return end; +} + +static SLJIT_INLINE pcre_uchar *compile_fail_accept_hotpath(compiler_common *common, pcre_uchar *cc, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; + +PUSH_FALLBACK(sizeof(bracket_fallback), cc, NULL); + +if (*cc == OP_FAIL) + { + add_jump(compiler, &fallback->topfallbacks, JUMP(SLJIT_JUMP)); + return cc + 1; + } + +if (*cc == OP_ASSERT_ACCEPT || common->currententry != NULL) + { + /* No need to check notempty conditions. */ + if (common->acceptlabel == NULL) + add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); + else + JUMPTO(SLJIT_JUMP, common->acceptlabel); + return cc + 1; + } + +if (common->acceptlabel == NULL) + add_jump(compiler, &common->accept, CMP(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0))); +else + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), common->acceptlabel); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty)); +add_jump(compiler, &fallback->topfallbacks, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0)); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart)); +if (common->acceptlabel == NULL) + add_jump(compiler, &common->accept, CMP(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0)); +else + CMPTO(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0, common->acceptlabel); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +if (common->acceptlabel == NULL) + add_jump(compiler, &common->accept, CMP(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0)); +else + CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0, common->acceptlabel); +add_jump(compiler, &fallback->topfallbacks, JUMP(SLJIT_JUMP)); +return cc + 1; +} + +static SLJIT_INLINE pcre_uchar *compile_close_hotpath(compiler_common *common, pcre_uchar *cc) +{ +DEFINE_COMPILER; +int offset = GET2(cc, 1); + +/* Data will be discarded anyway... */ +if (common->currententry != NULL) + return cc + 1 + IMM2_SIZE; + +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR_PRIV(offset)); +offset <<= 1; +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); +return cc + 1 + IMM2_SIZE; +} + +static void compile_hotpath(compiler_common *common, pcre_uchar *cc, pcre_uchar *ccend, fallback_common *parent) +{ +DEFINE_COMPILER; +fallback_common *fallback; + +while (cc < ccend) + { + switch(*cc) + { + case OP_SOD: + case OP_SOM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_ANYBYTE: + case OP_NOTPROP: + case OP_PROP: + case OP_ANYNL: + case OP_NOT_HSPACE: + case OP_HSPACE: + case OP_NOT_VSPACE: + case OP_VSPACE: + case OP_EXTUNI: + case OP_EODN: + case OP_EOD: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_NOT: + case OP_NOTI: + case OP_REVERSE: + cc = compile_char1_hotpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextfallbacks : &parent->topfallbacks); + break; + + case OP_SET_SOM: + PUSH_FALLBACK_NOVALUE(sizeof(fallback_common), cc); + allocate_stack(common, 1); + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), TMP1, 0); + cc++; + break; + + case OP_CHAR: + case OP_CHARI: + cc = compile_charn_hotpath(common, cc, ccend, parent->top != NULL ? &parent->top->nextfallbacks : &parent->topfallbacks); + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSUPTO: + cc = compile_iterator_hotpath(common, cc, parent); + break; + + case OP_CLASS: + case OP_NCLASS: + if (cc[1 + (32 / sizeof(pcre_uchar))] >= OP_CRSTAR && cc[1 + (32 / sizeof(pcre_uchar))] <= OP_CRMINRANGE) + cc = compile_iterator_hotpath(common, cc, parent); + else + cc = compile_char1_hotpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextfallbacks : &parent->topfallbacks); + break; + +#if defined SUPPORT_UTF || defined COMPILE_PCRE16 + case OP_XCLASS: + if (*(cc + GET(cc, 1)) >= OP_CRSTAR && *(cc + GET(cc, 1)) <= OP_CRMINRANGE) + cc = compile_iterator_hotpath(common, cc, parent); + else + cc = compile_char1_hotpath(common, *cc, cc + 1, parent->top != NULL ? &parent->top->nextfallbacks : &parent->topfallbacks); + break; +#endif + + case OP_REF: + case OP_REFI: + if (cc[1 + IMM2_SIZE] >= OP_CRSTAR && cc[1 + IMM2_SIZE] <= OP_CRMINRANGE) + cc = compile_ref_iterator_hotpath(common, cc, parent); + else + cc = compile_ref_hotpath(common, cc, parent->top != NULL ? &parent->top->nextfallbacks : &parent->topfallbacks, TRUE, FALSE); + break; + + case OP_RECURSE: + cc = compile_recurse_hotpath(common, cc, parent); + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + PUSH_FALLBACK_NOVALUE(sizeof(assert_fallback), cc); + cc = compile_assert_hotpath(common, cc, FALLBACK_AS(assert_fallback), FALSE); + break; + + case OP_BRAMINZERO: + PUSH_FALLBACK_NOVALUE(sizeof(braminzero_fallback), cc); + cc = bracketend(cc + 1); + if (*(cc - 1 - LINK_SIZE) != OP_KETRMIN) + { + allocate_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + } + else + { + allocate_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), STR_PTR, 0); + } + FALLBACK_AS(braminzero_fallback)->hotpath = LABEL(); + if (cc[1] > OP_ASSERTBACK_NOT) + decrease_call_count(common); + break; + + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRA: + case OP_CBRA: + case OP_COND: + case OP_SBRA: + case OP_SCBRA: + case OP_SCOND: + cc = compile_bracket_hotpath(common, cc, parent); + break; + + case OP_BRAZERO: + if (cc[1] > OP_ASSERTBACK_NOT) + cc = compile_bracket_hotpath(common, cc, parent); + else + { + PUSH_FALLBACK_NOVALUE(sizeof(assert_fallback), cc); + cc = compile_assert_hotpath(common, cc, FALLBACK_AS(assert_fallback), FALSE); + } + break; + + case OP_BRAPOS: + case OP_CBRAPOS: + case OP_SBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOSZERO: + cc = compile_bracketpos_hotpath(common, cc, parent); + break; + + case OP_FAIL: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + cc = compile_fail_accept_hotpath(common, cc, parent); + break; + + case OP_CLOSE: + cc = compile_close_hotpath(common, cc); + break; + + case OP_SKIPZERO: + cc = bracketend(cc + 1); + break; + + default: + SLJIT_ASSERT_STOP(); + return; + } + if (cc == NULL) + return; + } +SLJIT_ASSERT(cc == ccend); +} + +#undef PUSH_FALLBACK +#undef PUSH_FALLBACK_NOVALUE +#undef FALLBACK_AS + +#define COMPILE_FALLBACKPATH(current) \ + do \ + { \ + compile_fallbackpath(common, (current)); \ + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) \ + return; \ + } \ + while (0) + +#define CURRENT_AS(type) ((type*)current) + +static void compile_iterator_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; +pcre_uchar *cc = current->cc; +pcre_uchar opcode; +pcre_uchar type; +int arg1 = -1, arg2 = -1; +struct sljit_label *label = NULL; +struct sljit_jump *jump = NULL; + +cc = get_iterator_parameters(common, cc, &opcode, &type, &arg1, &arg2, NULL); + +switch(opcode) + { + case OP_STAR: + case OP_PLUS: + case OP_UPTO: + case OP_CRRANGE: + if (type == OP_ANYNL || type == OP_EXTUNI) + { + set_jumps(current->topfallbacks, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_fallback)->hotpath); + } + else + { + if (opcode == OP_STAR || opcode == OP_UPTO) + arg2 = 0; + else if (opcode == OP_PLUS) + arg2 = 1; + jump = CMP(SLJIT_C_LESS_EQUAL, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, arg2 + 1); + OP2(SLJIT_SUB, SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_MEM1(STACK_TOP), STACK(1), SLJIT_IMM, 1); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + skip_char_back(common); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_fallback)->hotpath); + if (opcode == OP_PLUS || opcode == OP_CRRANGE) + set_jumps(current->topfallbacks, LABEL()); + JUMPHERE(jump); + free_stack(common, 2); + } + break; + + case OP_MINSTAR: + case OP_MINPLUS: + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (opcode == OP_MINPLUS) + { + set_jumps(current->topfallbacks, LABEL()); + current->topfallbacks = NULL; + } + compile_char1_hotpath(common, type, cc, ¤t->topfallbacks); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_fallback)->hotpath); + set_jumps(current->topfallbacks, LABEL()); + free_stack(common, 1); + break; + + case OP_MINUPTO: + case OP_CRMINRANGE: + if (opcode == OP_CRMINRANGE) + { + set_jumps(current->topfallbacks, LABEL()); + current->topfallbacks = NULL; + label = LABEL(); + } + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + compile_char1_hotpath(common, type, cc, ¤t->topfallbacks); + + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + OP2(SLJIT_ADD, TMP1, 0, TMP1, 0, SLJIT_IMM, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(1), TMP1, 0); + + if (opcode == OP_CRMINRANGE) + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg2 + 1, label); + + if (opcode == OP_CRMINRANGE && arg1 == 0) + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_fallback)->hotpath); + else + CMPTO(SLJIT_C_LESS, TMP1, 0, SLJIT_IMM, arg1 + 2, CURRENT_AS(iterator_fallback)->hotpath); + + set_jumps(current->topfallbacks, LABEL()); + free_stack(common, 2); + break; + + case OP_QUERY: + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_fallback)->hotpath); + jump = JUMP(SLJIT_JUMP); + set_jumps(current->topfallbacks, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_fallback)->hotpath); + JUMPHERE(jump); + free_stack(common, 1); + break; + + case OP_MINQUERY: + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + jump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + compile_char1_hotpath(common, type, cc, ¤t->topfallbacks); + JUMPTO(SLJIT_JUMP, CURRENT_AS(iterator_fallback)->hotpath); + set_jumps(current->topfallbacks, LABEL()); + JUMPHERE(jump); + free_stack(common, 1); + break; + + case OP_EXACT: + case OP_POSPLUS: + set_jumps(current->topfallbacks, LABEL()); + break; + + case OP_POSSTAR: + case OP_POSQUERY: + case OP_POSUPTO: + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } +} + +static void compile_ref_iterator_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; +pcre_uchar *cc = current->cc; +pcre_uchar type; + +type = cc[1 + IMM2_SIZE]; +if ((type & 0x1) == 0) + { + set_jumps(current->topfallbacks, LABEL()); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_fallback)->hotpath); + return; + } + +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); +CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(iterator_fallback)->hotpath); +set_jumps(current->topfallbacks, LABEL()); +free_stack(common, 2); +} + +static void compile_recurse_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; + +set_jumps(current->topfallbacks, LABEL()); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); +free_stack(common, 1); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), TMP2, 0); +} + +static void compile_assert_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; +pcre_uchar *cc = current->cc; +pcre_uchar bra = OP_BRA; +struct sljit_jump *brajump = NULL; + +SLJIT_ASSERT(*cc != OP_BRAMINZERO); +if (*cc == OP_BRAZERO) + { + bra = *cc; + cc++; + } + +if (bra == OP_BRAZERO) + { + SLJIT_ASSERT(current->topfallbacks == NULL); + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + +if (CURRENT_AS(assert_fallback)->framesize < 0) + { + set_jumps(current->topfallbacks, LABEL()); + + if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_fallback)->hotpath); + free_stack(common, 1); + } + return; + } + +if (bra == OP_BRAZERO) + { + if (*cc == OP_ASSERT_NOT || *cc == OP_ASSERTBACK_NOT) + { + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(assert_fallback)->hotpath); + free_stack(common, 1); + return; + } + free_stack(common, 1); + brajump = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_IMM, 0); + } + +if (*cc == OP_ASSERT || *cc == OP_ASSERTBACK) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(assert_fallback)->localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(assert_fallback)->localptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(assert_fallback)->framesize * sizeof(sljit_w)); + + set_jumps(current->topfallbacks, LABEL()); + } +else + set_jumps(current->topfallbacks, LABEL()); + +if (bra == OP_BRAZERO) + { + /* We know there is enough place on the stack. */ + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), SLJIT_IMM, 0); + JUMPTO(SLJIT_JUMP, CURRENT_AS(assert_fallback)->hotpath); + JUMPHERE(brajump); + } +} + +static void compile_bracket_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; +int opcode; +int offset = 0; +int localptr = CURRENT_AS(bracket_fallback)->localptr; +int stacksize; +int count; +pcre_uchar *cc = current->cc; +pcre_uchar *ccbegin; +pcre_uchar *ccprev; +jump_list *jumplist = NULL; +jump_list *jumplistitem = NULL; +pcre_uchar bra = OP_BRA; +pcre_uchar ket; +assert_fallback *assert; +BOOL has_alternatives; +struct sljit_jump *brazero = NULL; +struct sljit_jump *once = NULL; +struct sljit_jump *cond = NULL; +struct sljit_label *rminlabel = NULL; + +if (*cc == OP_BRAZERO || *cc == OP_BRAMINZERO) + { + bra = *cc; + cc++; + } + +opcode = *cc; +ccbegin = cc; +ket = *(bracketend(ccbegin) - 1 - LINK_SIZE); +cc += GET(cc, 1); +has_alternatives = *cc == OP_ALT; +if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + has_alternatives = (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) || CURRENT_AS(bracket_fallback)->u.condfailed != NULL; +if (opcode == OP_CBRA || opcode == OP_SCBRA) + offset = (GET2(ccbegin, 1 + LINK_SIZE)) << 1; +if (SLJIT_UNLIKELY(opcode == OP_COND) && (*cc == OP_KETRMAX || *cc == OP_KETRMIN)) + opcode = OP_SCOND; +if (SLJIT_UNLIKELY(opcode == OP_ONCE_NC)) + opcode = OP_ONCE; + +if (ket == OP_KETRMAX) + { + if (bra != OP_BRAZERO) + free_stack(common, 1); + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + brazero = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 0); + } + } +else if (ket == OP_KETRMIN) + { + if (bra != OP_BRAMINZERO) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + if (opcode >= OP_SBRA || opcode == OP_ONCE) + { + /* Checking zero-length iteration. */ + if (opcode != OP_ONCE || CURRENT_AS(bracket_fallback)->u.framesize < 0) + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, CURRENT_AS(bracket_fallback)->recursivehotpath); + else + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_MEM1(TMP1), (CURRENT_AS(bracket_fallback)->u.framesize + 1) * sizeof(sljit_w), CURRENT_AS(bracket_fallback)->recursivehotpath); + } + if (opcode != OP_ONCE) + free_stack(common, 1); + } + else + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_fallback)->recursivehotpath); + } + rminlabel = LABEL(); + } +else if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + brazero = CMP(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0); + } + +if (SLJIT_UNLIKELY(opcode == OP_ONCE)) + { + if (CURRENT_AS(bracket_fallback)->u.framesize >= 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + } + once = JUMP(SLJIT_JUMP); + } +else if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + { + if (has_alternatives) + { + /* Always exactly one alternative. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + + jumplistitem = sljit_alloc_memory(compiler, sizeof(jump_list)); + if (SLJIT_UNLIKELY(!jumplistitem)) + return; + jumplist = jumplistitem; + jumplistitem->next = NULL; + jumplistitem->jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, 1); + } + } +else if (*cc == OP_ALT) + { + /* Build a jump list. Get the last successfully matched branch index. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + count = 1; + do + { + /* Append as the last item. */ + if (jumplist != NULL) + { + jumplistitem->next = sljit_alloc_memory(compiler, sizeof(jump_list)); + jumplistitem = jumplistitem->next; + } + else + { + jumplistitem = sljit_alloc_memory(compiler, sizeof(jump_list)); + jumplist = jumplistitem; + } + + if (SLJIT_UNLIKELY(!jumplistitem)) + return; + + jumplistitem->next = NULL; + jumplistitem->jump = CMP(SLJIT_C_EQUAL, TMP1, 0, SLJIT_IMM, count++); + cc += GET(cc, 1); + } + while (*cc == OP_ALT); + + cc = ccbegin + GET(ccbegin, 1); + } + +COMPILE_FALLBACKPATH(current->top); +if (current->topfallbacks) + set_jumps(current->topfallbacks, LABEL()); + +if (SLJIT_UNLIKELY(opcode == OP_COND) || SLJIT_UNLIKELY(opcode == OP_SCOND)) + { + /* Conditional block always has at most one alternative. */ + if (ccbegin[1 + LINK_SIZE] >= OP_ASSERT && ccbegin[1 + LINK_SIZE] <= OP_ASSERTBACK_NOT) + { + SLJIT_ASSERT(has_alternatives); + assert = CURRENT_AS(bracket_fallback)->u.assert; + if (assert->framesize >= 0 && (ccbegin[1 + LINK_SIZE] == OP_ASSERT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK)) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->localptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_w)); + } + cond = JUMP(SLJIT_JUMP); + set_jumps(CURRENT_AS(bracket_fallback)->u.assert->condfailed, LABEL()); + } + else if (CURRENT_AS(bracket_fallback)->u.condfailed != NULL) + { + SLJIT_ASSERT(has_alternatives); + cond = JUMP(SLJIT_JUMP); + set_jumps(CURRENT_AS(bracket_fallback)->u.condfailed, LABEL()); + } + else + SLJIT_ASSERT(!has_alternatives); + } + +if (has_alternatives) + { + count = 1; + do + { + current->top = NULL; + current->topfallbacks = NULL; + current->nextfallbacks = NULL; + if (*cc == OP_ALT) + { + ccprev = cc + 1 + LINK_SIZE; + cc += GET(cc, 1); + if (opcode != OP_COND && opcode != OP_SCOND) + { + if (localptr != 0 && opcode != OP_ONCE) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + else + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + compile_hotpath(common, ccprev, cc, current); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return; + } + + /* Instructions after the current alternative is succesfully matched. */ + /* There is a similar code in compile_bracket_hotpath. */ + if (opcode == OP_ONCE) + { + if (CURRENT_AS(bracket_fallback)->u.framesize < 0) + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + /* TMP2 which is set here used by OP_KETRMAX below. */ + if (ket == OP_KETRMAX) + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), 0); + else if (ket == OP_KETRMIN) + { + /* Move the STR_PTR to the localptr. */ + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), 0); + } + } + else + { + OP2(SLJIT_ADD, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_IMM, (CURRENT_AS(bracket_fallback)->u.framesize + 2) * sizeof(sljit_w)); + if (ket == OP_KETRMAX) + { + /* TMP2 which is set here used by OP_KETRMAX below. */ + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + } + } + } + + stacksize = 0; + if (opcode != OP_ONCE) + stacksize++; + if (ket != OP_KET || bra != OP_BRA) + stacksize++; + + if (stacksize > 0) { + if (opcode != OP_ONCE || CURRENT_AS(bracket_fallback)->u.framesize >= 0) + allocate_stack(common, stacksize); + else + { + /* We know we have place at least for one item on the top of the stack. */ + SLJIT_ASSERT(stacksize == 1); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, sizeof(sljit_w)); + } + } + + stacksize = 0; + if (ket != OP_KET || bra != OP_BRA) + { + if (ket != OP_KET) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), STR_PTR, 0); + else + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, 0); + stacksize++; + } + + if (opcode != OP_ONCE) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(stacksize), SLJIT_IMM, count++); + + if (offset != 0) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), STR_PTR, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 0), TMP1, 0); + } + + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_fallback)->althotpath); + + if (opcode != OP_ONCE) + { + SLJIT_ASSERT(jumplist); + JUMPHERE(jumplist->jump); + jumplist = jumplist->next; + } + + COMPILE_FALLBACKPATH(current->top); + if (current->topfallbacks) + set_jumps(current->topfallbacks, LABEL()); + SLJIT_ASSERT(!current->nextfallbacks); + } + while (*cc == OP_ALT); + SLJIT_ASSERT(!jumplist); + + if (cond != NULL) + { + SLJIT_ASSERT(opcode == OP_COND || opcode == OP_SCOND); + assert = CURRENT_AS(bracket_fallback)->u.assert; + if ((ccbegin[1 + LINK_SIZE] == OP_ASSERT_NOT || ccbegin[1 + LINK_SIZE] == OP_ASSERTBACK_NOT) && assert->framesize >= 0) + + { + OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->localptr); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), assert->localptr, SLJIT_MEM1(STACK_TOP), assert->framesize * sizeof(sljit_w)); + } + JUMPHERE(cond); + } + + /* Free the STR_PTR. */ + if (localptr == 0) + free_stack(common, 1); + } + +if (offset != 0) + { + /* Using both tmp register is better for instruction scheduling. */ + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP2, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), STACK(2)); + free_stack(common, 3); + } +else if (opcode == OP_SBRA || opcode == OP_SCOND) + { + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + } +else if (opcode == OP_ONCE) + { + cc = ccbegin + GET(ccbegin, 1); + if (CURRENT_AS(bracket_fallback)->u.framesize >= 0) + { + /* Reset head and drop saved frame. */ + stacksize = (ket == OP_KETRMAX || ket == OP_KETRMIN || *cc == OP_ALT) ? 2 : 1; + free_stack(common, CURRENT_AS(bracket_fallback)->u.framesize + stacksize); + } + else if (ket == OP_KETRMAX || (*cc == OP_ALT && ket != OP_KETRMIN)) + { + /* The STR_PTR must be released. */ + free_stack(common, 1); + } + + JUMPHERE(once); + /* Restore previous localptr */ + if (CURRENT_AS(bracket_fallback)->u.framesize >= 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracket_fallback)->u.framesize * sizeof(sljit_w)); + else if (ket == OP_KETRMIN) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + /* See the comment below. */ + free_stack(common, 2); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), localptr, TMP1, 0); + } + } + +if (ket == OP_KETRMAX) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + CMPTO(SLJIT_C_NOT_EQUAL, STR_PTR, 0, SLJIT_IMM, 0, CURRENT_AS(bracket_fallback)->recursivehotpath); + if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_fallback)->zerohotpath); + JUMPHERE(brazero); + } + free_stack(common, 1); + } +else if (ket == OP_KETRMIN) + { + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + /* OP_ONCE removes everything in case of a fallback, so we don't + need to explicitly release the STR_PTR. The extra release would + affect badly the free_stack(2) above. */ + if (opcode != OP_ONCE) + free_stack(common, 1); + CMPTO(SLJIT_C_NOT_EQUAL, TMP1, 0, SLJIT_IMM, 0, rminlabel); + if (opcode == OP_ONCE) + free_stack(common, bra == OP_BRAMINZERO ? 2 : 1); + else if (bra == OP_BRAMINZERO) + free_stack(common, 1); + } +else if (bra == OP_BRAZERO) + { + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + JUMPTO(SLJIT_JUMP, CURRENT_AS(bracket_fallback)->zerohotpath); + JUMPHERE(brazero); + } +} + +static void compile_bracketpos_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; +int offset; +struct sljit_jump *jump; + +if (CURRENT_AS(bracketpos_fallback)->framesize < 0) + { + if (*current->cc == OP_CBRAPOS || *current->cc == OP_SCBRAPOS) + { + offset = (GET2(current->cc, 1 + LINK_SIZE)) << 1; + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), STACK(1)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset), TMP1, 0); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(offset + 1), TMP2, 0); + } + set_jumps(current->topfallbacks, LABEL()); + free_stack(common, CURRENT_AS(bracketpos_fallback)->stacksize); + return; + } + +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(bracketpos_fallback)->localptr); +add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + +if (current->topfallbacks) + { + jump = JUMP(SLJIT_JUMP); + set_jumps(current->topfallbacks, LABEL()); + /* Drop the stack frame. */ + free_stack(common, CURRENT_AS(bracketpos_fallback)->stacksize); + JUMPHERE(jump); + } +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), CURRENT_AS(bracketpos_fallback)->localptr, SLJIT_MEM1(STACK_TOP), CURRENT_AS(bracketpos_fallback)->framesize * sizeof(sljit_w)); +} + +static void compile_braminzero_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +assert_fallback fallback; + +current->top = NULL; +current->topfallbacks = NULL; +current->nextfallbacks = NULL; +if (current->cc[1] > OP_ASSERTBACK_NOT) + { + /* Manual call of compile_bracket_hotpath and compile_bracket_fallbackpath. */ + compile_bracket_hotpath(common, current->cc, current); + compile_bracket_fallbackpath(common, current->top); + } +else + { + memset(&fallback, 0, sizeof(fallback)); + fallback.common.cc = current->cc; + fallback.hotpath = CURRENT_AS(braminzero_fallback)->hotpath; + /* Manual call of compile_assert_hotpath. */ + compile_assert_hotpath(common, current->cc, &fallback, FALSE); + } +SLJIT_ASSERT(!current->nextfallbacks && !current->topfallbacks); +} + +static void compile_fallbackpath(compiler_common *common, struct fallback_common *current) +{ +DEFINE_COMPILER; + +while (current) + { + if (current->nextfallbacks != NULL) + set_jumps(current->nextfallbacks, LABEL()); + switch(*current->cc) + { + case OP_SET_SOM: + OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + free_stack(common, 1); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), TMP1, 0); + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_EXACT: + case OP_POSSTAR: + case OP_POSPLUS: + case OP_POSQUERY: + case OP_POSUPTO: + case OP_STARI: + case OP_MINSTARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_UPTOI: + case OP_MINUPTOI: + case OP_EXACTI: + case OP_POSSTARI: + case OP_POSPLUSI: + case OP_POSQUERYI: + case OP_POSUPTOI: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTEXACT: + case OP_NOTPOSSTAR: + case OP_NOTPOSPLUS: + case OP_NOTPOSQUERY: + case OP_NOTPOSUPTO: + case OP_NOTSTARI: + case OP_NOTMINSTARI: + case OP_NOTPLUSI: + case OP_NOTMINPLUSI: + case OP_NOTQUERYI: + case OP_NOTMINQUERYI: + case OP_NOTUPTOI: + case OP_NOTMINUPTOI: + case OP_NOTEXACTI: + case OP_NOTPOSSTARI: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERYI: + case OP_NOTPOSUPTOI: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + case OP_TYPEPOSUPTO: + case OP_CLASS: + case OP_NCLASS: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: +#endif + compile_iterator_fallbackpath(common, current); + break; + + case OP_REF: + case OP_REFI: + compile_ref_iterator_fallbackpath(common, current); + break; + + case OP_RECURSE: + compile_recurse_fallbackpath(common, current); + break; + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + compile_assert_fallbackpath(common, current); + break; + + case OP_ONCE: + case OP_ONCE_NC: + case OP_BRA: + case OP_CBRA: + case OP_COND: + case OP_SBRA: + case OP_SCBRA: + case OP_SCOND: + compile_bracket_fallbackpath(common, current); + break; + + case OP_BRAZERO: + if (current->cc[1] > OP_ASSERTBACK_NOT) + compile_bracket_fallbackpath(common, current); + else + compile_assert_fallbackpath(common, current); + break; + + case OP_BRAPOS: + case OP_CBRAPOS: + case OP_SBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOSZERO: + compile_bracketpos_fallbackpath(common, current); + break; + + case OP_BRAMINZERO: + compile_braminzero_fallbackpath(common, current); + break; + + case OP_FAIL: + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + set_jumps(current->topfallbacks, LABEL()); + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + current = current->prev; + } +} + +static SLJIT_INLINE void compile_recurse(compiler_common *common) +{ +DEFINE_COMPILER; +pcre_uchar *cc = common->start + common->currententry->start; +pcre_uchar *ccbegin = cc + 1 + LINK_SIZE + (*cc == OP_BRA ? 0 : IMM2_SIZE); +pcre_uchar *ccend = bracketend(cc); +int localsize = get_localsize(common, ccbegin, ccend); +int framesize = get_framesize(common, cc, TRUE); +int alternativesize; +BOOL needsframe; +fallback_common altfallback; +struct sljit_jump *jump; + +SLJIT_ASSERT(*cc == OP_BRA || *cc == OP_CBRA || *cc == OP_CBRAPOS || *cc == OP_SCBRA || *cc == OP_SCBRAPOS); +needsframe = framesize >= 0; +if (!needsframe) + framesize = 0; +alternativesize = *(cc + GET(cc, 1)) == OP_ALT ? 1 : 0; + +SLJIT_ASSERT(common->currententry->entry == NULL); +common->currententry->entry = LABEL(); +set_jumps(common->currententry->calls, common->currententry->entry); + +sljit_emit_fast_enter(compiler, TMP2, 0, 1, 5, 5, common->localsize); +allocate_stack(common, localsize + framesize + alternativesize); +OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(localsize + framesize + alternativesize - 1), TMP2, 0); +copy_locals(common, ccbegin, ccend, TRUE, localsize + framesize + alternativesize, framesize + alternativesize); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), RECURSIVE_HEAD, STACK_TOP, 0); +if (needsframe) + init_frame(common, cc, framesize + alternativesize - 1, alternativesize, FALSE); + +if (alternativesize > 0) + OP1(SLJIT_MOV, SLJIT_MEM1(STACK_TOP), STACK(0), STR_PTR, 0); + +memset(&altfallback, 0, sizeof(fallback_common)); +common->acceptlabel = NULL; +common->accept = NULL; +altfallback.cc = ccbegin; +cc += GET(cc, 1); +while (1) + { + altfallback.top = NULL; + altfallback.topfallbacks = NULL; + + if (altfallback.cc != ccbegin) + OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(STACK_TOP), STACK(0)); + + compile_hotpath(common, altfallback.cc, cc, &altfallback); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return; + + add_jump(compiler, &common->accept, JUMP(SLJIT_JUMP)); + + compile_fallbackpath(common, altfallback.top); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + return; + set_jumps(altfallback.topfallbacks, LABEL()); + + if (*cc != OP_ALT) + break; + + altfallback.cc = cc + 1 + LINK_SIZE; + cc += GET(cc, 1); + } +/* None of them matched. */ +OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 0); +jump = JUMP(SLJIT_JUMP); + +set_jumps(common->accept, LABEL()); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), RECURSIVE_HEAD); +if (needsframe) + { + OP1(SLJIT_MOV, TMP3, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + OP2(SLJIT_SUB, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_w)); + add_jump(compiler, &common->revertframes, JUMP(SLJIT_FAST_CALL)); + OP2(SLJIT_ADD, STACK_TOP, 0, STACK_TOP, 0, SLJIT_IMM, (framesize + alternativesize) * sizeof(sljit_w)); + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), TMP3, 0); + } +OP1(SLJIT_MOV, TMP3, 0, SLJIT_IMM, 1); + +JUMPHERE(jump); +copy_locals(common, ccbegin, ccend, FALSE, localsize + framesize + alternativesize, framesize + alternativesize); +free_stack(common, localsize + framesize + alternativesize); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(STACK_TOP), sizeof(sljit_w)); +OP1(SLJIT_MOV, TMP1, 0, TMP3, 0); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), RECURSIVE_HEAD, TMP2, 0); +sljit_emit_fast_return(compiler, SLJIT_MEM1(STACK_TOP), 0); +} + +#undef COMPILE_FALLBACKPATH +#undef CURRENT_AS + +void +PRIV(jit_compile)(const REAL_PCRE *re, PUBL(extra) *extra) +{ +struct sljit_compiler *compiler; +fallback_common rootfallback; +compiler_common common_data; +compiler_common *common = &common_data; +const pcre_uint8 *tables = re->tables; +pcre_study_data *study; +pcre_uchar *ccend; +executable_function *function; +void *executable_func; +sljit_uw executable_size; +struct sljit_label *leave; +struct sljit_label *mainloop = NULL; +struct sljit_label *empty_match_found; +struct sljit_label *empty_match_fallback; +struct sljit_jump *alloc_error; +struct sljit_jump *reqbyte_notfound = NULL; +struct sljit_jump *empty_match; + +SLJIT_ASSERT((extra->flags & PCRE_EXTRA_STUDY_DATA) != 0); +study = extra->study_data; + +if (!tables) + tables = PRIV(default_tables); + +memset(&rootfallback, 0, sizeof(fallback_common)); +rootfallback.cc = (pcre_uchar *)re + re->name_table_offset + re->name_count * re->name_entry_size; + +common->compiler = NULL; +common->start = rootfallback.cc; +common->cbraptr = OVECTOR_START + (re->top_bracket + 1) * 2 * sizeof(sljit_w); +common->fcc = tables + fcc_offset; +common->lcc = (sljit_w)(tables + lcc_offset); +common->nltype = NLTYPE_FIXED; +switch(re->options & PCRE_NEWLINE_BITS) + { + case 0: + /* Compile-time default */ + switch (NEWLINE) + { + case -1: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break; + case -2: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break; + default: common->newline = NEWLINE; break; + } + break; + case PCRE_NEWLINE_CR: common->newline = CHAR_CR; break; + case PCRE_NEWLINE_LF: common->newline = CHAR_NL; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: common->newline = (CHAR_CR << 8) | CHAR_NL; break; + case PCRE_NEWLINE_ANY: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANY; break; + case PCRE_NEWLINE_ANYCRLF: common->newline = (CHAR_CR << 8) | CHAR_NL; common->nltype = NLTYPE_ANYCRLF; break; + default: return; + } +if ((re->options & PCRE_BSR_ANYCRLF) != 0) + common->bsr_nltype = NLTYPE_ANYCRLF; +else if ((re->options & PCRE_BSR_UNICODE) != 0) + common->bsr_nltype = NLTYPE_ANY; +else + { +#ifdef BSR_ANYCRLF + common->bsr_nltype = NLTYPE_ANYCRLF; +#else + common->bsr_nltype = NLTYPE_ANY; +#endif + } +common->endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; +common->ctypes = (sljit_w)(tables + ctypes_offset); +common->name_table = (sljit_w)((pcre_uchar *)re + re->name_table_offset); +common->name_count = re->name_count; +common->name_entry_size = re->name_entry_size; +common->acceptlabel = NULL; +common->stubs = NULL; +common->entries = NULL; +common->currententry = NULL; +common->accept = NULL; +common->calllimit = NULL; +common->stackalloc = NULL; +common->revertframes = NULL; +common->wordboundary = NULL; +common->anynewline = NULL; +common->hspace = NULL; +common->vspace = NULL; +common->casefulcmp = NULL; +common->caselesscmp = NULL; +common->jscript_compat = (re->options & PCRE_JAVASCRIPT_COMPAT) != 0; +#ifdef SUPPORT_UTF +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +common->utf = (re->options & PCRE_UTF8) != 0; +#ifdef SUPPORT_UCP +common->use_ucp = (re->options & PCRE_UCP) != 0; +#endif +common->utfreadchar = NULL; +#ifdef COMPILE_PCRE8 +common->utfreadtype8 = NULL; +#endif +#endif /* SUPPORT_UTF */ +#ifdef SUPPORT_UCP +common->getucd = NULL; +#endif +ccend = bracketend(rootfallback.cc); +SLJIT_ASSERT(*rootfallback.cc == OP_BRA && ccend[-(1 + LINK_SIZE)] == OP_KET); +common->localsize = get_localspace(common, rootfallback.cc, ccend); +if (common->localsize < 0) + return; +common->localsize += common->cbraptr + (re->top_bracket + 1) * sizeof(sljit_w); +if (common->localsize > SLJIT_MAX_LOCAL_SIZE) + return; +common->localptrs = (int*)SLJIT_MALLOC((ccend - rootfallback.cc) * sizeof(int)); +if (!common->localptrs) + return; +memset(common->localptrs, 0, (ccend - rootfallback.cc) * sizeof(int)); +set_localptrs(common, common->cbraptr + (re->top_bracket + 1) * sizeof(sljit_w), ccend); + +compiler = sljit_create_compiler(); +if (!compiler) + { + SLJIT_FREE(common->localptrs); + return; + } +common->compiler = compiler; + +/* Main pcre_jit_exec entry. */ +sljit_emit_enter(compiler, 1, 5, 5, common->localsize); + +/* Register init. */ +reset_ovector(common, (re->top_bracket + 1) * 2); +if ((re->flags & PCRE_REQCHSET) != 0) + OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), REQ_CHAR_PTR, SLJIT_TEMPORARY_REG1, 0); + +OP1(SLJIT_MOV, ARGUMENTS, 0, SLJIT_SAVED_REG1, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_SAVED_REG1, 0); +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +OP1(SLJIT_MOV, STR_END, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, end)); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV_SI, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, calllimit)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, base)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP2), SLJIT_OFFSETOF(struct sljit_stack, limit)); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), CALL_LIMIT, TMP1, 0); + +/* Main part of the matching */ +if ((re->options & PCRE_ANCHORED) == 0) + { + mainloop = mainloop_entry(common, (re->flags & PCRE_HASCRORLF) != 0, (re->options & PCRE_FIRSTLINE) != 0); + /* Forward search if possible. */ + if ((re->flags & PCRE_FIRSTSET) != 0) + fast_forward_first_char(common, re->first_char, (re->flags & PCRE_FCH_CASELESS) != 0, (re->options & PCRE_FIRSTLINE) != 0); + else if ((re->flags & PCRE_STARTLINE) != 0) + fast_forward_newline(common, (re->options & PCRE_FIRSTLINE) != 0); + else if ((re->flags & PCRE_STARTLINE) == 0 && study != NULL && (study->flags & PCRE_STUDY_MAPPED) != 0) + fast_forward_start_bits(common, (sljit_uw)study->start_bits, (re->options & PCRE_FIRSTLINE) != 0); + } +if ((re->flags & PCRE_REQCHSET) != 0) + reqbyte_notfound = search_requested_char(common, re->req_char, (re->flags & PCRE_RCH_CASELESS) != 0, (re->flags & PCRE_FIRSTSET) != 0); + +/* Store the current STR_PTR in OVECTOR(0). */ +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), STR_PTR, 0); +/* Copy the limit of allowed recursions. */ +OP1(SLJIT_MOV, CALL_COUNT, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), CALL_LIMIT); + +compile_hotpath(common, rootfallback.cc, ccend, &rootfallback); +if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + sljit_free_compiler(compiler); + SLJIT_FREE(common->localptrs); + return; + } + +empty_match = CMP(SLJIT_C_EQUAL, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); +empty_match_found = LABEL(); + +common->acceptlabel = LABEL(); +if (common->accept != NULL) + set_jumps(common->accept, common->acceptlabel); + +/* This means we have a match. Update the ovector. */ +copy_ovector(common, re->top_bracket + 1); +leave = LABEL(); +sljit_emit_return(compiler, SLJIT_MOV, SLJIT_RETURN_REG, 0); + +empty_match_fallback = LABEL(); +compile_fallbackpath(common, rootfallback.top); +if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + sljit_free_compiler(compiler); + SLJIT_FREE(common->localptrs); + return; + } + +SLJIT_ASSERT(rootfallback.prev == NULL); + +/* Check we have remaining characters. */ +OP1(SLJIT_MOV, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0)); + +if ((re->options & PCRE_ANCHORED) == 0) + { + if ((re->options & PCRE_FIRSTLINE) == 0) + { + if (study != NULL && study->minlength > 1) + { + OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(study->minlength)); + CMPTO(SLJIT_C_LESS_EQUAL, TMP1, 0, STR_END, 0, mainloop); + } + else + CMPTO(SLJIT_C_LESS, STR_PTR, 0, STR_END, 0, mainloop); + } + else + { + if (study != NULL && study->minlength > 1) + { + OP2(SLJIT_ADD, TMP1, 0, STR_PTR, 0, SLJIT_IMM, IN_UCHARS(study->minlength)); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, TMP1, 0, STR_END, 0); + COND_VALUE(SLJIT_MOV, TMP2, 0, SLJIT_C_GREATER); + OP2(SLJIT_SUB | SLJIT_SET_U, SLJIT_UNUSED, 0, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END); + COND_VALUE(SLJIT_OR | SLJIT_SET_E, TMP2, 0, SLJIT_C_GREATER_EQUAL); + JUMPTO(SLJIT_C_ZERO, mainloop); + } + else + CMPTO(SLJIT_C_LESS, STR_PTR, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), FIRSTLINE_END, mainloop); + } + } + +if (reqbyte_notfound != NULL) + JUMPHERE(reqbyte_notfound); +/* Copy OVECTOR(1) to OVECTOR(0) */ +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(0), SLJIT_MEM1(SLJIT_LOCALS_REG), OVECTOR(1)); +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_NOMATCH); +JUMPTO(SLJIT_JUMP, leave); + +flush_stubs(common); + +JUMPHERE(empty_match); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty)); +CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, SLJIT_IMM, 0, empty_match_fallback); +OP1(SLJIT_MOV_UB, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, notempty_atstart)); +CMPTO(SLJIT_C_EQUAL, TMP2, 0, SLJIT_IMM, 0, empty_match_found); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, str)); +CMPTO(SLJIT_C_NOT_EQUAL, TMP2, 0, STR_PTR, 0, empty_match_found); +JUMPTO(SLJIT_JUMP, empty_match_fallback); + +common->currententry = common->entries; +while (common->currententry != NULL) + { + /* Might add new entries. */ + compile_recurse(common); + if (SLJIT_UNLIKELY(sljit_get_compiler_error(compiler))) + { + sljit_free_compiler(compiler); + SLJIT_FREE(common->localptrs); + return; + } + flush_stubs(common); + common->currententry = common->currententry->next; + } + +/* Allocating stack, returns with PCRE_ERROR_JIT_STACKLIMIT if fails. */ +/* This is a (really) rare case. */ +set_jumps(common->stackalloc, LABEL()); +/* RETURN_ADDR is not a saved register. */ +sljit_emit_fast_enter(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0, 1, 5, 5, common->localsize); +OP1(SLJIT_MOV, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1, TMP2, 0); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top), STACK_TOP, 0); +OP2(SLJIT_ADD, TMP2, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit), SLJIT_IMM, STACK_GROWTH_RATE); + +sljit_emit_ijump(compiler, SLJIT_CALL2, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_stack_resize)); +alloc_error = CMP(SLJIT_C_NOT_EQUAL, SLJIT_RETURN_REG, 0, SLJIT_IMM, 0); +OP1(SLJIT_MOV, TMP1, 0, ARGUMENTS, 0); +OP1(SLJIT_MOV, TMP1, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(jit_arguments, stack)); +OP1(SLJIT_MOV, STACK_TOP, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, top)); +OP1(SLJIT_MOV, STACK_LIMIT, 0, SLJIT_MEM1(TMP1), SLJIT_OFFSETOF(struct sljit_stack, limit)); +OP1(SLJIT_MOV, TMP2, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS1); +sljit_emit_fast_return(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), LOCALS0); + +/* Allocation failed. */ +JUMPHERE(alloc_error); +/* We break the return address cache here, but this is a really rare case. */ +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_JIT_STACKLIMIT); +JUMPTO(SLJIT_JUMP, leave); + +/* Call limit reached. */ +set_jumps(common->calllimit, LABEL()); +OP1(SLJIT_MOV, SLJIT_RETURN_REG, 0, SLJIT_IMM, PCRE_ERROR_MATCHLIMIT); +JUMPTO(SLJIT_JUMP, leave); + +if (common->revertframes != NULL) + { + set_jumps(common->revertframes, LABEL()); + do_revertframes(common); + } +if (common->wordboundary != NULL) + { + set_jumps(common->wordboundary, LABEL()); + check_wordboundary(common); + } +if (common->anynewline != NULL) + { + set_jumps(common->anynewline, LABEL()); + check_anynewline(common); + } +if (common->hspace != NULL) + { + set_jumps(common->hspace, LABEL()); + check_hspace(common); + } +if (common->vspace != NULL) + { + set_jumps(common->vspace, LABEL()); + check_vspace(common); + } +if (common->casefulcmp != NULL) + { + set_jumps(common->casefulcmp, LABEL()); + do_casefulcmp(common); + } +if (common->caselesscmp != NULL) + { + set_jumps(common->caselesscmp, LABEL()); + do_caselesscmp(common); + } +#ifdef SUPPORT_UTF +if (common->utfreadchar != NULL) + { + set_jumps(common->utfreadchar, LABEL()); + do_utfreadchar(common); + } +#ifdef COMPILE_PCRE8 +if (common->utfreadtype8 != NULL) + { + set_jumps(common->utfreadtype8, LABEL()); + do_utfreadtype8(common); + } +#endif +#endif /* COMPILE_PCRE8 */ +#ifdef SUPPORT_UCP +if (common->getucd != NULL) + { + set_jumps(common->getucd, LABEL()); + do_getucd(common); + } +#endif + +SLJIT_FREE(common->localptrs); +executable_func = sljit_generate_code(compiler); +executable_size = sljit_get_generated_code_size(compiler); +sljit_free_compiler(compiler); +if (executable_func == NULL) + return; + +function = SLJIT_MALLOC(sizeof(executable_function)); +if (function == NULL) + { + /* This case is highly unlikely since we just recently + freed a lot of memory. Although not impossible. */ + sljit_free_code(executable_func); + return; + } + +function->executable_func = executable_func; +function->executable_size = executable_size; +function->callback = NULL; +function->userdata = NULL; +extra->executable_jit = function; +extra->flags |= PCRE_EXTRA_EXECUTABLE_JIT; +} + +static int jit_machine_stack_exec(jit_arguments *arguments, executable_function *function) +{ +union { + void* executable_func; + jit_function call_executable_func; +} convert_executable_func; +pcre_uint8 local_area[LOCAL_SPACE_SIZE]; +struct sljit_stack local_stack; + +local_stack.top = (sljit_w)&local_area; +local_stack.base = local_stack.top; +local_stack.limit = local_stack.base + LOCAL_SPACE_SIZE; +local_stack.max_limit = local_stack.limit; +arguments->stack = &local_stack; +convert_executable_func.executable_func = function->executable_func; +return convert_executable_func.call_executable_func(arguments); +} + +int +PRIV(jit_exec)(const REAL_PCRE *re, void *executable_func, + const pcre_uchar *subject, int length, int start_offset, int options, + int match_limit, int *offsets, int offsetcount) +{ +executable_function *function = (executable_function*)executable_func; +union { + void* executable_func; + jit_function call_executable_func; +} convert_executable_func; +jit_arguments arguments; +int maxoffsetcount; +int retval; + +/* Sanity checks should be handled by pcre_exec. */ +arguments.stack = NULL; +arguments.str = subject + start_offset; +arguments.begin = subject; +arguments.end = subject + length; +arguments.calllimit = match_limit; /* JIT decreases this value less times. */ +arguments.notbol = (options & PCRE_NOTBOL) != 0; +arguments.noteol = (options & PCRE_NOTEOL) != 0; +arguments.notempty = (options & PCRE_NOTEMPTY) != 0; +arguments.notempty_atstart = (options & PCRE_NOTEMPTY_ATSTART) != 0; +arguments.offsets = offsets; + +/* pcre_exec() rounds offsetcount to a multiple of 3, and then uses only 2/3 of +the output vector for storing captured strings, with the remainder used as +workspace. We don't need the workspace here. For compatibility, we limit the +number of captured strings in the same way as pcre_exec(), so that the user +gets the same result with and without JIT. */ + +if (offsetcount != 2) + offsetcount = ((offsetcount - (offsetcount % 3)) * 2) / 3; +maxoffsetcount = (re->top_bracket + 1) * 2; +if (offsetcount > maxoffsetcount) + offsetcount = maxoffsetcount; +arguments.offsetcount = offsetcount; + +if (function->callback) + arguments.stack = (struct sljit_stack*)function->callback(function->userdata); +else + arguments.stack = (struct sljit_stack*)function->userdata; + +if (arguments.stack == NULL) + retval = jit_machine_stack_exec(&arguments, function); +else + { + convert_executable_func.executable_func = function->executable_func; + retval = convert_executable_func.call_executable_func(&arguments); + } + +if (retval * 2 > offsetcount) + retval = 0; +return retval; +} + +void +PRIV(jit_free)(void *executable_func) +{ +executable_function *function = (executable_function*)executable_func; +sljit_free_code(function->executable_func); +SLJIT_FREE(function); +} + +int +PRIV(jit_get_size)(void *executable_func) +{ +return ((executable_function*)executable_func)->executable_size; +} + +const char* +PRIV(jit_get_target)(void) +{ +return sljit_get_platform_name(); +} + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL pcre_jit_stack * +pcre_jit_stack_alloc(int startsize, int maxsize) +#else +PCRE_EXP_DECL pcre16_jit_stack * +pcre16_jit_stack_alloc(int startsize, int maxsize) +#endif +{ +if (startsize < 1 || maxsize < 1) + return NULL; +if (startsize > maxsize) + startsize = maxsize; +startsize = (startsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1); +maxsize = (maxsize + STACK_GROWTH_RATE - 1) & ~(STACK_GROWTH_RATE - 1); +return (PUBL(jit_stack)*)sljit_allocate_stack(startsize, maxsize); +} + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_jit_stack_free(pcre_jit_stack *stack) +#else +PCRE_EXP_DECL void +pcre16_jit_stack_free(pcre16_jit_stack *stack) +#endif +{ +sljit_free_stack((struct sljit_stack*)stack); +} + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#else +PCRE_EXP_DECL void +pcre16_assign_jit_stack(pcre16_extra *extra, pcre16_jit_callback callback, void *userdata) +#endif +{ +executable_function *function; +if (extra != NULL && + (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra->executable_jit != NULL) + { + function = (executable_function*)extra->executable_jit; + function->callback = callback; + function->userdata = userdata; + } +} + +#else /* SUPPORT_JIT */ + +/* These are dummy functions to avoid linking errors when JIT support is not +being compiled. */ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL pcre_jit_stack * +pcre_jit_stack_alloc(int startsize, int maxsize) +#else +PCRE_EXP_DECL pcre16_jit_stack * +pcre16_jit_stack_alloc(int startsize, int maxsize) +#endif +{ +(void)startsize; +(void)maxsize; +return NULL; +} + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_jit_stack_free(pcre_jit_stack *stack) +#else +PCRE_EXP_DECL void +pcre16_jit_stack_free(pcre16_jit_stack *stack) +#endif +{ +(void)stack; +} + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DECL void +pcre_assign_jit_stack(pcre_extra *extra, pcre_jit_callback callback, void *userdata) +#else +PCRE_EXP_DECL void +pcre16_assign_jit_stack(pcre16_extra *extra, pcre16_jit_callback callback, void *userdata) +#endif +{ +(void)extra; +(void)callback; +(void)userdata; +} + +#endif + +/* End of pcre_jit_compile.c */ diff --git a/src/lib/pcre/pcre_maketables.c b/src/lib/pcre/pcre_maketables.c new file mode 100644 index 0000000..caacdba --- /dev/null +++ b/src/lib/pcre/pcre_maketables.c @@ -0,0 +1,148 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_maketables(), which builds +character tables for PCRE in the current locale. The file is compiled on its +own as part of the PCRE library. However, it is also included in the +compilation of dftables.c, in which case the macro DFTABLES is defined. */ + + +#ifndef DFTABLES +# ifdef PCRE_HAVE_CONFIG_H +# include "config.h" +# endif +# include "pcre_internal.h" +#endif + + +/************************************************* +* Create PCRE character tables * +*************************************************/ + +/* This function builds a set of character tables for use by PCRE and returns +a pointer to them. They are build using the ctype functions, and consequently +their contents will depend upon the current locale setting. When compiled as +part of the library, the store is obtained via PUBL(malloc)(), but when +compiled inside dftables, use malloc(). + +Arguments: none +Returns: pointer to the contiguous block of data +*/ + +#ifdef COMPILE_PCRE8 +const unsigned char * +pcre_maketables(void) +#else +const unsigned char * +pcre16_maketables(void) +#endif +{ +unsigned char *yield, *p; +int i; + +#ifndef DFTABLES +yield = (unsigned char*)(PUBL(malloc))(tables_length); +#else +yield = (unsigned char*)malloc(tables_length); +#endif + +if (yield == NULL) return NULL; +p = yield; + +/* First comes the lower casing table */ + +for (i = 0; i < 256; i++) *p++ = tolower(i); + +/* Next the case-flipping table */ + +for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i); + +/* Then the character class tables. Don't try to be clever and save effort on +exclusive ones - in some locales things may be different. Note that the table +for "space" includes everything "isspace" gives, including VT in the default +locale. This makes it work for the POSIX class [:space:]. Note also that it is +possible for a character to be alnum or alpha without being lower or upper, +such as "male and female ordinals" (\xAA and \xBA) in the fr_FR locale (at +least under Debian Linux's locales as of 12/2005). So we must test for alnum +specially. */ + +memset(p, 0, cbit_length); +for (i = 0; i < 256; i++) + { + if (isdigit(i)) p[cbit_digit + i/8] |= 1 << (i&7); + if (isupper(i)) p[cbit_upper + i/8] |= 1 << (i&7); + if (islower(i)) p[cbit_lower + i/8] |= 1 << (i&7); + if (isalnum(i)) p[cbit_word + i/8] |= 1 << (i&7); + if (i == '_') p[cbit_word + i/8] |= 1 << (i&7); + if (isspace(i)) p[cbit_space + i/8] |= 1 << (i&7); + if (isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7); + if (isgraph(i)) p[cbit_graph + i/8] |= 1 << (i&7); + if (isprint(i)) p[cbit_print + i/8] |= 1 << (i&7); + if (ispunct(i)) p[cbit_punct + i/8] |= 1 << (i&7); + if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1 << (i&7); + } +p += cbit_length; + +/* Finally, the character type table. In this, we exclude VT from the white +space chars, because Perl doesn't recognize it as such for \s and for comments +within regexes. */ + +for (i = 0; i < 256; i++) + { + int x = 0; + if (i != 0x0b && isspace(i)) x += ctype_space; + if (isalpha(i)) x += ctype_letter; + if (isdigit(i)) x += ctype_digit; + if (isxdigit(i)) x += ctype_xdigit; + if (isalnum(i) || i == '_') x += ctype_word; + + /* Note: strchr includes the terminating zero in the characters it considers. + In this instance, that is ok because we want binary zero to be flagged as a + meta-character, which in this sense is any character that terminates a run + of data characters. */ + + if (strchr("\\*+?{^.$|()[", i) != 0) x += ctype_meta; + *p++ = x; + } + +return yield; +} + +/* End of pcre_maketables.c */ diff --git a/src/lib/pcre/pcre_newline.c b/src/lib/pcre/pcre_newline.c new file mode 100644 index 0000000..8ee2a6e --- /dev/null +++ b/src/lib/pcre/pcre_newline.c @@ -0,0 +1,184 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains internal functions for testing newlines when more than +one kind of newline is to be recognized. When a newline is found, its length is +returned. In principle, we could implement several newline "types", each +referring to a different set of newline characters. At present, PCRE supports +only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF, +and NLTYPE_ANY. The full list of Unicode newline characters is taken from +http://unicode.org/unicode/reports/tr18/. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + + +/************************************************* +* Check for newline at given position * +*************************************************/ + +/* It is guaranteed that the initial value of ptr is less than the end of the +string that is being processed. + +Arguments: + ptr pointer to possible newline + type the newline type + endptr pointer to the end of the string + lenptr where to return the length + utf TRUE if in utf mode + +Returns: TRUE or FALSE +*/ + +BOOL +PRIV(is_newline)(PCRE_PUCHAR ptr, int type, PCRE_PUCHAR endptr, int *lenptr, + BOOL utf) +{ +int c; +(void)utf; +#ifdef SUPPORT_UTF +if (utf) + { + GETCHAR(c, ptr); + } +else +#endif /* SUPPORT_UTF */ + c = *ptr; + +if (type == NLTYPE_ANYCRLF) switch(c) + { + case 0x000a: *lenptr = 1; return TRUE; /* LF */ + case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1; + return TRUE; /* CR */ + default: return FALSE; + } + +/* NLTYPE_ANY */ + +else switch(c) + { + case 0x000a: /* LF */ + case 0x000b: /* VT */ + case 0x000c: *lenptr = 1; return TRUE; /* FF */ + case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1; + return TRUE; /* CR */ +#ifdef COMPILE_PCRE8 + case 0x0085: *lenptr = utf? 2 : 1; return TRUE; /* NEL */ + case 0x2028: /* LS */ + case 0x2029: *lenptr = 3; return TRUE; /* PS */ +#else + case 0x0085: /* NEL */ + case 0x2028: /* LS */ + case 0x2029: *lenptr = 1; return TRUE; /* PS */ +#endif /* COMPILE_PCRE8 */ + default: return FALSE; + } +} + + + +/************************************************* +* Check for newline at previous position * +*************************************************/ + +/* It is guaranteed that the initial value of ptr is greater than the start of +the string that is being processed. + +Arguments: + ptr pointer to possible newline + type the newline type + startptr pointer to the start of the string + lenptr where to return the length + utf TRUE if in utf mode + +Returns: TRUE or FALSE +*/ + +BOOL +PRIV(was_newline)(PCRE_PUCHAR ptr, int type, PCRE_PUCHAR startptr, int *lenptr, + BOOL utf) +{ +int c; +(void)utf; +ptr--; +#ifdef SUPPORT_UTF +if (utf) + { + BACKCHAR(ptr); + GETCHAR(c, ptr); + } +else +#endif /* SUPPORT_UTF */ + c = *ptr; + +if (type == NLTYPE_ANYCRLF) switch(c) + { + case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1; + return TRUE; /* LF */ + case 0x000d: *lenptr = 1; return TRUE; /* CR */ + default: return FALSE; + } + +else switch(c) + { + case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1; + return TRUE; /* LF */ + case 0x000b: /* VT */ + case 0x000c: /* FF */ + case 0x000d: *lenptr = 1; return TRUE; /* CR */ +#ifdef COMPILE_PCRE8 + case 0x0085: *lenptr = utf? 2 : 1; return TRUE; /* NEL */ + case 0x2028: /* LS */ + case 0x2029: *lenptr = 3; return TRUE; /* PS */ +#else + case 0x0085: /* NEL */ + case 0x2028: /* LS */ + case 0x2029: *lenptr = 1; return TRUE; /* PS */ +#endif /* COMPILE_PCRE8 */ + default: return FALSE; + } +} + +/* End of pcre_newline.c */ diff --git a/src/lib/pcre/pcre_ord2utf8.c b/src/lib/pcre/pcre_ord2utf8.c new file mode 100644 index 0000000..6afd235 --- /dev/null +++ b/src/lib/pcre/pcre_ord2utf8.c @@ -0,0 +1,97 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This file contains a private PCRE function that converts an ordinal +character value into a UTF8 string. */ + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Convert character value to UTF-8 * +*************************************************/ + +/* This function takes an integer value in the range 0 - 0x10ffff +and encodes it as a UTF-8 character in 1 to 6 pcre_uchars. + +Arguments: + cvalue the character value + buffer pointer to buffer for result - at least 6 pcre_uchars long + +Returns: number of characters placed in the buffer +*/ + +int +PRIV(ord2utf)(pcre_uint32 cvalue, pcre_uchar *buffer) +{ +#ifdef SUPPORT_UTF + +register int i, j; + +/* Checking invalid cvalue character, encoded as invalid UTF-16 character. +Should never happen in practice. */ +if ((cvalue & 0xf800) == 0xd800 || cvalue >= 0x110000) + cvalue = 0xfffe; + +for (i = 0; i < PRIV(utf8_table1_size); i++) + if ((int)cvalue <= PRIV(utf8_table1)[i]) break; +buffer += i; +for (j = i; j > 0; j--) + { + *buffer-- = 0x80 | (cvalue & 0x3f); + cvalue >>= 6; + } +*buffer = PRIV(utf8_table2)[i] | cvalue; +return i + 1; + +#else + +(void)(cvalue); /* Keep compiler happy; this function won't ever be */ +(void)(buffer); /* called when SUPPORT_UTF is not defined. */ +return 0; + +#endif +} + +/* End of pcre_ord2utf8.c */ diff --git a/src/lib/pcre/pcre_refcount.c b/src/lib/pcre/pcre_refcount.c new file mode 100644 index 0000000..263a1e1 --- /dev/null +++ b/src/lib/pcre/pcre_refcount.c @@ -0,0 +1,89 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_refcount(), which is an +auxiliary function that can be used to maintain a reference count in a compiled +pattern data block. This might be helpful in applications where the block is +shared by different users. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Maintain reference count * +*************************************************/ + +/* The reference count is a 16-bit field, initialized to zero. It is not +possible to transfer a non-zero count from one host to a different host that +has a different byte order - though I can't see why anyone in their right mind +would ever want to do that! + +Arguments: + argument_re points to compiled code + adjust value to add to the count + +Returns: the (possibly updated) count value (a non-negative number), or + a negative error number +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre_refcount(pcre *argument_re, int adjust) +#else +PCRE_EXP_DEFN int PCRE_CALL_CONVENTION +pcre16_refcount(pcre16 *argument_re, int adjust) +#endif +{ +REAL_PCRE *re = (REAL_PCRE *)argument_re; +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) return PCRE_ERROR_BADMAGIC; +if ((re->flags & PCRE_MODE) == 0) return PCRE_ERROR_BADMODE; +re->ref_count = (-adjust > re->ref_count)? 0 : + (adjust + re->ref_count > 65535)? 65535 : + re->ref_count + adjust; +return re->ref_count; +} + +/* End of pcre_refcount.c */ diff --git a/src/lib/pcre/pcre_string_utils.c b/src/lib/pcre/pcre_string_utils.c new file mode 100644 index 0000000..d454553 --- /dev/null +++ b/src/lib/pcre/pcre_string_utils.c @@ -0,0 +1,168 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function that is used to match an extended +class. It is used by both pcre_exec() and pcre_def_exec(). */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#ifndef COMPILE_PCRE8 + +/************************************************* +* Compare string utilities * +*************************************************/ + +/* The following two functions compares two strings. Basically an strcmp +for non 8 bit characters. + +Arguments: + str1 first string + str2 second string + +Returns: 0 if both string are equal (like strcmp), 1 otherwise +*/ + +int +PRIV(strcmp_uc_uc)(const pcre_uchar *str1, const pcre_uchar *str2) +{ +pcre_uchar c1; +pcre_uchar c2; + +while (*str1 != '\0' || *str2 != '\0') + { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +int +PRIV(strcmp_uc_c8)(const pcre_uchar *str1, const char *str2) +{ +const pcre_uint8 *ustr2 = (pcre_uint8 *)str2; +pcre_uchar c1; +pcre_uchar c2; + +while (*str1 != '\0' || *ustr2 != '\0') + { + c1 = *str1++; + c2 = (pcre_uchar)*ustr2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +/* The following two functions compares two, fixed length +strings. Basically an strncmp for non 8 bit characters. + +Arguments: + str1 first string + str2 second string + num size of the string + +Returns: 0 if both string are equal (like strcmp), 1 otherwise +*/ + +int +PRIV(strncmp_uc_uc)(const pcre_uchar *str1, const pcre_uchar *str2, unsigned int num) +{ +pcre_uchar c1; +pcre_uchar c2; + +while (num-- > 0) + { + c1 = *str1++; + c2 = *str2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +int +PRIV(strncmp_uc_c8)(const pcre_uchar *str1, const char *str2, unsigned int num) +{ +const pcre_uint8 *ustr2 = (pcre_uint8 *)str2; +pcre_uchar c1; +pcre_uchar c2; + +while (num-- > 0) + { + c1 = *str1++; + c2 = (pcre_uchar)*ustr2++; + if (c1 != c2) + return ((c1 > c2) << 1) - 1; + } +/* Both length and characters must be equal. */ +return 0; +} + +/* The following function returns with the length of +a zero terminated string. Basically an strlen for non 8 bit characters. + +Arguments: + str string + +Returns: length of the string +*/ + +unsigned int +PRIV(strlen_uc)(const pcre_uchar *str) +{ +unsigned int len = 0; +while (*str++ != 0) + len++; +return len; +} + +#endif /* COMPILE_PCRE8 */ + +/* End of pcre_string_utils.c */ diff --git a/src/lib/pcre/pcre_study.c b/src/lib/pcre/pcre_study.c new file mode 100644 index 0000000..6b6edc3 --- /dev/null +++ b/src/lib/pcre/pcre_study.c @@ -0,0 +1,1527 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_study(), along with local +supporting functions. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#define SET_BIT(c) start_bits[c/8] |= (1 << (c&7)) + +/* Returns from set_start_bits() */ + +enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN }; + + + +/************************************************* +* Find the minimum subject length for a group * +*************************************************/ + +/* Scan a parenthesized group and compute the minimum length of subject that +is needed to match it. This is a lower bound; it does not mean there is a +string of that length that matches. In UTF8 mode, the result is in characters +rather than bytes. + +Arguments: + code pointer to start of group (the bracket) + startcode pointer to start of the whole pattern + options the compiling options + int RECURSE depth + +Returns: the minimum length + -1 if \C in UTF-8 mode or (*ACCEPT) was encountered + -2 internal error (missing capturing bracket) + -3 internal error (opcode not listed) +*/ + +static int +find_minlength(const pcre_uchar *code, const pcre_uchar *startcode, int options, + int recurse_depth) +{ +int length = -1; +/* PCRE_UTF16 has the same value as PCRE_UTF8. */ +BOOL utf = (options & PCRE_UTF8) != 0; +BOOL had_recurse = FALSE; +register int branchlength = 0; +register pcre_uchar *cc = (pcre_uchar *)code + 1 + LINK_SIZE; + +if (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS) cc += IMM2_SIZE; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d, min; + pcre_uchar *cs, *ce; + register int op = *cc; + + switch (op) + { + case OP_COND: + case OP_SCOND: + + /* If there is only one branch in a condition, the implied branch has zero + length, so we don't add anything. This covers the DEFINE "condition" + automatically. */ + + cs = cc + GET(cc, 1); + if (*cs != OP_ALT) + { + cc = cs + 1 + LINK_SIZE; + break; + } + + /* Otherwise we can fall through and treat it the same as any other + subpattern. */ + + case OP_CBRA: + case OP_SCBRA: + case OP_BRA: + case OP_SBRA: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_ONCE: + case OP_ONCE_NC: + d = find_minlength(cc, startcode, options, recurse_depth); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* ACCEPT makes things far too complicated; we have to give up. */ + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + return -1; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is END it's + the end of the outer call. All can be handled by the same code. If an + ACCEPT was previously encountered, use the length that was in force at that + time, and pass back the shortest ACCEPT length. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + case OP_END: + if (length < 0 || (!had_recurse && branchlength < length)) + length = branchlength; + if (op != OP_ALT) return length; + cc += 1 + LINK_SIZE; + branchlength = 0; + had_recurse = FALSE; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += GET(cc, 1); while (*cc == OP_ALT); + /* Fall through */ + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + case OP_CREF: + case OP_NCREF: + case OP_RREF: + case OP_NRREF: + case OP_DEF: + case OP_CALLOUT: + case OP_SOD: + case OP_SOM: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_CIRCM: + case OP_DOLL: + case OP_DOLLM: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc += PRIV(OP_lengths)[*cc]; + break; + + /* Skip over a subpattern that has a {0} or {0,x} quantifier */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + case OP_SKIPZERO: + cc += PRIV(OP_lengths)[*cc]; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* Handle literal characters and + repetitions */ + + case OP_CHAR: + case OP_CHARI: + case OP_NOT: + case OP_NOTI: + case OP_PLUS: + case OP_PLUSI: + case OP_MINPLUS: + case OP_MINPLUSI: + case OP_POSPLUS: + case OP_POSPLUSI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + branchlength++; + cc += 2; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + branchlength++; + cc += (cc[1] == OP_PROP || cc[1] == OP_NOTPROP)? 4 : 2; + break; + + /* Handle exact repetitions. The count is already in characters, but we + need to skip over a multibyte character in UTF8 mode. */ + + case OP_EXACT: + case OP_EXACTI: + case OP_NOTEXACT: + case OP_NOTEXACTI: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + case OP_TYPEEXACT: + branchlength += GET2(cc,1); + cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP + || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0); + break; + + /* Handle single-char non-literal matchers */ + + case OP_PROP: + case OP_NOTPROP: + cc += 2; + /* Fall through */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ALLANY: + case OP_EXTUNI: + case OP_HSPACE: + case OP_NOT_HSPACE: + case OP_VSPACE: + case OP_NOT_VSPACE: + branchlength++; + cc++; + break; + + /* "Any newline" might match two characters, but it also might match just + one. */ + + case OP_ANYNL: + branchlength += 1; + cc++; + break; + + /* The single-byte matcher means we can't proceed in UTF-8 mode. (In + non-UTF-8 mode \C will actually be turned into OP_ALLANY, so won't ever + appear, but leave the code, just in case.) */ + + case OP_ANYBYTE: +#ifdef SUPPORT_UTF + if (utf) return -1; +#endif + branchlength++; + cc++; + break; + + /* For repeated character types, we have to test for \p and \P, which have + an extra two bytes of parameters. */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSQUERY: + if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2; + cc += PRIV(OP_lengths)[op]; + break; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + if (cc[1 + IMM2_SIZE] == OP_PROP + || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2; + cc += PRIV(OP_lengths)[op]; + break; + + /* Check a class for variable quantification */ + +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: + cc += GET(cc, 1) - PRIV(OP_lengths)[OP_CLASS]; + /* Fall through */ +#endif + + case OP_CLASS: + case OP_NCLASS: + cc += PRIV(OP_lengths)[OP_CLASS]; + + switch (*cc) + { + case OP_CRPLUS: + case OP_CRMINPLUS: + branchlength++; + /* Fall through */ + + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + branchlength += GET2(cc,1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + branchlength++; + break; + } + break; + + /* Backreferences and subroutine calls are treated in the same way: we find + the minimum length for the subpattern. A recursion, however, causes an + a flag to be set that causes the length of this branch to be ignored. The + logic is that a recursion can only make sense if there is another + alternation that stops the recursing. That will provide the minimum length + (when no recursion happens). A backreference within the group that it is + referencing behaves in the same way. + + If PCRE_JAVASCRIPT_COMPAT is set, a backreference to an unset bracket + matches an empty string (by default it causes a matching failure), so in + that case we must set the minimum length to zero. */ + + case OP_REF: + case OP_REFI: + if ((options & PCRE_JAVASCRIPT_COMPAT) == 0) + { + ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1)); + if (cs == NULL) return -2; + do ce += GET(ce, 1); while (*ce == OP_ALT); + if (cc > cs && cc < ce) + { + d = 0; + had_recurse = TRUE; + } + else + { + d = find_minlength(cs, startcode, options, recurse_depth); + } + } + else d = 0; + cc += 1 + IMM2_SIZE; + + /* Handle repeated back references */ + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + min = 0; + cc++; + break; + + case OP_CRPLUS: + case OP_CRMINPLUS: + min = 1; + cc++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = GET2(cc, 1); + cc += 1 + 2 * IMM2_SIZE; + break; + + default: + min = 1; + break; + } + + branchlength += min * d; + break; + + /* We can easily detect direct recursion, but not mutual recursion. This is + caught by a recursion depth count. */ + + case OP_RECURSE: + cs = ce = (pcre_uchar *)startcode + GET(cc, 1); + do ce += GET(ce, 1); while (*ce == OP_ALT); + if ((cc > cs && cc < ce) || recurse_depth > 10) + had_recurse = TRUE; + else + { + branchlength += find_minlength(cs, startcode, options, recurse_depth + 1); + } + cc += 1 + LINK_SIZE; + break; + + /* Anything else does not or need not match a character. We can get the + item's length from the table, but for those that can match zero occurrences + of a character, we must take special action for UTF-8 characters. As it + happens, the "NOT" versions of these opcodes are used at present only for + ASCII characters, so they could be omitted from this list. However, in + future that may change, so we include them here so as not to leave a + gotcha for a future maintainer. */ + + case OP_UPTO: + case OP_UPTOI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_MINUPTO: + case OP_MINUPTOI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_POSUPTO: + case OP_POSUPTOI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + + case OP_STAR: + case OP_STARI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_MINSTAR: + case OP_MINSTARI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_POSSTAR: + case OP_POSSTARI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + + case OP_QUERY: + case OP_QUERYI: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_MINQUERY: + case OP_MINQUERYI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_POSQUERY: + case OP_POSQUERYI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + + cc += PRIV(OP_lengths)[op]; +#ifdef SUPPORT_UTF + if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]); +#endif + break; + + /* Skip these, but we need to add in the name length. */ + + case OP_MARK: + case OP_PRUNE_ARG: + case OP_SKIP_ARG: + case OP_THEN_ARG: + cc += PRIV(OP_lengths)[op] + cc[1]; + break; + + /* The remaining opcodes are just skipped over. */ + + case OP_CLOSE: + case OP_COMMIT: + case OP_FAIL: + case OP_PRUNE: + case OP_SET_SOM: + case OP_SKIP: + case OP_THEN: + cc += PRIV(OP_lengths)[op]; + break; + + /* This should not occur: we list all opcodes explicitly so that when + new ones get added they are properly considered. */ + + default: + return -3; + } + } +/* Control never gets here */ +} + + + +/************************************************* +* Set a bit and maybe its alternate case * +*************************************************/ + +/* Given a character, set its first byte's bit in the table, and also the +corresponding bit for the other version of a letter if we are caseless. In +UTF-8 mode, for characters greater than 127, we can only do the caseless thing +when Unicode property support is available. + +Arguments: + start_bits points to the bit map + p points to the character + caseless the caseless flag + cd the block with char table pointers + utf TRUE for UTF-8 / UTF-16 mode + +Returns: pointer after the character +*/ + +static const pcre_uchar * +set_table_bit(pcre_uint8 *start_bits, const pcre_uchar *p, BOOL caseless, + compile_data *cd, BOOL utf) +{ +unsigned int c = *p; + +#ifdef COMPILE_PCRE8 +SET_BIT(c); + +#ifdef SUPPORT_UTF +if (utf && c > 127) + { + GETCHARINC(c, p); +#ifdef SUPPORT_UCP + if (caseless) + { + pcre_uchar buff[6]; + c = UCD_OTHERCASE(c); + (void)PRIV(ord2utf)(c, buff); + SET_BIT(buff[0]); + } +#endif + return p; + } +#endif + +/* Not UTF-8 mode, or character is less than 127. */ + +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]); +return p + 1; +#endif + +#ifdef COMPILE_PCRE16 +if (c > 0xff) + { + c = 0xff; + caseless = FALSE; + } +SET_BIT(c); + +#ifdef SUPPORT_UTF +if (utf && c > 127) + { + GETCHARINC(c, p); +#ifdef SUPPORT_UCP + if (caseless) + { + c = UCD_OTHERCASE(c); + if (c > 0xff) + c = 0xff; + SET_BIT(c); + } +#endif + return p; + } +#endif + +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]); +return p + 1; +#endif +} + + + +/************************************************* +* Set bits for a positive character type * +*************************************************/ + +/* This function sets starting bits for a character type. In UTF-8 mode, we can +only do a direct setting for bytes less than 128, as otherwise there can be +confusion with bytes in the middle of UTF-8 characters. In a "traditional" +environment, the tables will only recognize ASCII characters anyway, but in at +least one Windows environment, some higher bytes bits were set in the tables. +So we deal with that case by considering the UTF-8 encoding. + +Arguments: + start_bits the starting bitmap + cbit type the type of character wanted + table_limit 32 for non-UTF-8; 16 for UTF-8 + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_type_bits(pcre_uint8 *start_bits, int cbit_type, int table_limit, + compile_data *cd) +{ +register int c; +for (c = 0; c < table_limit; c++) start_bits[c] |= cd->cbits[c+cbit_type]; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (table_limit == 32) return; +for (c = 128; c < 256; c++) + { + if ((cd->cbits[c/8] & (1 << (c&7))) != 0) + { + pcre_uchar buff[6]; + (void)PRIV(ord2utf)(c, buff); + SET_BIT(buff[0]); + } + } +#endif +} + + +/************************************************* +* Set bits for a negative character type * +*************************************************/ + +/* This function sets starting bits for a negative character type such as \D. +In UTF-8 mode, we can only do a direct setting for bytes less than 128, as +otherwise there can be confusion with bytes in the middle of UTF-8 characters. +Unlike in the positive case, where we can set appropriate starting bits for +specific high-valued UTF-8 characters, in this case we have to set the bits for +all high-valued characters. The lowest is 0xc2, but we overkill by starting at +0xc0 (192) for simplicity. + +Arguments: + start_bits the starting bitmap + cbit type the type of character wanted + table_limit 32 for non-UTF-8; 16 for UTF-8 + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_nottype_bits(pcre_uint8 *start_bits, int cbit_type, int table_limit, + compile_data *cd) +{ +register int c; +for (c = 0; c < table_limit; c++) start_bits[c] |= ~cd->cbits[c+cbit_type]; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +if (table_limit != 32) for (c = 24; c < 32; c++) start_bits[c] = 0xff; +#endif +} + + + +/************************************************* +* Create bitmap of starting bytes * +*************************************************/ + +/* This function scans a compiled unanchored expression recursively and +attempts to build a bitmap of the set of possible starting bytes. As time goes +by, we may be able to get more clever at doing this. The SSB_CONTINUE return is +useful for parenthesized groups in patterns such as (a*)b where the group +provides some optional starting bytes but scanning must continue at the outer +level to find at least one mandatory byte. At the outermost level, this +function fails unless the result is SSB_DONE. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + utf TRUE if in UTF-8 / UTF-16 mode + cd the block with char table pointers + +Returns: SSB_FAIL => Failed to find any starting bytes + SSB_DONE => Found mandatory starting bytes + SSB_CONTINUE => Found optional starting bytes + SSB_UNKNOWN => Hit an unrecognized opcode +*/ + +static int +set_start_bits(const pcre_uchar *code, pcre_uint8 *start_bits, BOOL utf, + compile_data *cd) +{ +register int c; +int yield = SSB_DONE; +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 +int table_limit = utf? 16:32; +#else +int table_limit = 32; +#endif + +#if 0 +/* ========================================================================= */ +/* The following comment and code was inserted in January 1999. In May 2006, +when it was observed to cause compiler warnings about unused values, I took it +out again. If anybody is still using OS/2, they will have to put it back +manually. */ + +/* This next statement and the later reference to dummy are here in order to +trick the optimizer of the IBM C compiler for OS/2 into generating correct +code. Apparently IBM isn't going to fix the problem, and we would rather not +disable optimization (in this module it actually makes a big difference, and +the pcre module can use all the optimization it can get). */ + +volatile int dummy; +/* ========================================================================= */ +#endif + +do + { + BOOL try_next = TRUE; + const pcre_uchar *tcode = code + 1 + LINK_SIZE; + + if (*code == OP_CBRA || *code == OP_SCBRA || + *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE; + + while (try_next) /* Loop for items in this branch */ + { + int rc; + + switch(*tcode) + { + /* If we reach something we don't understand, it means a new opcode has + been created that hasn't been added to this code. Hopefully this problem + will be discovered during testing. */ + + default: + return SSB_UNKNOWN; + + /* Fail for a valid opcode that implies no starting bits. */ + + case OP_ACCEPT: + case OP_ASSERT_ACCEPT: + case OP_ALLANY: + case OP_ANY: + case OP_ANYBYTE: + case OP_CIRC: + case OP_CIRCM: + case OP_CLOSE: + case OP_COMMIT: + case OP_COND: + case OP_CREF: + case OP_DEF: + case OP_DOLL: + case OP_DOLLM: + case OP_END: + case OP_EOD: + case OP_EODN: + case OP_EXTUNI: + case OP_FAIL: + case OP_MARK: + case OP_NCREF: + case OP_NOT: + case OP_NOTEXACT: + case OP_NOTEXACTI: + case OP_NOTI: + case OP_NOTMINPLUS: + case OP_NOTMINPLUSI: + case OP_NOTMINQUERY: + case OP_NOTMINQUERYI: + case OP_NOTMINSTAR: + case OP_NOTMINSTARI: + case OP_NOTMINUPTO: + case OP_NOTMINUPTOI: + case OP_NOTPLUS: + case OP_NOTPLUSI: + case OP_NOTPOSPLUS: + case OP_NOTPOSPLUSI: + case OP_NOTPOSQUERY: + case OP_NOTPOSQUERYI: + case OP_NOTPOSSTAR: + case OP_NOTPOSSTARI: + case OP_NOTPOSUPTO: + case OP_NOTPOSUPTOI: + case OP_NOTPROP: + case OP_NOTQUERY: + case OP_NOTQUERYI: + case OP_NOTSTAR: + case OP_NOTSTARI: + case OP_NOTUPTO: + case OP_NOTUPTOI: + case OP_NOT_HSPACE: + case OP_NOT_VSPACE: + case OP_NRREF: + case OP_PROP: + case OP_PRUNE: + case OP_PRUNE_ARG: + case OP_RECURSE: + case OP_REF: + case OP_REFI: + case OP_REVERSE: + case OP_RREF: + case OP_SCOND: + case OP_SET_SOM: + case OP_SKIP: + case OP_SKIP_ARG: + case OP_SOD: + case OP_SOM: + case OP_THEN: + case OP_THEN_ARG: +#if defined SUPPORT_UTF || !defined COMPILE_PCRE8 + case OP_XCLASS: +#endif + return SSB_FAIL; + + /* We can ignore word boundary tests. */ + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + tcode++; + break; + + /* If we hit a bracket or a positive lookahead assertion, recurse to set + bits from within the subpattern. If it can't find anything, we have to + give up. If it finds some mandatory character(s), we are done for this + branch. Otherwise, carry on scanning after the subpattern. */ + + case OP_BRA: + case OP_SBRA: + case OP_CBRA: + case OP_SCBRA: + case OP_BRAPOS: + case OP_SBRAPOS: + case OP_CBRAPOS: + case OP_SCBRAPOS: + case OP_ONCE: + case OP_ONCE_NC: + case OP_ASSERT: + rc = set_start_bits(tcode, start_bits, utf, cd); + if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; + if (rc == SSB_DONE) try_next = FALSE; else + { + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + } + break; + + /* If we hit ALT or KET, it means we haven't found anything mandatory in + this branch, though we might have found something optional. For ALT, we + continue with the next alternative, but we have to arrange that the final + result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET, + return SSB_CONTINUE: if this is the top level, that indicates failure, + but after a nested subpattern, it causes scanning to continue. */ + + case OP_ALT: + yield = SSB_CONTINUE; + try_next = FALSE; + break; + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_KETRPOS: + return SSB_CONTINUE; + + /* Skip over callout */ + + case OP_CALLOUT: + tcode += 2 + 2*LINK_SIZE; + break; + + /* Skip over lookbehind and negative lookahead assertions */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + case OP_BRAPOSZERO: + rc = set_start_bits(++tcode, start_bits, utf, cd); + if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc; +/* ========================================================================= + See the comment at the head of this function concerning the next line, + which was an old fudge for the benefit of OS/2. + dummy = 1; + ========================================================================= */ + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* SKIPZERO skips the bracket. */ + + case OP_SKIPZERO: + tcode++; + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + tcode = set_table_bit(start_bits, tcode + 1, FALSE, cd, utf); + break; + + case OP_STARI: + case OP_MINSTARI: + case OP_POSSTARI: + case OP_QUERYI: + case OP_MINQUERYI: + case OP_POSQUERYI: + tcode = set_table_bit(start_bits, tcode + 1, TRUE, cd, utf); + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, FALSE, cd, utf); + break; + + case OP_UPTOI: + case OP_MINUPTOI: + case OP_POSUPTOI: + tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, TRUE, cd, utf); + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: + tcode += IMM2_SIZE; + /* Fall through */ + case OP_CHAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + (void)set_table_bit(start_bits, tcode + 1, FALSE, cd, utf); + try_next = FALSE; + break; + + case OP_EXACTI: + tcode += IMM2_SIZE; + /* Fall through */ + case OP_CHARI: + case OP_PLUSI: + case OP_MINPLUSI: + case OP_POSPLUSI: + (void)set_table_bit(start_bits, tcode + 1, TRUE, cd, utf); + try_next = FALSE; + break; + + /* Special spacing and line-terminating items. These recognize specific + lists of characters. The difference between VSPACE and ANYNL is that the + latter can match the two-character CRLF sequence, but that is not + relevant for finding the first character, so their code here is + identical. */ + + case OP_HSPACE: + SET_BIT(0x09); + SET_BIT(0x20); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+00A0 */ + SET_BIT(0xE1); /* For U+1680, U+180E */ + SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ + SET_BIT(0xE3); /* For U+3000 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0xA0); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + { + SET_BIT(0xA0); +#ifdef COMPILE_PCRE16 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + try_next = FALSE; + break; + + case OP_ANYNL: + case OP_VSPACE: + SET_BIT(0x0A); + SET_BIT(0x0B); + SET_BIT(0x0C); + SET_BIT(0x0D); +#ifdef SUPPORT_UTF + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+0085 */ + SET_BIT(0xE2); /* For U+2028, U+2029 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0x85); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + { + SET_BIT(0x85); +#ifdef COMPILE_PCRE16 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + try_next = FALSE; + break; + + /* Single character types set the bits and stop. Note that if PCRE_UCP + is set, we do not see these op codes because \d etc are converted to + properties. Therefore, these apply in the case when only characters less + than 256 are recognized to match the types. */ + + case OP_NOT_DIGIT: + set_nottype_bits(start_bits, cbit_digit, table_limit, cd); + try_next = FALSE; + break; + + case OP_DIGIT: + set_type_bits(start_bits, cbit_digit, table_limit, cd); + try_next = FALSE; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + ensure it is set as not whitespace. */ + + case OP_NOT_WHITESPACE: + set_nottype_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] |= 0x08; + try_next = FALSE; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + not set it from the table. */ + + case OP_WHITESPACE: + c = start_bits[1]; /* Save in case it was already set */ + set_type_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] = (start_bits[1] & ~0x08) | c; + try_next = FALSE; + break; + + case OP_NOT_WORDCHAR: + set_nottype_bits(start_bits, cbit_word, table_limit, cd); + try_next = FALSE; + break; + + case OP_WORDCHAR: + set_type_bits(start_bits, cbit_word, table_limit, cd); + try_next = FALSE; + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + tcode++; + break; + + case OP_TYPEEXACT: + tcode += 1 + IMM2_SIZE; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + tcode += IMM2_SIZE; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + switch(tcode[1]) + { + default: + case OP_ANY: + case OP_ALLANY: + return SSB_FAIL; + + case OP_HSPACE: + SET_BIT(0x09); + SET_BIT(0x20); +#ifdef COMPILE_PCRE8 + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+00A0 */ + SET_BIT(0xE1); /* For U+1680, U+180E */ + SET_BIT(0xE2); /* For U+2000 - U+200A, U+202F, U+205F */ + SET_BIT(0xE3); /* For U+3000 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0xA0); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + SET_BIT(0xA0); + break; + + case OP_ANYNL: + case OP_VSPACE: + SET_BIT(0x0A); + SET_BIT(0x0B); + SET_BIT(0x0C); + SET_BIT(0x0D); +#ifdef COMPILE_PCRE8 + if (utf) + { +#ifdef COMPILE_PCRE8 + SET_BIT(0xC2); /* For U+0085 */ + SET_BIT(0xE2); /* For U+2028, U+2029 */ +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0x85); + SET_BIT(0xFF); /* For characters > 255 */ +#endif + } + else +#endif /* SUPPORT_UTF */ + SET_BIT(0x85); + break; + + case OP_NOT_DIGIT: + set_nottype_bits(start_bits, cbit_digit, table_limit, cd); + break; + + case OP_DIGIT: + set_type_bits(start_bits, cbit_digit, table_limit, cd); + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + ensure it gets set as not whitespace. */ + + case OP_NOT_WHITESPACE: + set_nottype_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] |= 0x08; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + avoid setting it. */ + + case OP_WHITESPACE: + c = start_bits[1]; /* Save in case it was already set */ + set_type_bits(start_bits, cbit_space, table_limit, cd); + start_bits[1] = (start_bits[1] & ~0x08) | c; + break; + + case OP_NOT_WORDCHAR: + set_nottype_bits(start_bits, cbit_word, table_limit, cd); + break; + + case OP_WORDCHAR: + set_type_bits(start_bits, cbit_word, table_limit, cd); + break; + } + + tcode += 2; + break; + + /* Character class where all the information is in a bit map: set the + bits and either carry on or not, according to the repeat count. If it was + a negative class, and we are operating with UTF-8 characters, any byte + with a value >= 0xc4 is a potentially valid starter because it starts a + character with a value > 255. */ + + case OP_NCLASS: +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + if (utf) + { + start_bits[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */ + memset(start_bits+25, 0xff, 7); /* Bits for 0xc9 - 0xff */ + } +#endif +#ifdef COMPILE_PCRE16 + SET_BIT(0xFF); /* For characters > 255 */ +#endif + /* Fall through */ + + case OP_CLASS: + { + pcre_uint8 *map; + tcode++; + map = (pcre_uint8 *)tcode; + + /* In UTF-8 mode, the bits in a bit map correspond to character + values, not to byte values. However, the bit map we are constructing is + for byte values. So we have to do a conversion for characters whose + value is > 127. In fact, there are only two possible starting bytes for + characters in the range 128 - 255. */ + +#if defined SUPPORT_UTF && defined COMPILE_PCRE8 + if (utf) + { + for (c = 0; c < 16; c++) start_bits[c] |= map[c]; + for (c = 128; c < 256; c++) + { + if ((map[c/8] && (1 << (c&7))) != 0) + { + int d = (c >> 6) | 0xc0; /* Set bit for this starter */ + start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */ + c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */ + } + } + } + else +#endif + { + /* In non-UTF-8 mode, the two bit maps are completely compatible. */ + for (c = 0; c < 32; c++) start_bits[c] |= map[c]; + } + + /* Advance past the bit map, and act on what follows. For a zero + minimum repeat, continue; otherwise stop processing. */ + + tcode += 32 / sizeof(pcre_uchar); + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE; + else try_next = FALSE; + break; + + default: + try_next = FALSE; + break; + } + } + break; /* End of bitmap class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += GET(code, 1); /* Advance to next branch */ + } +while (*code == OP_ALT); +return yield; +} + + + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre[16]_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre[16]_extra block, with study_data filled in and + the appropriate flags set; + NULL on error or if no optimization possible +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION +pcre_study(const pcre *external_re, int options, const char **errorptr) +#else +PCRE_EXP_DEFN pcre16_extra * PCRE_CALL_CONVENTION +pcre16_study(const pcre16 *external_re, int options, const char **errorptr) +#endif +{ +int min; +BOOL bits_set = FALSE; +pcre_uint8 start_bits[32]; +PUBL(extra) *extra = NULL; +pcre_study_data *study; +const pcre_uint8 *tables; +pcre_uchar *code; +compile_data compile_block; +const REAL_PCRE *re = (const REAL_PCRE *)external_re; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((re->flags & PCRE_MODE) == 0) + { +#ifdef COMPILE_PCRE8 + *errorptr = "argument is compiled in 16 bit mode"; +#else + *errorptr = "argument is compiled in 8 bit mode"; +#endif + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +code = (pcre_uchar *)re + re->name_table_offset + + (re->name_count * re->name_entry_size); + +/* For an anchored pattern, or an unanchored pattern that has a first char, or +a multiline pattern that matches only at "line starts", there is no point in +seeking a list of starting bytes. */ + +if ((re->options & PCRE_ANCHORED) == 0 && + (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0) + { + int rc; + + /* Set the character tables in the block that is passed around */ + + tables = re->tables; + +#ifdef COMPILE_PCRE8 + if (tables == NULL) + (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#else + if (tables == NULL) + (void)pcre16_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); +#endif + + compile_block.lcc = tables + lcc_offset; + compile_block.fcc = tables + fcc_offset; + compile_block.cbits = tables + cbits_offset; + compile_block.ctypes = tables + ctypes_offset; + + /* See if we can find a fixed set of initial characters for the pattern. */ + + memset(start_bits, 0, 32 * sizeof(pcre_uint8)); + rc = set_start_bits(code, start_bits, (re->options & PCRE_UTF8) != 0, + &compile_block); + bits_set = rc == SSB_DONE; + if (rc == SSB_UNKNOWN) + { + *errorptr = "internal error: opcode not recognized"; + return NULL; + } + } + +/* Find the minimum length of subject string. */ + +switch(min = find_minlength(code, code, re->options, 0)) + { + case -2: *errorptr = "internal error: missing capturing bracket"; return NULL; + case -3: *errorptr = "internal error: opcode not recognized"; return NULL; + default: break; + } + +/* If a set of starting bytes has been identified, or if the minimum length is +greater than zero, or if JIT optimization has been requested, get a +pcre[16]_extra block and a pcre_study_data block. The study data is put in the +latter, which is pointed to by the former, which may also get additional data +set later by the calling program. At the moment, the size of pcre_study_data +is fixed. We nevertheless save it in a field for returning via the +pcre_fullinfo() function so that if it becomes variable in the future, +we don't have to change that code. */ + +if (bits_set || min > 0 +#ifdef SUPPORT_JIT + || (options & PCRE_STUDY_JIT_COMPILE) != 0 +#endif + ) + { + extra = (PUBL(extra) *)(PUBL(malloc)) + (sizeof(PUBL(extra)) + sizeof(pcre_study_data)); + if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + + study = (pcre_study_data *)((char *)extra + sizeof(PUBL(extra))); + extra->flags = PCRE_EXTRA_STUDY_DATA; + extra->study_data = study; + + study->size = sizeof(pcre_study_data); + study->flags = 0; + + /* Set the start bits always, to avoid unset memory errors if the + study data is written to a file, but set the flag only if any of the bits + are set, to save time looking when none are. */ + + if (bits_set) + { + study->flags |= PCRE_STUDY_MAPPED; + memcpy(study->start_bits, start_bits, sizeof(start_bits)); + } + else memset(study->start_bits, 0, 32 * sizeof(pcre_uint8)); + +#ifdef PCRE_DEBUG + if (bits_set) + { + pcre_uint8 *ptr = start_bits; + int i; + + printf("Start bits:\n"); + for (i = 0; i < 32; i++) + printf("%3d: %02x%s", i * 8, *ptr++, ((i + 1) & 0x7) != 0? " " : "\n"); + } +#endif + + /* Always set the minlength value in the block, because the JIT compiler + makes use of it. However, don't set the bit unless the length is greater than + zero - the interpretive pcre_exec() and pcre_dfa_exec() needn't waste time + checking the zero case. */ + + if (min > 0) + { + study->flags |= PCRE_STUDY_MINLEN; + study->minlength = min; + } + else study->minlength = 0; + + /* If JIT support was compiled and requested, attempt the JIT compilation. + If no starting bytes were found, and the minimum length is zero, and JIT + compilation fails, abandon the extra block and return NULL. */ + +#ifdef SUPPORT_JIT + extra->executable_jit = NULL; + if ((options & PCRE_STUDY_JIT_COMPILE) != 0) PRIV(jit_compile)(re, extra); + if (study->flags == 0 && (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) == 0) + { +#ifdef COMPILE_PCRE8 + pcre_free_study(extra); +#endif +#ifdef COMPILE_PCRE16 + pcre16_free_study(extra); +#endif + extra = NULL; + } +#endif + } + +return extra; +} + + +/************************************************* +* Free the study data * +*************************************************/ + +/* This function frees the memory that was obtained by pcre_study(). + +Argument: a pointer to the pcre[16]_extra block +Returns: nothing +*/ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN void +pcre_free_study(pcre_extra *extra) +#else +PCRE_EXP_DEFN void +pcre16_free_study(pcre16_extra *extra) +#endif +{ +if (extra == NULL) + return; +#ifdef SUPPORT_JIT +if ((extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 && + extra->executable_jit != NULL) + PRIV(jit_free)(extra->executable_jit); +#endif +PUBL(free)(extra); +} + +/* End of pcre_study.c */ diff --git a/src/lib/pcre/pcre_tables.c b/src/lib/pcre/pcre_tables.c new file mode 100644 index 0000000..9e449f8 --- /dev/null +++ b/src/lib/pcre/pcre_tables.c @@ -0,0 +1,568 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 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 PCRE_INCLUDED + +/* This module contains some fixed tables that are used by more than one of the +PCRE code modules. The tables are also #included by the pcretest program, which +uses macros to change their names from _pcre_xxx to xxxx, thereby avoiding name +clashes with the library. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#endif /* PCRE_INCLUDED */ + +/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that +the definition is next to the definition of the opcodes in pcre_internal.h. */ + +const pcre_uint8 PRIV(OP_lengths)[] = { OP_LENGTHS }; + + + +/************************************************* +* Tables for UTF-8 support * +*************************************************/ + +/* These are the breakpoints for different numbers of bytes in a UTF-8 +character. */ + +#if (defined SUPPORT_UTF && defined COMPILE_PCRE8) \ + || (defined PCRE_INCLUDED && defined SUPPORT_PCRE16) + +/* These tables are also required by pcretest in 16 bit mode. */ + +const int PRIV(utf8_table1)[] = + { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; + +const int PRIV(utf8_table1_size) = sizeof(PRIV(utf8_table1)) / sizeof(int); + +/* These are the indicator bits and the mask for the data bits to set in the +first byte of a character, indexed by the number of additional bytes. */ + +const int PRIV(utf8_table2)[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; +const int PRIV(utf8_table3)[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +/* Table of the number of extra bytes, indexed by the first byte masked with +0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */ + +const pcre_uint8 PRIV(utf8_table4)[] = { + 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, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; + +#endif /* (SUPPORT_UTF && COMPILE_PCRE8) || (PCRE_INCLUDED && SUPPORT_PCRE16)*/ + +#ifdef SUPPORT_UTF + +/* Table to translate from particular type value to the general value. */ + +const int PRIV(ucp_gentype)[] = { + ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */ + ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */ + ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */ + ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */ + ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */ + ucp_P, ucp_P, /* Ps, Po */ + ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */ + ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */ +}; + +#ifdef SUPPORT_JIT +/* This table reverses PRIV(ucp_gentype). We can save the cost +of a memory load. */ + +const int PRIV(ucp_typerange)[] = { + ucp_Cc, ucp_Cs, + ucp_Ll, ucp_Lu, + ucp_Mc, ucp_Mn, + ucp_Nd, ucp_No, + ucp_Pc, ucp_Ps, + ucp_Sc, ucp_So, + ucp_Zl, ucp_Zs, +}; +#endif /* SUPPORT_JIT */ + +/* The pcre_utt[] table below translates Unicode property names into type and +code values. It is searched by binary chop, so must be in collating sequence of +name. Originally, the table contained pointers to the name strings in the first +field of each entry. However, that leads to a large number of relocations when +a shared library is dynamically loaded. A significant reduction is made by +putting all the names into a single, large string and then using offsets in the +table itself. Maintenance is more error-prone, but frequent changes to this +data are unlikely. + +July 2008: There is now a script called maint/GenerateUtt.py that can be used +to generate this data automatically instead of maintaining it by hand. + +The script was updated in March 2009 to generate a new EBCDIC-compliant +version. Like all other character and string literals that are compared against +the regular expression pattern, we must use STR_ macros instead of literal +strings to make sure that UTF-8 support works on EBCDIC platforms. */ + +#define STRING_Any0 STR_A STR_n STR_y "\0" +#define STRING_Arabic0 STR_A STR_r STR_a STR_b STR_i STR_c "\0" +#define STRING_Armenian0 STR_A STR_r STR_m STR_e STR_n STR_i STR_a STR_n "\0" +#define STRING_Avestan0 STR_A STR_v STR_e STR_s STR_t STR_a STR_n "\0" +#define STRING_Balinese0 STR_B STR_a STR_l STR_i STR_n STR_e STR_s STR_e "\0" +#define STRING_Bamum0 STR_B STR_a STR_m STR_u STR_m "\0" +#define STRING_Batak0 STR_B STR_a STR_t STR_a STR_k "\0" +#define STRING_Bengali0 STR_B STR_e STR_n STR_g STR_a STR_l STR_i "\0" +#define STRING_Bopomofo0 STR_B STR_o STR_p STR_o STR_m STR_o STR_f STR_o "\0" +#define STRING_Brahmi0 STR_B STR_r STR_a STR_h STR_m STR_i "\0" +#define STRING_Braille0 STR_B STR_r STR_a STR_i STR_l STR_l STR_e "\0" +#define STRING_Buginese0 STR_B STR_u STR_g STR_i STR_n STR_e STR_s STR_e "\0" +#define STRING_Buhid0 STR_B STR_u STR_h STR_i STR_d "\0" +#define STRING_C0 STR_C "\0" +#define STRING_Canadian_Aboriginal0 STR_C STR_a STR_n STR_a STR_d STR_i STR_a STR_n STR_UNDERSCORE STR_A STR_b STR_o STR_r STR_i STR_g STR_i STR_n STR_a STR_l "\0" +#define STRING_Carian0 STR_C STR_a STR_r STR_i STR_a STR_n "\0" +#define STRING_Cc0 STR_C STR_c "\0" +#define STRING_Cf0 STR_C STR_f "\0" +#define STRING_Cham0 STR_C STR_h STR_a STR_m "\0" +#define STRING_Cherokee0 STR_C STR_h STR_e STR_r STR_o STR_k STR_e STR_e "\0" +#define STRING_Cn0 STR_C STR_n "\0" +#define STRING_Co0 STR_C STR_o "\0" +#define STRING_Common0 STR_C STR_o STR_m STR_m STR_o STR_n "\0" +#define STRING_Coptic0 STR_C STR_o STR_p STR_t STR_i STR_c "\0" +#define STRING_Cs0 STR_C STR_s "\0" +#define STRING_Cuneiform0 STR_C STR_u STR_n STR_e STR_i STR_f STR_o STR_r STR_m "\0" +#define STRING_Cypriot0 STR_C STR_y STR_p STR_r STR_i STR_o STR_t "\0" +#define STRING_Cyrillic0 STR_C STR_y STR_r STR_i STR_l STR_l STR_i STR_c "\0" +#define STRING_Deseret0 STR_D STR_e STR_s STR_e STR_r STR_e STR_t "\0" +#define STRING_Devanagari0 STR_D STR_e STR_v STR_a STR_n STR_a STR_g STR_a STR_r STR_i "\0" +#define STRING_Egyptian_Hieroglyphs0 STR_E STR_g STR_y STR_p STR_t STR_i STR_a STR_n STR_UNDERSCORE STR_H STR_i STR_e STR_r STR_o STR_g STR_l STR_y STR_p STR_h STR_s "\0" +#define STRING_Ethiopic0 STR_E STR_t STR_h STR_i STR_o STR_p STR_i STR_c "\0" +#define STRING_Georgian0 STR_G STR_e STR_o STR_r STR_g STR_i STR_a STR_n "\0" +#define STRING_Glagolitic0 STR_G STR_l STR_a STR_g STR_o STR_l STR_i STR_t STR_i STR_c "\0" +#define STRING_Gothic0 STR_G STR_o STR_t STR_h STR_i STR_c "\0" +#define STRING_Greek0 STR_G STR_r STR_e STR_e STR_k "\0" +#define STRING_Gujarati0 STR_G STR_u STR_j STR_a STR_r STR_a STR_t STR_i "\0" +#define STRING_Gurmukhi0 STR_G STR_u STR_r STR_m STR_u STR_k STR_h STR_i "\0" +#define STRING_Han0 STR_H STR_a STR_n "\0" +#define STRING_Hangul0 STR_H STR_a STR_n STR_g STR_u STR_l "\0" +#define STRING_Hanunoo0 STR_H STR_a STR_n STR_u STR_n STR_o STR_o "\0" +#define STRING_Hebrew0 STR_H STR_e STR_b STR_r STR_e STR_w "\0" +#define STRING_Hiragana0 STR_H STR_i STR_r STR_a STR_g STR_a STR_n STR_a "\0" +#define STRING_Imperial_Aramaic0 STR_I STR_m STR_p STR_e STR_r STR_i STR_a STR_l STR_UNDERSCORE STR_A STR_r STR_a STR_m STR_a STR_i STR_c "\0" +#define STRING_Inherited0 STR_I STR_n STR_h STR_e STR_r STR_i STR_t STR_e STR_d "\0" +#define STRING_Inscriptional_Pahlavi0 STR_I STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_UNDERSCORE STR_P STR_a STR_h STR_l STR_a STR_v STR_i "\0" +#define STRING_Inscriptional_Parthian0 STR_I STR_n STR_s STR_c STR_r STR_i STR_p STR_t STR_i STR_o STR_n STR_a STR_l STR_UNDERSCORE STR_P STR_a STR_r STR_t STR_h STR_i STR_a STR_n "\0" +#define STRING_Javanese0 STR_J STR_a STR_v STR_a STR_n STR_e STR_s STR_e "\0" +#define STRING_Kaithi0 STR_K STR_a STR_i STR_t STR_h STR_i "\0" +#define STRING_Kannada0 STR_K STR_a STR_n STR_n STR_a STR_d STR_a "\0" +#define STRING_Katakana0 STR_K STR_a STR_t STR_a STR_k STR_a STR_n STR_a "\0" +#define STRING_Kayah_Li0 STR_K STR_a STR_y STR_a STR_h STR_UNDERSCORE STR_L STR_i "\0" +#define STRING_Kharoshthi0 STR_K STR_h STR_a STR_r STR_o STR_s STR_h STR_t STR_h STR_i "\0" +#define STRING_Khmer0 STR_K STR_h STR_m STR_e STR_r "\0" +#define STRING_L0 STR_L "\0" +#define STRING_L_AMPERSAND0 STR_L STR_AMPERSAND "\0" +#define STRING_Lao0 STR_L STR_a STR_o "\0" +#define STRING_Latin0 STR_L STR_a STR_t STR_i STR_n "\0" +#define STRING_Lepcha0 STR_L STR_e STR_p STR_c STR_h STR_a "\0" +#define STRING_Limbu0 STR_L STR_i STR_m STR_b STR_u "\0" +#define STRING_Linear_B0 STR_L STR_i STR_n STR_e STR_a STR_r STR_UNDERSCORE STR_B "\0" +#define STRING_Lisu0 STR_L STR_i STR_s STR_u "\0" +#define STRING_Ll0 STR_L STR_l "\0" +#define STRING_Lm0 STR_L STR_m "\0" +#define STRING_Lo0 STR_L STR_o "\0" +#define STRING_Lt0 STR_L STR_t "\0" +#define STRING_Lu0 STR_L STR_u "\0" +#define STRING_Lycian0 STR_L STR_y STR_c STR_i STR_a STR_n "\0" +#define STRING_Lydian0 STR_L STR_y STR_d STR_i STR_a STR_n "\0" +#define STRING_M0 STR_M "\0" +#define STRING_Malayalam0 STR_M STR_a STR_l STR_a STR_y STR_a STR_l STR_a STR_m "\0" +#define STRING_Mandaic0 STR_M STR_a STR_n STR_d STR_a STR_i STR_c "\0" +#define STRING_Mc0 STR_M STR_c "\0" +#define STRING_Me0 STR_M STR_e "\0" +#define STRING_Meetei_Mayek0 STR_M STR_e STR_e STR_t STR_e STR_i STR_UNDERSCORE STR_M STR_a STR_y STR_e STR_k "\0" +#define STRING_Mn0 STR_M STR_n "\0" +#define STRING_Mongolian0 STR_M STR_o STR_n STR_g STR_o STR_l STR_i STR_a STR_n "\0" +#define STRING_Myanmar0 STR_M STR_y STR_a STR_n STR_m STR_a STR_r "\0" +#define STRING_N0 STR_N "\0" +#define STRING_Nd0 STR_N STR_d "\0" +#define STRING_New_Tai_Lue0 STR_N STR_e STR_w STR_UNDERSCORE STR_T STR_a STR_i STR_UNDERSCORE STR_L STR_u STR_e "\0" +#define STRING_Nko0 STR_N STR_k STR_o "\0" +#define STRING_Nl0 STR_N STR_l "\0" +#define STRING_No0 STR_N STR_o "\0" +#define STRING_Ogham0 STR_O STR_g STR_h STR_a STR_m "\0" +#define STRING_Ol_Chiki0 STR_O STR_l STR_UNDERSCORE STR_C STR_h STR_i STR_k STR_i "\0" +#define STRING_Old_Italic0 STR_O STR_l STR_d STR_UNDERSCORE STR_I STR_t STR_a STR_l STR_i STR_c "\0" +#define STRING_Old_Persian0 STR_O STR_l STR_d STR_UNDERSCORE STR_P STR_e STR_r STR_s STR_i STR_a STR_n "\0" +#define STRING_Old_South_Arabian0 STR_O STR_l STR_d STR_UNDERSCORE STR_S STR_o STR_u STR_t STR_h STR_UNDERSCORE STR_A STR_r STR_a STR_b STR_i STR_a STR_n "\0" +#define STRING_Old_Turkic0 STR_O STR_l STR_d STR_UNDERSCORE STR_T STR_u STR_r STR_k STR_i STR_c "\0" +#define STRING_Oriya0 STR_O STR_r STR_i STR_y STR_a "\0" +#define STRING_Osmanya0 STR_O STR_s STR_m STR_a STR_n STR_y STR_a "\0" +#define STRING_P0 STR_P "\0" +#define STRING_Pc0 STR_P STR_c "\0" +#define STRING_Pd0 STR_P STR_d "\0" +#define STRING_Pe0 STR_P STR_e "\0" +#define STRING_Pf0 STR_P STR_f "\0" +#define STRING_Phags_Pa0 STR_P STR_h STR_a STR_g STR_s STR_UNDERSCORE STR_P STR_a "\0" +#define STRING_Phoenician0 STR_P STR_h STR_o STR_e STR_n STR_i STR_c STR_i STR_a STR_n "\0" +#define STRING_Pi0 STR_P STR_i "\0" +#define STRING_Po0 STR_P STR_o "\0" +#define STRING_Ps0 STR_P STR_s "\0" +#define STRING_Rejang0 STR_R STR_e STR_j STR_a STR_n STR_g "\0" +#define STRING_Runic0 STR_R STR_u STR_n STR_i STR_c "\0" +#define STRING_S0 STR_S "\0" +#define STRING_Samaritan0 STR_S STR_a STR_m STR_a STR_r STR_i STR_t STR_a STR_n "\0" +#define STRING_Saurashtra0 STR_S STR_a STR_u STR_r STR_a STR_s STR_h STR_t STR_r STR_a "\0" +#define STRING_Sc0 STR_S STR_c "\0" +#define STRING_Shavian0 STR_S STR_h STR_a STR_v STR_i STR_a STR_n "\0" +#define STRING_Sinhala0 STR_S STR_i STR_n STR_h STR_a STR_l STR_a "\0" +#define STRING_Sk0 STR_S STR_k "\0" +#define STRING_Sm0 STR_S STR_m "\0" +#define STRING_So0 STR_S STR_o "\0" +#define STRING_Sundanese0 STR_S STR_u STR_n STR_d STR_a STR_n STR_e STR_s STR_e "\0" +#define STRING_Syloti_Nagri0 STR_S STR_y STR_l STR_o STR_t STR_i STR_UNDERSCORE STR_N STR_a STR_g STR_r STR_i "\0" +#define STRING_Syriac0 STR_S STR_y STR_r STR_i STR_a STR_c "\0" +#define STRING_Tagalog0 STR_T STR_a STR_g STR_a STR_l STR_o STR_g "\0" +#define STRING_Tagbanwa0 STR_T STR_a STR_g STR_b STR_a STR_n STR_w STR_a "\0" +#define STRING_Tai_Le0 STR_T STR_a STR_i STR_UNDERSCORE STR_L STR_e "\0" +#define STRING_Tai_Tham0 STR_T STR_a STR_i STR_UNDERSCORE STR_T STR_h STR_a STR_m "\0" +#define STRING_Tai_Viet0 STR_T STR_a STR_i STR_UNDERSCORE STR_V STR_i STR_e STR_t "\0" +#define STRING_Tamil0 STR_T STR_a STR_m STR_i STR_l "\0" +#define STRING_Telugu0 STR_T STR_e STR_l STR_u STR_g STR_u "\0" +#define STRING_Thaana0 STR_T STR_h STR_a STR_a STR_n STR_a "\0" +#define STRING_Thai0 STR_T STR_h STR_a STR_i "\0" +#define STRING_Tibetan0 STR_T STR_i STR_b STR_e STR_t STR_a STR_n "\0" +#define STRING_Tifinagh0 STR_T STR_i STR_f STR_i STR_n STR_a STR_g STR_h "\0" +#define STRING_Ugaritic0 STR_U STR_g STR_a STR_r STR_i STR_t STR_i STR_c "\0" +#define STRING_Vai0 STR_V STR_a STR_i "\0" +#define STRING_Xan0 STR_X STR_a STR_n "\0" +#define STRING_Xps0 STR_X STR_p STR_s "\0" +#define STRING_Xsp0 STR_X STR_s STR_p "\0" +#define STRING_Xwd0 STR_X STR_w STR_d "\0" +#define STRING_Yi0 STR_Y STR_i "\0" +#define STRING_Z0 STR_Z "\0" +#define STRING_Zl0 STR_Z STR_l "\0" +#define STRING_Zp0 STR_Z STR_p "\0" +#define STRING_Zs0 STR_Z STR_s "\0" + +const char PRIV(utt_names)[] = + STRING_Any0 + STRING_Arabic0 + STRING_Armenian0 + STRING_Avestan0 + STRING_Balinese0 + STRING_Bamum0 + STRING_Batak0 + STRING_Bengali0 + STRING_Bopomofo0 + STRING_Brahmi0 + STRING_Braille0 + STRING_Buginese0 + STRING_Buhid0 + STRING_C0 + STRING_Canadian_Aboriginal0 + STRING_Carian0 + STRING_Cc0 + STRING_Cf0 + STRING_Cham0 + STRING_Cherokee0 + STRING_Cn0 + STRING_Co0 + STRING_Common0 + STRING_Coptic0 + STRING_Cs0 + STRING_Cuneiform0 + STRING_Cypriot0 + STRING_Cyrillic0 + STRING_Deseret0 + STRING_Devanagari0 + STRING_Egyptian_Hieroglyphs0 + STRING_Ethiopic0 + STRING_Georgian0 + STRING_Glagolitic0 + STRING_Gothic0 + STRING_Greek0 + STRING_Gujarati0 + STRING_Gurmukhi0 + STRING_Han0 + STRING_Hangul0 + STRING_Hanunoo0 + STRING_Hebrew0 + STRING_Hiragana0 + STRING_Imperial_Aramaic0 + STRING_Inherited0 + STRING_Inscriptional_Pahlavi0 + STRING_Inscriptional_Parthian0 + STRING_Javanese0 + STRING_Kaithi0 + STRING_Kannada0 + STRING_Katakana0 + STRING_Kayah_Li0 + STRING_Kharoshthi0 + STRING_Khmer0 + STRING_L0 + STRING_L_AMPERSAND0 + STRING_Lao0 + STRING_Latin0 + STRING_Lepcha0 + STRING_Limbu0 + STRING_Linear_B0 + STRING_Lisu0 + STRING_Ll0 + STRING_Lm0 + STRING_Lo0 + STRING_Lt0 + STRING_Lu0 + STRING_Lycian0 + STRING_Lydian0 + STRING_M0 + STRING_Malayalam0 + STRING_Mandaic0 + STRING_Mc0 + STRING_Me0 + STRING_Meetei_Mayek0 + STRING_Mn0 + STRING_Mongolian0 + STRING_Myanmar0 + STRING_N0 + STRING_Nd0 + STRING_New_Tai_Lue0 + STRING_Nko0 + STRING_Nl0 + STRING_No0 + STRING_Ogham0 + STRING_Ol_Chiki0 + STRING_Old_Italic0 + STRING_Old_Persian0 + STRING_Old_South_Arabian0 + STRING_Old_Turkic0 + STRING_Oriya0 + STRING_Osmanya0 + STRING_P0 + STRING_Pc0 + STRING_Pd0 + STRING_Pe0 + STRING_Pf0 + STRING_Phags_Pa0 + STRING_Phoenician0 + STRING_Pi0 + STRING_Po0 + STRING_Ps0 + STRING_Rejang0 + STRING_Runic0 + STRING_S0 + STRING_Samaritan0 + STRING_Saurashtra0 + STRING_Sc0 + STRING_Shavian0 + STRING_Sinhala0 + STRING_Sk0 + STRING_Sm0 + STRING_So0 + STRING_Sundanese0 + STRING_Syloti_Nagri0 + STRING_Syriac0 + STRING_Tagalog0 + STRING_Tagbanwa0 + STRING_Tai_Le0 + STRING_Tai_Tham0 + STRING_Tai_Viet0 + STRING_Tamil0 + STRING_Telugu0 + STRING_Thaana0 + STRING_Thai0 + STRING_Tibetan0 + STRING_Tifinagh0 + STRING_Ugaritic0 + STRING_Vai0 + STRING_Xan0 + STRING_Xps0 + STRING_Xsp0 + STRING_Xwd0 + STRING_Yi0 + STRING_Z0 + STRING_Zl0 + STRING_Zp0 + STRING_Zs0; + +const ucp_type_table PRIV(utt)[] = { + { 0, PT_ANY, 0 }, + { 4, PT_SC, ucp_Arabic }, + { 11, PT_SC, ucp_Armenian }, + { 20, PT_SC, ucp_Avestan }, + { 28, PT_SC, ucp_Balinese }, + { 37, PT_SC, ucp_Bamum }, + { 43, PT_SC, ucp_Batak }, + { 49, PT_SC, ucp_Bengali }, + { 57, PT_SC, ucp_Bopomofo }, + { 66, PT_SC, ucp_Brahmi }, + { 73, PT_SC, ucp_Braille }, + { 81, PT_SC, ucp_Buginese }, + { 90, PT_SC, ucp_Buhid }, + { 96, PT_GC, ucp_C }, + { 98, PT_SC, ucp_Canadian_Aboriginal }, + { 118, PT_SC, ucp_Carian }, + { 125, PT_PC, ucp_Cc }, + { 128, PT_PC, ucp_Cf }, + { 131, PT_SC, ucp_Cham }, + { 136, PT_SC, ucp_Cherokee }, + { 145, PT_PC, ucp_Cn }, + { 148, PT_PC, ucp_Co }, + { 151, PT_SC, ucp_Common }, + { 158, PT_SC, ucp_Coptic }, + { 165, PT_PC, ucp_Cs }, + { 168, PT_SC, ucp_Cuneiform }, + { 178, PT_SC, ucp_Cypriot }, + { 186, PT_SC, ucp_Cyrillic }, + { 195, PT_SC, ucp_Deseret }, + { 203, PT_SC, ucp_Devanagari }, + { 214, PT_SC, ucp_Egyptian_Hieroglyphs }, + { 235, PT_SC, ucp_Ethiopic }, + { 244, PT_SC, ucp_Georgian }, + { 253, PT_SC, ucp_Glagolitic }, + { 264, PT_SC, ucp_Gothic }, + { 271, PT_SC, ucp_Greek }, + { 277, PT_SC, ucp_Gujarati }, + { 286, PT_SC, ucp_Gurmukhi }, + { 295, PT_SC, ucp_Han }, + { 299, PT_SC, ucp_Hangul }, + { 306, PT_SC, ucp_Hanunoo }, + { 314, PT_SC, ucp_Hebrew }, + { 321, PT_SC, ucp_Hiragana }, + { 330, PT_SC, ucp_Imperial_Aramaic }, + { 347, PT_SC, ucp_Inherited }, + { 357, PT_SC, ucp_Inscriptional_Pahlavi }, + { 379, PT_SC, ucp_Inscriptional_Parthian }, + { 402, PT_SC, ucp_Javanese }, + { 411, PT_SC, ucp_Kaithi }, + { 418, PT_SC, ucp_Kannada }, + { 426, PT_SC, ucp_Katakana }, + { 435, PT_SC, ucp_Kayah_Li }, + { 444, PT_SC, ucp_Kharoshthi }, + { 455, PT_SC, ucp_Khmer }, + { 461, PT_GC, ucp_L }, + { 463, PT_LAMP, 0 }, + { 466, PT_SC, ucp_Lao }, + { 470, PT_SC, ucp_Latin }, + { 476, PT_SC, ucp_Lepcha }, + { 483, PT_SC, ucp_Limbu }, + { 489, PT_SC, ucp_Linear_B }, + { 498, PT_SC, ucp_Lisu }, + { 503, PT_PC, ucp_Ll }, + { 506, PT_PC, ucp_Lm }, + { 509, PT_PC, ucp_Lo }, + { 512, PT_PC, ucp_Lt }, + { 515, PT_PC, ucp_Lu }, + { 518, PT_SC, ucp_Lycian }, + { 525, PT_SC, ucp_Lydian }, + { 532, PT_GC, ucp_M }, + { 534, PT_SC, ucp_Malayalam }, + { 544, PT_SC, ucp_Mandaic }, + { 552, PT_PC, ucp_Mc }, + { 555, PT_PC, ucp_Me }, + { 558, PT_SC, ucp_Meetei_Mayek }, + { 571, PT_PC, ucp_Mn }, + { 574, PT_SC, ucp_Mongolian }, + { 584, PT_SC, ucp_Myanmar }, + { 592, PT_GC, ucp_N }, + { 594, PT_PC, ucp_Nd }, + { 597, PT_SC, ucp_New_Tai_Lue }, + { 609, PT_SC, ucp_Nko }, + { 613, PT_PC, ucp_Nl }, + { 616, PT_PC, ucp_No }, + { 619, PT_SC, ucp_Ogham }, + { 625, PT_SC, ucp_Ol_Chiki }, + { 634, PT_SC, ucp_Old_Italic }, + { 645, PT_SC, ucp_Old_Persian }, + { 657, PT_SC, ucp_Old_South_Arabian }, + { 675, PT_SC, ucp_Old_Turkic }, + { 686, PT_SC, ucp_Oriya }, + { 692, PT_SC, ucp_Osmanya }, + { 700, PT_GC, ucp_P }, + { 702, PT_PC, ucp_Pc }, + { 705, PT_PC, ucp_Pd }, + { 708, PT_PC, ucp_Pe }, + { 711, PT_PC, ucp_Pf }, + { 714, PT_SC, ucp_Phags_Pa }, + { 723, PT_SC, ucp_Phoenician }, + { 734, PT_PC, ucp_Pi }, + { 737, PT_PC, ucp_Po }, + { 740, PT_PC, ucp_Ps }, + { 743, PT_SC, ucp_Rejang }, + { 750, PT_SC, ucp_Runic }, + { 756, PT_GC, ucp_S }, + { 758, PT_SC, ucp_Samaritan }, + { 768, PT_SC, ucp_Saurashtra }, + { 779, PT_PC, ucp_Sc }, + { 782, PT_SC, ucp_Shavian }, + { 790, PT_SC, ucp_Sinhala }, + { 798, PT_PC, ucp_Sk }, + { 801, PT_PC, ucp_Sm }, + { 804, PT_PC, ucp_So }, + { 807, PT_SC, ucp_Sundanese }, + { 817, PT_SC, ucp_Syloti_Nagri }, + { 830, PT_SC, ucp_Syriac }, + { 837, PT_SC, ucp_Tagalog }, + { 845, PT_SC, ucp_Tagbanwa }, + { 854, PT_SC, ucp_Tai_Le }, + { 861, PT_SC, ucp_Tai_Tham }, + { 870, PT_SC, ucp_Tai_Viet }, + { 879, PT_SC, ucp_Tamil }, + { 885, PT_SC, ucp_Telugu }, + { 892, PT_SC, ucp_Thaana }, + { 899, PT_SC, ucp_Thai }, + { 904, PT_SC, ucp_Tibetan }, + { 912, PT_SC, ucp_Tifinagh }, + { 921, PT_SC, ucp_Ugaritic }, + { 930, PT_SC, ucp_Vai }, + { 934, PT_ALNUM, 0 }, + { 938, PT_PXSPACE, 0 }, + { 942, PT_SPACE, 0 }, + { 946, PT_WORD, 0 }, + { 950, PT_SC, ucp_Yi }, + { 953, PT_GC, ucp_Z }, + { 955, PT_PC, ucp_Zl }, + { 958, PT_PC, ucp_Zp }, + { 961, PT_PC, ucp_Zs } +}; + +const int PRIV(utt_size) = sizeof(PRIV(utt)) / sizeof(ucp_type_table); + +#endif /* SUPPORT_UTF */ + +/* End of pcre_tables.c */ diff --git a/src/lib/pcre/pcre_ucd.c b/src/lib/pcre/pcre_ucd.c new file mode 100644 index 0000000..48fa486 --- /dev/null +++ b/src/lib/pcre/pcre_ucd.c @@ -0,0 +1,2981 @@ +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +/* Unicode character database. */ +/* This file was autogenerated by the MultiStage2.py script. */ +/* Total size: 60384 bytes, block size: 128. */ + +/* The tables herein are needed only when UCP support is built */ +/* into PCRE. This module should not be referenced otherwise, so */ +/* it should not matter whether it is compiled or not. However */ +/* a comment was received about space saving - maybe the guy linked */ +/* all the modules rather than using a library - so we include a */ +/* condition to cut out the tables when not needed. But don't leave */ +/* a totally empty module because some compilers barf at that. */ +/* Instead, just supply small dummy tables. */ + +#ifndef SUPPORT_UCP +const ucd_record PRIV(ucd_records)[] = {{0,0,0 }}; +const pcre_uint8 PRIV(ucd_stage1)[] = {0}; +const pcre_uint16 PRIV(ucd_stage2)[] = {0}; +#else + +/* When recompiling tables with a new Unicode version, +please check types in the structure definition from pcre_internal.h: +typedef struct { +pcre_uint8 property_0; +pcre_uint8 property_1; +pcre_int32 property_2; +} ucd_record; */ + + +const ucd_record PRIV(ucd_records)[] = { /* 4320 bytes, record size 8 */ + { 9, 0, 0, }, /* 0 */ + { 9, 29, 0, }, /* 1 */ + { 9, 21, 0, }, /* 2 */ + { 9, 23, 0, }, /* 3 */ + { 9, 22, 0, }, /* 4 */ + { 9, 18, 0, }, /* 5 */ + { 9, 25, 0, }, /* 6 */ + { 9, 17, 0, }, /* 7 */ + { 9, 13, 0, }, /* 8 */ + { 33, 9, 32, }, /* 9 */ + { 9, 24, 0, }, /* 10 */ + { 9, 16, 0, }, /* 11 */ + { 33, 5, -32, }, /* 12 */ + { 9, 26, 0, }, /* 13 */ + { 33, 5, 0, }, /* 14 */ + { 9, 20, 0, }, /* 15 */ + { 9, 1, 0, }, /* 16 */ + { 9, 15, 0, }, /* 17 */ + { 9, 5, 743, }, /* 18 */ + { 9, 19, 0, }, /* 19 */ + { 33, 5, 121, }, /* 20 */ + { 33, 9, 1, }, /* 21 */ + { 33, 5, -1, }, /* 22 */ + { 33, 9, -199, }, /* 23 */ + { 33, 5, -232, }, /* 24 */ + { 33, 9, -121, }, /* 25 */ + { 33, 5, -300, }, /* 26 */ + { 33, 5, 195, }, /* 27 */ + { 33, 9, 210, }, /* 28 */ + { 33, 9, 206, }, /* 29 */ + { 33, 9, 205, }, /* 30 */ + { 33, 9, 79, }, /* 31 */ + { 33, 9, 202, }, /* 32 */ + { 33, 9, 203, }, /* 33 */ + { 33, 9, 207, }, /* 34 */ + { 33, 5, 97, }, /* 35 */ + { 33, 9, 211, }, /* 36 */ + { 33, 9, 209, }, /* 37 */ + { 33, 5, 163, }, /* 38 */ + { 33, 9, 213, }, /* 39 */ + { 33, 5, 130, }, /* 40 */ + { 33, 9, 214, }, /* 41 */ + { 33, 9, 218, }, /* 42 */ + { 33, 9, 217, }, /* 43 */ + { 33, 9, 219, }, /* 44 */ + { 33, 7, 0, }, /* 45 */ + { 33, 5, 56, }, /* 46 */ + { 33, 9, 2, }, /* 47 */ + { 33, 8, -1, }, /* 48 */ + { 33, 5, -2, }, /* 49 */ + { 33, 5, -79, }, /* 50 */ + { 33, 9, -97, }, /* 51 */ + { 33, 9, -56, }, /* 52 */ + { 33, 9, -130, }, /* 53 */ + { 33, 9, 10795, }, /* 54 */ + { 33, 9, -163, }, /* 55 */ + { 33, 9, 10792, }, /* 56 */ + { 33, 5, 10815, }, /* 57 */ + { 33, 9, -195, }, /* 58 */ + { 33, 9, 69, }, /* 59 */ + { 33, 9, 71, }, /* 60 */ + { 33, 5, 10783, }, /* 61 */ + { 33, 5, 10780, }, /* 62 */ + { 33, 5, 10782, }, /* 63 */ + { 33, 5, -210, }, /* 64 */ + { 33, 5, -206, }, /* 65 */ + { 33, 5, -205, }, /* 66 */ + { 33, 5, -202, }, /* 67 */ + { 33, 5, -203, }, /* 68 */ + { 33, 5, -207, }, /* 69 */ + { 33, 5, 42280, }, /* 70 */ + { 33, 5, -209, }, /* 71 */ + { 33, 5, -211, }, /* 72 */ + { 33, 5, 10743, }, /* 73 */ + { 33, 5, 10749, }, /* 74 */ + { 33, 5, -213, }, /* 75 */ + { 33, 5, -214, }, /* 76 */ + { 33, 5, 10727, }, /* 77 */ + { 33, 5, -218, }, /* 78 */ + { 33, 5, -69, }, /* 79 */ + { 33, 5, -217, }, /* 80 */ + { 33, 5, -71, }, /* 81 */ + { 33, 5, -219, }, /* 82 */ + { 33, 6, 0, }, /* 83 */ + { 9, 6, 0, }, /* 84 */ + { 3, 24, 0, }, /* 85 */ + { 27, 12, 0, }, /* 86 */ + { 27, 12, 84, }, /* 87 */ + { 19, 9, 1, }, /* 88 */ + { 19, 5, -1, }, /* 89 */ + { 19, 24, 0, }, /* 90 */ + { 9, 2, 0, }, /* 91 */ + { 19, 6, 0, }, /* 92 */ + { 19, 5, 130, }, /* 93 */ + { 19, 9, 38, }, /* 94 */ + { 19, 9, 37, }, /* 95 */ + { 19, 9, 64, }, /* 96 */ + { 19, 9, 63, }, /* 97 */ + { 19, 5, 0, }, /* 98 */ + { 19, 9, 32, }, /* 99 */ + { 19, 5, -38, }, /* 100 */ + { 19, 5, -37, }, /* 101 */ + { 19, 5, -32, }, /* 102 */ + { 19, 5, -31, }, /* 103 */ + { 19, 5, -64, }, /* 104 */ + { 19, 5, -63, }, /* 105 */ + { 19, 9, 8, }, /* 106 */ + { 19, 5, -62, }, /* 107 */ + { 19, 5, -57, }, /* 108 */ + { 19, 9, 0, }, /* 109 */ + { 19, 5, -47, }, /* 110 */ + { 19, 5, -54, }, /* 111 */ + { 19, 5, -8, }, /* 112 */ + { 10, 9, 1, }, /* 113 */ + { 10, 5, -1, }, /* 114 */ + { 19, 5, -86, }, /* 115 */ + { 19, 5, -80, }, /* 116 */ + { 19, 5, 7, }, /* 117 */ + { 19, 9, -60, }, /* 118 */ + { 19, 5, -96, }, /* 119 */ + { 19, 25, 0, }, /* 120 */ + { 19, 9, -7, }, /* 121 */ + { 19, 9, -130, }, /* 122 */ + { 12, 9, 80, }, /* 123 */ + { 12, 9, 32, }, /* 124 */ + { 12, 5, -32, }, /* 125 */ + { 12, 5, -80, }, /* 126 */ + { 12, 9, 1, }, /* 127 */ + { 12, 5, -1, }, /* 128 */ + { 12, 26, 0, }, /* 129 */ + { 12, 12, 0, }, /* 130 */ + { 12, 11, 0, }, /* 131 */ + { 12, 9, 15, }, /* 132 */ + { 12, 5, -15, }, /* 133 */ + { 1, 9, 48, }, /* 134 */ + { 1, 6, 0, }, /* 135 */ + { 1, 21, 0, }, /* 136 */ + { 1, 5, -48, }, /* 137 */ + { 1, 5, 0, }, /* 138 */ + { 1, 17, 0, }, /* 139 */ + { 25, 12, 0, }, /* 140 */ + { 25, 17, 0, }, /* 141 */ + { 25, 21, 0, }, /* 142 */ + { 25, 7, 0, }, /* 143 */ + { 0, 1, 0, }, /* 144 */ + { 0, 25, 0, }, /* 145 */ + { 0, 21, 0, }, /* 146 */ + { 0, 23, 0, }, /* 147 */ + { 0, 26, 0, }, /* 148 */ + { 0, 12, 0, }, /* 149 */ + { 0, 7, 0, }, /* 150 */ + { 0, 6, 0, }, /* 151 */ + { 0, 13, 0, }, /* 152 */ + { 49, 21, 0, }, /* 153 */ + { 49, 1, 0, }, /* 154 */ + { 49, 7, 0, }, /* 155 */ + { 49, 12, 0, }, /* 156 */ + { 55, 7, 0, }, /* 157 */ + { 55, 12, 0, }, /* 158 */ + { 63, 13, 0, }, /* 159 */ + { 63, 7, 0, }, /* 160 */ + { 63, 12, 0, }, /* 161 */ + { 63, 6, 0, }, /* 162 */ + { 63, 26, 0, }, /* 163 */ + { 63, 21, 0, }, /* 164 */ + { 89, 7, 0, }, /* 165 */ + { 89, 12, 0, }, /* 166 */ + { 89, 6, 0, }, /* 167 */ + { 89, 21, 0, }, /* 168 */ + { 94, 7, 0, }, /* 169 */ + { 94, 12, 0, }, /* 170 */ + { 94, 21, 0, }, /* 171 */ + { 14, 12, 0, }, /* 172 */ + { 14, 10, 0, }, /* 173 */ + { 14, 7, 0, }, /* 174 */ + { 14, 13, 0, }, /* 175 */ + { 14, 6, 0, }, /* 176 */ + { 2, 12, 0, }, /* 177 */ + { 2, 10, 0, }, /* 178 */ + { 2, 7, 0, }, /* 179 */ + { 2, 13, 0, }, /* 180 */ + { 2, 23, 0, }, /* 181 */ + { 2, 15, 0, }, /* 182 */ + { 2, 26, 0, }, /* 183 */ + { 21, 12, 0, }, /* 184 */ + { 21, 10, 0, }, /* 185 */ + { 21, 7, 0, }, /* 186 */ + { 21, 13, 0, }, /* 187 */ + { 20, 12, 0, }, /* 188 */ + { 20, 10, 0, }, /* 189 */ + { 20, 7, 0, }, /* 190 */ + { 20, 13, 0, }, /* 191 */ + { 20, 23, 0, }, /* 192 */ + { 43, 12, 0, }, /* 193 */ + { 43, 10, 0, }, /* 194 */ + { 43, 7, 0, }, /* 195 */ + { 43, 13, 0, }, /* 196 */ + { 43, 26, 0, }, /* 197 */ + { 43, 15, 0, }, /* 198 */ + { 53, 12, 0, }, /* 199 */ + { 53, 7, 0, }, /* 200 */ + { 53, 10, 0, }, /* 201 */ + { 53, 13, 0, }, /* 202 */ + { 53, 15, 0, }, /* 203 */ + { 53, 26, 0, }, /* 204 */ + { 53, 23, 0, }, /* 205 */ + { 54, 10, 0, }, /* 206 */ + { 54, 7, 0, }, /* 207 */ + { 54, 12, 0, }, /* 208 */ + { 54, 13, 0, }, /* 209 */ + { 54, 15, 0, }, /* 210 */ + { 54, 26, 0, }, /* 211 */ + { 28, 10, 0, }, /* 212 */ + { 28, 7, 0, }, /* 213 */ + { 28, 12, 0, }, /* 214 */ + { 28, 13, 0, }, /* 215 */ + { 36, 10, 0, }, /* 216 */ + { 36, 7, 0, }, /* 217 */ + { 36, 12, 0, }, /* 218 */ + { 36, 13, 0, }, /* 219 */ + { 36, 15, 0, }, /* 220 */ + { 36, 26, 0, }, /* 221 */ + { 47, 10, 0, }, /* 222 */ + { 47, 7, 0, }, /* 223 */ + { 47, 12, 0, }, /* 224 */ + { 47, 21, 0, }, /* 225 */ + { 56, 7, 0, }, /* 226 */ + { 56, 12, 0, }, /* 227 */ + { 56, 6, 0, }, /* 228 */ + { 56, 21, 0, }, /* 229 */ + { 56, 13, 0, }, /* 230 */ + { 32, 7, 0, }, /* 231 */ + { 32, 12, 0, }, /* 232 */ + { 32, 6, 0, }, /* 233 */ + { 32, 13, 0, }, /* 234 */ + { 57, 7, 0, }, /* 235 */ + { 57, 26, 0, }, /* 236 */ + { 57, 21, 0, }, /* 237 */ + { 57, 12, 0, }, /* 238 */ + { 57, 13, 0, }, /* 239 */ + { 57, 15, 0, }, /* 240 */ + { 57, 22, 0, }, /* 241 */ + { 57, 18, 0, }, /* 242 */ + { 57, 10, 0, }, /* 243 */ + { 38, 7, 0, }, /* 244 */ + { 38, 10, 0, }, /* 245 */ + { 38, 12, 0, }, /* 246 */ + { 38, 13, 0, }, /* 247 */ + { 38, 21, 0, }, /* 248 */ + { 38, 26, 0, }, /* 249 */ + { 16, 9, 7264, }, /* 250 */ + { 16, 7, 0, }, /* 251 */ + { 16, 6, 0, }, /* 252 */ + { 23, 7, 0, }, /* 253 */ + { 15, 7, 0, }, /* 254 */ + { 15, 12, 0, }, /* 255 */ + { 15, 26, 0, }, /* 256 */ + { 15, 21, 0, }, /* 257 */ + { 15, 15, 0, }, /* 258 */ + { 8, 7, 0, }, /* 259 */ + { 7, 17, 0, }, /* 260 */ + { 7, 7, 0, }, /* 261 */ + { 7, 21, 0, }, /* 262 */ + { 40, 29, 0, }, /* 263 */ + { 40, 7, 0, }, /* 264 */ + { 40, 22, 0, }, /* 265 */ + { 40, 18, 0, }, /* 266 */ + { 45, 7, 0, }, /* 267 */ + { 45, 14, 0, }, /* 268 */ + { 50, 7, 0, }, /* 269 */ + { 50, 12, 0, }, /* 270 */ + { 24, 7, 0, }, /* 271 */ + { 24, 12, 0, }, /* 272 */ + { 6, 7, 0, }, /* 273 */ + { 6, 12, 0, }, /* 274 */ + { 51, 7, 0, }, /* 275 */ + { 51, 12, 0, }, /* 276 */ + { 31, 7, 0, }, /* 277 */ + { 31, 1, 0, }, /* 278 */ + { 31, 10, 0, }, /* 279 */ + { 31, 12, 0, }, /* 280 */ + { 31, 21, 0, }, /* 281 */ + { 31, 6, 0, }, /* 282 */ + { 31, 23, 0, }, /* 283 */ + { 31, 13, 0, }, /* 284 */ + { 31, 15, 0, }, /* 285 */ + { 37, 21, 0, }, /* 286 */ + { 37, 17, 0, }, /* 287 */ + { 37, 12, 0, }, /* 288 */ + { 37, 29, 0, }, /* 289 */ + { 37, 13, 0, }, /* 290 */ + { 37, 7, 0, }, /* 291 */ + { 37, 6, 0, }, /* 292 */ + { 34, 7, 0, }, /* 293 */ + { 34, 12, 0, }, /* 294 */ + { 34, 10, 0, }, /* 295 */ + { 34, 26, 0, }, /* 296 */ + { 34, 21, 0, }, /* 297 */ + { 34, 13, 0, }, /* 298 */ + { 52, 7, 0, }, /* 299 */ + { 39, 7, 0, }, /* 300 */ + { 39, 10, 0, }, /* 301 */ + { 39, 13, 0, }, /* 302 */ + { 39, 15, 0, }, /* 303 */ + { 39, 26, 0, }, /* 304 */ + { 31, 26, 0, }, /* 305 */ + { 5, 7, 0, }, /* 306 */ + { 5, 12, 0, }, /* 307 */ + { 5, 10, 0, }, /* 308 */ + { 5, 21, 0, }, /* 309 */ + { 90, 7, 0, }, /* 310 */ + { 90, 10, 0, }, /* 311 */ + { 90, 12, 0, }, /* 312 */ + { 90, 13, 0, }, /* 313 */ + { 90, 21, 0, }, /* 314 */ + { 90, 6, 0, }, /* 315 */ + { 61, 12, 0, }, /* 316 */ + { 61, 10, 0, }, /* 317 */ + { 61, 7, 0, }, /* 318 */ + { 61, 13, 0, }, /* 319 */ + { 61, 21, 0, }, /* 320 */ + { 61, 26, 0, }, /* 321 */ + { 75, 12, 0, }, /* 322 */ + { 75, 10, 0, }, /* 323 */ + { 75, 7, 0, }, /* 324 */ + { 75, 13, 0, }, /* 325 */ + { 92, 7, 0, }, /* 326 */ + { 92, 12, 0, }, /* 327 */ + { 92, 10, 0, }, /* 328 */ + { 92, 21, 0, }, /* 329 */ + { 69, 7, 0, }, /* 330 */ + { 69, 10, 0, }, /* 331 */ + { 69, 12, 0, }, /* 332 */ + { 69, 21, 0, }, /* 333 */ + { 69, 13, 0, }, /* 334 */ + { 72, 13, 0, }, /* 335 */ + { 72, 7, 0, }, /* 336 */ + { 72, 6, 0, }, /* 337 */ + { 72, 21, 0, }, /* 338 */ + { 9, 10, 0, }, /* 339 */ + { 9, 7, 0, }, /* 340 */ + { 12, 5, 0, }, /* 341 */ + { 12, 6, 0, }, /* 342 */ + { 33, 5, 35332, }, /* 343 */ + { 33, 5, 3814, }, /* 344 */ + { 33, 5, -59, }, /* 345 */ + { 33, 9, -7615, }, /* 346 */ + { 19, 5, 8, }, /* 347 */ + { 19, 9, -8, }, /* 348 */ + { 19, 5, 74, }, /* 349 */ + { 19, 5, 86, }, /* 350 */ + { 19, 5, 100, }, /* 351 */ + { 19, 5, 128, }, /* 352 */ + { 19, 5, 112, }, /* 353 */ + { 19, 5, 126, }, /* 354 */ + { 19, 8, -8, }, /* 355 */ + { 19, 5, 9, }, /* 356 */ + { 19, 9, -74, }, /* 357 */ + { 19, 8, -9, }, /* 358 */ + { 19, 5, -7205, }, /* 359 */ + { 19, 9, -86, }, /* 360 */ + { 19, 9, -100, }, /* 361 */ + { 19, 9, -112, }, /* 362 */ + { 19, 9, -128, }, /* 363 */ + { 19, 9, -126, }, /* 364 */ + { 27, 1, 0, }, /* 365 */ + { 9, 27, 0, }, /* 366 */ + { 9, 28, 0, }, /* 367 */ + { 27, 11, 0, }, /* 368 */ + { 9, 9, 0, }, /* 369 */ + { 9, 5, 0, }, /* 370 */ + { 19, 9, -7517, }, /* 371 */ + { 33, 9, -8383, }, /* 372 */ + { 33, 9, -8262, }, /* 373 */ + { 33, 9, 28, }, /* 374 */ + { 33, 5, -28, }, /* 375 */ + { 33, 14, 16, }, /* 376 */ + { 33, 14, -16, }, /* 377 */ + { 33, 14, 0, }, /* 378 */ + { 9, 26, 26, }, /* 379 */ + { 9, 26, -26, }, /* 380 */ + { 4, 26, 0, }, /* 381 */ + { 17, 9, 48, }, /* 382 */ + { 17, 5, -48, }, /* 383 */ + { 33, 9, -10743, }, /* 384 */ + { 33, 9, -3814, }, /* 385 */ + { 33, 9, -10727, }, /* 386 */ + { 33, 5, -10795, }, /* 387 */ + { 33, 5, -10792, }, /* 388 */ + { 33, 9, -10780, }, /* 389 */ + { 33, 9, -10749, }, /* 390 */ + { 33, 9, -10783, }, /* 391 */ + { 33, 9, -10782, }, /* 392 */ + { 33, 9, -10815, }, /* 393 */ + { 10, 5, 0, }, /* 394 */ + { 10, 26, 0, }, /* 395 */ + { 10, 12, 0, }, /* 396 */ + { 10, 21, 0, }, /* 397 */ + { 10, 15, 0, }, /* 398 */ + { 16, 5, -7264, }, /* 399 */ + { 58, 7, 0, }, /* 400 */ + { 58, 6, 0, }, /* 401 */ + { 58, 21, 0, }, /* 402 */ + { 58, 12, 0, }, /* 403 */ + { 22, 26, 0, }, /* 404 */ + { 22, 6, 0, }, /* 405 */ + { 22, 14, 0, }, /* 406 */ + { 23, 12, 0, }, /* 407 */ + { 26, 7, 0, }, /* 408 */ + { 26, 6, 0, }, /* 409 */ + { 29, 7, 0, }, /* 410 */ + { 29, 6, 0, }, /* 411 */ + { 3, 7, 0, }, /* 412 */ + { 23, 26, 0, }, /* 413 */ + { 29, 26, 0, }, /* 414 */ + { 22, 7, 0, }, /* 415 */ + { 60, 7, 0, }, /* 416 */ + { 60, 6, 0, }, /* 417 */ + { 60, 26, 0, }, /* 418 */ + { 85, 7, 0, }, /* 419 */ + { 85, 6, 0, }, /* 420 */ + { 85, 21, 0, }, /* 421 */ + { 76, 7, 0, }, /* 422 */ + { 76, 6, 0, }, /* 423 */ + { 76, 21, 0, }, /* 424 */ + { 76, 13, 0, }, /* 425 */ + { 12, 7, 0, }, /* 426 */ + { 12, 21, 0, }, /* 427 */ + { 78, 7, 0, }, /* 428 */ + { 78, 14, 0, }, /* 429 */ + { 78, 12, 0, }, /* 430 */ + { 78, 21, 0, }, /* 431 */ + { 33, 9, -35332, }, /* 432 */ + { 33, 9, -42280, }, /* 433 */ + { 48, 7, 0, }, /* 434 */ + { 48, 12, 0, }, /* 435 */ + { 48, 10, 0, }, /* 436 */ + { 48, 26, 0, }, /* 437 */ + { 64, 7, 0, }, /* 438 */ + { 64, 21, 0, }, /* 439 */ + { 74, 10, 0, }, /* 440 */ + { 74, 7, 0, }, /* 441 */ + { 74, 12, 0, }, /* 442 */ + { 74, 21, 0, }, /* 443 */ + { 74, 13, 0, }, /* 444 */ + { 14, 21, 0, }, /* 445 */ + { 68, 13, 0, }, /* 446 */ + { 68, 7, 0, }, /* 447 */ + { 68, 12, 0, }, /* 448 */ + { 68, 21, 0, }, /* 449 */ + { 73, 7, 0, }, /* 450 */ + { 73, 12, 0, }, /* 451 */ + { 73, 10, 0, }, /* 452 */ + { 73, 21, 0, }, /* 453 */ + { 83, 12, 0, }, /* 454 */ + { 83, 10, 0, }, /* 455 */ + { 83, 7, 0, }, /* 456 */ + { 83, 21, 0, }, /* 457 */ + { 83, 6, 0, }, /* 458 */ + { 83, 13, 0, }, /* 459 */ + { 67, 7, 0, }, /* 460 */ + { 67, 12, 0, }, /* 461 */ + { 67, 10, 0, }, /* 462 */ + { 67, 13, 0, }, /* 463 */ + { 67, 21, 0, }, /* 464 */ + { 38, 6, 0, }, /* 465 */ + { 91, 7, 0, }, /* 466 */ + { 91, 12, 0, }, /* 467 */ + { 91, 6, 0, }, /* 468 */ + { 91, 21, 0, }, /* 469 */ + { 86, 7, 0, }, /* 470 */ + { 86, 10, 0, }, /* 471 */ + { 86, 12, 0, }, /* 472 */ + { 86, 21, 0, }, /* 473 */ + { 86, 13, 0, }, /* 474 */ + { 9, 4, 0, }, /* 475 */ + { 9, 3, 0, }, /* 476 */ + { 25, 25, 0, }, /* 477 */ + { 0, 24, 0, }, /* 478 */ + { 35, 7, 0, }, /* 479 */ + { 19, 14, 0, }, /* 480 */ + { 19, 15, 0, }, /* 481 */ + { 19, 26, 0, }, /* 482 */ + { 70, 7, 0, }, /* 483 */ + { 66, 7, 0, }, /* 484 */ + { 41, 7, 0, }, /* 485 */ + { 41, 15, 0, }, /* 486 */ + { 18, 7, 0, }, /* 487 */ + { 18, 14, 0, }, /* 488 */ + { 59, 7, 0, }, /* 489 */ + { 59, 21, 0, }, /* 490 */ + { 42, 7, 0, }, /* 491 */ + { 42, 21, 0, }, /* 492 */ + { 42, 14, 0, }, /* 493 */ + { 13, 9, 40, }, /* 494 */ + { 13, 5, -40, }, /* 495 */ + { 46, 7, 0, }, /* 496 */ + { 44, 7, 0, }, /* 497 */ + { 44, 13, 0, }, /* 498 */ + { 11, 7, 0, }, /* 499 */ + { 80, 7, 0, }, /* 500 */ + { 80, 21, 0, }, /* 501 */ + { 80, 15, 0, }, /* 502 */ + { 65, 7, 0, }, /* 503 */ + { 65, 15, 0, }, /* 504 */ + { 65, 21, 0, }, /* 505 */ + { 71, 7, 0, }, /* 506 */ + { 71, 21, 0, }, /* 507 */ + { 30, 7, 0, }, /* 508 */ + { 30, 12, 0, }, /* 509 */ + { 30, 15, 0, }, /* 510 */ + { 30, 21, 0, }, /* 511 */ + { 87, 7, 0, }, /* 512 */ + { 87, 15, 0, }, /* 513 */ + { 87, 21, 0, }, /* 514 */ + { 77, 7, 0, }, /* 515 */ + { 77, 21, 0, }, /* 516 */ + { 82, 7, 0, }, /* 517 */ + { 82, 15, 0, }, /* 518 */ + { 81, 7, 0, }, /* 519 */ + { 81, 15, 0, }, /* 520 */ + { 88, 7, 0, }, /* 521 */ + { 0, 15, 0, }, /* 522 */ + { 93, 10, 0, }, /* 523 */ + { 93, 12, 0, }, /* 524 */ + { 93, 7, 0, }, /* 525 */ + { 93, 21, 0, }, /* 526 */ + { 93, 15, 0, }, /* 527 */ + { 93, 13, 0, }, /* 528 */ + { 84, 12, 0, }, /* 529 */ + { 84, 10, 0, }, /* 530 */ + { 84, 7, 0, }, /* 531 */ + { 84, 21, 0, }, /* 532 */ + { 84, 1, 0, }, /* 533 */ + { 62, 7, 0, }, /* 534 */ + { 62, 14, 0, }, /* 535 */ + { 62, 21, 0, }, /* 536 */ + { 79, 7, 0, }, /* 537 */ + { 19, 12, 0, }, /* 538 */ + { 26, 26, 0, }, /* 539 */ +}; + +const pcre_uint8 PRIV(ucd_stage1)[] = { /* 8704 bytes */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* U+0000 */ + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, /* U+0800 */ + 32, 33, 34, 34, 35, 36, 37, 38, 39, 40, 40, 40, 41, 42, 43, 44, /* U+1000 */ + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, /* U+1800 */ + 61, 62, 63, 64, 65, 65, 66, 67, 68, 69, 70, 71, 72, 70, 73, 74, /* U+2000 */ + 75, 75, 65, 76, 65, 65, 77, 17, 78, 79, 80, 81, 82, 83, 84, 85, /* U+2800 */ + 86, 87, 88, 89, 90, 91, 92, 70, 93, 93, 93, 93, 93, 93, 93, 93, /* U+3000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+3800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+4000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 94, 93, 93, 93, 93, /* U+4800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+5000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+5800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+6000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+6800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+7000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+7800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+8000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+8800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+9000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 95, /* U+9800 */ + 96, 97, 97, 97, 97, 97, 97, 97, 97, 98, 99, 99,100,101,102,103, /* U+A000 */ +104,105,106,107,108,109,110,111, 34, 34, 34, 34, 34, 34, 34, 34, /* U+A800 */ + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, /* U+B000 */ + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, /* U+B800 */ + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, /* U+C000 */ + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, /* U+C800 */ + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,112, /* U+D000 */ +113,113,113,113,113,113,113,113,113,113,113,113,113,113,113,113, /* U+D800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+E000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+E800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F000 */ +114,114, 93, 93,115,116,117,118,119,119,120,121,122,123,124,125, /* U+F800 */ +126,127,128,129, 17,130,131,132,133,134, 17, 17, 17, 17, 17, 17, /* U+10000 */ +135, 17,136, 17,137, 17,138, 17,139, 17, 17, 17,140, 17, 17, 17, /* U+10800 */ +141,142, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+11000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+11800 */ +143,143,143,143,143,143,144, 17,145, 17, 17, 17, 17, 17, 17, 17, /* U+12000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+12800 */ +146,146,146,146,146,146,146,146,147, 17, 17, 17, 17, 17, 17, 17, /* U+13000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+13800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+14000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+14800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+15000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+15800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+16000 */ +148,148,148,148,149, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+16800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+17000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+17800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+18000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+18800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+19000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+19800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1A800 */ +150, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1C800 */ + 70,151,152,153,154, 17,155, 17,156,157,158,159,160,161,162,163, /* U+1D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1E800 */ +164,165,166,167,168, 17,169,170,171,172,173,174,175,176,177, 17, /* U+1F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+1F800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+20000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+20800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+21000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+21800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+22000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+22800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+23000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+23800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+24000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+24800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+25000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+25800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+26000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+26800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+27000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+27800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+28000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+28800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+29000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+29800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,178, 93, 93, /* U+2A000 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, /* U+2A800 */ + 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93,179, 93, /* U+2B000 */ +180, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2F000 */ + 93, 93, 93, 93,180, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+2F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+30000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+30800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+31000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+31800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+32000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+32800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+33000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+33800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+34000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+34800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+35000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+35800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+36000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+36800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+37000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+37800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+38000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+38800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+39000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+39800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+3F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+40000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+40800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+41000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+41800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+42000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+42800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+43000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+43800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+44000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+44800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+45000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+45800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+46000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+46800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+47000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+47800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+48000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+48800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+49000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+49800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+4F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+50000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+50800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+51000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+51800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+52000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+52800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+53000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+53800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+54000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+54800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+55000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+55800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+56000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+56800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+57000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+57800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+58000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+58800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+59000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+59800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+5F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+60000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+60800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+61000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+61800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+62000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+62800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+63000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+63800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+64000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+64800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+65000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+65800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+66000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+66800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+67000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+67800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+68000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+68800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+69000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+69800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+6F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+70000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+70800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+71000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+71800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+72000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+72800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+73000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+73800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+74000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+74800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+75000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+75800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+76000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+76800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+77000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+77800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+78000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+78800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+79000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+79800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+7F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+80000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+80800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+81000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+81800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+82000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+82800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+83000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+83800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+84000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+84800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+85000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+85800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+86000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+86800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+87000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+87800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+88000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+88800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+89000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+89800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+8F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+90000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+90800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+91000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+91800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+92000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+92800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+93000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+93800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+94000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+94800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+95000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+95800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+96000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+96800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+97000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+97800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+98000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+98800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+99000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+99800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9A000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9A800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9B000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9B800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9C000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9C800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9D000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9D800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9E000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9E800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9F000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+9F800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A0000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A0800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A1000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A1800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A2000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A2800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A3000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A3800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A4000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A4800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A5000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A5800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A6000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A6800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A7000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A7800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A8000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A8800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A9000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+A9800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AA000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AA800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AB000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AB800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AC000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AC800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AD000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AD800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AE000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AE800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AF000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+AF800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B0000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B0800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B1000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B1800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B2000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B2800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B3000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B3800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B4000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B4800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B5000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B5800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B6000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B6800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B7000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B7800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B8000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B8800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B9000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+B9800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BA000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BA800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BB000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BB800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BC000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BC800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BD000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BD800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BE000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BE800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BF000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+BF800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C0000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C0800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C1000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C1800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C2000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C2800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C3000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C3800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C4000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C4800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C5000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C5800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C6000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C6800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C7000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C7800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C8000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C8800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C9000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+C9800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CA000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CA800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CB000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CB800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CC000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CC800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CD000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CD800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CE000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CE800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CF000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+CF800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D0000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D0800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D1000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D1800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D2000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D2800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D3000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D3800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D4000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D4800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D5000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D5800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D6000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D6800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D7000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D7800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D8000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D8800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D9000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+D9800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DA000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DA800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DB000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DB800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DC000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DC800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DD000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DD800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DE000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DE800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DF000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+DF800 */ +181, 17,182,183, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E0000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E0800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E1000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E1800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E2000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E2800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E3000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E3800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E4000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E4800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E5000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E5800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E6000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E6800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E7000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E7800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E8000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E8800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E9000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+E9800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EA000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EA800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EB000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EB800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EC000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EC800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+ED000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+ED800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EE000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EE800 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EF000 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, /* U+EF800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F0000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F0800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F1000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F1800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F2000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F2800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F3000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F3800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F4000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F4800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F5000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F5800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F6000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F6800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F7000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F7800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F8000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F8800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F9000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+F9800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FA000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FA800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FB000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FB800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FC000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FC800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FD000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FD800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FE000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FE800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+FF000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,184, /* U+FF800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+100000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+100800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+101000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+101800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+102000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+102800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+103000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+103800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+104000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+104800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+105000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+105800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+106000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+106800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+107000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+107800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+108000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+108800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+109000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+109800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10A000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10A800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10B000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10B800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10C000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10C800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10D000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10D800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10E000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10E800 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,114, /* U+10F000 */ +114,114,114,114,114,114,114,114,114,114,114,114,114,114,114,184, /* U+10F800 */ +}; + +const pcre_uint16 PRIV(ucd_stage2)[] = { /* 47360 bytes, block = 128 */ +/* block 0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 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, 2, 2, 3, 2, 2, 2, 4, 5, 2, 6, 2, 7, 2, 2, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 2, 6, 6, 6, 2, + 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 4, 2, 5, 10, 11, + 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 4, 6, 5, 6, 0, + +/* block 1 */ + 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 3, 3, 13, 13, 10, 13, 14, 15, 6, 16, 13, 10, + 13, 6, 17, 17, 10, 18, 13, 2, 10, 17, 14, 19, 17, 17, 17, 2, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 6, 9, 9, 9, 9, 9, 9, 9, 14, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 6, 12, 12, 12, 12, 12, 12, 12, 20, + +/* block 2 */ + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 23, 24, 21, 22, 21, 22, 21, 22, 14, 21, 22, 21, 22, 21, 22, 21, + 22, 21, 22, 21, 22, 21, 22, 21, 22, 14, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 25, 21, 22, 21, 22, 21, 22, 26, + +/* block 3 */ + 27, 28, 21, 22, 21, 22, 29, 21, 22, 30, 30, 21, 22, 14, 31, 32, + 33, 21, 22, 30, 34, 35, 36, 37, 21, 22, 38, 14, 36, 39, 40, 41, + 21, 22, 21, 22, 21, 22, 42, 21, 22, 42, 14, 14, 21, 22, 42, 21, + 22, 43, 43, 21, 22, 21, 22, 44, 21, 22, 14, 45, 21, 22, 14, 46, + 45, 45, 45, 45, 47, 48, 49, 47, 48, 49, 47, 48, 49, 21, 22, 21, + 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 50, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 14, 47, 48, 49, 21, 22, 51, 52, 21, 22, 21, 22, 21, 22, 21, 22, + +/* block 4 */ + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 53, 14, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 14, 14, 14, 14, 14, 14, 54, 21, 22, 55, 56, 57, + 57, 21, 22, 58, 59, 60, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 61, 62, 63, 64, 65, 14, 66, 66, 14, 67, 14, 68, 14, 14, 14, 14, + 66, 14, 14, 69, 14, 70, 14, 14, 71, 72, 14, 73, 14, 14, 14, 72, + 14, 74, 75, 14, 14, 76, 14, 14, 14, 14, 14, 14, 14, 77, 14, 14, + +/* block 5 */ + 78, 14, 14, 78, 14, 14, 14, 14, 78, 79, 80, 80, 81, 14, 14, 14, + 14, 14, 82, 14, 45, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 10, 10, 10, 10, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 84, 84, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 83, 83, 83, 83, 83, 10, 10, 10, 10, 10, 85, 85, 84, 10, 84, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + +/* block 6 */ + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 87, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 88, 89, 88, 89, 84, 90, 88, 89, 91, 91, 92, 93, 93, 93, 2, 91, + +/* block 7 */ + 91, 91, 91, 91, 90, 10, 94, 2, 95, 95, 95, 91, 96, 91, 97, 97, + 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 91, 99, 99, 99, 99, 99, 99, 99, 99, 99,100,101,101,101, + 98,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102, +102,102,103,102,102,102,102,102,102,102,102,102,104,105,105,106, +107,108,109,109,109,110,111,112, 88, 89, 88, 89, 88, 89, 88, 89, + 88, 89,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +115,116,117, 98,118,119,120, 88, 89,121, 88, 89, 98,122,122,122, + +/* block 8 */ +123,123,123,123,123,123,123,123,123,123,123,123,123,123,123,123, +124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, +124,124,124,124,124,124,124,124,124,124,124,124,124,124,124,124, +125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, +125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125, +126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, + +/* block 9 */ +127,128,129,130,130, 86, 86,130,131,131,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +132,127,128,127,128,127,128,127,128,127,128,127,128,127,128,133, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, + +/* block 10 */ +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128, 91, 91, 91, 91, 91, 91, 91, 91, + 91,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, +134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134, +134,134,134,134,134,134,134, 91, 91,135,136,136,136,136,136,136, + 91,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, +137,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137, + +/* block 11 */ +137,137,137,137,137,137,137,138, 91, 2,139, 91, 91, 91, 91, 91, + 91,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, +140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140, +140,140,140,140,140,140,140,140,140,140,140,140,140,140,141,140, +142,140,140,142,140,140,142,140, 91, 91, 91, 91, 91, 91, 91, 91, +143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143, +143,143,143,143,143,143,143,143,143,143,143, 91, 91, 91, 91, 91, +143,143,143,142,142, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 12 */ +144,144,144,144, 91, 91,145,145,145,146,146,147, 2,146,148,148, +149,149,149,149,149,149,149,149,149,149,149, 2, 91, 91,146, 2, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + 84,150,150,150,150,150,150,150,150,150,150, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86,149,149,149,149,149,149,149,149,149, 86, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,146,146,146,146,150,150, + 86,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + +/* block 13 */ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,146,150,149,149,149,149,149,149,149, 16,148,149, +149,149,149,149,149,151,151,149,149,148,149,149,149,149,150,150, +152,152,152,152,152,152,152,152,152,152,150,150,150,148,148,150, + +/* block 14 */ +153,153,153,153,153,153,153,153,153,153,153,153,153,153, 91,154, +155,156,155,155,155,155,155,155,155,155,155,155,155,155,155,155, +155,155,155,155,155,155,155,155,155,155,155,155,155,155,155,155, +156,156,156,156,156,156,156,156,156,156,156,156,156,156,156,156, +156,156,156,156,156,156,156,156,156,156,156, 91, 91,155,155,155, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + +/* block 15 */ +157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, +157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157, +157,157,157,157,157,157,158,158,158,158,158,158,158,158,158,158, +158,157, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +159,159,159,159,159,159,159,159,159,159,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,160, +160,160,160,160,160,160,160,160,160,160,160,161,161,161,161,161, +161,161,161,161,162,162,163,164,164,164,162, 91, 91, 91, 91, 91, + +/* block 16 */ +165,165,165,165,165,165,165,165,165,165,165,165,165,165,165,165, +165,165,165,165,165,165,166,166,166,166,167,166,166,166,166,166, +166,166,166,166,167,166,166,166,167,166,166,166,166,166, 91, 91, +168,168,168,168,168,168,168,168,168,168,168,168,168,168,168, 91, +169,169,169,169,169,169,169,169,169,169,169,169,169,169,169,169, +169,169,169,169,169,169,169,169,169,170,170,170, 91, 91,171, 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, 91, 91, 91, 91, 91, 91, 91, + +/* block 17 */ + 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, 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, 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, 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, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 18 */ +172,172,172,173,174,174,174,174,174,174,174,174,174,174,174,174, +174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, +174,174,174,174,174,174,174,174,174,174,174,174,174,174,174,174, +174,174,174,174,174,174,174,174,174,174,172,173,172,174,173,173, +173,172,172,172,172,172,172,172,172,173,173,173,173,172,173,173, +174, 86, 86,172,172,172,172,172,174,174,174,174,174,174,174,174, +174,174,172,172, 2, 2,175,175,175,175,175,175,175,175,175,175, + 2,176,174,174,174,174,174,174, 91,174,174,174,174,174,174,174, + +/* block 19 */ + 91,177,178,178, 91,179,179,179,179,179,179,179,179, 91, 91,179, +179, 91, 91,179,179,179,179,179,179,179,179,179,179,179,179,179, +179,179,179,179,179,179,179,179,179, 91,179,179,179,179,179,179, +179, 91,179, 91, 91, 91,179,179,179,179, 91, 91,177,179,178,178, +178,177,177,177,177, 91, 91,178,178, 91, 91,178,178,177,179, 91, + 91, 91, 91, 91, 91, 91, 91,178, 91, 91, 91, 91,179,179, 91,179, +179,179,177,177, 91, 91,180,180,180,180,180,180,180,180,180,180, +179,179,181,181,182,182,182,182,182,182,183,181, 91, 91, 91, 91, + +/* block 20 */ + 91,184,184,185, 91,186,186,186,186,186,186, 91, 91, 91, 91,186, +186, 91, 91,186,186,186,186,186,186,186,186,186,186,186,186,186, +186,186,186,186,186,186,186,186,186, 91,186,186,186,186,186,186, +186, 91,186,186, 91,186,186, 91,186,186, 91, 91,184, 91,185,185, +185,184,184, 91, 91, 91, 91,184,184, 91, 91,184,184,184, 91, 91, + 91,184, 91, 91, 91, 91, 91, 91, 91,186,186,186,186, 91,186, 91, + 91, 91, 91, 91, 91, 91,187,187,187,187,187,187,187,187,187,187, +184,184,186,186,186,184, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 21 */ + 91,188,188,189, 91,190,190,190,190,190,190,190,190,190, 91,190, +190,190, 91,190,190,190,190,190,190,190,190,190,190,190,190,190, +190,190,190,190,190,190,190,190,190, 91,190,190,190,190,190,190, +190, 91,190,190, 91,190,190,190,190,190, 91, 91,188,190,189,189, +189,188,188,188,188,188, 91,188,188,189, 91,189,189,188, 91, 91, +190, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +190,190,188,188, 91, 91,191,191,191,191,191,191,191,191,191,191, + 91,192, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 22 */ + 91,193,194,194, 91,195,195,195,195,195,195,195,195, 91, 91,195, +195, 91, 91,195,195,195,195,195,195,195,195,195,195,195,195,195, +195,195,195,195,195,195,195,195,195, 91,195,195,195,195,195,195, +195, 91,195,195, 91,195,195,195,195,195, 91, 91,193,195,194,193, +194,193,193,193,193, 91, 91,194,194, 91, 91,194,194,193, 91, 91, + 91, 91, 91, 91, 91, 91,193,194, 91, 91, 91, 91,195,195, 91,195, +195,195,193,193, 91, 91,196,196,196,196,196,196,196,196,196,196, +197,195,198,198,198,198,198,198, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 23 */ + 91, 91,199,200, 91,200,200,200,200,200,200, 91, 91, 91,200,200, +200, 91,200,200,200,200, 91, 91, 91,200,200, 91,200, 91,200,200, + 91, 91, 91,200,200, 91, 91, 91,200,200,200, 91, 91, 91,200,200, +200,200,200,200,200,200,200,200,200,200, 91, 91, 91, 91,201,201, +199,201,201, 91, 91, 91,201,201,201, 91,201,201,201,199, 91, 91, +200, 91, 91, 91, 91, 91, 91,201, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91,202,202,202,202,202,202,202,202,202,202, +203,203,203,204,204,204,204,204,204,205,204, 91, 91, 91, 91, 91, + +/* block 24 */ + 91,206,206,206, 91,207,207,207,207,207,207,207,207, 91,207,207, +207, 91,207,207,207,207,207,207,207,207,207,207,207,207,207,207, +207,207,207,207,207,207,207,207,207, 91,207,207,207,207,207,207, +207,207,207,207, 91,207,207,207,207,207, 91, 91, 91,207,208,208, +208,206,206,206,206, 91,208,208,208, 91,208,208,208,208, 91, 91, + 91, 91, 91, 91, 91,208,208, 91,207,207, 91, 91, 91, 91, 91, 91, +207,207,208,208, 91, 91,209,209,209,209,209,209,209,209,209,209, + 91, 91, 91, 91, 91, 91, 91, 91,210,210,210,210,210,210,210,211, + +/* block 25 */ + 91, 91,212,212, 91,213,213,213,213,213,213,213,213, 91,213,213, +213, 91,213,213,213,213,213,213,213,213,213,213,213,213,213,213, +213,213,213,213,213,213,213,213,213, 91,213,213,213,213,213,213, +213,213,213,213, 91,213,213,213,213,213, 91, 91,214,213,212,214, +212,212,212,212,212, 91,214,212,212, 91,212,212,214,214, 91, 91, + 91, 91, 91, 91, 91,212,212, 91, 91, 91, 91, 91, 91, 91,213, 91, +213,213,214,214, 91, 91,215,215,215,215,215,215,215,215,215,215, + 91,213,213, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 26 */ + 91, 91,216,216, 91,217,217,217,217,217,217,217,217, 91,217,217, +217, 91,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217,217,217,217,217,217, +217,217,217,217,217,217,217,217,217,217,217, 91, 91,217,216,216, +216,218,218,218,218, 91,216,216,216, 91,216,216,216,218,217, 91, + 91, 91, 91, 91, 91, 91, 91,216, 91, 91, 91, 91, 91, 91, 91, 91, +217,217,218,218, 91, 91,219,219,219,219,219,219,219,219,219,219, +220,220,220,220,220,220, 91, 91, 91,221,217,217,217,217,217,217, + +/* block 27 */ + 91, 91,222,222, 91,223,223,223,223,223,223,223,223,223,223,223, +223,223,223,223,223,223,223, 91, 91, 91,223,223,223,223,223,223, +223,223,223,223,223,223,223,223,223,223,223,223,223,223,223,223, +223,223, 91,223,223,223,223,223,223,223,223,223, 91,223, 91, 91, +223,223,223,223,223,223,223, 91, 91, 91,224, 91, 91, 91, 91,222, +222,222,224,224,224, 91,224, 91,222,222,222,222,222,222,222,222, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91,222,222,225, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 28 */ + 91,226,226,226,226,226,226,226,226,226,226,226,226,226,226,226, +226,226,226,226,226,226,226,226,226,226,226,226,226,226,226,226, +226,226,226,226,226,226,226,226,226,226,226,226,226,226,226,226, +226,227,226,226,227,227,227,227,227,227,227, 91, 91, 91, 91, 3, +226,226,226,226,226,226,228,227,227,227,227,227,227,227,227,229, +230,230,230,230,230,230,230,230,230,230,229,229, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 29 */ + 91,231,231, 91,231, 91, 91,231,231, 91,231, 91, 91,231, 91, 91, + 91, 91, 91, 91,231,231,231,231, 91,231,231,231,231,231,231,231, + 91,231,231,231, 91,231, 91,231, 91, 91,231,231, 91,231,231,231, +231,232,231,231,232,232,232,232,232,232, 91,232,232,231, 91, 91, +231,231,231,231,231, 91,233, 91,232,232,232,232,232,232, 91, 91, +234,234,234,234,234,234,234,234,234,234, 91, 91,231,231, 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, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 30 */ +235,236,236,236,237,237,237,237,237,237,237,237,237,237,237,237, +237,237,237,236,236,236,236,236,238,238,236,236,236,236,236,236, +239,239,239,239,239,239,239,239,239,239,240,240,240,240,240,240, +240,240,240,240,236,238,236,238,236,238,241,242,241,242,243,243, +235,235,235,235,235,235,235,235, 91,235,235,235,235,235,235,235, +235,235,235,235,235,235,235,235,235,235,235,235,235,235,235,235, +235,235,235,235,235,235,235,235,235,235,235,235,235, 91, 91, 91, + 91,238,238,238,238,238,238,238,238,238,238,238,238,238,238,243, + +/* block 31 */ +238,238,238,238,238,237,238,238,235,235,235,235,235,238,238,238, +238,238,238,238,238,238,238,238, 91,238,238,238,238,238,238,238, +238,238,238,238,238,238,238,238,238,238,238,238,238,238,238,238, +238,238,238,238,238,238,238,238,238,238,238,238,238, 91,236,236, +236,236,236,236,236,236,238,236,236,236,236,236,236, 91,236,236, +237,237,237,237,237, 13, 13, 13, 13,237,237, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 32 */ +244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +244,244,244,244,244,244,244,244,244,244,244,245,245,246,246,246, +246,245,246,246,246,246,246,246,245,246,246,245,245,246,246,244, +247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248, +244,244,244,244,244,244,245,245,246,246,244,244,244,244,246,246, +246,244,245,245,245,244,244,245,245,245,245,245,245,245,244,244, +244,246,246,246,246,244,244,244,244,244,244,244,244,244,244,244, + +/* block 33 */ +244,244,246,245,245,246,246,245,245,245,245,245,245,246,244,245, +247,247,247,247,247,247,247,247,247,247,245,245,245,246,249,249, +250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, +250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250, +250,250,250,250,250,250, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251, +251,251,251,251,251,251,251,251,251,251,251,251,251,251,251,251, +251,251,251,251,251,251,251,251,251,251,251, 2,252, 91, 91, 91, + +/* block 34 */ +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, + +/* block 35 */ +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254, 91,254,254,254,254, 91, 91, +254,254,254,254,254,254,254, 91,254, 91,254,254,254,254, 91, 91, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + +/* block 36 */ +254,254,254,254,254,254,254,254,254, 91,254,254,254,254, 91, 91, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254, 91,254,254,254,254, 91, 91,254,254,254,254,254,254,254, 91, +254, 91,254,254,254,254, 91, 91,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254, 91,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + +/* block 37 */ +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254, 91,254,254,254,254, 91, 91,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254,254,254,254,254, 91, 91,255,255,255, +256,257,257,257,257,257,257,257,257,258,258,258,258,258,258,258, +258,258,258,258,258,258,258,258,258,258,258,258,258, 91, 91, 91, + +/* block 38 */ +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +256,256,256,256,256,256,256,256,256,256, 91, 91, 91, 91, 91, 91, +259,259,259,259,259,259,259,259,259,259,259,259,259,259,259,259, +259,259,259,259,259,259,259,259,259,259,259,259,259,259,259,259, +259,259,259,259,259,259,259,259,259,259,259,259,259,259,259,259, +259,259,259,259,259,259,259,259,259,259,259,259,259,259,259,259, +259,259,259,259,259,259,259,259,259,259,259,259,259,259,259,259, +259,259,259,259,259, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 39 */ +260,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, + +/* block 40 */ +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, + +/* block 41 */ +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,262,262,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, + +/* block 42 */ +263,264,264,264,264,264,264,264,264,264,264,264,264,264,264,264, +264,264,264,264,264,264,264,264,264,264,264,265,266, 91, 91, 91, +267,267,267,267,267,267,267,267,267,267,267,267,267,267,267,267, +267,267,267,267,267,267,267,267,267,267,267,267,267,267,267,267, +267,267,267,267,267,267,267,267,267,267,267,267,267,267,267,267, +267,267,267,267,267,267,267,267,267,267,267,267,267,267,267,267, +267,267,267,267,267,267,267,267,267,267,267, 2, 2, 2,268,268, +268, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 43 */ +269,269,269,269,269,269,269,269,269,269,269,269,269, 91,269,269, +269,269,270,270,270, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +271,271,271,271,271,271,271,271,271,271,271,271,271,271,271,271, +271,271,272,272,272, 2, 2, 91, 91, 91, 91, 91, 91, 91, 91, 91, +273,273,273,273,273,273,273,273,273,273,273,273,273,273,273,273, +273,273,274,274, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +275,275,275,275,275,275,275,275,275,275,275,275,275, 91,275,275, +275, 91,276,276, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 44 */ +277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277, +277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277, +277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277, +277,277,277,277,278,278,279,280,280,280,280,280,280,280,279,279, +279,279,279,279,279,279,280,279,279,280,280,280,280,280,280,280, +280,280,280,280,281,281,281,282,281,281,281,283,277,280, 91, 91, +284,284,284,284,284,284,284,284,284,284, 91, 91, 91, 91, 91, 91, +285,285,285,285,285,285,285,285,285,285, 91, 91, 91, 91, 91, 91, + +/* block 45 */ +286,286, 2, 2,286, 2,287,286,286,286,286,288,288,288,289, 91, +290,290,290,290,290,290,290,290,290,290, 91, 91, 91, 91, 91, 91, +291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,292,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,291,291,291,291,291, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 46 */ +291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,291,291,291,291,291,291,291,291,291,291,291,291,291, +291,291,291,291,291,291,291,291,291,288,291, 91, 91, 91, 91, 91, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261,261,261,261,261,261,261,261,261,261,261, +261,261,261,261,261,261, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 47 */ +293,293,293,293,293,293,293,293,293,293,293,293,293,293,293,293, +293,293,293,293,293,293,293,293,293,293,293,293,293, 91, 91, 91, +294,294,294,295,295,295,295,294,294,295,295,295, 91, 91, 91, 91, +295,295,294,295,295,295,295,295,295,294,294,294, 91, 91, 91, 91, +296, 91, 91, 91,297,297,298,298,298,298,298,298,298,298,298,298, +299,299,299,299,299,299,299,299,299,299,299,299,299,299,299,299, +299,299,299,299,299,299,299,299,299,299,299,299,299,299, 91, 91, +299,299,299,299,299, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 48 */ +300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300, +300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300, +300,300,300,300,300,300,300,300,300,300,300,300, 91, 91, 91, 91, +301,301,301,301,301,301,301,301,301,301,301,301,301,301,301,301, +301,300,300,300,300,300,300,300,301,301, 91, 91, 91, 91, 91, 91, +302,302,302,302,302,302,302,302,302,302,303, 91, 91, 91,304,304, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, +305,305,305,305,305,305,305,305,305,305,305,305,305,305,305,305, + +/* block 49 */ +306,306,306,306,306,306,306,306,306,306,306,306,306,306,306,306, +306,306,306,306,306,306,306,307,307,308,308,308, 91, 91,309,309, +310,310,310,310,310,310,310,310,310,310,310,310,310,310,310,310, +310,310,310,310,310,310,310,310,310,310,310,310,310,310,310,310, +310,310,310,310,310,310,310,310,310,310,310,310,310,310,310,310, +310,310,310,310,310,311,312,311,312,312,312,312,312,312,312, 91, +312,311,312,311,311,312,312,312,312,312,312,312,312,311,311,311, +311,311,311,312,312,312,312,312,312,312,312,312,312, 91, 91,312, + +/* block 50 */ +313,313,313,313,313,313,313,313,313,313, 91, 91, 91, 91, 91, 91, +313,313,313,313,313,313,313,313,313,313, 91, 91, 91, 91, 91, 91, +314,314,314,314,314,314,314,315,314,314,314,314,314,314, 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, 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, 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, 91, 91, 91, 91, + +/* block 51 */ +316,316,316,316,317,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,318,318,318,318,318,318,318,318,318,318,318,318, +318,318,318,318,316,317,316,316,316,316,316,317,316,317,317,317, +317,317,316,317,317,318,318,318,318,318,318,318, 91, 91, 91, 91, +319,319,319,319,319,319,319,319,319,319,320,320,320,320,320,320, +320,321,321,321,321,321,321,321,321,321,321,316,316,316,316,316, +316,316,316,316,321,321,321,321,321,321,321,321,321, 91, 91, 91, + +/* block 52 */ +322,322,323,324,324,324,324,324,324,324,324,324,324,324,324,324, +324,324,324,324,324,324,324,324,324,324,324,324,324,324,324,324, +324,323,322,322,322,322,323,323,322,322,323, 91, 91, 91,324,324, +325,325,325,325,325,325,325,325,325,325, 91, 91, 91, 91, 91, 91, +326,326,326,326,326,326,326,326,326,326,326,326,326,326,326,326, +326,326,326,326,326,326,326,326,326,326,326,326,326,326,326,326, +326,326,326,326,326,326,327,328,327,327,328,328,328,327,328,327, +327,327,328,328, 91, 91, 91, 91, 91, 91, 91, 91,329,329,329,329, + +/* block 53 */ +330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, +330,330,330,330,330,330,330,330,330,330,330,330,330,330,330,330, +330,330,330,330,331,331,331,331,331,331,331,331,332,332,332,332, +332,332,332,332,331,331,332,332, 91, 91, 91,333,333,333,333,333, +334,334,334,334,334,334,334,334,334,334, 91, 91, 91,330,330,330, +335,335,335,335,335,335,335,335,335,335,336,336,336,336,336,336, +336,336,336,336,336,336,336,336,336,336,336,336,336,336,336,336, +336,336,336,336,336,336,336,336,337,337,337,337,337,337,338,338, + +/* block 54 */ + 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, 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, 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, 91, 91, + 86, 86, 86, 2, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86,339, 86, 86, 86, 86, 86, 86, 86,340,340,340,340, 86,340,340, +340,340,339, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 55 */ + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 98, 98, 98, 98, 98,341, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 92, 92, 92, + 92, 92, 14, 14, 14, 14, 98, 98, 98, 98, 98, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14,342,343, 14, 14, 14,344, 14, 14, + +/* block 56 */ + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 92, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 86, 86, 86, 86, + +/* block 57 */ + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + +/* block 58 */ + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 14, 14, 14, 14, 14,345, 14, 14,346, 14, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + +/* block 59 */ +347,347,347,347,347,347,347,347,348,348,348,348,348,348,348,348, +347,347,347,347,347,347, 91, 91,348,348,348,348,348,348, 91, 91, +347,347,347,347,347,347,347,347,348,348,348,348,348,348,348,348, +347,347,347,347,347,347,347,347,348,348,348,348,348,348,348,348, +347,347,347,347,347,347, 91, 91,348,348,348,348,348,348, 91, 91, + 98,347, 98,347, 98,347, 98,347, 91,348, 91,348, 91,348, 91,348, +347,347,347,347,347,347,347,347,348,348,348,348,348,348,348,348, +349,349,350,350,350,350,351,351,352,352,353,353,354,354, 91, 91, + +/* block 60 */ +347,347,347,347,347,347,347,347,355,355,355,355,355,355,355,355, +347,347,347,347,347,347,347,347,355,355,355,355,355,355,355,355, +347,347,347,347,347,347,347,347,355,355,355,355,355,355,355,355, +347,347, 98,356, 98, 91, 98, 98,348,348,357,357,358, 90,359, 90, + 90, 90, 98,356, 98, 91, 98, 98,360,360,360,360,358, 90, 90, 90, +347,347, 98, 98, 91, 91, 98, 98,348,348,361,361, 91, 90, 90, 90, +347,347, 98, 98, 98,117, 98, 98,348,348,362,362,121, 90, 90, 90, + 91, 91, 98,356, 98, 91, 98, 98,363,363,364,364,358, 90, 90, 91, + +/* block 61 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 16,365,365, 16, 16, + 7, 7, 7, 7, 7, 7, 2, 2, 15, 19, 4, 15, 15, 19, 4, 15, + 2, 2, 2, 2, 2, 2, 2, 2,366,367, 16, 16, 16, 16, 16, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 19, 2, 2, 2, 2, 11, + 11, 2, 2, 2, 6, 4, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 6, 2, 11, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 16, 16, 16, 16, 16, 91, 91, 91, 91, 91, 16, 16, 16, 16, 16, 16, + 17, 83, 91, 91, 17, 17, 17, 17, 17, 17, 6, 6, 6, 4, 5, 83, + +/* block 62 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 6, 6, 6, 4, 5, 91, + 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 91, 91, 91, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,368,368,368, +368, 86,368,368,368, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 63 */ + 13, 13,369, 13, 13, 13, 13,369, 13, 13,370,369,369,369,370,370, +369,369,369,370, 13,369, 13, 13, 6,369,369,369,369,369, 13, 13, + 13, 13, 13, 13,369, 13,371, 13,369, 13,372,373,369,369, 13,370, +369,369,374,369,370,340,340,340,340,370, 13, 13,370,370,369,369, + 6, 6, 6, 6, 6,369,370,370,370,370, 13, 6, 13, 13,375, 13, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, +376,376,376,376,376,376,376,376,376,376,376,376,376,376,376,376, +377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377, + +/* block 64 */ +378,378,378, 21, 22,378,378,378,378, 17, 91, 91, 91, 91, 91, 91, + 6, 6, 6, 6, 6, 13, 13, 13, 13, 13, 6, 6, 13, 13, 13, 13, + 6, 13, 13, 6, 13, 13, 6, 13, 13, 13, 13, 13, 13, 13, 6, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, + 13, 13, 6, 13, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + +/* block 65 */ + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + +/* block 66 */ + 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 6, 6, 13, 13, 13, 13, 13, 13, 13, 4, 5, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 13, 13, 13, + +/* block 67 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, + 6, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 68 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 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, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + +/* block 69 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13,379,379,379,379,379,379,379,379,379,379, +379,379,379,379,379,379,379,379,379,379,379,379,379,379,379,379, +380,380,380,380,380,380,380,380,380,380,380,380,380,380,380,380, +380,380,380,380,380,380,380,380,380,380, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + +/* block 70 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 71 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 6, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 6, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 6, 6, 6, 6, 6, 6, 6, 6, + +/* block 72 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 6, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 73 */ + 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 4, 5, 4, 5, 4, 5, 4, 5, + 4, 5, 4, 5, 4, 5, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + +/* block 74 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 6, 6, 6, 6, 6, 4, 5, 6, 6, 6, 6, 91, 6, 91, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + +/* block 75 */ +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, +381,381,381,381,381,381,381,381,381,381,381,381,381,381,381,381, + +/* block 76 */ + 6, 6, 6, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, + 5, 4, 5, 4, 5, 4, 5, 4, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 4, 5, 4, 5, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 5, 6, 6, + +/* block 77 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 13, 13, 6, 6, 6, 6, 6, 6, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 78 */ +382,382,382,382,382,382,382,382,382,382,382,382,382,382,382,382, +382,382,382,382,382,382,382,382,382,382,382,382,382,382,382,382, +382,382,382,382,382,382,382,382,382,382,382,382,382,382,382, 91, +383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383, +383,383,383,383,383,383,383,383,383,383,383,383,383,383,383,383, +383,383,383,383,383,383,383,383,383,383,383,383,383,383,383, 91, + 21, 22,384,385,386,387,388, 21, 22, 21, 22, 21, 22,389,390,391, +392, 14, 21, 22, 14, 21, 22, 14, 14, 14, 14, 14, 14, 83,393,393, + +/* block 79 */ +113,114,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +113,114,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +113,114,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +113,114,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +113,114,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +113,114,113,114,113,114,113,114,113,114,113,114,113,114,113,114, +113,114,113,114,394,395,395,395,395,395,395,113,114,113,114,396, +396,396, 91, 91, 91, 91, 91, 91, 91,397,397,397,397,398,397,397, + +/* block 80 */ +399,399,399,399,399,399,399,399,399,399,399,399,399,399,399,399, +399,399,399,399,399,399,399,399,399,399,399,399,399,399,399,399, +399,399,399,399,399,399, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400, +400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400, +400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400, +400,400,400,400,400,400, 91, 91, 91, 91, 91, 91, 91, 91, 91,401, +402, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,403, + +/* block 81 */ +254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, +254,254,254,254,254,254,254, 91, 91, 91, 91, 91, 91, 91, 91, 91, +254,254,254,254,254,254,254, 91,254,254,254,254,254,254,254, 91, +254,254,254,254,254,254,254, 91,254,254,254,254,254,254,254, 91, +254,254,254,254,254,254,254, 91,254,254,254,254,254,254,254, 91, +254,254,254,254,254,254,254, 91,254,254,254,254,254,254,254, 91, +130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, +130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130, + +/* block 82 */ + 2, 2, 15, 19, 15, 19, 2, 2, 2, 15, 19, 2, 15, 19, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 7, 2, 2, 7, 2, 15, 19, 2, 2, + 15, 19, 4, 5, 4, 5, 4, 5, 4, 5, 2, 2, 2, 2, 2, 84, + 2, 2, 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, 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, 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, + +/* block 83 */ +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404, 91,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 84 */ +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, + +/* block 85 */ +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404,404,404,404,404,404,404,404,404,404,404, +404,404,404,404,404,404, 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, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, + +/* block 86 */ + 1, 2, 2, 2, 13,405,340,406, 4, 5, 4, 5, 4, 5, 4, 5, + 4, 5, 13, 13, 4, 5, 4, 5, 4, 5, 4, 5, 7, 4, 5, 5, + 13,406,406,406,406,406,406,406,406,406, 86, 86, 86, 86,407,407, + 7, 84, 84, 84, 84, 84, 13, 13,406,406,406,405,340, 2, 13, 13, + 91,408,408,408,408,408,408,408,408,408,408,408,408,408,408,408, +408,408,408,408,408,408,408,408,408,408,408,408,408,408,408,408, +408,408,408,408,408,408,408,408,408,408,408,408,408,408,408,408, +408,408,408,408,408,408,408,408,408,408,408,408,408,408,408,408, + +/* block 87 */ +408,408,408,408,408,408,408,408,408,408,408,408,408,408,408,408, +408,408,408,408,408,408,408, 91, 91, 86, 86, 10, 10,409,409,408, + 7,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, +410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, +410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, +410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, +410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, +410,410,410,410,410,410,410,410,410,410,410, 2, 84,411,411,410, + +/* block 88 */ + 91, 91, 91, 91, 91,412,412,412,412,412,412,412,412,412,412,412, +412,412,412,412,412,412,412,412,412,412,412,412,412,412,412,412, +412,412,412,412,412,412,412,412,412,412,412,412,412,412, 91, 91, + 91,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, + +/* block 89 */ +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, 91, + 13, 13, 17, 17, 17, 17, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +412,412,412,412,412,412,412,412,412,412,412,412,412,412,412,412, +412,412,412,412,412,412,412,412,412,412,412, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, + +/* block 90 */ +413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, +413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, 91, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, +413,413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, +413,413,413,413,413,413,413,413,413,413,413,413,413,413,413, 13, + +/* block 91 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, 91, + +/* block 92 */ +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414,414,414,414,414,414,414,414,414, +414,414,414,414,414,414,414,414, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 93 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, + +/* block 94 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 95 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415, 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, 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, + +/* block 96 */ +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,417,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, + +/* block 97 */ +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, +416,416,416,416,416,416,416,416,416,416,416,416,416,416,416,416, + +/* block 98 */ +416,416,416,416,416,416,416,416,416,416,416,416,416, 91, 91, 91, +418,418,418,418,418,418,418,418,418,418,418,418,418,418,418,418, +418,418,418,418,418,418,418,418,418,418,418,418,418,418,418,418, +418,418,418,418,418,418,418,418,418,418,418,418,418,418,418,418, +418,418,418,418,418,418,418, 91, 91, 91, 91, 91, 91, 91, 91, 91, +419,419,419,419,419,419,419,419,419,419,419,419,419,419,419,419, +419,419,419,419,419,419,419,419,419,419,419,419,419,419,419,419, +419,419,419,419,419,419,419,419,420,420,420,420,420,420,421,421, + +/* block 99 */ +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, + +/* block 100 */ +422,422,422,422,422,422,422,422,422,422,422,422,423,424,424,424, +422,422,422,422,422,422,422,422,422,422,422,422,422,422,422,422, +425,425,425,425,425,425,425,425,425,425,422,422, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128,127,128,127,128,127,128,426,130, +131,131,131,427, 91, 91, 91, 91, 91, 91, 91, 91,130,130,427,342, + +/* block 101 */ +127,128,127,128,127,128,127,128,127,128,127,128,127,128,127,128, +127,128,127,128,127,128,127,128, 91, 91, 91, 91, 91, 91, 91, 91, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,429,429,429,429,429,429,429,429,429,429, +430,430,431,431,431,431,431,431, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 102 */ + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 84, 84, 84, 84, 84, 84, 84, 84, 84, + 10, 10, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 14, 14, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, + 83, 14, 14, 14, 14, 14, 14, 14, 14, 21, 22, 21, 22,432, 21, 22, + +/* block 103 */ + 21, 22, 21, 22, 21, 22, 21, 22, 84, 10, 10, 21, 22,433, 14, 91, + 21, 22, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 21, 22, 21, 22, 21, 22, 21, 22, 21, 22, 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, 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, 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, 91, 91, 14, 45, 45, 45, 45, 45, + +/* block 104 */ +434,434,435,434,434,434,435,434,434,434,434,435,434,434,434,434, +434,434,434,434,434,434,434,434,434,434,434,434,434,434,434,434, +434,434,434,436,436,435,435,436,437,437,437,437, 91, 91, 91, 91, + 17, 17, 17, 17, 17, 17, 13, 13, 3, 13, 91, 91, 91, 91, 91, 91, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,438,438,438,438,438,438,438,438,438,438,438,438, +438,438,438,438,439,439,439,439, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 105 */ +440,440,441,441,441,441,441,441,441,441,441,441,441,441,441,441, +441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,441, +441,441,441,441,441,441,441,441,441,441,441,441,441,441,441,441, +441,441,441,441,440,440,440,440,440,440,440,440,440,440,440,440, +440,440,440,440,442, 91, 91, 91, 91, 91, 91, 91, 91, 91,443,443, +444,444,444,444,444,444,444,444,444,444, 91, 91, 91, 91, 91, 91, +172,172,172,172,172,172,172,172,172,172,172,172,172,172,172,172, +172,172,174,174,174,174,174,174,445,445,445,174, 91, 91, 91, 91, + +/* block 106 */ +446,446,446,446,446,446,446,446,446,446,447,447,447,447,447,447, +447,447,447,447,447,447,447,447,447,447,447,447,447,447,447,447, +447,447,447,447,447,447,448,448,448,448,448,448,448,448,449,449, +450,450,450,450,450,450,450,450,450,450,450,450,450,450,450,450, +450,450,450,450,450,450,450,451,451,451,451,451,451,451,451,451, +451,451,452,452, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,453, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253, 91, 91, 91, + +/* block 107 */ +454,454,454,455,456,456,456,456,456,456,456,456,456,456,456,456, +456,456,456,456,456,456,456,456,456,456,456,456,456,456,456,456, +456,456,456,456,456,456,456,456,456,456,456,456,456,456,456,456, +456,456,456,454,455,455,454,454,454,454,455,455,454,455,455,455, +455,457,457,457,457,457,457,457,457,457,457,457,457,457, 91,458, +459,459,459,459,459,459,459,459,459,459, 91, 91, 91, 91,457,457, + 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, 91, 91, 91, 91, 91, 91, + +/* block 108 */ +460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460, +460,460,460,460,460,460,460,460,460,460,460,460,460,460,460,460, +460,460,460,460,460,460,460,460,460,461,461,461,461,461,461,462, +462,461,461,462,462,461,461, 91, 91, 91, 91, 91, 91, 91, 91, 91, +460,460,460,461,460,460,460,460,460,460,460,460,461,462, 91, 91, +463,463,463,463,463,463,463,463,463,463, 91, 91,464,464,464,464, +244,244,244,244,244,244,244,244,244,244,244,244,244,244,244,244, +465,244,244,244,244,244,244,249,249,249,244,245, 91, 91, 91, 91, + +/* block 109 */ +466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466, +466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466, +466,466,466,466,466,466,466,466,466,466,466,466,466,466,466,466, +467,466,467,467,467,466,466,467,467,466,466,466,466,466,467,467, +466,467,466, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91,466,466,468,469,469, + 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, 91, 91, 91, 91, 91, 91, + +/* block 110 */ + 91,254,254,254,254,254,254, 91, 91,254,254,254,254,254,254, 91, + 91,254,254,254,254,254,254, 91, 91, 91, 91, 91, 91, 91, 91, 91, +254,254,254,254,254,254,254, 91,254,254,254,254,254,254,254, 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, 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, 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, 91, 91, 91, + +/* block 111 */ + 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, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +470,470,470,470,470,470,470,470,470,470,470,470,470,470,470,470, +470,470,470,470,470,470,470,470,470,470,470,470,470,470,470,470, +470,470,470,471,471,472,471,471,472,471,471,473,471,472, 91, 91, +474,474,474,474,474,474,474,474,474,474, 91, 91, 91, 91, 91, 91, + +/* block 112 */ +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253, 91, 91, 91, 91,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253, 91, 91, 91, 91, + +/* block 113 */ +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, +475,475,475,475,475,475,475,475,475,475,475,475,475,475,475,475, + +/* block 114 */ +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, + +/* block 115 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415, 91, 91, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415, 91, 91, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, + +/* block 116 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 117 */ + 14, 14, 14, 14, 14, 14, 14, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91,138,138,138,138,138, 91, 91, 91, 91, 91,143,140,143, +143,143,143,143,143,143,143,143,143,477,143,143,143,143,143,143, +143,143,143,143,143,143,143, 91,143,143,143,143,143, 91,143, 91, +143,143, 91,143,143, 91,143,143,143,143,143,143,143,143,143,143, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + +/* block 118 */ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,478,478,478,478,478,478,478,478,478,478,478,478,478,478, +478,478, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + +/* block 119 */ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + +/* block 120 */ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150, 4, 5, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + +/* block 121 */ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, + 91, 91,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +150,150,150,150,150,150,150,150,150,150,150,150,147, 13, 91, 91, + +/* block 122 */ + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 2, 2, 2, 2, 2, 2, 2, 4, 5, 2, 91, 91, 91, 91, 91, 91, + 86, 86, 86, 86, 86, 86, 86, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 2, 7, 7, 11, 11, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, + 5, 4, 5, 4, 5, 2, 2, 4, 5, 2, 2, 2, 2, 11, 11, 11, + 2, 2, 2, 91, 2, 2, 2, 2, 7, 4, 5, 4, 5, 4, 5, 2, + 2, 2, 6, 7, 6, 6, 6, 91, 2, 3, 2, 2, 91, 91, 91, 91, +150,150,150,150,150, 91,150,150,150,150,150,150,150,150,150,150, + +/* block 123 */ +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150,150,150,150, +150,150,150,150,150,150,150,150,150,150,150,150,150, 91, 91, 16, + +/* block 124 */ + 91, 2, 2, 2, 3, 2, 2, 2, 4, 5, 2, 6, 2, 7, 2, 2, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 2, 6, 6, 6, 2, + 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 4, 2, 5, 10, 11, + 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 4, 6, 5, 6, 4, + 5, 2, 4, 5, 2, 2,410,410,410,410,410,410,410,410,410,410, + 84,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, + +/* block 125 */ +410,410,410,410,410,410,410,410,410,410,410,410,410,410,410,410, +410,410,410,410,410,410,410,410,410,410,410,410,410,410, 84, 84, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, +253,253,253,253,253,253,253,253,253,253,253,253,253,253,253, 91, + 91, 91,253,253,253,253,253,253, 91, 91,253,253,253,253,253,253, + 91, 91,253,253,253,253,253,253, 91, 91,253,253,253, 91, 91, 91, + 3, 3, 6, 10, 13, 3, 3, 91, 13, 6, 6, 6, 6, 13, 13, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 16, 16, 16, 13, 13, 91, 91, + +/* block 126 */ +479,479,479,479,479,479,479,479,479,479,479,479, 91,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479, 91,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479, 91,479,479, 91,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479, 91, 91, +479,479,479,479,479,479,479,479,479,479,479,479,479,479, 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, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 127 */ +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479,479,479,479,479,479, +479,479,479,479,479,479,479,479,479,479,479, 91, 91, 91, 91, 91, + +/* block 128 */ + 2, 2, 13, 91, 91, 91, 91, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 91, 91, 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, +480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480, +480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480, +480,480,480,480,480,480,480,480,480,480,480,480,480,480,480,480, +480,480,480,480,480,481,481,481,481,482,482,482,482,482,482,482, + +/* block 129 */ +482,482,482,482,482,482,482,482,482,482,481, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 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, 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, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 86, 91, 91, + +/* block 130 */ +483,483,483,483,483,483,483,483,483,483,483,483,483,483,483,483, +483,483,483,483,483,483,483,483,483,483,483,483,483, 91, 91, 91, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484,484,484,484,484,484,484,484,484,484,484,484,484,484,484,484, +484, 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, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 131 */ +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, +485,485,485,485,485,485,485,485,485,485,485,485,485,485,485, 91, +486,486,486,486, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +487,487,487,487,487,487,487,487,487,487,487,487,487,487,487,487, +487,488,487,487,487,487,487,487,487,487,488, 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, 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, 91, + +/* block 132 */ +489,489,489,489,489,489,489,489,489,489,489,489,489,489,489,489, +489,489,489,489,489,489,489,489,489,489,489,489,489,489, 91,490, +491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, +491,491,491,491,491,491,491,491,491,491,491,491,491,491,491,491, +491,491,491,491, 91, 91, 91, 91,491,491,491,491,491,491,491,491, +492,493,493,493,493,493, 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, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 133 */ +494,494,494,494,494,494,494,494,494,494,494,494,494,494,494,494, +494,494,494,494,494,494,494,494,494,494,494,494,494,494,494,494, +494,494,494,494,494,494,494,494,495,495,495,495,495,495,495,495, +495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495, +495,495,495,495,495,495,495,495,495,495,495,495,495,495,495,495, +496,496,496,496,496,496,496,496,496,496,496,496,496,496,496,496, +496,496,496,496,496,496,496,496,496,496,496,496,496,496,496,496, +496,496,496,496,496,496,496,496,496,496,496,496,496,496,496,496, + +/* block 134 */ +497,497,497,497,497,497,497,497,497,497,497,497,497,497,497,497, +497,497,497,497,497,497,497,497,497,497,497,497,497,497, 91, 91, +498,498,498,498,498,498,498,498,498,498, 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, 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, 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, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 135 */ +499,499,499,499,499,499, 91, 91,499, 91,499,499,499,499,499,499, +499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499, +499,499,499,499,499,499,499,499,499,499,499,499,499,499,499,499, +499,499,499,499,499,499, 91,499,499, 91, 91, 91,499, 91, 91,499, +500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500, +500,500,500,500,500,500, 91,501,502,502,502,502,502,502,502,502, + 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, 91, 91, 91, 91, 91, 91, + +/* block 136 */ +503,503,503,503,503,503,503,503,503,503,503,503,503,503,503,503, +503,503,503,503,503,503,504,504,504,504,504,504, 91, 91, 91,505, +506,506,506,506,506,506,506,506,506,506,506,506,506,506,506,506, +506,506,506,506,506,506,506,506,506,506, 91, 91, 91, 91, 91,507, + 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, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 137 */ +508,509,509,509, 91,509,509, 91, 91, 91, 91, 91,509,509,509,509, +508,508,508,508, 91,508,508,508, 91,508,508,508,508,508,508,508, +508,508,508,508,508,508,508,508,508,508,508,508,508,508,508,508, +508,508,508,508, 91, 91, 91, 91,509,509,509, 91, 91, 91, 91,509, +510,510,510,510,510,510,510,510, 91, 91, 91, 91, 91, 91, 91, 91, +511,511,511,511,511,511,511,511,511, 91, 91, 91, 91, 91, 91, 91, +512,512,512,512,512,512,512,512,512,512,512,512,512,512,512,512, +512,512,512,512,512,512,512,512,512,512,512,512,512,513,513,514, + +/* block 138 */ +515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515, +515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515, +515,515,515,515,515,515,515,515,515,515,515,515,515,515,515,515, +515,515,515,515,515,515, 91, 91, 91,516,516,516,516,516,516,516, +517,517,517,517,517,517,517,517,517,517,517,517,517,517,517,517, +517,517,517,517,517,517, 91, 91,518,518,518,518,518,518,518,518, +519,519,519,519,519,519,519,519,519,519,519,519,519,519,519,519, +519,519,519, 91, 91, 91, 91, 91,520,520,520,520,520,520,520,520, + +/* block 139 */ +521,521,521,521,521,521,521,521,521,521,521,521,521,521,521,521, +521,521,521,521,521,521,521,521,521,521,521,521,521,521,521,521, +521,521,521,521,521,521,521,521,521,521,521,521,521,521,521,521, +521,521,521,521,521,521,521,521,521,521,521,521,521,521,521,521, +521,521,521,521,521,521,521,521,521, 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, 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, 91, 91, 91, + +/* block 140 */ + 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, 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, 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, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +522,522,522,522,522,522,522,522,522,522,522,522,522,522,522,522, +522,522,522,522,522,522,522,522,522,522,522,522,522,522,522, 91, + +/* block 141 */ +523,524,523,525,525,525,525,525,525,525,525,525,525,525,525,525, +525,525,525,525,525,525,525,525,525,525,525,525,525,525,525,525, +525,525,525,525,525,525,525,525,525,525,525,525,525,525,525,525, +525,525,525,525,525,525,525,525,524,524,524,524,524,524,524,524, +524,524,524,524,524,524,524,526,526,526,526,526,526,526, 91, 91, + 91, 91,527,527,527,527,527,527,527,527,527,527,527,527,527,527, +527,527,527,527,527,527,528,528,528,528,528,528,528,528,528,528, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 142 */ +529,529,530,531,531,531,531,531,531,531,531,531,531,531,531,531, +531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531, +531,531,531,531,531,531,531,531,531,531,531,531,531,531,531,531, +530,530,530,529,529,529,529,530,530,529,529,532,532,533,532,532, +532,532, 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, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 143 */ +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, + +/* block 144 */ +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, +534,534,534,534,534,534,534,534,534,534,534,534,534,534,534, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 145 */ +535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, +535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, +535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, +535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, +535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, +535,535,535,535,535,535,535,535,535,535,535,535,535,535,535,535, +535,535,535, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +536,536,536,536, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 146 */ +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, + +/* block 147 */ +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, +537,537,537,537,537,537,537,537,537,537,537,537,537,537,537, 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, 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, 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, 91, 91, 91, + +/* block 148 */ +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, + +/* block 149 */ +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428,428,428,428,428,428,428,428, +428,428,428,428,428,428,428,428,428, 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, 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, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 150 */ +410,408, 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, 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, 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, + 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, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 151 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 152 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 91, 91, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13,339,339, 86, 86, 86, 13, 13, 13,339,339,339, +339,339,339, 16, 16, 16, 16, 16, 16, 16, 16, 86, 86, 86, 86, 86, + +/* block 153 */ + 86, 86, 86, 13, 13, 86, 86, 86, 86, 86, 86, 86, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 86, 86, 86, 86, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 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, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 154 */ +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,482,482,482,482,482,482,482,482,482,482,482,482,482,482, +482,482,538,538,538,482, 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, + 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, 91, 91, 91, 91, 91, 91, + +/* block 155 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 156 */ +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,370,370, +370,370,370,370,370, 91,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, + +/* block 157 */ +369,369,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,369, 91,369,369, + 91, 91,369, 91, 91,369,369, 91, 91,369,369,369,369, 91,369,369, +369,369,369,369,369,369,370,370,370,370, 91,370, 91,370,370,370, +370,370,370,370, 91,370,370,370,370,370,370,370,370,370,370,370, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, + +/* block 158 */ +370,370,370,370,369,369, 91,369,369,369,369, 91, 91,369,369,369, +369,369,369,369,369, 91,369,369,369,369,369,369,369, 91,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,369,369, 91,369,369,369,369, 91, +369,369,369,369,369, 91,369, 91, 91, 91,369,369,369,369,369,369, +369, 91,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, + +/* block 159 */ +369,369,369,369,369,369,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, + +/* block 160 */ +370,370,370,370,370,370,370,370,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, + +/* block 161 */ +369,369,369,369,369,369,369,369,369,369,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370, 91, 91,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369, 6,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370, 6,370,370,370,370, +370,370,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369, 6,370,370,370,370, + +/* block 162 */ +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370, 6,370,370,370,370,370,370,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369, 6,370,370,370,370,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, 6, +370,370,370,370,370,370,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, 6, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, + +/* block 163 */ +370,370,370,370,370,370,370,370,370, 6,370,370,370,370,370,370, +369,369,369,369,369,369,369,369,369,369,369,369,369,369,369,369, +369,369,369,369,369,369,369,369,369, 6,370,370,370,370,370,370, +370,370,370,370,370,370,370,370,370,370,370,370,370,370,370,370, +370,370,370, 6,370,370,370,370,370,370,369,370, 91, 91, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + +/* block 164 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 165 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, + 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, + 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 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, 91, 91, 91, 91, 91, 91, + +/* block 166 */ + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 167 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 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, 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, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 168 */ +539, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 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, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 169 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, + +/* block 170 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 91, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 171 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, + 13, 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + +/* block 172 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 91, 13, 13, 13, 13, 91, 91, 91, + +/* block 173 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 174 */ + 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, 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, 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, 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, 91, 91, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 13, 13, 13, 13, 13, + +/* block 175 */ + 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 91, 13, 13, 13, 91, 13, 91, 13, 91, 13, 91, 13, 13, 13, 91, + 13, 13, 13, 13, 13, 13, 91, 91, 13, 13, 13, 13, 91, 13, 91, 91, + 13, 13, 13, 13, 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 91, 91, 91, 91, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 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, 91, 91, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 176 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 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, + 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, 91, 91, 91, 91, 91, 91, + +/* block 177 */ + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 178 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415, 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, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 179 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, + +/* block 180 */ +415,415,415,415,415,415,415,415,415,415,415,415,415,415,415,415, +415,415,415,415,415,415,415,415,415,415,415,415,415,415, 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, 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, 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, 91, 91, 91, 91, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 181 */ + 91, 16, 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, 91, 91, 91, 91, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + +/* block 182 */ + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + +/* block 183 */ + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, + 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, + +/* block 184 */ +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476,476,476, +476,476,476,476,476,476,476,476,476,476,476,476,476,476, 91, 91, + +}; + +#if UCD_BLOCK_SIZE != 128 +#error Please correct UCD_BLOCK_SIZE in pcre_internal.h +#endif +#endif /* SUPPORT_UCP */ diff --git a/src/lib/pcre/pcre_valid_utf8.c b/src/lib/pcre/pcre_valid_utf8.c new file mode 100644 index 0000000..b2dc5c9 --- /dev/null +++ b/src/lib/pcre/pcre_valid_utf8.c @@ -0,0 +1,299 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function for validating UTF-8 character +strings. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Validate a UTF-8 string * +*************************************************/ + +/* This function is called (optionally) at the start of compile or match, to +check that a supposed UTF-8 string is actually valid. The early check means +that subsequent code can assume it is dealing with a valid string. The check +can be turned off for maximum performance, but the consequences of supplying an +invalid string are then undefined. + +Originally, this function checked according to RFC 2279, allowing for values in +the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were in +the canonical format. Once somebody had pointed out RFC 3629 to me (it +obsoletes 2279), additional restrictions were applied. The values are now +limited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the +subrange 0xd000 to 0xdfff is excluded. However, the format of 5-byte and 6-byte +characters is still checked. + +From release 8.13 more information about the details of the error are passed +back in the returned value: + +PCRE_UTF8_ERR0 No error +PCRE_UTF8_ERR1 Missing 1 byte at the end of the string +PCRE_UTF8_ERR2 Missing 2 bytes at the end of the string +PCRE_UTF8_ERR3 Missing 3 bytes at the end of the string +PCRE_UTF8_ERR4 Missing 4 bytes at the end of the string +PCRE_UTF8_ERR5 Missing 5 bytes at the end of the string +PCRE_UTF8_ERR6 2nd-byte's two top bits are not 0x80 +PCRE_UTF8_ERR7 3rd-byte's two top bits are not 0x80 +PCRE_UTF8_ERR8 4th-byte's two top bits are not 0x80 +PCRE_UTF8_ERR9 5th-byte's two top bits are not 0x80 +PCRE_UTF8_ERR10 6th-byte's two top bits are not 0x80 +PCRE_UTF8_ERR11 5-byte character is not permitted by RFC 3629 +PCRE_UTF8_ERR12 6-byte character is not permitted by RFC 3629 +PCRE_UTF8_ERR13 4-byte character with value > 0x10ffff is not permitted +PCRE_UTF8_ERR14 3-byte character with value 0xd000-0xdfff is not permitted +PCRE_UTF8_ERR15 Overlong 2-byte sequence +PCRE_UTF8_ERR16 Overlong 3-byte sequence +PCRE_UTF8_ERR17 Overlong 4-byte sequence +PCRE_UTF8_ERR18 Overlong 5-byte sequence (won't ever occur) +PCRE_UTF8_ERR19 Overlong 6-byte sequence (won't ever occur) +PCRE_UTF8_ERR20 Isolated 0x80 byte (not within UTF-8 character) +PCRE_UTF8_ERR21 Byte with the illegal value 0xfe or 0xff + +Arguments: + string points to the string + length length of string, or -1 if the string is zero-terminated + errp pointer to an error position offset variable + +Returns: = 0 if the string is a valid UTF-8 string + > 0 otherwise, setting the offset of the bad character +*/ + +int +PRIV(valid_utf)(PCRE_PUCHAR string, int length, int *erroroffset) +{ +#ifdef SUPPORT_UTF +register PCRE_PUCHAR p; + +if (length < 0) + { + for (p = string; *p != 0; p++); + length = (int)(p - string); + } + +for (p = string; length-- > 0; p++) + { + register int ab, c, d; + + c = *p; + if (c < 128) continue; /* ASCII character */ + + if (c < 0xc0) /* Isolated 10xx xxxx byte */ + { + *erroroffset = (int)(p - string); + return PCRE_UTF8_ERR20; + } + + if (c >= 0xfe) /* Invalid 0xfe or 0xff bytes */ + { + *erroroffset = (int)(p - string); + return PCRE_UTF8_ERR21; + } + + ab = PRIV(utf8_table4)[c & 0x3f]; /* Number of additional bytes */ + if (length < ab) + { + *erroroffset = (int)(p - string); /* Missing bytes */ + return ab - length; /* Codes ERR1 to ERR5 */ + } + length -= ab; /* Length remaining */ + + /* Check top bits in the second byte */ + + if (((d = *(++p)) & 0xc0) != 0x80) + { + *erroroffset = (int)(p - string) - 1; + return PCRE_UTF8_ERR6; + } + + /* For each length, check that the remaining bytes start with the 0x80 bit + set and not the 0x40 bit. Then check for an overlong sequence, and for the + excluded range 0xd800 to 0xdfff. */ + + switch (ab) + { + /* 2-byte character. No further bytes to check for 0x80. Check first byte + for for xx00 000x (overlong sequence). */ + + case 1: if ((c & 0x3e) == 0) + { + *erroroffset = (int)(p - string) - 1; + return PCRE_UTF8_ERR15; + } + break; + + /* 3-byte character. Check third byte for 0x80. Then check first 2 bytes + for 1110 0000, xx0x xxxx (overlong sequence) or + 1110 1101, 1010 xxxx (0xd800 - 0xdfff) */ + + case 2: + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if (c == 0xe0 && (d & 0x20) == 0) + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR16; + } + if (c == 0xed && d >= 0xa0) + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR14; + } + break; + + /* 4-byte character. Check 3rd and 4th bytes for 0x80. Then check first 2 + bytes for for 1111 0000, xx00 xxxx (overlong sequence), then check for a + character greater than 0x0010ffff (f4 8f bf bf) */ + + case 3: + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR8; + } + if (c == 0xf0 && (d & 0x30) == 0) + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR17; + } + if (c > 0xf4 || (c == 0xf4 && d > 0x8f)) + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR13; + } + break; + + /* 5-byte and 6-byte characters are not allowed by RFC 3629, and will be + rejected by the length test below. However, we do the appropriate tests + here so that overlong sequences get diagnosed, and also in case there is + ever an option for handling these larger code points. */ + + /* 5-byte character. Check 3rd, 4th, and 5th bytes for 0x80. Then check for + 1111 1000, xx00 0xxx */ + + case 4: + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR8; + } + if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */ + { + *erroroffset = (int)(p - string) - 4; + return PCRE_UTF8_ERR9; + } + if (c == 0xf8 && (d & 0x38) == 0) + { + *erroroffset = (int)(p - string) - 4; + return PCRE_UTF8_ERR18; + } + break; + + /* 6-byte character. Check 3rd-6th bytes for 0x80. Then check for + 1111 1100, xx00 00xx. */ + + case 5: + if ((*(++p) & 0xc0) != 0x80) /* Third byte */ + { + *erroroffset = (int)(p - string) - 2; + return PCRE_UTF8_ERR7; + } + if ((*(++p) & 0xc0) != 0x80) /* Fourth byte */ + { + *erroroffset = (int)(p - string) - 3; + return PCRE_UTF8_ERR8; + } + if ((*(++p) & 0xc0) != 0x80) /* Fifth byte */ + { + *erroroffset = (int)(p - string) - 4; + return PCRE_UTF8_ERR9; + } + if ((*(++p) & 0xc0) != 0x80) /* Sixth byte */ + { + *erroroffset = (int)(p - string) - 5; + return PCRE_UTF8_ERR10; + } + if (c == 0xfc && (d & 0x3c) == 0) + { + *erroroffset = (int)(p - string) - 5; + return PCRE_UTF8_ERR19; + } + break; + } + + /* Character is valid under RFC 2279, but 4-byte and 5-byte characters are + excluded by RFC 3629. The pointer p is currently at the last byte of the + character. */ + + if (ab > 3) + { + *erroroffset = (int)(p - string) - ab; + return (ab == 4)? PCRE_UTF8_ERR11 : PCRE_UTF8_ERR12; + } + } + +#else /* SUPPORT_UTF */ +(void)(string); /* Keep picky compilers happy */ +(void)(length); +#endif + +return PCRE_UTF8_ERR0; /* This indicates success */ +} + +/* End of pcre_valid_utf8.c */ diff --git a/src/lib/pcre/pcre_version.c b/src/lib/pcre/pcre_version.c new file mode 100644 index 0000000..915ae87 --- /dev/null +++ b/src/lib/pcre/pcre_version.c @@ -0,0 +1,95 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_version(), which returns a +string that identifies the PCRE version that is in use. */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Return version string * +*************************************************/ + +/* These macros are the standard way of turning unquoted text into C strings. +They allow macros like PCRE_MAJOR to be defined without quotes, which is +convenient for user programs that want to test its value. */ + +#define STRING(a) # a +#define XSTRING(s) STRING(s) + +/* A problem turned up with PCRE_PRERELEASE, which is defined empty for +production releases. Originally, it was used naively in this code: + + return XSTRING(PCRE_MAJOR) + "." XSTRING(PCRE_MINOR) + XSTRING(PCRE_PRERELEASE) + " " XSTRING(PCRE_DATE); + +However, when PCRE_PRERELEASE is empty, this leads to an attempted expansion of +STRING(). The C standard states: "If (before argument substitution) any +argument consists of no preprocessing tokens, the behavior is undefined." It +turns out the gcc treats this case as a single empty string - which is what we +really want - but Visual C grumbles about the lack of an argument for the +macro. Unfortunately, both are within their rights. To cope with both ways of +handling this, I had resort to some messy hackery that does a test at run time. +I could find no way of detecting that a macro is defined as an empty string at +pre-processor time. This hack uses a standard trick for avoiding calling +the STRING macro with an empty argument when doing the test. */ + +#ifdef COMPILE_PCRE8 +PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION +pcre_version(void) +#else +PCRE_EXP_DEFN const char * PCRE_CALL_CONVENTION +pcre16_version(void) +#endif +{ +return (XSTRING(Z PCRE_PRERELEASE)[1] == 0)? + XSTRING(PCRE_MAJOR.PCRE_MINOR PCRE_DATE) : + XSTRING(PCRE_MAJOR.PCRE_MINOR) XSTRING(PCRE_PRERELEASE PCRE_DATE); +} + +/* End of pcre_version.c */ diff --git a/src/lib/pcre/pcre_xclass.c b/src/lib/pcre/pcre_xclass.c new file mode 100644 index 0000000..45f1c5b --- /dev/null +++ b/src/lib/pcre/pcre_xclass.c @@ -0,0 +1,198 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2012 University of Cambridge + +----------------------------------------------------------------------------- +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 the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function that is used to match an extended +class. It is used by both pcre_exec() and pcre_def_exec(). */ + + +#ifdef PCRE_HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Match character against an XCLASS * +*************************************************/ + +/* This function is called to match a character against an extended class that +might contain values > 255 and/or Unicode properties. + +Arguments: + c the character + data points to the flag byte of the XCLASS data + +Returns: TRUE if character matches, else FALSE +*/ + +BOOL +PRIV(xclass)(int c, const pcre_uchar *data, BOOL utf) +{ +int t; +BOOL negated = (*data & XCL_NOT) != 0; + +(void)utf; +#ifdef COMPILE_PCRE8 +/* In 8 bit mode, this must always be TRUE. Help the compiler to know that. */ +utf = TRUE; +#endif + +/* Character values < 256 are matched against a bitmap, if one is present. If +not, we still carry on, because there may be ranges that start below 256 in the +additional data. */ + +if (c < 256) + { + if ((*data & XCL_MAP) != 0 && + (((pcre_uint8 *)(data + 1))[c/8] & (1 << (c&7))) != 0) + return !negated; /* char found */ + } + +/* First skip the bit map if present. Then match against the list of Unicode +properties or large chars or ranges that end with a large char. We won't ever +encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */ + +if ((*data++ & XCL_MAP) != 0) data += 32 / sizeof(pcre_uchar); + +while ((t = *data++) != XCL_END) + { + int x, y; + if (t == XCL_SINGLE) + { +#ifdef SUPPORT_UTF + if (utf) + { + GETCHARINC(x, data); /* macro generates multiple statements */ + } + else +#endif + x = *data++; + if (c == x) return !negated; + } + else if (t == XCL_RANGE) + { +#ifdef SUPPORT_UTF + if (utf) + { + GETCHARINC(x, data); /* macro generates multiple statements */ + GETCHARINC(y, data); /* macro generates multiple statements */ + } + else +#endif + { + x = *data++; + y = *data++; + } + if (c >= x && c <= y) return !negated; + } + +#ifdef SUPPORT_UCP + else /* XCL_PROP & XCL_NOTPROP */ + { + const ucd_record *prop = GET_UCD(c); + + switch(*data) + { + case PT_ANY: + if (t == XCL_PROP) return !negated; + break; + + case PT_LAMP: + if ((prop->chartype == ucp_Lu || prop->chartype == ucp_Ll || + prop->chartype == ucp_Lt) == (t == XCL_PROP)) return !negated; + break; + + case PT_GC: + if ((data[1] == PRIV(ucp_gentype)[prop->chartype]) == (t == XCL_PROP)) + return !negated; + break; + + case PT_PC: + if ((data[1] == prop->chartype) == (t == XCL_PROP)) return !negated; + break; + + case PT_SC: + if ((data[1] == prop->script) == (t == XCL_PROP)) return !negated; + break; + + case PT_ALNUM: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N) == (t == XCL_PROP)) + return !negated; + break; + + case PT_SPACE: /* Perl space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_FF || c == CHAR_CR) + == (t == XCL_PROP)) + return !negated; + break; + + case PT_PXSPACE: /* POSIX space */ + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_Z || + c == CHAR_HT || c == CHAR_NL || c == CHAR_VT || + c == CHAR_FF || c == CHAR_CR) == (t == XCL_PROP)) + return !negated; + break; + + case PT_WORD: + if ((PRIV(ucp_gentype)[prop->chartype] == ucp_L || + PRIV(ucp_gentype)[prop->chartype] == ucp_N || c == CHAR_UNDERSCORE) + == (t == XCL_PROP)) + return !negated; + break; + + /* This should never occur, but compilers may mutter if there is no + default. */ + + default: + return FALSE; + } + + data += 2; + } +#endif /* SUPPORT_UCP */ + } + +return negated; /* char did not match */ +} + +/* End of pcre_xclass.c */ diff --git a/src/lib/pcre/sljit/sljitConfig.h b/src/lib/pcre/sljit/sljitConfig.h new file mode 100644 index 0000000..c832dfe --- /dev/null +++ b/src/lib/pcre/sljit/sljitConfig.h @@ -0,0 +1,96 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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 _SLJIT_CONFIG_H_ +#define _SLJIT_CONFIG_H_ + +/* --------------------------------------------------------------------- */ +/* Architecture */ +/* --------------------------------------------------------------------- */ + +/* Architecture selection */ +/* #define SLJIT_CONFIG_X86_32 1 */ +/* #define SLJIT_CONFIG_X86_64 1 */ +/* #define SLJIT_CONFIG_ARM_V5 1 */ +/* #define SLJIT_CONFIG_ARM_V7 1 */ +/* #define SLJIT_CONFIG_ARM_THUMB2 1 */ +/* #define SLJIT_CONFIG_PPC_32 1 */ +/* #define SLJIT_CONFIG_PPC_64 1 */ +/* #define SLJIT_CONFIG_MIPS_32 1 */ + +/* #define SLJIT_CONFIG_AUTO 1 */ +/* #define SLJIT_CONFIG_UNSUPPORTED 1 */ + +/* --------------------------------------------------------------------- */ +/* Utilities */ +/* --------------------------------------------------------------------- */ + +/* Useful for thread-safe compiling of global functions. */ +#ifndef SLJIT_UTIL_GLOBAL_LOCK +/* Enabled by default */ +#define SLJIT_UTIL_GLOBAL_LOCK 1 +#endif + +/* Implements a stack like data structure (by using mmap / VirtualAlloc). */ +#ifndef SLJIT_UTIL_STACK +/* Enabled by default */ +#define SLJIT_UTIL_STACK 1 +#endif + +/* --------------------------------------------------------------------- */ +/* Configuration */ +/* --------------------------------------------------------------------- */ + +/* If SLJIT_STD_MACROS_DEFINED is not defined, the application should + define SLJIT_MALLOC, SLJIT_FREE, SLJIT_MEMMOVE, and NULL. */ +#ifndef SLJIT_STD_MACROS_DEFINED +/* Disabled by default. */ +#define SLJIT_STD_MACROS_DEFINED 0 +#endif + +/* Executable code allocation: + If SLJIT_EXECUTABLE_ALLOCATOR is not defined, the application should + define both SLJIT_MALLOC_EXEC and SLJIT_FREE_EXEC. */ +#ifndef SLJIT_EXECUTABLE_ALLOCATOR +/* Enabled by default. */ +#define SLJIT_EXECUTABLE_ALLOCATOR 1 +#endif + +/* Debug checks (assertions, etc.). */ +#ifndef SLJIT_DEBUG +/* Enabled by default */ +#define SLJIT_DEBUG 1 +#endif + +/* Verbose operations */ +#ifndef SLJIT_VERBOSE +/* Enabled by default */ +#define SLJIT_VERBOSE 1 +#endif + +/* See the beginning of sljitConfigInternal.h */ + +#endif diff --git a/src/lib/pcre/sljit/sljitConfigInternal.h b/src/lib/pcre/sljit/sljitConfigInternal.h new file mode 100644 index 0000000..de6e9f0 --- /dev/null +++ b/src/lib/pcre/sljit/sljitConfigInternal.h @@ -0,0 +1,424 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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 _SLJIT_CONFIG_INTERNAL_H_ +#define _SLJIT_CONFIG_INTERNAL_H_ + +/* + SLJIT defines the following macros depending on the target architecture: + + Feature detection (boolean) macros: + SLJIT_32BIT_ARCHITECTURE : 32 bit architecture + SLJIT_64BIT_ARCHITECTURE : 64 bit architecture + SLJIT_WORD_SHIFT : the shift required to apply when accessing a sljit_w/sljit_uw array by index + SLJIT_FLOAT_SHIFT : the shift required to apply when accessing a double array by index + SLJIT_LITTLE_ENDIAN : little endian architecture + SLJIT_BIG_ENDIAN : big endian architecture + SLJIT_UNALIGNED : allows unaligned memory accesses for non-fpu operations (only!) + SLJIT_INDIRECT_CALL : see SLJIT_FUNC_OFFSET() for more information + + Types and useful macros: + sljit_b, sljit_ub : signed and unsigned 8 bit byte + sljit_h, sljit_uh : signed and unsigned 16 bit half-word (short) type + sljit_i, sljit_ui : signed and unsigned 32 bit integer type + sljit_w, sljit_uw : signed and unsigned machine word, enough to store a pointer (same as intptr_t) + SLJIT_CALL : C calling convention define for both calling JIT form C and C callbacks for JIT + SLJIT_W(number) : defining 64 bit constants on 64 bit architectures (compiler independent helper) +*/ + +#if !((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \ + || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \ + || (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \ + || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \ + || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \ + || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \ + || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ + || (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ + || (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ + || (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED)) +#error "An architecture must be selected" +#endif + +/* Sanity check. */ +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \ + + (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \ + + (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) \ + + (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \ + + (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \ + + (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \ + + (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) \ + + (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) \ + + (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) \ + + (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) >= 2 +#error "Multiple architectures are selected" +#endif + +/* Auto select option (requires compiler support) */ +#if (defined SLJIT_CONFIG_AUTO && SLJIT_CONFIG_AUTO) + +#ifndef _WIN32 + +#if defined(__i386__) || defined(__i386) +#define SLJIT_CONFIG_X86_32 1 +#elif defined(__x86_64__) +#define SLJIT_CONFIG_X86_64 1 +#elif defined(__arm__) || defined(__ARM__) +#ifdef __thumb2__ +#define SLJIT_CONFIG_ARM_THUMB2 1 +#elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) +#define SLJIT_CONFIG_ARM_V7 1 +#else +#define SLJIT_CONFIG_ARM_V5 1 +#endif +#elif defined(__ppc64__) || defined(__powerpc64__) +#define SLJIT_CONFIG_PPC_64 1 +#elif defined(__ppc__) || defined(__powerpc__) +#define SLJIT_CONFIG_PPC_32 1 +#elif defined(__mips__) +#define SLJIT_CONFIG_MIPS_32 1 +#else +/* Unsupported architecture */ +#define SLJIT_CONFIG_UNSUPPORTED 1 +#endif + +#else /* !_WIN32 */ + +#if defined(_M_X64) || defined(__x86_64__) +#define SLJIT_CONFIG_X86_64 1 +#elif defined(_ARM_) +#define SLJIT_CONFIG_ARM_V5 1 +#else +#define SLJIT_CONFIG_X86_32 1 +#endif + +#endif /* !WIN32 */ +#endif /* SLJIT_CONFIG_AUTO */ + +#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) +#undef SLJIT_EXECUTABLE_ALLOCATOR +#endif + +#if !(defined SLJIT_STD_MACROS_DEFINED && SLJIT_STD_MACROS_DEFINED) + +/* These libraries are needed for the macros below. */ +#include +#include + +#endif /* STD_MACROS_DEFINED */ + +/* General macros: + Note: SLJIT is designed to be independent from them as possible. + + In release mode (SLJIT_DEBUG is not defined) only the following macros are needed: +*/ + +#ifndef SLJIT_MALLOC +#define SLJIT_MALLOC(size) malloc(size) +#endif + +#ifndef SLJIT_FREE +#define SLJIT_FREE(ptr) free(ptr) +#endif + +#ifndef SLJIT_MEMMOVE +#define SLJIT_MEMMOVE(dest, src, len) memmove(dest, src, len) +#endif + +#ifndef SLJIT_ZEROMEM +#define SLJIT_ZEROMEM(dest, len) memset(dest, 0, len) +#endif + +#if !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) + +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define SLJIT_LIKELY(x) __builtin_expect((x), 1) +#define SLJIT_UNLIKELY(x) __builtin_expect((x), 0) +#else +#define SLJIT_LIKELY(x) (x) +#define SLJIT_UNLIKELY(x) (x) +#endif + +#endif /* !defined(SLJIT_LIKELY) && !defined(SLJIT_UNLIKELY) */ + +#ifndef SLJIT_INLINE +/* Inline functions. */ +#define SLJIT_INLINE __inline +#endif + +#ifndef SLJIT_CONST +/* Const variables. */ +#define SLJIT_CONST const +#endif + +#ifndef SLJIT_UNUSED_ARG +/* Unused arguments. */ +#define SLJIT_UNUSED_ARG(arg) (void)arg +#endif + +#if (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) +/* Static ABI functions. For all-in-one programs. */ + +#if defined(__GNUC__) +/* Disable unused warnings in gcc. */ +#define SLJIT_API_FUNC_ATTRIBUTE static __attribute__((unused)) +#else +#define SLJIT_API_FUNC_ATTRIBUTE static +#endif + +#else +#define SLJIT_API_FUNC_ATTRIBUTE +#endif /* (defined SLJIT_CONFIG_STATIC && SLJIT_CONFIG_STATIC) */ + +#ifndef SLJIT_CACHE_FLUSH + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + +/* The __clear_cache() implementation of GCC is a dummy function on PowerPC. */ +#define SLJIT_CACHE_FLUSH(from, to) \ + ppc_cache_flush((from), (to)) + +#elif (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + +/* Not required to implement on archs with unified caches. */ +#define SLJIT_CACHE_FLUSH(from, to) + +#else + +/* Calls __ARM_NR_cacheflush on ARM-Linux. */ +#define SLJIT_CACHE_FLUSH(from, to) \ + __clear_cache((char*)(from), (char*)(to)) + +#endif + +#endif /* !SLJIT_CACHE_FLUSH */ + +/* 8 bit byte type. */ +typedef unsigned char sljit_ub; +typedef signed char sljit_b; + +/* 16 bit half-word type. */ +typedef unsigned short int sljit_uh; +typedef signed short int sljit_h; + +/* 32 bit integer type. */ +typedef unsigned int sljit_ui; +typedef signed int sljit_i; + +/* Machine word type. Can encapsulate a pointer. + 32 bit for 32 bit machines. + 64 bit for 64 bit machines. */ +#if (defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) +/* Just to have something. */ +#define SLJIT_WORD_SHIFT 0 +typedef unsigned long int sljit_uw; +typedef long int sljit_w; +#elif !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && !(defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define SLJIT_32BIT_ARCHITECTURE 1 +#define SLJIT_WORD_SHIFT 2 +typedef unsigned int sljit_uw; +typedef int sljit_w; +#else +#define SLJIT_64BIT_ARCHITECTURE 1 +#define SLJIT_WORD_SHIFT 3 +#ifdef _WIN32 +typedef unsigned __int64 sljit_uw; +typedef __int64 sljit_w; +#else +typedef unsigned long int sljit_uw; +typedef long int sljit_w; +#endif +#endif + +/* Double precision. */ +#define SLJIT_FLOAT_SHIFT 3 + +#ifndef SLJIT_W + +/* Defining long constants. */ +#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) +#define SLJIT_W(w) (w##ll) +#else +#define SLJIT_W(w) (w) +#endif + +#endif /* !SLJIT_W */ + +#ifndef SLJIT_CALL + +/* ABI (Application Binary Interface) types. */ +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + +#if defined(__GNUC__) + +#define SLJIT_CALL __attribute__ ((fastcall)) +#define SLJIT_X86_32_FASTCALL 1 + +#elif defined(_WIN32) + +#ifdef __BORLANDC__ +#define SLJIT_CALL __msfastcall +#else /* __BORLANDC__ */ +#define SLJIT_CALL __fastcall +#endif /* __BORLANDC__ */ +#define SLJIT_X86_32_FASTCALL 1 + +#else /* defined(_WIN32) */ +#define SLJIT_CALL __stdcall +#endif + +#else /* Other architectures. */ + +#define SLJIT_CALL + +#endif /* SLJIT_CONFIG_X86_32 */ + +#endif /* !SLJIT_CALL */ + +#if !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) + +/* These macros are useful for the application. */ +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define SLJIT_BIG_ENDIAN 1 + +#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + +#ifdef __MIPSEL__ +#define SLJIT_LITTLE_ENDIAN 1 +#else +#define SLJIT_BIG_ENDIAN 1 +#endif + +#else +#define SLJIT_LITTLE_ENDIAN 1 +#endif + +#endif /* !defined(SLJIT_BIG_ENDIAN) && !defined(SLJIT_LITTLE_ENDIAN) */ + +/* Sanity check. */ +#if (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) +#error "Exactly one endianness must be selected" +#endif + +#if !(defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) && !(defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) +#error "Exactly one endianness must be selected" +#endif + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +/* It seems ppc64 compilers use an indirect addressing for functions. + It makes things really complicated. */ +#define SLJIT_INDIRECT_CALL 1 +#endif + +#ifndef SLJIT_SSE2 + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +/* Turn on SSE2 support on x86 (operating on doubles). + (Better performance than legacy fpu instructions). */ +#define SLJIT_SSE2 1 + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) +/* Auto detect SSE2 support using CPUID. + On 64 bit x86 cpus, sse2 must be present. */ +#define SLJIT_SSE2_AUTO 1 +#endif + +#endif /* (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) */ + +#endif /* !SLJIT_SSE2 */ + +#ifndef SLJIT_UNALIGNED + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) \ + || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) \ + || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) \ + || (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) \ + || (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) \ + || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define SLJIT_UNALIGNED 1 +#endif + +#endif /* !SLJIT_UNALIGNED */ + +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) +SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size); +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr); +#define SLJIT_MALLOC_EXEC(size) sljit_malloc_exec(size) +#define SLJIT_FREE_EXEC(ptr) sljit_free_exec(ptr) +#endif + +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) || (defined SLJIT_VERBOSE && SLJIT_VERBOSE) +#include +#endif + +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + +/* Feel free to redefine these two macros. */ +#ifndef SLJIT_ASSERT + +#define SLJIT_HALT_PROCESS() \ + *((int*)0) = 0 + +#define SLJIT_ASSERT(x) \ + do { \ + if (SLJIT_UNLIKELY(!(x))) { \ + printf("Assertion failed at " __FILE__ ":%d\n", __LINE__); \ + SLJIT_HALT_PROCESS(); \ + } \ + } while (0) + +#endif /* !SLJIT_ASSERT */ + +#ifndef SLJIT_ASSERT_STOP + +#define SLJIT_ASSERT_STOP() \ + do { \ + printf("Should never been reached " __FILE__ ":%d\n", __LINE__); \ + SLJIT_HALT_PROCESS(); \ + } while (0) + +#endif /* !SLJIT_ASSERT_STOP */ + +#else /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */ + +#undef SLJIT_ASSERT +#undef SLJIT_ASSERT_STOP + +#define SLJIT_ASSERT(x) \ + do { } while (0) +#define SLJIT_ASSERT_STOP() \ + do { } while (0) + +#endif /* (defined SLJIT_DEBUG && SLJIT_DEBUG) */ + +#ifndef SLJIT_COMPILE_ASSERT + +/* Should be improved eventually. */ +#define SLJIT_COMPILE_ASSERT(x, description) \ + SLJIT_ASSERT(x) + +#endif /* !SLJIT_COMPILE_ASSERT */ + +#endif diff --git a/src/lib/pcre/sljit/sljitExecAllocator.c b/src/lib/pcre/sljit/sljitExecAllocator.c new file mode 100644 index 0000000..f66744d --- /dev/null +++ b/src/lib/pcre/sljit/sljitExecAllocator.c @@ -0,0 +1,277 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + This file contains a simple executable memory allocator + + It is assumed, that executable code blocks are usually medium (or sometimes + large) memory blocks, and the allocator is not too frequently called (less + optimized than other allocators). Thus, using it as a generic allocator is + not suggested. + + How does it work: + Memory is allocated in continuous memory areas called chunks by alloc_chunk() + Chunk format: + [ block ][ block ] ... [ block ][ block terminator ] + + All blocks and the block terminator is started with block_header. The block + header contains the size of the previous and the next block. These sizes + can also contain special values. + Block size: + 0 - The block is a free_block, with a different size member. + 1 - The block is a block terminator. + n - The block is used at the moment, and the value contains its size. + Previous block size: + 0 - This is the first block of the memory chunk. + n - The size of the previous block. + + Using these size values we can go forward or backward on the block chain. + The unused blocks are stored in a chain list pointed by free_blocks. This + list is useful if we need to find a suitable memory area when the allocator + is called. + + When a block is freed, the new free block is connected to its adjacent free + blocks if possible. + + [ free block ][ used block ][ free block ] + and "used block" is freed, the three blocks are connected together: + [ one big free block ] +*/ + +/* --------------------------------------------------------------------- */ +/* System (OS) functions */ +/* --------------------------------------------------------------------- */ + +/* 64 KByte. */ +#define CHUNK_SIZE 0x10000 + +/* + alloc_chunk / free_chunk : + * allocate executable system memory chunks + * the size is always divisible by CHUNK_SIZE + allocator_grab_lock / allocator_release_lock : + * make the allocator thread safe + * can be empty if the OS (or the application) does not support threading + * only the allocator requires this lock, sljit is fully thread safe + as it only uses local variables +*/ + +#ifdef _WIN32 + +static SLJIT_INLINE void* alloc_chunk(sljit_uw size) +{ + return VirtualAlloc(0, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +} + +static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size) +{ + SLJIT_UNUSED_ARG(size); + VirtualFree(chunk, 0, MEM_RELEASE); +} + +#else + +#include + +static SLJIT_INLINE void* alloc_chunk(sljit_uw size) +{ + void* retval = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); + return (retval != MAP_FAILED) ? retval : NULL; +} + +static SLJIT_INLINE void free_chunk(void* chunk, sljit_uw size) +{ + munmap(chunk, size); +} + +#endif + +/* --------------------------------------------------------------------- */ +/* Common functions */ +/* --------------------------------------------------------------------- */ + +#define CHUNK_MASK (~(CHUNK_SIZE - 1)) + +struct block_header { + sljit_uw size; + sljit_uw prev_size; +}; + +struct free_block { + struct block_header header; + struct free_block *next; + struct free_block *prev; + sljit_uw size; +}; + +#define AS_BLOCK_HEADER(base, offset) \ + ((struct block_header*)(((sljit_ub*)base) + offset)) +#define AS_FREE_BLOCK(base, offset) \ + ((struct free_block*)(((sljit_ub*)base) + offset)) +#define MEM_START(base) ((void*)(((sljit_ub*)base) + sizeof(struct block_header))) +#define ALIGN_SIZE(size) (((size) + sizeof(struct block_header) + 7) & ~7) + +static struct free_block* free_blocks; +static sljit_uw allocated_size; +static sljit_uw total_size; + +static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size) +{ + free_block->header.size = 0; + free_block->size = size; + + free_block->next = free_blocks; + free_block->prev = 0; + if (free_blocks) + free_blocks->prev = free_block; + free_blocks = free_block; +} + +static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block) +{ + if (free_block->next) + free_block->next->prev = free_block->prev; + + if (free_block->prev) + free_block->prev->next = free_block->next; + else { + SLJIT_ASSERT(free_blocks == free_block); + free_blocks = free_block->next; + } +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size) +{ + struct block_header *header; + struct block_header *next_header; + struct free_block *free_block; + sljit_uw chunk_size; + + allocator_grab_lock(); + if (size < sizeof(struct free_block)) + size = sizeof(struct free_block); + size = ALIGN_SIZE(size); + + free_block = free_blocks; + while (free_block) { + if (free_block->size >= size) { + chunk_size = free_block->size; + if (chunk_size > size + 64) { + /* We just cut a block from the end of the free block. */ + chunk_size -= size; + free_block->size = chunk_size; + header = AS_BLOCK_HEADER(free_block, chunk_size); + header->prev_size = chunk_size; + AS_BLOCK_HEADER(header, size)->prev_size = size; + } + else { + sljit_remove_free_block(free_block); + header = (struct block_header*)free_block; + size = chunk_size; + } + allocated_size += size; + header->size = size; + allocator_release_lock(); + return MEM_START(header); + } + free_block = free_block->next; + } + + chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK; + header = (struct block_header*)alloc_chunk(chunk_size); + PTR_FAIL_IF(!header); + + chunk_size -= sizeof(struct block_header); + total_size += chunk_size; + + header->prev_size = 0; + if (chunk_size > size + 64) { + /* Cut the allocated space into a free and a used block. */ + allocated_size += size; + header->size = size; + chunk_size -= size; + + free_block = AS_FREE_BLOCK(header, size); + free_block->header.prev_size = size; + sljit_insert_free_block(free_block, chunk_size); + next_header = AS_BLOCK_HEADER(free_block, chunk_size); + } + else { + /* All space belongs to this allocation. */ + allocated_size += chunk_size; + header->size = chunk_size; + next_header = AS_BLOCK_HEADER(header, chunk_size); + } + next_header->size = 1; + next_header->prev_size = chunk_size; + allocator_release_lock(); + return MEM_START(header); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr) +{ + struct block_header *header; + struct free_block* free_block; + + allocator_grab_lock(); + header = AS_BLOCK_HEADER(ptr, -(sljit_w)sizeof(struct block_header)); + allocated_size -= header->size; + + /* Connecting free blocks together if possible. */ + + /* If header->prev_size == 0, free_block will equal to header. + In this case, free_block->header.size will be > 0. */ + free_block = AS_FREE_BLOCK(header, -(sljit_w)header->prev_size); + if (SLJIT_UNLIKELY(!free_block->header.size)) { + free_block->size += header->size; + header = AS_BLOCK_HEADER(free_block, free_block->size); + header->prev_size = free_block->size; + } + else { + free_block = (struct free_block*)header; + sljit_insert_free_block(free_block, header->size); + } + + header = AS_BLOCK_HEADER(free_block, free_block->size); + if (SLJIT_UNLIKELY(!header->size)) { + free_block->size += ((struct free_block*)header)->size; + sljit_remove_free_block((struct free_block*)header); + header = AS_BLOCK_HEADER(free_block, free_block->size); + header->prev_size = free_block->size; + } + + /* The whole chunk is free. */ + if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) { + /* If this block is freed, we still have (allocated_size / 2) free space. */ + if (total_size - free_block->size > (allocated_size * 3 / 2)) { + total_size -= free_block->size; + sljit_remove_free_block(free_block); + free_chunk(free_block, free_block->size + sizeof(struct block_header)); + } + } + + allocator_release_lock(); +} diff --git a/src/lib/pcre/sljit/sljitLir.c b/src/lib/pcre/sljit/sljitLir.c new file mode 100644 index 0000000..c1fa5e4 --- /dev/null +++ b/src/lib/pcre/sljit/sljitLir.c @@ -0,0 +1,1594 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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 "sljitLir.h" + +#define CHECK_ERROR() \ + do { \ + if (SLJIT_UNLIKELY(compiler->error)) \ + return compiler->error; \ + } while (0) + +#define CHECK_ERROR_PTR() \ + do { \ + if (SLJIT_UNLIKELY(compiler->error)) \ + return NULL; \ + } while (0) + +#define CHECK_ERROR_VOID() \ + do { \ + if (SLJIT_UNLIKELY(compiler->error)) \ + return; \ + } while (0) + +#define FAIL_IF(expr) \ + do { \ + if (SLJIT_UNLIKELY(expr)) \ + return compiler->error; \ + } while (0) + +#define PTR_FAIL_IF(expr) \ + do { \ + if (SLJIT_UNLIKELY(expr)) \ + return NULL; \ + } while (0) + +#define FAIL_IF_NULL(ptr) \ + do { \ + if (SLJIT_UNLIKELY(!(ptr))) { \ + compiler->error = SLJIT_ERR_ALLOC_FAILED; \ + return SLJIT_ERR_ALLOC_FAILED; \ + } \ + } while (0) + +#define PTR_FAIL_IF_NULL(ptr) \ + do { \ + if (SLJIT_UNLIKELY(!(ptr))) { \ + compiler->error = SLJIT_ERR_ALLOC_FAILED; \ + return NULL; \ + } \ + } while (0) + +#define PTR_FAIL_WITH_EXEC_IF(ptr) \ + do { \ + if (SLJIT_UNLIKELY(!(ptr))) { \ + compiler->error = SLJIT_ERR_EX_ALLOC_FAILED; \ + return NULL; \ + } \ + } while (0) + +#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) + +#define GET_OPCODE(op) \ + ((op) & ~(SLJIT_INT_OP | SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS)) + +#define GET_FLAGS(op) \ + ((op) & (SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C)) + +#define GET_ALL_FLAGS(op) \ + ((op) & (SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS)) + +#define BUF_SIZE 4096 + +#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE) +#define ABUF_SIZE 2048 +#else +#define ABUF_SIZE 4096 +#endif + +/* Jump flags. */ +#define JUMP_LABEL 0x1 +#define JUMP_ADDR 0x2 +/* SLJIT_REWRITABLE_JUMP is 0x1000. */ + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + #define PATCH_MB 0x4 + #define PATCH_MW 0x8 +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + #define PATCH_MD 0x10 +#endif +#endif + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + #define IS_BL 0x4 + #define PATCH_B 0x8 +#endif + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + #define CPOOL_SIZE 512 +#endif + +#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) + #define IS_CONDITIONAL 0x04 + #define IS_BL 0x08 + /* cannot be encoded as branch */ + #define B_TYPE0 0x00 + /* conditional + imm8 */ + #define B_TYPE1 0x10 + /* conditional + imm20 */ + #define B_TYPE2 0x20 + /* IT + imm24 */ + #define B_TYPE3 0x30 + /* imm11 */ + #define B_TYPE4 0x40 + /* imm24 */ + #define B_TYPE5 0x50 + /* BL + imm24 */ + #define BL_TYPE6 0x60 + /* 0xf00 cc code for branches */ +#endif + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + #define UNCOND_B 0x04 + #define PATCH_B 0x08 + #define ABSOLUTE_B 0x10 +#endif + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + #define IS_MOVABLE 0x04 + #define IS_JAL 0x08 + #define IS_BIT26_COND 0x10 + #define IS_BIT16_COND 0x20 + + #define IS_COND (IS_BIT26_COND | IS_BIT16_COND) + + #define PATCH_B 0x40 + #define PATCH_J 0x80 + + /* instruction types */ + #define UNMOVABLE_INS 0 + /* 1 - 31 last destination register */ + #define FCSR_FCC 32 + /* no destination (i.e: store) */ + #define MOVABLE_INS 33 +#endif + +#endif /* !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) */ + +/* Utils can still be used even if SLJIT_CONFIG_UNSUPPORTED is set. */ +#include "sljitUtils.c" + +#if !(defined SLJIT_CONFIG_UNSUPPORTED && SLJIT_CONFIG_UNSUPPORTED) + +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) +#include "sljitExecAllocator.c" +#endif + +#if (defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) && !(defined SLJIT_SSE2 && SLJIT_SSE2) +#error SLJIT_SSE2_AUTO cannot be enabled without SLJIT_SSE2 +#endif + +/* --------------------------------------------------------------------- */ +/* Public functions */ +/* --------------------------------------------------------------------- */ + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || ((defined SLJIT_SSE2 && SLJIT_SSE2) && ((defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64))) +#define SLJIT_NEEDS_COMPILER_INIT 1 +static int compiler_initialized = 0; +/* A thread safe initialization. */ +static void init_compiler(void); +#endif + + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void) +{ + struct sljit_compiler *compiler = (struct sljit_compiler*)SLJIT_MALLOC(sizeof(struct sljit_compiler)); + if (!compiler) + return NULL; + SLJIT_ZEROMEM(compiler, sizeof(struct sljit_compiler)); + + SLJIT_COMPILE_ASSERT( + sizeof(sljit_b) == 1 && sizeof(sljit_ub) == 1 + && sizeof(sljit_h) == 2 && sizeof(sljit_uh) == 2 + && sizeof(sljit_i) == 4 && sizeof(sljit_ui) == 4 + && ((sizeof(sljit_w) == 4 && sizeof(sljit_uw) == 4) || (sizeof(sljit_w) == 8 && sizeof(sljit_uw) == 8)), + invalid_integer_types); + + /* Only the non-zero members must be set. */ + compiler->error = SLJIT_SUCCESS; + + compiler->buf = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE); + compiler->abuf = (struct sljit_memory_fragment*)SLJIT_MALLOC(ABUF_SIZE); + + if (!compiler->buf || !compiler->abuf) { + if (compiler->buf) + SLJIT_FREE(compiler->buf); + if (compiler->abuf) + SLJIT_FREE(compiler->abuf); + SLJIT_FREE(compiler); + return NULL; + } + + compiler->buf->next = NULL; + compiler->buf->used_size = 0; + compiler->abuf->next = NULL; + compiler->abuf->used_size = 0; + + compiler->temporaries = -1; + compiler->saveds = -1; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + compiler->args = -1; +#endif + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + compiler->cpool = (sljit_uw*)SLJIT_MALLOC(CPOOL_SIZE * sizeof(sljit_uw) + CPOOL_SIZE * sizeof(sljit_ub)); + if (!compiler->cpool) { + SLJIT_FREE(compiler->buf); + SLJIT_FREE(compiler->abuf); + SLJIT_FREE(compiler); + return NULL; + } + compiler->cpool_unique = (sljit_ub*)(compiler->cpool + CPOOL_SIZE); + compiler->cpool_diff = 0xffffffff; +#endif + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + compiler->delay_slot = UNMOVABLE_INS; +#endif + +#if (defined SLJIT_NEEDS_COMPILER_INIT && SLJIT_NEEDS_COMPILER_INIT) + if (!compiler_initialized) { + init_compiler(); + compiler_initialized = 1; + } +#endif + + return compiler; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + struct sljit_memory_fragment *curr; + + buf = compiler->buf; + while (buf) { + curr = buf; + buf = buf->next; + SLJIT_FREE(curr); + } + + buf = compiler->abuf; + while (buf) { + curr = buf; + buf = buf->next; + SLJIT_FREE(curr); + } + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + SLJIT_FREE(compiler->cpool); +#endif + SLJIT_FREE(compiler); +} + +#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code) +{ + /* Remove thumb mode flag. */ + SLJIT_FREE_EXEC((void*)((sljit_uw)code & ~0x1)); +} +#elif (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code) +{ + /* Resolve indirection. */ + code = (void*)(*(sljit_uw*)code); + SLJIT_FREE_EXEC(code); +} +#else +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code) +{ + SLJIT_FREE_EXEC(code); +} +#endif + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label) +{ + if (SLJIT_LIKELY(!!jump) && SLJIT_LIKELY(!!label)) { + jump->flags &= ~JUMP_ADDR; + jump->flags |= JUMP_LABEL; + jump->u.label = label; + } +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target) +{ + if (SLJIT_LIKELY(!!jump)) { + SLJIT_ASSERT(jump->flags & SLJIT_REWRITABLE_JUMP); + + jump->flags &= ~JUMP_LABEL; + jump->flags |= JUMP_ADDR; + jump->u.target = target; + } +} + +/* --------------------------------------------------------------------- */ +/* Private functions */ +/* --------------------------------------------------------------------- */ + +static void* ensure_buf(struct sljit_compiler *compiler, int size) +{ + sljit_ub *ret; + struct sljit_memory_fragment *new_frag; + + if (compiler->buf->used_size + size <= (int)(BUF_SIZE - sizeof(sljit_uw) - sizeof(void*))) { + ret = compiler->buf->memory + compiler->buf->used_size; + compiler->buf->used_size += size; + return ret; + } + new_frag = (struct sljit_memory_fragment*)SLJIT_MALLOC(BUF_SIZE); + PTR_FAIL_IF_NULL(new_frag); + new_frag->next = compiler->buf; + compiler->buf = new_frag; + new_frag->used_size = size; + return new_frag->memory; +} + +static void* ensure_abuf(struct sljit_compiler *compiler, int size) +{ + sljit_ub *ret; + struct sljit_memory_fragment *new_frag; + + if (compiler->abuf->used_size + size <= (int)(ABUF_SIZE - sizeof(sljit_uw) - sizeof(void*))) { + ret = compiler->abuf->memory + compiler->abuf->used_size; + compiler->abuf->used_size += size; + return ret; + } + new_frag = (struct sljit_memory_fragment*)SLJIT_MALLOC(ABUF_SIZE); + PTR_FAIL_IF_NULL(new_frag); + new_frag->next = compiler->abuf; + compiler->abuf = new_frag; + new_frag->used_size = size; + return new_frag->memory; +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, int size) +{ + CHECK_ERROR_PTR(); + +#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) + if (size <= 0 || size > 128) + return NULL; + size = (size + 7) & ~7; +#else + if (size <= 0 || size > 64) + return NULL; + size = (size + 3) & ~3; +#endif + return ensure_abuf(compiler, size); +} + +static SLJIT_INLINE void reverse_buf(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf = compiler->buf; + struct sljit_memory_fragment *prev = NULL; + struct sljit_memory_fragment *tmp; + + do { + tmp = buf->next; + buf->next = prev; + prev = buf; + buf = tmp; + } while (buf != NULL); + + compiler->buf = prev; +} + +static SLJIT_INLINE void set_label(struct sljit_label *label, struct sljit_compiler *compiler) +{ + label->next = NULL; + label->size = compiler->size; + if (compiler->last_label) + compiler->last_label->next = label; + else + compiler->labels = label; + compiler->last_label = label; +} + +static SLJIT_INLINE void set_jump(struct sljit_jump *jump, struct sljit_compiler *compiler, int flags) +{ + jump->next = NULL; + jump->flags = flags; + if (compiler->last_jump) + compiler->last_jump->next = jump; + else + compiler->jumps = jump; + compiler->last_jump = jump; +} + +static SLJIT_INLINE void set_const(struct sljit_const *const_, struct sljit_compiler *compiler) +{ + const_->next = NULL; + const_->addr = compiler->size; + if (compiler->last_const) + compiler->last_const->next = const_; + else + compiler->consts = const_; + compiler->last_const = const_; +} + +#define ADDRESSING_DEPENDS_ON(exp, reg) \ + (((exp) & SLJIT_MEM) && (((exp) & 0xf) == reg || (((exp) >> 4) & 0xf) == reg)) + +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) +#define FUNCTION_CHECK_OP() \ + SLJIT_ASSERT(!GET_FLAGS(op) || !(op & SLJIT_KEEP_FLAGS)); \ + switch (GET_OPCODE(op)) { \ + case SLJIT_NOT: \ + case SLJIT_CLZ: \ + case SLJIT_AND: \ + case SLJIT_OR: \ + case SLJIT_XOR: \ + case SLJIT_SHL: \ + case SLJIT_LSHR: \ + case SLJIT_ASHR: \ + SLJIT_ASSERT(!(op & (SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C))); \ + break; \ + case SLJIT_NEG: \ + SLJIT_ASSERT(!(op & (SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_C))); \ + break; \ + case SLJIT_MUL: \ + SLJIT_ASSERT(!(op & (SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_C))); \ + break; \ + case SLJIT_FCMP: \ + SLJIT_ASSERT(!(op & (SLJIT_INT_OP | SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \ + SLJIT_ASSERT((op & (SLJIT_SET_E | SLJIT_SET_S))); \ + break; \ + case SLJIT_ADD: \ + SLJIT_ASSERT(!(op & (SLJIT_SET_S | SLJIT_SET_U))); \ + break; \ + case SLJIT_SUB: \ + break; \ + case SLJIT_ADDC: \ + case SLJIT_SUBC: \ + SLJIT_ASSERT(!(op & (SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O))); \ + break; \ + default: \ + /* Nothing allowed */ \ + SLJIT_ASSERT(!(op & (SLJIT_INT_OP | SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O | SLJIT_SET_C | SLJIT_KEEP_FLAGS))); \ + break; \ + } + +#define FUNCTION_CHECK_IS_REG(r) \ + ((r) == SLJIT_UNUSED || (r) == SLJIT_LOCALS_REG || \ + ((r) >= SLJIT_TEMPORARY_REG1 && (r) <= SLJIT_TEMPORARY_REG3 && (r) <= SLJIT_TEMPORARY_REG1 - 1 + compiler->temporaries) || \ + ((r) >= SLJIT_SAVED_REG1 && (r) <= SLJIT_SAVED_REG3 && (r) <= SLJIT_SAVED_REG1 - 1 + compiler->saveds)) \ + +#define FUNCTION_CHECK_SRC(p, i) \ + SLJIT_ASSERT(compiler->temporaries != -1 && compiler->saveds != -1); \ + if (((p) >= SLJIT_TEMPORARY_REG1 && (p) <= SLJIT_TEMPORARY_REG1 - 1 + compiler->temporaries) || \ + ((p) >= SLJIT_SAVED_REG1 && (p) <= SLJIT_SAVED_REG1 - 1 + compiler->saveds) || \ + (p) == SLJIT_LOCALS_REG) \ + SLJIT_ASSERT(i == 0); \ + else if ((p) == SLJIT_IMM) \ + ; \ + else if ((p) & SLJIT_MEM) { \ + SLJIT_ASSERT(FUNCTION_CHECK_IS_REG((p) & 0xf)); \ + if ((p) & 0xf0) { \ + SLJIT_ASSERT(FUNCTION_CHECK_IS_REG(((p) >> 4) & 0xf)); \ + SLJIT_ASSERT(((p) & 0xf0) != (SLJIT_LOCALS_REG << 4) && !(i & ~0x3)); \ + } else \ + SLJIT_ASSERT((((p) >> 4) & 0xf) == 0); \ + SLJIT_ASSERT(((p) >> 9) == 0); \ + } \ + else \ + SLJIT_ASSERT_STOP(); + +#define FUNCTION_CHECK_DST(p, i) \ + SLJIT_ASSERT(compiler->temporaries != -1 && compiler->saveds != -1); \ + if (((p) >= SLJIT_TEMPORARY_REG1 && (p) <= SLJIT_TEMPORARY_REG1 - 1 + compiler->temporaries) || \ + ((p) >= SLJIT_SAVED_REG1 && (p) <= SLJIT_SAVED_REG1 - 1 + compiler->saveds) || \ + (p) == SLJIT_UNUSED) \ + SLJIT_ASSERT(i == 0); \ + else if ((p) & SLJIT_MEM) { \ + SLJIT_ASSERT(FUNCTION_CHECK_IS_REG((p) & 0xf)); \ + if ((p) & 0xf0) { \ + SLJIT_ASSERT(FUNCTION_CHECK_IS_REG(((p) >> 4) & 0xf)); \ + SLJIT_ASSERT(((p) & 0xf0) != (SLJIT_LOCALS_REG << 4) && !(i & ~0x3)); \ + } else \ + SLJIT_ASSERT((((p) >> 4) & 0xf) == 0); \ + SLJIT_ASSERT(((p) >> 9) == 0); \ + } \ + else \ + SLJIT_ASSERT_STOP(); + +#define FUNCTION_FCHECK(p, i) \ + if ((p) >= SLJIT_FLOAT_REG1 && (p) <= SLJIT_FLOAT_REG4) \ + SLJIT_ASSERT(i == 0); \ + else if ((p) & SLJIT_MEM) { \ + SLJIT_ASSERT(FUNCTION_CHECK_IS_REG((p) & 0xf)); \ + if ((p) & 0xf0) { \ + SLJIT_ASSERT(FUNCTION_CHECK_IS_REG(((p) >> 4) & 0xf)); \ + SLJIT_ASSERT(((p) & 0xf0) != (SLJIT_LOCALS_REG << 4) && !(i & ~0x3)); \ + } else \ + SLJIT_ASSERT((((p) >> 4) & 0xf) == 0); \ + SLJIT_ASSERT(((p) >> 9) == 0); \ + } \ + else \ + SLJIT_ASSERT_STOP(); + +#define FUNCTION_CHECK_OP1() \ + if (GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_MOVU_SI) { \ + SLJIT_ASSERT(!GET_ALL_FLAGS(op)); \ + } \ + if (GET_OPCODE(op) >= SLJIT_MOVU && GET_OPCODE(op) <= SLJIT_MOVU_SI) { \ + SLJIT_ASSERT(!(src & SLJIT_MEM) || (src & 0xf) != SLJIT_LOCALS_REG); \ + SLJIT_ASSERT(!(dst & SLJIT_MEM) || (dst & 0xf) != SLJIT_LOCALS_REG); \ + if ((src & SLJIT_MEM) && (src & 0xf)) \ + SLJIT_ASSERT((dst & 0xf) != (src & 0xf) && ((dst >> 4) & 0xf) != (src & 0xf)); \ + } + +#endif + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + +SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose) +{ + compiler->verbose = verbose; +} + +static char* reg_names[] = { + (char*)"", (char*)"t1", (char*)"t2", (char*)"t3", + (char*)"te1", (char*)"te2", (char*)"s1", (char*)"s2", + (char*)"s3", (char*)"se1", (char*)"se2", (char*)"lcr" +}; + +static char* freg_names[] = { + (char*)"", (char*)"float_r1", (char*)"float_r2", (char*)"float_r3", (char*)"float_r4" +}; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#ifdef _WIN64 + #define SLJIT_PRINT_D "I64" +#else + #define SLJIT_PRINT_D "l" +#endif +#else + #define SLJIT_PRINT_D "" +#endif + +#define sljit_verbose_param(p, i) \ + if ((p) & SLJIT_IMM) \ + fprintf(compiler->verbose, "#%"SLJIT_PRINT_D"d", (i)); \ + else if ((p) & SLJIT_MEM) { \ + if ((p) & 0xf) { \ + if (i) { \ + if (((p) >> 4) & 0xf) \ + fprintf(compiler->verbose, "[%s + %s * %d]", reg_names[(p) & 0xF], reg_names[((p) >> 4)& 0xF], 1 << (i)); \ + else \ + fprintf(compiler->verbose, "[%s + #%"SLJIT_PRINT_D"d]", reg_names[(p) & 0xF], (i)); \ + } \ + else { \ + if (((p) >> 4) & 0xf) \ + fprintf(compiler->verbose, "[%s + %s]", reg_names[(p) & 0xF], reg_names[((p) >> 4)& 0xF]); \ + else \ + fprintf(compiler->verbose, "[%s]", reg_names[(p) & 0xF]); \ + } \ + } \ + else \ + fprintf(compiler->verbose, "[#%"SLJIT_PRINT_D"d]", (i)); \ + } else \ + fprintf(compiler->verbose, "%s", reg_names[p]); +#define sljit_verbose_fparam(p, i) \ + if ((p) & SLJIT_MEM) { \ + if ((p) & 0xf) { \ + if (i) { \ + if (((p) >> 4) & 0xf) \ + fprintf(compiler->verbose, "[%s + %s * %d]", reg_names[(p) & 0xF], reg_names[((p) >> 4)& 0xF], 1 << (i)); \ + else \ + fprintf(compiler->verbose, "[%s + #%"SLJIT_PRINT_D"d]", reg_names[(p) & 0xF], (i)); \ + } \ + else { \ + if (((p) >> 4) & 0xF) \ + fprintf(compiler->verbose, "[%s + %s]", reg_names[(p) & 0xF], reg_names[((p) >> 4)& 0xF]); \ + else \ + fprintf(compiler->verbose, "[%s]", reg_names[(p) & 0xF]); \ + } \ + } \ + else \ + fprintf(compiler->verbose, "[#%"SLJIT_PRINT_D"d]", (i)); \ + } else \ + fprintf(compiler->verbose, "%s", freg_names[p]); + +static SLJIT_CONST char* op_names[] = { + /* op0 */ + (char*)"breakpoint", (char*)"nop", + (char*)"umul", (char*)"smul", (char*)"udiv", (char*)"sdiv", + /* op1 */ + (char*)"mov", (char*)"mov.ub", (char*)"mov.sb", (char*)"mov.uh", + (char*)"mov.sh", (char*)"mov.ui", (char*)"mov.si", (char*)"movu", + (char*)"movu.ub", (char*)"movu.sb", (char*)"movu.uh", (char*)"movu.sh", + (char*)"movu.ui", (char*)"movu.si", (char*)"not", (char*)"neg", + (char*)"clz", + /* op2 */ + (char*)"add", (char*)"addc", (char*)"sub", (char*)"subc", + (char*)"mul", (char*)"and", (char*)"or", (char*)"xor", + (char*)"shl", (char*)"lshr", (char*)"ashr", + /* fop1 */ + (char*)"fcmp", (char*)"fmov", (char*)"fneg", (char*)"fabs", + /* fop2 */ + (char*)"fadd", (char*)"fsub", (char*)"fmul", (char*)"fdiv" +}; + +static char* jump_names[] = { + (char*)"c_equal", (char*)"c_not_equal", + (char*)"c_less", (char*)"c_greater_equal", + (char*)"c_greater", (char*)"c_less_equal", + (char*)"c_sig_less", (char*)"c_sig_greater_equal", + (char*)"c_sig_greater", (char*)"c_sig_less_equal", + (char*)"c_overflow", (char*)"c_not_overflow", + (char*)"c_mul_overflow", (char*)"c_mul_not_overflow", + (char*)"c_float_equal", (char*)"c_float_not_equal", + (char*)"c_float_less", (char*)"c_float_greater_equal", + (char*)"c_float_greater", (char*)"c_float_less_equal", + (char*)"c_float_nan", (char*)"c_float_not_nan", + (char*)"jump", (char*)"fast_call", + (char*)"call0", (char*)"call1", (char*)"call2", (char*)"call3" +}; + +#endif + +/* --------------------------------------------------------------------- */ +/* Arch dependent */ +/* --------------------------------------------------------------------- */ + +static SLJIT_INLINE void check_sljit_generate_code(struct sljit_compiler *compiler) +{ +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + struct sljit_jump *jump; +#endif + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + + SLJIT_ASSERT(compiler->size > 0); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + jump = compiler->jumps; + while (jump) { + /* All jumps have target. */ + SLJIT_ASSERT(jump->flags & (JUMP_LABEL | JUMP_ADDR)); + jump = jump->next; + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(temporaries); + SLJIT_UNUSED_ARG(saveds); + SLJIT_UNUSED_ARG(local_size); + + SLJIT_ASSERT(args >= 0 && args <= 3); + SLJIT_ASSERT(temporaries >= 0 && temporaries <= SLJIT_NO_TMP_REGISTERS); + SLJIT_ASSERT(saveds >= 0 && saveds <= SLJIT_NO_GEN_REGISTERS); + SLJIT_ASSERT(args <= saveds); + SLJIT_ASSERT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) + fprintf(compiler->verbose, " enter args=%d temporaries=%d saveds=%d local_size=%d\n", args, temporaries, saveds, local_size); +#endif +} + +static SLJIT_INLINE void check_sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(temporaries); + SLJIT_UNUSED_ARG(saveds); + SLJIT_UNUSED_ARG(local_size); + + SLJIT_ASSERT(args >= 0 && args <= 3); + SLJIT_ASSERT(temporaries >= 0 && temporaries <= SLJIT_NO_TMP_REGISTERS); + SLJIT_ASSERT(saveds >= 0 && saveds <= SLJIT_NO_GEN_REGISTERS); + SLJIT_ASSERT(args <= saveds); + SLJIT_ASSERT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) + fprintf(compiler->verbose, " fake_enter args=%d temporaries=%d saveds=%d local_size=%d\n", args, temporaries, saveds, local_size); +#endif +} + +static SLJIT_INLINE void check_sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + if (op != SLJIT_UNUSED) { + SLJIT_ASSERT(op >= SLJIT_MOV && op <= SLJIT_MOV_SI); + FUNCTION_CHECK_SRC(src, srcw); + } + else + SLJIT_ASSERT(src == 0 && srcw == 0); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + if (op == SLJIT_UNUSED) + fprintf(compiler->verbose, " return\n"); + else { + fprintf(compiler->verbose, " return %s ", op_names[op]); + sljit_verbose_param(src, srcw); + fprintf(compiler->verbose, "\n"); + } + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(temporaries); + SLJIT_UNUSED_ARG(saveds); + SLJIT_UNUSED_ARG(local_size); + + SLJIT_ASSERT(args >= 0 && args <= 3); + SLJIT_ASSERT(temporaries >= 0 && temporaries <= SLJIT_NO_TMP_REGISTERS); + SLJIT_ASSERT(saveds >= 0 && saveds <= SLJIT_NO_GEN_REGISTERS); + SLJIT_ASSERT(args <= saveds); + SLJIT_ASSERT(local_size >= 0 && local_size <= SLJIT_MAX_LOCAL_SIZE); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->temporaries = temporaries; + compiler->saveds = saveds; + FUNCTION_CHECK_DST(dst, dstw); + compiler->temporaries = -1; + compiler->saveds = -1; +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " fast_enter "); + sljit_verbose_param(dst, dstw); + fprintf(compiler->verbose, " args=%d temporaries=%d saveds=%d local_size=%d\n", args, temporaries, saveds, local_size); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_SRC(src, srcw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " fast_return "); + sljit_verbose_param(src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + + SLJIT_ASSERT((op >= SLJIT_BREAKPOINT && op <= SLJIT_SMUL) + || ((op & ~SLJIT_INT_OP) >= SLJIT_UDIV && (op & ~SLJIT_INT_OP) <= SLJIT_SDIV)); +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) + fprintf(compiler->verbose, " %s%s\n", !(op & SLJIT_INT_OP) ? "" : "i", op_names[GET_OPCODE(op)]); +#endif +} + +static SLJIT_INLINE void check_sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + return; + } +#endif + + SLJIT_ASSERT(GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_CLZ); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_OP(); + FUNCTION_CHECK_SRC(src, srcw); + FUNCTION_CHECK_DST(dst, dstw); + FUNCTION_CHECK_OP1(); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s%s%s%s%s%s%s%s ", !(op & SLJIT_INT_OP) ? "" : "i", op_names[GET_OPCODE(op)], + !(op & SLJIT_SET_E) ? "" : "E", !(op & SLJIT_SET_S) ? "" : "S", !(op & SLJIT_SET_U) ? "" : "U", !(op & SLJIT_SET_O) ? "" : "O", !(op & SLJIT_SET_C) ? "" : "C", !(op & SLJIT_KEEP_FLAGS) ? "" : "K"); + sljit_verbose_param(dst, dstw); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + return; + } +#endif + + SLJIT_ASSERT(GET_OPCODE(op) >= SLJIT_ADD && GET_OPCODE(op) <= SLJIT_ASHR); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_OP(); + FUNCTION_CHECK_SRC(src1, src1w); + FUNCTION_CHECK_SRC(src2, src2w); + FUNCTION_CHECK_DST(dst, dstw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s%s%s%s%s%s%s%s ", !(op & SLJIT_INT_OP) ? "" : "i", op_names[GET_OPCODE(op)], + !(op & SLJIT_SET_E) ? "" : "E", !(op & SLJIT_SET_S) ? "" : "S", !(op & SLJIT_SET_U) ? "" : "U", !(op & SLJIT_SET_O) ? "" : "O", !(op & SLJIT_SET_C) ? "" : "C", !(op & SLJIT_KEEP_FLAGS) ? "" : "K"); + sljit_verbose_param(dst, dstw); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(src1, src1w); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(src2, src2w); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_get_register_index(int reg) +{ + SLJIT_UNUSED_ARG(reg); + SLJIT_ASSERT(reg > 0 && reg <= SLJIT_NO_REGISTERS); +} + +static SLJIT_INLINE void check_sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(instruction); + SLJIT_UNUSED_ARG(size); + SLJIT_ASSERT(instruction); +} + +static SLJIT_INLINE void check_sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + return; + } +#endif + + SLJIT_ASSERT(sljit_is_fpu_available()); + SLJIT_ASSERT(GET_OPCODE(op) >= SLJIT_FCMP && GET_OPCODE(op) <= SLJIT_FABS); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_OP(); + FUNCTION_FCHECK(src, srcw); + FUNCTION_FCHECK(dst, dstw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s%s%s ", op_names[GET_OPCODE(op)], + !(op & SLJIT_SET_E) ? "" : "E", !(op & SLJIT_SET_S) ? "" : "S"); + sljit_verbose_fparam(dst, dstw); + fprintf(compiler->verbose, ", "); + sljit_verbose_fparam(src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + + SLJIT_ASSERT(sljit_is_fpu_available()); + SLJIT_ASSERT(GET_OPCODE(op) >= SLJIT_FADD && GET_OPCODE(op) <= SLJIT_FDIV); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_OP(); + FUNCTION_FCHECK(src1, src1w); + FUNCTION_FCHECK(src2, src2w); + FUNCTION_FCHECK(dst, dstw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %s ", op_names[GET_OPCODE(op)]); + sljit_verbose_fparam(dst, dstw); + fprintf(compiler->verbose, ", "); + sljit_verbose_fparam(src1, src1w); + fprintf(compiler->verbose, ", "); + sljit_verbose_fparam(src2, src2w); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_label(struct sljit_compiler *compiler) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) + fprintf(compiler->verbose, "label:\n"); +#endif +} + +static SLJIT_INLINE void check_sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + if (SLJIT_UNLIKELY(compiler->skip_checks)) { + compiler->skip_checks = 0; + return; + } +#endif + + SLJIT_ASSERT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP))); + SLJIT_ASSERT((type & 0xff) >= SLJIT_C_EQUAL && (type & 0xff) <= SLJIT_CALL3); +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) + fprintf(compiler->verbose, " jump%s <%s>\n", !(type & SLJIT_REWRITABLE_JUMP) ? "" : "R", jump_names[type & 0xff]); +#endif +} + +static SLJIT_INLINE void check_sljit_emit_cmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + + SLJIT_ASSERT(!(type & ~(0xff | SLJIT_INT_OP | SLJIT_REWRITABLE_JUMP))); + SLJIT_ASSERT((type & 0xff) >= SLJIT_C_EQUAL && (type & 0xff) <= SLJIT_C_SIG_LESS_EQUAL); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_SRC(src1, src1w); + FUNCTION_CHECK_SRC(src2, src2w); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " %scmp%s <%s> ", !(type & SLJIT_INT_OP) ? "" : "i", !(type & SLJIT_REWRITABLE_JUMP) ? "" : "R", jump_names[type & 0xff]); + sljit_verbose_param(src1, src1w); + fprintf(compiler->verbose, ", "); + sljit_verbose_param(src2, src2w); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_fcmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + + SLJIT_ASSERT(sljit_is_fpu_available()); + SLJIT_ASSERT(!(type & ~(0xff | SLJIT_REWRITABLE_JUMP))); + SLJIT_ASSERT((type & 0xff) >= SLJIT_C_FLOAT_EQUAL && (type & 0xff) <= SLJIT_C_FLOAT_NOT_NAN); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_FCHECK(src1, src1w); + FUNCTION_FCHECK(src2, src2w); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " fcmp%s <%s> ", !(type & SLJIT_REWRITABLE_JUMP) ? "" : "R", jump_names[type & 0xff]); + sljit_verbose_fparam(src1, src1w); + fprintf(compiler->verbose, ", "); + sljit_verbose_fparam(src2, src2w); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + + SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_SRC(src, srcw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " ijump <%s> ", jump_names[type]); + sljit_verbose_param(src, srcw); + fprintf(compiler->verbose, "\n"); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(type); + + SLJIT_ASSERT(type >= SLJIT_C_EQUAL && type < SLJIT_JUMP); + SLJIT_ASSERT(op == SLJIT_MOV || GET_OPCODE(op) == SLJIT_OR); + SLJIT_ASSERT(GET_ALL_FLAGS(op) == 0 || GET_ALL_FLAGS(op) == SLJIT_SET_E || GET_ALL_FLAGS(op) == SLJIT_KEEP_FLAGS); +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_DST(dst, dstw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " cond_set%s%s <%s> ", !(op & SLJIT_SET_E) ? "" : "E", + !(op & SLJIT_KEEP_FLAGS) ? "" : "K", op_names[GET_OPCODE(op)]); + sljit_verbose_param(dst, dstw); + fprintf(compiler->verbose, ", <%s>\n", jump_names[type]); + } +#endif +} + +static SLJIT_INLINE void check_sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w init_value) +{ + /* If debug and verbose are disabled, all arguments are unused. */ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(init_value); + +#if (defined SLJIT_DEBUG && SLJIT_DEBUG) + FUNCTION_CHECK_DST(dst, dstw); +#endif +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + if (SLJIT_UNLIKELY(!!compiler->verbose)) { + fprintf(compiler->verbose, " const "); + sljit_verbose_param(dst, dstw); + fprintf(compiler->verbose, ", #%"SLJIT_PRINT_D"d\n", init_value); + } +#endif +} + +static SLJIT_INLINE int emit_mov_before_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + /* Return if don't need to do anything. */ + if (op == SLJIT_UNUSED) + return SLJIT_SUCCESS; + +#if (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) + if (src == SLJIT_RETURN_REG && op == SLJIT_MOV) + return SLJIT_SUCCESS; +#else + if (src == SLJIT_RETURN_REG && (op == SLJIT_MOV || op == SLJIT_MOV_UI || op == SLJIT_MOV_SI)) + return SLJIT_SUCCESS; +#endif + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_op1(compiler, op, SLJIT_RETURN_REG, 0, src, srcw); +} + +/* CPU description section */ + +#if (defined SLJIT_32BIT_ARCHITECTURE && SLJIT_32BIT_ARCHITECTURE) +#define SLJIT_CPUINFO_PART1 " 32bit (" +#elif (defined SLJIT_64BIT_ARCHITECTURE && SLJIT_64BIT_ARCHITECTURE) +#define SLJIT_CPUINFO_PART1 " 64bit (" +#else +#error "Internal error: CPU type info missing" +#endif + +#if (defined SLJIT_LITTLE_ENDIAN && SLJIT_LITTLE_ENDIAN) +#define SLJIT_CPUINFO_PART2 "little endian + " +#elif (defined SLJIT_BIG_ENDIAN && SLJIT_BIG_ENDIAN) +#define SLJIT_CPUINFO_PART2 "big endian + " +#else +#error "Internal error: CPU type info missing" +#endif + +#if (defined SLJIT_UNALIGNED && SLJIT_UNALIGNED) +#define SLJIT_CPUINFO_PART3 "unaligned)" +#else +#define SLJIT_CPUINFO_PART3 "aligned)" +#endif + +#define SLJIT_CPUINFO SLJIT_CPUINFO_PART1 SLJIT_CPUINFO_PART2 SLJIT_CPUINFO_PART3 + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + #include "sljitNativeX86_common.c" +#elif (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + #include "sljitNativeX86_common.c" +#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + #include "sljitNativeARM_v5.c" +#elif (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + #include "sljitNativeARM_v5.c" +#elif (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) + #include "sljitNativeARM_Thumb2.c" +#elif (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + #include "sljitNativePPC_common.c" +#elif (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + #include "sljitNativePPC_common.c" +#elif (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + #include "sljitNativeMIPS_common.c" +#endif + +#if !(defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* Default compare for most architectures. */ + int flags, tmp_src, condition; + sljit_w tmp_srcw; + + CHECK_ERROR_PTR(); + check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w); + + condition = type & 0xff; + if (SLJIT_UNLIKELY((src1 & SLJIT_IMM) && !(src2 & SLJIT_IMM))) { + /* Immediate is prefered as second argument by most architectures. */ + switch (condition) { + case SLJIT_C_LESS: + condition = SLJIT_C_GREATER; + break; + case SLJIT_C_GREATER_EQUAL: + condition = SLJIT_C_LESS_EQUAL; + break; + case SLJIT_C_GREATER: + condition = SLJIT_C_LESS; + break; + case SLJIT_C_LESS_EQUAL: + condition = SLJIT_C_GREATER_EQUAL; + break; + case SLJIT_C_SIG_LESS: + condition = SLJIT_C_SIG_GREATER; + break; + case SLJIT_C_SIG_GREATER_EQUAL: + condition = SLJIT_C_SIG_LESS_EQUAL; + break; + case SLJIT_C_SIG_GREATER: + condition = SLJIT_C_SIG_LESS; + break; + case SLJIT_C_SIG_LESS_EQUAL: + condition = SLJIT_C_SIG_GREATER_EQUAL; + break; + } + type = condition | (type & (SLJIT_INT_OP | SLJIT_REWRITABLE_JUMP)); + tmp_src = src1; + src1 = src2; + src2 = tmp_src; + tmp_srcw = src1w; + src1w = src2w; + src2w = tmp_srcw; + } + + if (condition <= SLJIT_C_NOT_ZERO) + flags = SLJIT_SET_E; + else if (condition <= SLJIT_C_LESS_EQUAL) + flags = SLJIT_SET_U; + else + flags = SLJIT_SET_S; + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + PTR_FAIL_IF(sljit_emit_op2(compiler, SLJIT_SUB | flags | (type & SLJIT_INT_OP), + SLJIT_UNUSED, 0, src1, src1w, src2, src2w)); +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_jump(compiler, condition | (type & SLJIT_REWRITABLE_JUMP)); +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int flags, condition; + + check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w); + + condition = type & 0xff; + if (condition <= SLJIT_C_FLOAT_NOT_EQUAL) + flags = SLJIT_SET_E; + else + flags = SLJIT_SET_S; + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + sljit_emit_fop1(compiler, SLJIT_FCMP | flags, src1, src1w, src2, src2w); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_jump(compiler, condition | (type & SLJIT_REWRITABLE_JUMP)); +} + +#endif + +#else /* SLJIT_CONFIG_UNSUPPORTED */ + +/* Empty function bodies for those machines, which are not (yet) supported. */ + +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name() +{ + return "unsupported"; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void) +{ + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, int size) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(size); + SLJIT_ASSERT_STOP(); + return NULL; +} + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) +SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(verbose); + SLJIT_ASSERT_STOP(); +} +#endif + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code) +{ + SLJIT_UNUSED_ARG(code); + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(temporaries); + SLJIT_UNUSED_ARG(saveds); + SLJIT_UNUSED_ARG(local_size); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(temporaries); + SLJIT_UNUSED_ARG(saveds); + SLJIT_UNUSED_ARG(local_size); + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(args); + SLJIT_UNUSED_ARG(temporaries); + SLJIT_UNUSED_ARG(saveds); + SLJIT_UNUSED_ARG(local_size); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_register_index(int reg) +{ + SLJIT_ASSERT_STOP(); + return reg; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(instruction); + SLJIT_UNUSED_ARG(size); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ + SLJIT_ASSERT_STOP(); + return 0; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(src1); + SLJIT_UNUSED_ARG(src1w); + SLJIT_UNUSED_ARG(src2); + SLJIT_UNUSED_ARG(src2w); + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_label(struct sljit_jump *jump, struct sljit_label* label) +{ + SLJIT_UNUSED_ARG(jump); + SLJIT_UNUSED_ARG(label); + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_target(struct sljit_jump *jump, sljit_uw target) +{ + SLJIT_UNUSED_ARG(jump); + SLJIT_UNUSED_ARG(target); + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(type); + SLJIT_UNUSED_ARG(src); + SLJIT_UNUSED_ARG(srcw); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(op); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(type); + SLJIT_ASSERT_STOP(); + return SLJIT_ERR_UNSUPPORTED; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w initval) +{ + SLJIT_UNUSED_ARG(compiler); + SLJIT_UNUSED_ARG(dst); + SLJIT_UNUSED_ARG(dstw); + SLJIT_UNUSED_ARG(initval); + SLJIT_ASSERT_STOP(); + return NULL; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ + SLJIT_UNUSED_ARG(addr); + SLJIT_UNUSED_ARG(new_addr); + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + SLJIT_UNUSED_ARG(addr); + SLJIT_UNUSED_ARG(new_constant); + SLJIT_ASSERT_STOP(); +} + +#endif diff --git a/src/lib/pcre/sljit/sljitLir.h b/src/lib/pcre/sljit/sljitLir.h new file mode 100644 index 0000000..0cb1c1e --- /dev/null +++ b/src/lib/pcre/sljit/sljitLir.h @@ -0,0 +1,853 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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 _SLJIT_LIR_H_ +#define _SLJIT_LIR_H_ + +/* + ------------------------------------------------------------------------ + Stack-Less JIT compiler for multiple architectures (x86, ARM, PowerPC) + ------------------------------------------------------------------------ + + Short description + Advantages: + - The execution can be continued from any LIR instruction + In other words, jump into and out of the code is safe + - Both target of (conditional) jump and call instructions + and constants can be dynamically modified during runtime + - although it is not suggested to do it frequently + - very effective to cache an important value once + - A fixed stack space can be allocated for local variables + - The compiler is thread-safe + Disadvantages: + - Limited number of registers (only 6+4 integer registers, max 3+2 + temporary, max 3+2 saved and 4 floating point registers) + In practice: + - This approach is very effective for interpreters + - One of the saved registers typically points to a stack interface + - It can jump to any exception handler anytime (even for another + function. It is safe for SLJIT.) + - Fast paths can be modified during runtime reflecting the changes + of the fastest execution path of the dynamic language + - SLJIT supports complex memory addressing modes + - mainly position independent code + - Optimizations (perhaps later) + - Only for basic blocks (when no labels inserted between LIR instructions) + + For valgrind users: + - pass --smc-check=all argument to valgrind, since JIT is a "self-modifying code" +*/ + +#if !(defined SLJIT_NO_DEFAULT_CONFIG && SLJIT_NO_DEFAULT_CONFIG) +#include "sljitConfig.h" +#endif + +/* The following header file defines useful macros for fine tuning +sljit based code generators. They are listed in the begining +of sljitConfigInternal.h */ + +#include "sljitConfigInternal.h" + +/* --------------------------------------------------------------------- */ +/* Error codes */ +/* --------------------------------------------------------------------- */ + +/* Indicates no error. */ +#define SLJIT_SUCCESS 0 +/* After the call of sljit_generate_code(), the error code of the compiler + is set to this value to avoid future sljit calls (in debug mode at least). + The complier should be freed after sljit_generate_code(). */ +#define SLJIT_ERR_COMPILED 1 +/* Cannot allocate non executable memory. */ +#define SLJIT_ERR_ALLOC_FAILED 2 +/* Cannot allocate executable memory. + Only for sljit_generate_code() */ +#define SLJIT_ERR_EX_ALLOC_FAILED 3 +/* return value for SLJIT_CONFIG_UNSUPPORTED empty architecture. */ +#define SLJIT_ERR_UNSUPPORTED 4 + +/* --------------------------------------------------------------------- */ +/* Registers */ +/* --------------------------------------------------------------------- */ + +#define SLJIT_UNUSED 0 + +/* Temporary (scratch) registers may not preserve their values across function calls. */ +#define SLJIT_TEMPORARY_REG1 1 +#define SLJIT_TEMPORARY_REG2 2 +#define SLJIT_TEMPORARY_REG3 3 +/* Note: Extra Registers cannot be used for memory addressing. */ +/* Note: on x86-32, these registers are emulated (using stack loads & stores). */ +#define SLJIT_TEMPORARY_EREG1 4 +#define SLJIT_TEMPORARY_EREG2 5 + +/* Saved registers whose preserve their values across function calls. */ +#define SLJIT_SAVED_REG1 6 +#define SLJIT_SAVED_REG2 7 +#define SLJIT_SAVED_REG3 8 +/* Note: Extra Registers cannot be used for memory addressing. */ +/* Note: on x86-32, these registers are emulated (using stack loads & stores). */ +#define SLJIT_SAVED_EREG1 9 +#define SLJIT_SAVED_EREG2 10 + +/* Read-only register (cannot be the destination of an operation). */ +/* Note: SLJIT_MEM2( ... , SLJIT_LOCALS_REG) is not supported (x86 limitation). */ +/* Note: SLJIT_LOCALS_REG is not necessary the real stack pointer. See sljit_emit_enter. */ +#define SLJIT_LOCALS_REG 11 + +/* Number of registers. */ +#define SLJIT_NO_TMP_REGISTERS 5 +#define SLJIT_NO_GEN_REGISTERS 5 +#define SLJIT_NO_REGISTERS 11 + +/* Return with machine word. */ + +#define SLJIT_RETURN_REG SLJIT_TEMPORARY_REG1 + +/* x86 prefers specific registers for special purposes. In case of shift + by register it supports only SLJIT_TEMPORARY_REG3 for shift argument + (which is the src2 argument of sljit_emit_op2). If another register is + used, sljit must exchange data between registers which cause a minor + slowdown. Other architectures has no such limitation. */ + +#define SLJIT_PREF_SHIFT_REG SLJIT_TEMPORARY_REG3 + +/* --------------------------------------------------------------------- */ +/* Floating point registers */ +/* --------------------------------------------------------------------- */ + +/* Note: SLJIT_UNUSED as destination is not valid for floating point + operations, since they cannot be used for setting flags. */ + +/* Floating point operations are performed on double precision values. */ + +#define SLJIT_FLOAT_REG1 1 +#define SLJIT_FLOAT_REG2 2 +#define SLJIT_FLOAT_REG3 3 +#define SLJIT_FLOAT_REG4 4 + +/* --------------------------------------------------------------------- */ +/* Main structures and functions */ +/* --------------------------------------------------------------------- */ + +struct sljit_memory_fragment { + struct sljit_memory_fragment *next; + sljit_uw used_size; + sljit_ub memory[1]; +}; + +struct sljit_label { + struct sljit_label *next; + sljit_uw addr; + /* The maximum size difference. */ + sljit_uw size; +}; + +struct sljit_jump { + struct sljit_jump *next; + sljit_uw addr; + sljit_w flags; + union { + sljit_uw target; + struct sljit_label* label; + } u; +}; + +struct sljit_const { + struct sljit_const *next; + sljit_uw addr; +}; + +struct sljit_compiler { + int error; + + struct sljit_label *labels; + struct sljit_jump *jumps; + struct sljit_const *consts; + struct sljit_label *last_label; + struct sljit_jump *last_jump; + struct sljit_const *last_const; + + struct sljit_memory_fragment *buf; + struct sljit_memory_fragment *abuf; + + /* Used local registers. */ + int temporaries; + /* Used saved registers. */ + int saveds; + /* Local stack size. */ + int local_size; + /* Code size. */ + sljit_uw size; + /* For statistical purposes. */ + sljit_uw executable_size; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + int args; + int temporaries_start; + int saveds_start; +#endif + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + int mode32; +#ifdef _WIN64 + int has_locals; +#endif +#endif + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + int flags_saved; +#endif + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + /* Constant pool handling. */ + sljit_uw *cpool; + sljit_ub *cpool_unique; + sljit_uw cpool_diff; + sljit_uw cpool_fill; + /* Other members. */ + /* Contains pointer, "ldr pc, [...]" pairs. */ + sljit_uw patches; +#endif + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) || (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + /* Temporary fields. */ + sljit_uw shift_imm; + int cache_arg; + sljit_w cache_argw; +#endif + +#if (defined SLJIT_CONFIG_ARM_THUMB2 && SLJIT_CONFIG_ARM_THUMB2) + int cache_arg; + sljit_w cache_argw; +#endif + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) || (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + int has_locals; + sljit_w imm; + int cache_arg; + sljit_w cache_argw; +#endif + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + int has_locals; + int delay_slot; + int cache_arg; + sljit_w cache_argw; +#endif + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) + FILE* verbose; +#endif + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + int skip_checks; +#endif +}; + +/* --------------------------------------------------------------------- */ +/* Main functions */ +/* --------------------------------------------------------------------- */ + +/* Creates an sljit compiler. + Returns NULL if failed. */ +SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void); +/* Free everything except the codes. */ +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler); + +static SLJIT_INLINE int sljit_get_compiler_error(struct sljit_compiler *compiler) { return compiler->error; } + +/* + Allocate a small amount of memory. The size must be <= 64 bytes on 32 bit, + and <= 128 bytes on 64 bit architectures. The memory area is owned by the compiler, + and freed by sljit_free_compiler. The returned pointer is sizeof(sljit_w) aligned. + Excellent for allocating small blocks during the compiling, and no need to worry + about freeing them. The size is enough to contain at most 16 pointers. + If the size is outside of the range, the function will return with NULL, + but this return value does not indicate that there is no more memory (does + not set the compiler to out-of-memory status). +*/ +SLJIT_API_FUNC_ATTRIBUTE void* sljit_alloc_memory(struct sljit_compiler *compiler, int size); + +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) +/* Passing NULL disables verbose. */ +SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose); +#endif + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler); +SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code); + +/* + After the code generation we can retrieve the allocated executable memory size, + although this area may not be fully filled with instructions depending on some + optimizations. This function is useful only for statistical purposes. + + Before a successful code generation, this function returns with 0. +*/ +static SLJIT_INLINE sljit_uw sljit_get_generated_code_size(struct sljit_compiler *compiler) { return compiler->executable_size; } + +/* Instruction generation. Returns with error code. */ + +/* + The executable code is basically a function call from the viewpoint of + the C language. The function calls must obey to the ABI (Application + Binary Interface) of the platform, which specify the purpose of machine + registers and stack handling among other things. The sljit_emit_enter + function emits the necessary instructions for setting up a new context + for the executable code and moves function arguments to the saved + registers. The number of arguments are specified in the "args" + parameter and the first argument goes to SLJIT_SAVED_REG1, the second + goes to SLJIT_SAVED_REG2 and so on. The number of temporary and + saved registers are passed in "temporaries" and "saveds" arguments + respectively. Since the saved registers contains the arguments, + "args" must be less or equal than "saveds". The sljit_emit_enter + is also capable of allocating a stack space for local variables. The + "local_size" argument contains the size in bytes of this local area + and its staring address is stored in SLJIT_LOCALS_REG. However + the SLJIT_LOCALS_REG is not necessary the machine stack pointer. + The memory bytes between SLJIT_LOCALS_REG (inclusive) and + SLJIT_LOCALS_REG + local_size (exclusive) can be modified freely + until the function returns. The stack space is uninitialized. + + Note: every call of sljit_emit_enter and sljit_set_context overwrites + the previous context. */ + +#define SLJIT_MAX_LOCAL_SIZE 65536 + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, + int args, int temporaries, int saveds, int local_size); + +/* The machine code has a context (which contains the local stack space size, + number of used registers, etc.) which initialized by sljit_emit_enter. Several + functions (like sljit_emit_return) requres this context to be able to generate + the appropriate code. However, some code fragments (like inline cache) may have + no normal entry point so their context is unknown for the compiler. Using the + function below we can specify thir context. + + Note: every call of sljit_emit_enter and sljit_set_context overwrites + the previous context. */ + +/* Note: multiple calls of this function overwrites the previous call. */ + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, + int args, int temporaries, int saveds, int local_size); + +/* Return from machine code. The op argument can be SLJIT_UNUSED which means the + function does not return with anything or any opcode between SLJIT_MOV and + SLJIT_MOV_SI (see sljit_emit_op1). As for src and srcw they must be 0 if op + is SLJIT_UNUSED, otherwise see below the description about source and + destination arguments. */ +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, + int src, sljit_w srcw); + +/* Really fast calling method for utility functions inside sljit (see SLJIT_FAST_CALL). + All registers and even the stack frame is passed to the callee. The return address is + preserved in dst/dstw by sljit_emit_fast_enter, and sljit_emit_fast_return can + use this as a return value later. */ + +/* Note: only for sljit specific, non ABI compilant calls. Fast, since only a few machine instructions + are needed. Excellent for small uility functions, where saving registers and setting up + a new stack frame would cost too much performance. However, it is still possible to return + to the address of the caller (or anywhere else). */ + +/* Note: flags are not changed (unlike sljit_emit_enter / sljit_emit_return). */ + +/* Note: although sljit_emit_fast_return could be replaced by an ijump, it is not suggested, + since many architectures do clever branch prediction on call / return instruction pairs. */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size); +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw); + +/* + Source and destination values for arithmetical instructions + imm - a simple immediate value (cannot be used as a destination) + reg - any of the registers (immediate argument must be 0) + [imm] - absolute immediate memory address + [reg+imm] - indirect memory address + [reg+(reg<addr; } +static SLJIT_INLINE sljit_uw sljit_get_jump_addr(struct sljit_jump *jump) { return jump->addr; } +static SLJIT_INLINE sljit_uw sljit_get_const_addr(struct sljit_const *const_) { return const_->addr; } + +/* Only the address is required to rewrite the code. */ +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr); +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant); + +/* --------------------------------------------------------------------- */ +/* Miscellaneous utility functions */ +/* --------------------------------------------------------------------- */ + +#define SLJIT_MAJOR_VERSION 0 +#define SLJIT_MINOR_VERSION 87 + +/* Get the human readable name of the platfrom. + Can be useful for debugging on platforms like ARM, where ARM and + Thumb2 functions can be mixed. */ +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name(void); + +/* Portble helper function to get an offset of a member. */ +#define SLJIT_OFFSETOF(base, member) ((sljit_w)(&((base*)0x10)->member) - 0x10) + +#if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK) +/* This global lock is useful to compile common functions. */ +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void); +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void); +#endif + +#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) + +/* The sljit_stack is a utiliy feature of sljit, which allocates a + writable memory region between base (inclusive) and limit (exclusive). + Both base and limit is a pointer, and base is always <= than limit. + This feature uses the "address space reserve" feature + of modern operating systems. Basically we don't need to allocate a + huge memory block in one step for the worst case, we can start with + a smaller chunk and extend it later. Since the address space is + reserved, the data never copied to other regions, thus it is safe + to store pointers here. */ + +/* Note: The base field is aligned to PAGE_SIZE bytes (usually 4k or more). + Note: stack growing should not happen in small steps: 4k, 16k or even + bigger growth is better. + Note: this structure may not be supported by all operating systems. + Some kind of fallback mechanism is suggested when SLJIT_UTIL_STACK + is not defined. */ + +struct sljit_stack { + /* User data, anything can be stored here. + Starting with the same value as base. */ + sljit_uw top; + /* These members are read only. */ + sljit_uw base; + sljit_uw limit; + sljit_uw max_limit; +}; + +/* Returns NULL if unsuccessful. + Note: limit and max_limit contains the size for stack allocation + Note: the top field is initialized to base. */ +SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(sljit_uw limit, sljit_uw max_limit); +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_free_stack(struct sljit_stack* stack); + +/* Can be used to increase (allocate) or decrease (free) the memory area. + Returns with a non-zero value if unsuccessful. If new_limit is greater than + max_limit, it will fail. It is very easy to implement a stack data structure, + since the growth ratio can be added to the current limit, and sljit_stack_resize + will do all the necessary checks. The fields of the stack are not changed if + sljit_stack_resize fails. */ +SLJIT_API_FUNC_ATTRIBUTE sljit_w SLJIT_CALL sljit_stack_resize(struct sljit_stack* stack, sljit_uw new_limit); + +#endif /* (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) */ + +#if !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) + +/* Get the entry address of a given function. */ +#define SLJIT_FUNC_OFFSET(func_name) ((sljit_w)func_name) + +#else /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */ + +/* All JIT related code should be placed in the same context (library, binary, etc.). */ + +#define SLJIT_FUNC_OFFSET(func_name) ((sljit_w)*(void**)func_name) + +/* For powerpc64, the function pointers point to a context descriptor. */ +struct sljit_function_context { + sljit_w addr; + sljit_w r2; + sljit_w r11; +}; + +/* Fill the context arguments using the addr and the function. + If func_ptr is NULL, it will not be set to the address of context + If addr is NULL, the function address also comes from the func pointer. */ +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_w addr, void* func); + +#endif /* !(defined SLJIT_INDIRECT_CALL && SLJIT_INDIRECT_CALL) */ + +#endif /* _SLJIT_LIR_H_ */ diff --git a/src/lib/pcre/sljit/sljitNativeARM_Thumb2.c b/src/lib/pcre/sljit/sljitNativeARM_Thumb2.c new file mode 100644 index 0000000..a51536b --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeARM_Thumb2.c @@ -0,0 +1,1913 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name() +{ + return "ARM-Thumb2" SLJIT_CPUINFO; +} + +/* Last register + 1. */ +#define TMP_REG1 (SLJIT_NO_REGISTERS + 1) +#define TMP_REG2 (SLJIT_NO_REGISTERS + 2) +#define TMP_REG3 (SLJIT_NO_REGISTERS + 3) +#define TMP_PC (SLJIT_NO_REGISTERS + 4) + +#define TMP_FREG1 (SLJIT_FLOAT_REG4 + 1) +#define TMP_FREG2 (SLJIT_FLOAT_REG4 + 2) + +/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */ +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 5] = { + 0, 0, 1, 2, 12, 5, 6, 7, 8, 10, 11, 13, 3, 4, 14, 15 +}; + +#define COPY_BITS(src, from, to, bits) \ + ((from >= to ? (src >> (from - to)) : (src << (to - from))) & (((1 << bits) - 1) << to)) + +/* Thumb16 encodings. */ +#define RD3(rd) (reg_map[rd]) +#define RN3(rn) (reg_map[rn] << 3) +#define RM3(rm) (reg_map[rm] << 6) +#define RDN3(rdn) (reg_map[rdn] << 8) +#define IMM3(imm) (imm << 6) +#define IMM8(imm) (imm) + +/* Thumb16 helpers. */ +#define SET_REGS44(rd, rn) \ + ((reg_map[rn] << 3) | (reg_map[rd] & 0x7) | ((reg_map[rd] & 0x8) << 4)) +#define IS_2_LO_REGS(reg1, reg2) \ + (reg_map[reg1] <= 7 && reg_map[reg2] <= 7) +#define IS_3_LO_REGS(reg1, reg2, reg3) \ + (reg_map[reg1] <= 7 && reg_map[reg2] <= 7 && reg_map[reg3] <= 7) + +/* Thumb32 encodings. */ +#define RD4(rd) (reg_map[rd] << 8) +#define RN4(rn) (reg_map[rn] << 16) +#define RM4(rm) (reg_map[rm]) +#define RT4(rt) (reg_map[rt] << 12) +#define DD4(dd) ((dd) << 12) +#define DN4(dn) ((dn) << 16) +#define DM4(dm) (dm) +#define IMM5(imm) \ + (COPY_BITS(imm, 2, 12, 3) | ((imm & 0x3) << 6)) +#define IMM12(imm) \ + (COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff)) + +typedef sljit_ui sljit_ins; + +/* --------------------------------------------------------------------- */ +/* Instrucion forms */ +/* --------------------------------------------------------------------- */ + +/* dot '.' changed to _ + I immediate form (possibly followed by number of immediate bits). */ +#define ADCI 0xf1400000 +#define ADCS 0x4140 +#define ADC_W 0xeb400000 +#define ADD 0x4400 +#define ADDS 0x1800 +#define ADDSI3 0x1c00 +#define ADDSI8 0x3000 +#define ADD_W 0xeb000000 +#define ADDWI 0xf2000000 +#define ADD_SP 0xb000 +#define ADD_W 0xeb000000 +#define ADD_WI 0xf1000000 +#define ANDI 0xf0000000 +#define ANDS 0x4000 +#define AND_W 0xea000000 +#define ASRS 0x4100 +#define ASRSI 0x1000 +#define ASR_W 0xfa40f000 +#define ASR_WI 0xea4f0020 +#define BICI 0xf0200000 +#define BKPT 0xbe00 +#define BLX 0x4780 +#define BX 0x4700 +#define CLZ 0xfab0f080 +#define CMPI 0x2800 +#define CMP_W 0xebb00f00 +#define EORI 0xf0800000 +#define EORS 0x4040 +#define EOR_W 0xea800000 +#define IT 0xbf00 +#define LSLS 0x4080 +#define LSLSI 0x0000 +#define LSL_W 0xfa00f000 +#define LSL_WI 0xea4f0000 +#define LSRS 0x40c0 +#define LSRSI 0x0800 +#define LSR_W 0xfa20f000 +#define LSR_WI 0xea4f0010 +#define MOV 0x4600 +#define MOVS 0x0000 +#define MOVSI 0x2000 +#define MOVT 0xf2c00000 +#define MOVW 0xf2400000 +#define MOV_W 0xea4f0000 +#define MOV_WI 0xf04f0000 +#define MUL 0xfb00f000 +#define MVNS 0x43c0 +#define MVN_W 0xea6f0000 +#define MVN_WI 0xf06f0000 +#define NOP 0xbf00 +#define ORNI 0xf0600000 +#define ORRI 0xf0400000 +#define ORRS 0x4300 +#define ORR_W 0xea400000 +#define POP 0xbd00 +#define POP_W 0xe8bd0000 +#define PUSH 0xb500 +#define PUSH_W 0xe92d0000 +#define RSB_WI 0xf1c00000 +#define RSBSI 0x4240 +#define SBCI 0xf1600000 +#define SBCS 0x4180 +#define SBC_W 0xeb600000 +#define SMULL 0xfb800000 +#define STR_SP 0x9000 +#define SUBS 0x1a00 +#define SUBSI3 0x1e00 +#define SUBSI8 0x3800 +#define SUB_W 0xeba00000 +#define SUBWI 0xf2a00000 +#define SUB_SP 0xb080 +#define SUB_WI 0xf1a00000 +#define SXTB 0xb240 +#define SXTB_W 0xfa4ff080 +#define SXTH 0xb200 +#define SXTH_W 0xfa0ff080 +#define TST 0x4200 +#define UMULL 0xfba00000 +#define UXTB 0xb2c0 +#define UXTB_W 0xfa5ff080 +#define UXTH 0xb280 +#define UXTH_W 0xfa1ff080 +#define VABS_F64 0xeeb00bc0 +#define VADD_F64 0xee300b00 +#define VCMP_F64 0xeeb40b40 +#define VDIV_F64 0xee800b00 +#define VMOV_F64 0xeeb00b40 +#define VMRS 0xeef1fa10 +#define VMUL_F64 0xee200b00 +#define VNEG_F64 0xeeb10b40 +#define VSTR 0xed000b00 +#define VSUB_F64 0xee300b40 + +static int push_inst16(struct sljit_compiler *compiler, sljit_ins inst) +{ + sljit_uh *ptr; + SLJIT_ASSERT(!(inst & 0xffff0000)); + + ptr = (sljit_uh*)ensure_buf(compiler, sizeof(sljit_uh)); + FAIL_IF(!ptr); + *ptr = inst; + compiler->size++; + return SLJIT_SUCCESS; +} + +static int push_inst32(struct sljit_compiler *compiler, sljit_ins inst) +{ + sljit_uh *ptr = (sljit_uh*)ensure_buf(compiler, sizeof(sljit_ins)); + FAIL_IF(!ptr); + *ptr++ = inst >> 16; + *ptr = inst; + compiler->size += 2; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_imm32_const(struct sljit_compiler *compiler, int dst, sljit_uw imm) +{ + FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) | + COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff))); + return push_inst32(compiler, MOVT | RD4(dst) | + COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16)); +} + +static SLJIT_INLINE void modify_imm32_const(sljit_uh* inst, sljit_uw new_imm) +{ + int dst = inst[1] & 0x0f00; + SLJIT_ASSERT(((inst[0] & 0xfbf0) == (MOVW >> 16)) && ((inst[2] & 0xfbf0) == (MOVT >> 16)) && dst == (inst[3] & 0x0f00)); + inst[0] = (MOVW >> 16) | COPY_BITS(new_imm, 12, 0, 4) | COPY_BITS(new_imm, 11, 10, 1); + inst[1] = dst | COPY_BITS(new_imm, 8, 12, 3) | (new_imm & 0xff); + inst[2] = (MOVT >> 16) | COPY_BITS(new_imm, 12 + 16, 0, 4) | COPY_BITS(new_imm, 11 + 16, 10, 1); + inst[3] = dst | COPY_BITS(new_imm, 8 + 16, 12, 3) | ((new_imm & 0xff0000) >> 16); +} + +static SLJIT_INLINE int detect_jump_type(struct sljit_jump *jump, sljit_uh *code_ptr, sljit_uh *code) +{ + sljit_w diff; + + if (jump->flags & SLJIT_REWRITABLE_JUMP) + return 0; + + if (jump->flags & JUMP_ADDR) { + /* Branch to ARM code is not optimized yet. */ + if (!(jump->u.target & 0x1)) + return 0; + diff = ((sljit_w)jump->u.target - (sljit_w)(code_ptr + 2)) >> 1; + } + else { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + diff = ((sljit_w)(code + jump->u.label->size) - (sljit_w)(code_ptr + 2)) >> 1; + } + + if (jump->flags & IS_CONDITIONAL) { + SLJIT_ASSERT(!(jump->flags & IS_BL)); + if (diff <= 127 && diff >= -128) { + jump->flags |= B_TYPE1; + return 5; + } + if (diff <= 524287 && diff >= -524288) { + jump->flags |= B_TYPE2; + return 4; + } + /* +1 comes from the prefix IT instruction. */ + diff--; + if (diff <= 8388607 && diff >= -8388608) { + jump->flags |= B_TYPE3; + return 3; + } + } + else if (jump->flags & IS_BL) { + if (diff <= 8388607 && diff >= -8388608) { + jump->flags |= BL_TYPE6; + return 3; + } + } + else { + if (diff <= 1023 && diff >= -1024) { + jump->flags |= B_TYPE4; + return 4; + } + if (diff <= 8388607 && diff >= -8388608) { + jump->flags |= B_TYPE5; + return 3; + } + } + + return 0; +} + +static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, int flush) +{ + sljit_uh* inst = (sljit_uh*)addr; + modify_imm32_const(inst, new_addr); + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 3); + } +} + +static SLJIT_INLINE void set_jump_instruction(struct sljit_jump *jump) +{ + int type = (jump->flags >> 4) & 0xf; + sljit_w diff; + sljit_uh *jump_inst; + int s, j1, j2; + + if (SLJIT_UNLIKELY(type == 0)) { + inline_set_jump_addr(jump->addr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); + return; + } + + if (jump->flags & JUMP_ADDR) { + SLJIT_ASSERT(jump->u.target & 0x1); + diff = ((sljit_w)jump->u.target - (sljit_w)(jump->addr + 4)) >> 1; + } + else + diff = ((sljit_w)(jump->u.label->addr) - (sljit_w)(jump->addr + 4)) >> 1; + jump_inst = (sljit_uh*)jump->addr; + + switch (type) { + case 1: + /* Encoding T1 of 'B' instruction */ + SLJIT_ASSERT(diff <= 127 && diff >= -128 && (jump->flags & IS_CONDITIONAL)); + jump_inst[0] = 0xd000 | (jump->flags & 0xf00) | (diff & 0xff); + return; + case 2: + /* Encoding T3 of 'B' instruction */ + SLJIT_ASSERT(diff <= 524287 && diff >= -524288 && (jump->flags & IS_CONDITIONAL)); + jump_inst[0] = 0xf000 | COPY_BITS(jump->flags, 8, 6, 4) | COPY_BITS(diff, 11, 0, 6) | COPY_BITS(diff, 19, 10, 1); + jump_inst[1] = 0x8000 | COPY_BITS(diff, 17, 13, 1) | COPY_BITS(diff, 18, 11, 1) | (diff & 0x7ff); + return; + case 3: + SLJIT_ASSERT(jump->flags & IS_CONDITIONAL); + *jump_inst++ = IT | ((jump->flags >> 4) & 0xf0) | 0x8; + diff--; + type = 5; + break; + case 4: + /* Encoding T2 of 'B' instruction */ + SLJIT_ASSERT(diff <= 1023 && diff >= -1024 && !(jump->flags & IS_CONDITIONAL)); + jump_inst[0] = 0xe000 | (diff & 0x7ff); + return; + } + + SLJIT_ASSERT(diff <= 8388607 && diff >= -8388608); + + /* Really complex instruction form for branches. */ + s = (diff >> 23) & 0x1; + j1 = (~(diff >> 21) ^ s) & 0x1; + j2 = (~(diff >> 22) ^ s) & 0x1; + jump_inst[0] = 0xf000 | (s << 10) | COPY_BITS(diff, 11, 0, 10); + jump_inst[1] = (j1 << 13) | (j2 << 11) | (diff & 0x7ff); + + /* The others have a common form. */ + if (type == 5) /* Encoding T4 of 'B' instruction */ + jump_inst[1] |= 0x9000; + else if (type == 6) /* Encoding T1 of 'BL' instruction */ + jump_inst[1] |= 0xd000; + else + SLJIT_ASSERT_STOP(); +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + sljit_uh *code; + sljit_uh *code_ptr; + sljit_uh *buf_ptr; + sljit_uh *buf_end; + sljit_uw half_count; + + struct sljit_label *label; + struct sljit_jump *jump; + struct sljit_const *const_; + + CHECK_ERROR_PTR(); + check_sljit_generate_code(compiler); + reverse_buf(compiler); + + code = (sljit_uh*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_uh)); + PTR_FAIL_WITH_EXEC_IF(code); + buf = compiler->buf; + + code_ptr = code; + half_count = 0; + label = compiler->labels; + jump = compiler->jumps; + const_ = compiler->consts; + + do { + buf_ptr = (sljit_uh*)buf->memory; + buf_end = buf_ptr + (buf->used_size >> 1); + do { + *code_ptr = *buf_ptr++; + /* These structures are ordered by their address. */ + SLJIT_ASSERT(!label || label->size >= half_count); + SLJIT_ASSERT(!jump || jump->addr >= half_count); + SLJIT_ASSERT(!const_ || const_->addr >= half_count); + if (label && label->size == half_count) { + label->addr = ((sljit_uw)code_ptr) | 0x1; + label->size = code_ptr - code; + label = label->next; + } + if (jump && jump->addr == half_count) { + jump->addr = (sljit_uw)code_ptr - ((jump->flags & IS_CONDITIONAL) ? 10 : 8); + code_ptr -= detect_jump_type(jump, code_ptr, code); + jump = jump->next; + } + if (const_ && const_->addr == half_count) { + const_->addr = (sljit_uw)code_ptr; + const_ = const_->next; + } + code_ptr ++; + half_count ++; + } while (buf_ptr < buf_end); + + buf = buf->next; + } while (buf); + + if (label && label->size == half_count) { + label->addr = ((sljit_uw)code_ptr) | 0x1; + label->size = code_ptr - code; + label = label->next; + } + + SLJIT_ASSERT(!label); + SLJIT_ASSERT(!jump); + SLJIT_ASSERT(!const_); + SLJIT_ASSERT(code_ptr - code <= (int)compiler->size); + + jump = compiler->jumps; + while (jump) { + set_jump_instruction(jump); + jump = jump->next; + } + + SLJIT_CACHE_FLUSH(code, code_ptr); + compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_size = compiler->size * sizeof(sljit_uh); + /* Set thumb mode flag. */ + return (void*)((sljit_uw)code | 0x1); +} + +#define INVALID_IMM 0x80000000 +static sljit_uw get_imm(sljit_uw imm) +{ + /* Thumb immediate form. */ + int counter; + + if (imm <= 0xff) + return imm; + + if ((imm & 0xffff) == (imm >> 16)) { + /* Some special cases. */ + if (!(imm & 0xff00)) + return (1 << 12) | (imm & 0xff); + if (!(imm & 0xff)) + return (2 << 12) | ((imm >> 8) & 0xff); + if ((imm & 0xff00) == ((imm & 0xff) << 8)) + return (3 << 12) | (imm & 0xff); + } + + /* Assembly optimization: count leading zeroes? */ + counter = 8; + if (!(imm & 0xffff0000)) { + counter += 16; + imm <<= 16; + } + if (!(imm & 0xff000000)) { + counter += 8; + imm <<= 8; + } + if (!(imm & 0xf0000000)) { + counter += 4; + imm <<= 4; + } + if (!(imm & 0xc0000000)) { + counter += 2; + imm <<= 2; + } + if (!(imm & 0x80000000)) { + counter += 1; + imm <<= 1; + } + /* Since imm >= 128, this must be true. */ + SLJIT_ASSERT(counter <= 31); + + if (imm & 0x00ffffff) + return INVALID_IMM; /* Cannot be encoded. */ + + return ((imm >> 24) & 0x7f) | COPY_BITS(counter, 4, 26, 1) | COPY_BITS(counter, 1, 12, 3) | COPY_BITS(counter, 0, 7, 1); +} + +static int load_immediate(struct sljit_compiler *compiler, int dst, sljit_uw imm) +{ + sljit_uw tmp; + + if (imm >= 0x10000) { + tmp = get_imm(imm); + if (tmp != INVALID_IMM) + return push_inst32(compiler, MOV_WI | RD4(dst) | tmp); + tmp = get_imm(~imm); + if (tmp != INVALID_IMM) + return push_inst32(compiler, MVN_WI | RD4(dst) | tmp); + } + + /* set low 16 bits, set hi 16 bits to 0. */ + FAIL_IF(push_inst32(compiler, MOVW | RD4(dst) | + COPY_BITS(imm, 12, 16, 4) | COPY_BITS(imm, 11, 26, 1) | COPY_BITS(imm, 8, 12, 3) | (imm & 0xff))); + + /* set hi 16 bit if needed. */ + if (imm >= 0x10000) + return push_inst32(compiler, MOVT | RD4(dst) | + COPY_BITS(imm, 12 + 16, 16, 4) | COPY_BITS(imm, 11 + 16, 26, 1) | COPY_BITS(imm, 8 + 16, 12, 3) | ((imm & 0xff0000) >> 16)); + return SLJIT_SUCCESS; +} + +#define ARG1_IMM 0x0010000 +#define ARG2_IMM 0x0020000 +#define KEEP_FLAGS 0x0040000 +#define SET_MULOV 0x0080000 +/* SET_FLAGS must be 0x100000 as it is also the value of S bit (can be used for optimization). */ +#define SET_FLAGS 0x0100000 +#define UNUSED_RETURN 0x0200000 +#define SLOW_DEST 0x0400000 +#define SLOW_SRC1 0x0800000 +#define SLOW_SRC2 0x1000000 + +static int emit_op_imm(struct sljit_compiler *compiler, int flags, int dst, sljit_uw arg1, sljit_uw arg2) +{ + /* dst must be register, TMP_REG1 + arg1 must be register, TMP_REG1, imm + arg2 must be register, TMP_REG2, imm */ + int reg; + sljit_uw imm; + + if (SLJIT_UNLIKELY((flags & (ARG1_IMM | ARG2_IMM)) == (ARG1_IMM | ARG2_IMM))) { + /* Both are immediates. */ + flags &= ~ARG1_IMM; + FAIL_IF(load_immediate(compiler, TMP_REG1, arg1)); + arg1 = TMP_REG1; + } + + if (flags & (ARG1_IMM | ARG2_IMM)) { + reg = (flags & ARG2_IMM) ? arg1 : arg2; + imm = (flags & ARG2_IMM) ? arg2 : arg1; + + switch (flags & 0xffff) { + case SLJIT_MOV: + SLJIT_ASSERT(!(flags & SET_FLAGS) && (flags & ARG2_IMM) && arg1 == TMP_REG1); + return load_immediate(compiler, dst, imm); + case SLJIT_NOT: + if (!(flags & SET_FLAGS)) + return load_immediate(compiler, dst, ~imm); + /* Since the flags should be set, we just fallback to the register mode. + Although I could do some clever things here, "NOT IMM" does not worth the efforts. */ + break; + case SLJIT_CLZ: + /* No form with immediate operand. */ + break; + case SLJIT_ADD: + if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(reg, dst)) { + if (imm <= 0x7) + return push_inst16(compiler, ADDSI3 | IMM3(imm) | RD3(dst) | RN3(reg)); + if (reg == dst && imm <= 0xff) + return push_inst16(compiler, ADDSI8 | IMM8(imm) | RDN3(dst)); + } + if (imm <= 0xfff && !(flags & SET_FLAGS)) + return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(imm)); + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, ADD_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + break; + case SLJIT_ADDC: + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, ADCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + break; + case SLJIT_SUB: + if (flags & ARG2_IMM) { + if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(reg, dst)) { + if (imm <= 0x7) + return push_inst16(compiler, SUBSI3 | IMM3(imm) | RD3(dst) | RN3(reg)); + if (imm <= 0xff) { + if (reg == dst) + return push_inst16(compiler, SUBSI8 | IMM8(imm) | RDN3(dst)); + if (flags & UNUSED_RETURN) + return push_inst16(compiler, CMPI | IMM8(imm) | RDN3(reg)); + } + } + if (imm <= 0xfff && !(flags & SET_FLAGS)) + return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(imm)); + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, SUB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + } + else { + if (!(flags & KEEP_FLAGS) && imm == 0 && IS_2_LO_REGS(reg, dst)) + return push_inst16(compiler, RSBSI | RD3(dst) | RN3(reg)); + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, RSB_WI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + } + break; + case SLJIT_SUBC: + if (flags & ARG2_IMM) { + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, SBCI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + } + break; + case SLJIT_MUL: + /* No form with immediate operand. */ + break; + case SLJIT_AND: + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, ANDI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + imm = get_imm(~((flags & ARG2_IMM) ? arg2 : arg1)); + if (imm != INVALID_IMM) + return push_inst32(compiler, BICI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + break; + case SLJIT_OR: + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, ORRI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + imm = get_imm(~((flags & ARG2_IMM) ? arg2 : arg1)); + if (imm != INVALID_IMM) + return push_inst32(compiler, ORNI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + break; + case SLJIT_XOR: + imm = get_imm(imm); + if (imm != INVALID_IMM) + return push_inst32(compiler, EORI | (flags & SET_FLAGS) | RD4(dst) | RN4(reg) | imm); + break; + case SLJIT_SHL: + if (flags & ARG2_IMM) { + imm &= 0x1f; + if (imm == 0) { + if (!(flags & SET_FLAGS)) + return push_inst16(compiler, MOV | SET_REGS44(dst, reg)); + if (IS_2_LO_REGS(dst, reg)) + return push_inst16(compiler, MOVS | RD3(dst) | RN3(reg)); + return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(dst) | RM4(reg)); + } + if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg)) + return push_inst16(compiler, LSLSI | RD3(dst) | RN3(reg) | (imm << 6)); + return push_inst32(compiler, LSL_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); + } + break; + case SLJIT_LSHR: + if (flags & ARG2_IMM) { + imm &= 0x1f; + if (imm == 0) { + if (!(flags & SET_FLAGS)) + return push_inst16(compiler, MOV | SET_REGS44(dst, reg)); + if (IS_2_LO_REGS(dst, reg)) + return push_inst16(compiler, MOVS | RD3(dst) | RN3(reg)); + return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(dst) | RM4(reg)); + } + if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg)) + return push_inst16(compiler, LSRSI | RD3(dst) | RN3(reg) | (imm << 6)); + return push_inst32(compiler, LSR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); + } + break; + case SLJIT_ASHR: + if (flags & ARG2_IMM) { + imm &= 0x1f; + if (imm == 0) { + if (!(flags & SET_FLAGS)) + return push_inst16(compiler, MOV | SET_REGS44(dst, reg)); + if (IS_2_LO_REGS(dst, reg)) + return push_inst16(compiler, MOVS | RD3(dst) | RN3(reg)); + return push_inst32(compiler, MOV_W | SET_FLAGS | RD4(dst) | RM4(reg)); + } + if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, reg)) + return push_inst16(compiler, ASRSI | RD3(dst) | RN3(reg) | (imm << 6)); + return push_inst32(compiler, ASR_WI | (flags & SET_FLAGS) | RD4(dst) | RM4(reg) | IMM5(imm)); + } + break; + default: + SLJIT_ASSERT_STOP(); + break; + } + + if (flags & ARG2_IMM) { + FAIL_IF(load_immediate(compiler, TMP_REG2, arg2)); + arg2 = TMP_REG2; + } + else { + FAIL_IF(load_immediate(compiler, TMP_REG1, arg1)); + arg1 = TMP_REG1; + } + } + + /* Both arguments are registers. */ + switch (flags & 0xffff) { + case SLJIT_MOV: + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: + case SLJIT_MOVU: + case SLJIT_MOVU_UI: + case SLJIT_MOVU_SI: + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + return push_inst16(compiler, MOV | SET_REGS44(dst, arg2)); + case SLJIT_MOV_UB: + case SLJIT_MOVU_UB: + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + if (IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, UXTB | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, UXTB_W | RD4(dst) | RM4(arg2)); + case SLJIT_MOV_SB: + case SLJIT_MOVU_SB: + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + if (IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, SXTB | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, SXTB_W | RD4(dst) | RM4(arg2)); + case SLJIT_MOV_UH: + case SLJIT_MOVU_UH: + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + if (IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, UXTH | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, UXTH_W | RD4(dst) | RM4(arg2)); + case SLJIT_MOV_SH: + case SLJIT_MOVU_SH: + SLJIT_ASSERT(!(flags & SET_FLAGS) && arg1 == TMP_REG1); + if (IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, SXTH | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, SXTH_W | RD4(dst) | RM4(arg2)); + case SLJIT_NOT: + SLJIT_ASSERT(arg1 == TMP_REG1); + if (!(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, MVNS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, MVN_W | (flags & SET_FLAGS) | RD4(dst) | RM4(arg2)); + case SLJIT_CLZ: + SLJIT_ASSERT(arg1 == TMP_REG1); + FAIL_IF(push_inst32(compiler, CLZ | RN4(arg2) | RD4(dst) | RM4(arg2))); + if (flags & SET_FLAGS) { + if (reg_map[dst] <= 7) + return push_inst16(compiler, CMPI | RDN3(dst)); + return push_inst32(compiler, ADD_WI | SET_FLAGS | RN4(dst) | RD4(dst)); + } + return SLJIT_SUCCESS; + case SLJIT_ADD: + if (!(flags & KEEP_FLAGS) && IS_3_LO_REGS(dst, arg1, arg2)) + return push_inst16(compiler, ADDS | RD3(dst) | RN3(arg1) | RM3(arg2)); + if (dst == arg1 && !(flags & SET_FLAGS)) + return push_inst16(compiler, ADD | SET_REGS44(dst, arg2)); + return push_inst32(compiler, ADD_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_ADDC: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, ADCS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, ADC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_SUB: + if (!(flags & KEEP_FLAGS) && IS_3_LO_REGS(dst, arg1, arg2)) + return push_inst16(compiler, SUBS | RD3(dst) | RN3(arg1) | RM3(arg2)); + return push_inst32(compiler, SUB_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_SUBC: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, SBCS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, SBC_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_MUL: + if (!(flags & SET_FLAGS)) + return push_inst32(compiler, MUL | RD4(dst) | RN4(arg1) | RM4(arg2)); + SLJIT_ASSERT(reg_map[TMP_REG2] <= 7 && dst != TMP_REG2); + FAIL_IF(push_inst32(compiler, SMULL | RT4(dst) | RD4(TMP_REG2) | RN4(arg1) | RM4(arg2))); + /* cmp TMP_REG2, dst asr #31. */ + return push_inst32(compiler, CMP_W | RN4(TMP_REG2) | 0x70e0 | RM4(dst)); + case SLJIT_AND: + if (!(flags & KEEP_FLAGS)) { + if (dst == arg1 && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, ANDS | RD3(dst) | RN3(arg2)); + if ((flags & UNUSED_RETURN) && IS_2_LO_REGS(arg1, arg2)) + return push_inst16(compiler, TST | RD3(arg1) | RN3(arg2)); + } + return push_inst32(compiler, AND_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_OR: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, ORRS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, ORR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_XOR: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, EORS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, EOR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_SHL: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, LSLS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, LSL_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_LSHR: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, LSRS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, LSR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + case SLJIT_ASHR: + if (dst == arg1 && !(flags & KEEP_FLAGS) && IS_2_LO_REGS(dst, arg2)) + return push_inst16(compiler, ASRS | RD3(dst) | RN3(arg2)); + return push_inst32(compiler, ASR_W | (flags & SET_FLAGS) | RD4(dst) | RN4(arg1) | RM4(arg2)); + } + + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; +} + +#define STORE 0x01 +#define SIGNED 0x02 + +#define WORD_SIZE 0x00 +#define BYTE_SIZE 0x04 +#define HALF_SIZE 0x08 + +#define UPDATE 0x10 +#define ARG_TEST 0x20 + +#define IS_WORD_SIZE(flags) (!(flags & (BYTE_SIZE | HALF_SIZE))) +#define OFFSET_CHECK(imm, shift) (!(argw & ~(imm << shift))) + +/* + 1st letter: + w = word + b = byte + h = half + + 2nd letter: + s = signed + u = unsigned + + 3rd letter: + l = load + s = store +*/ + +static SLJIT_CONST sljit_uw sljit_mem16[12] = { +/* w u l */ 0x5800 /* ldr */, +/* w u s */ 0x5000 /* str */, +/* w s l */ 0x5800 /* ldr */, +/* w s s */ 0x5000 /* str */, + +/* b u l */ 0x5c00 /* ldrb */, +/* b u s */ 0x5400 /* strb */, +/* b s l */ 0x5600 /* ldrsb */, +/* b s s */ 0x5400 /* strb */, + +/* h u l */ 0x5a00 /* ldrh */, +/* h u s */ 0x5200 /* strh */, +/* h s l */ 0x5e00 /* ldrsh */, +/* h s s */ 0x5200 /* strh */, +}; + +static SLJIT_CONST sljit_uw sljit_mem16_imm5[12] = { +/* w u l */ 0x6800 /* ldr imm5 */, +/* w u s */ 0x6000 /* str imm5 */, +/* w s l */ 0x6800 /* ldr imm5 */, +/* w s s */ 0x6000 /* str imm5 */, + +/* b u l */ 0x7800 /* ldrb imm5 */, +/* b u s */ 0x7000 /* strb imm5 */, +/* b s l */ 0x0000 /* not allowed */, +/* b s s */ 0x7000 /* strb imm5 */, + +/* h u l */ 0x8800 /* ldrh imm5 */, +/* h u s */ 0x8000 /* strh imm5 */, +/* h s l */ 0x0000 /* not allowed */, +/* h s s */ 0x8000 /* strh imm5 */, +}; + +#define MEM_IMM8 0xc00 +#define MEM_IMM12 0x800000 +static SLJIT_CONST sljit_uw sljit_mem32[12] = { +/* w u l */ 0xf8500000 /* ldr.w */, +/* w u s */ 0xf8400000 /* str.w */, +/* w s l */ 0xf8500000 /* ldr.w */, +/* w s s */ 0xf8400000 /* str.w */, + +/* b u l */ 0xf8100000 /* ldrb.w */, +/* b u s */ 0xf8000000 /* strb.w */, +/* b s l */ 0xf9100000 /* ldrsb.w */, +/* b s s */ 0xf8000000 /* strb.w */, + +/* h u l */ 0xf8300000 /* ldrh.w */, +/* h u s */ 0xf8200000 /* strsh.w */, +/* h s l */ 0xf9300000 /* ldrsh.w */, +/* h s s */ 0xf8200000 /* strsh.w */, +}; + +/* Helper function. Dst should be reg + value, using at most 1 instruction, flags does not set. */ +static int emit_set_delta(struct sljit_compiler *compiler, int dst, int reg, sljit_w value) +{ + if (value >= 0) { + if (value <= 0xfff) + return push_inst32(compiler, ADDWI | RD4(dst) | RN4(reg) | IMM12(value)); + value = get_imm(value); + if (value != INVALID_IMM) + return push_inst32(compiler, ADD_WI | RD4(dst) | RN4(reg) | value); + } + else { + value = -value; + if (value <= 0xfff) + return push_inst32(compiler, SUBWI | RD4(dst) | RN4(reg) | IMM12(value)); + value = get_imm(value); + if (value != INVALID_IMM) + return push_inst32(compiler, SUB_WI | RD4(dst) | RN4(reg) | value); + } + return SLJIT_ERR_UNSUPPORTED; +} + +/* Can perform an operation using at most 1 instruction. */ +static int getput_arg_fast(struct sljit_compiler *compiler, int flags, int reg, int arg, sljit_w argw) +{ + int tmp; + + SLJIT_ASSERT(arg & SLJIT_MEM); + + if (SLJIT_UNLIKELY(flags & UPDATE)) { + if ((arg & 0xf) && !(arg & 0xf0) && argw <= 0xff && argw >= -0xff) { + flags &= ~UPDATE; + arg &= 0xf; + if (SLJIT_UNLIKELY(flags & ARG_TEST)) + return 1; + + if (argw >= 0) + argw |= 0x200; + else { + argw = -argw; + } + SLJIT_ASSERT(argw >= 0 && (argw & 0xff) <= 0xff); + FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | 0x100 | argw)); + return -1; + } + return (flags & ARG_TEST) ? SLJIT_SUCCESS : 0; + } + + if (SLJIT_UNLIKELY(arg & 0xf0)) { + argw &= 0x3; + tmp = (arg >> 4) & 0xf; + arg &= 0xf; + if (SLJIT_UNLIKELY(flags & ARG_TEST)) + return 1; + + if (!argw && IS_3_LO_REGS(reg, arg, tmp)) + FAIL_IF(push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(tmp))); + else + FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp) | (argw << 4))); + return -1; + } + + if (!(arg & 0xf) || argw > 0xfff || argw < -0xff) + return (flags & ARG_TEST) ? SLJIT_SUCCESS : 0; + + if (SLJIT_UNLIKELY(flags & ARG_TEST)) + return 1; + + arg &= 0xf; + if (IS_2_LO_REGS(reg, arg) && sljit_mem16_imm5[flags]) { + tmp = 3; + if (IS_WORD_SIZE(flags)) { + if (OFFSET_CHECK(0x1f, 2)) + tmp = 2; + } + else if (flags & BYTE_SIZE) + { + if (OFFSET_CHECK(0x1f, 0)) + tmp = 0; + } + else { + SLJIT_ASSERT(flags & HALF_SIZE); + if (OFFSET_CHECK(0x1f, 1)) + tmp = 1; + } + + if (tmp != 3) { + FAIL_IF(push_inst16(compiler, sljit_mem16_imm5[flags] | RD3(reg) | RN3(arg) | (argw << (6 - tmp)))); + return -1; + } + } + + /* SP based immediate. */ + if (SLJIT_UNLIKELY(arg == SLJIT_LOCALS_REG) && OFFSET_CHECK(0xff, 2) && IS_WORD_SIZE(flags) && reg_map[reg] <= 7) { + FAIL_IF(push_inst16(compiler, STR_SP | ((flags & STORE) ? 0 : 0x800) | RDN3(reg) | (argw >> 2))); + return -1; + } + + if (argw >= 0) + FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw)); + else + FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(arg) | -argw)); + return -1; +} + +/* see getput_arg below. + Note: can_cache is called only for binary operators. Those + operators always uses word arguments without write back. */ +static int can_cache(int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + /* Simple operation except for updates. */ + if ((arg & 0xf0) || !(next_arg & SLJIT_MEM)) + return 0; + + if (!(arg & 0xf)) { + if ((sljit_uw)(argw - next_argw) <= 0xfff || (sljit_uw)(next_argw - argw) <= 0xfff) + return 1; + return 0; + } + + if (argw == next_argw) + return 1; + + if (arg == next_arg && ((sljit_uw)(argw - next_argw) <= 0xfff || (sljit_uw)(next_argw - argw) <= 0xfff)) + return 1; + + return 0; +} + +/* Emit the necessary instructions. See can_cache above. */ +static int getput_arg(struct sljit_compiler *compiler, int flags, int reg, int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + int tmp_r; + sljit_w tmp; + + SLJIT_ASSERT(arg & SLJIT_MEM); + if (!(next_arg & SLJIT_MEM)) { + next_arg = 0; + next_argw = 0; + } + + tmp_r = (flags & STORE) ? TMP_REG3 : reg; + + if (SLJIT_UNLIKELY(flags & UPDATE)) { + flags &= ~UPDATE; + /* Update only applies if a base register exists. */ + if (arg & 0xf) { + /* There is no caching here. */ + tmp = (arg & 0xf0) >> 4; + arg &= 0xf; + + if (!tmp) { + if (!(argw & ~0xfff)) { + FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(arg) | argw)); + return push_inst32(compiler, ADDWI | RD4(arg) | RN4(arg) | IMM12(argw)); + } + + if (compiler->cache_arg == SLJIT_MEM) { + if (argw == compiler->cache_argw) { + tmp = TMP_REG3; + argw = 0; + } + else if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { + FAIL_IF(compiler->error); + compiler->cache_argw = argw; + tmp = TMP_REG3; + argw = 0; + } + } + + if (argw) { + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + compiler->cache_arg = SLJIT_MEM; + compiler->cache_argw = argw; + tmp = TMP_REG3; + argw = 0; + } + } + + argw &= 0x3; + if (!argw && IS_3_LO_REGS(reg, arg, tmp)) { + FAIL_IF(push_inst16(compiler, sljit_mem16[flags] | RD3(reg) | RN3(arg) | RM3(tmp))); + return push_inst16(compiler, ADD | SET_REGS44(arg, tmp)); + } + FAIL_IF(push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(tmp) | (argw << 4))); + return push_inst32(compiler, ADD_W | RD4(arg) | RN4(arg) | RM4(tmp) | (argw << 6)); + } + } + + SLJIT_ASSERT(!(arg & 0xf0)); + + if (compiler->cache_arg == arg) { + if (!((argw - compiler->cache_argw) & ~0xfff)) + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | (argw - compiler->cache_argw)); + if (!((compiler->cache_argw - argw) & ~0xff)) + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM8 | RT4(reg) | RN4(TMP_REG3) | (compiler->cache_argw - argw)); + if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, argw - compiler->cache_argw) != SLJIT_ERR_UNSUPPORTED) { + FAIL_IF(compiler->error); + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | 0); + } + } + + next_arg = (arg & 0xf) && (arg == next_arg); + arg &= 0xf; + if (arg && compiler->cache_arg == SLJIT_MEM && compiler->cache_argw == argw) + return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3)); + + compiler->cache_argw = argw; + if (next_arg && emit_set_delta(compiler, TMP_REG3, arg, argw) != SLJIT_ERR_UNSUPPORTED) { + FAIL_IF(compiler->error); + compiler->cache_arg = SLJIT_MEM | arg; + arg = 0; + } + else { + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + compiler->cache_arg = SLJIT_MEM; + + if (next_arg) { + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG3, arg))); + compiler->cache_arg = SLJIT_MEM | arg; + arg = 0; + } + } + + if (arg) + return push_inst32(compiler, sljit_mem32[flags] | RT4(reg) | RN4(arg) | RM4(TMP_REG3)); + return push_inst32(compiler, sljit_mem32[flags] | MEM_IMM12 | RT4(reg) | RN4(TMP_REG3) | 0); +} + +static SLJIT_INLINE int emit_op_mem(struct sljit_compiler *compiler, int flags, int reg, int arg, sljit_w argw) +{ + if (getput_arg_fast(compiler, flags, reg, arg, argw)) + return compiler->error; + compiler->cache_arg = 0; + compiler->cache_argw = 0; + return getput_arg(compiler, flags, reg, arg, argw, 0, 0); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int size; + sljit_ins push; + + CHECK_ERROR(); + check_sljit_emit_enter(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + push = (1 << 4); + if (saveds >= 5) + push |= 1 << 11; + if (saveds >= 4) + push |= 1 << 10; + if (saveds >= 3) + push |= 1 << 8; + if (saveds >= 2) + push |= 1 << 7; + if (saveds >= 1) + push |= 1 << 6; + if (temporaries >= 5) + push |= 1 << 5; + FAIL_IF(saveds >= 3 + ? push_inst32(compiler, PUSH_W | (1 << 14) | push) + : push_inst16(compiler, PUSH | push)); + + /* Stack must be aligned to 8 bytes: */ + size = (3 + saveds) * sizeof(sljit_uw); + local_size += size; + local_size = (local_size + 7) & ~7; + local_size -= size; + compiler->local_size = local_size; + if (local_size > 0) { + if (local_size <= (127 << 2)) + FAIL_IF(push_inst16(compiler, SUB_SP | (local_size >> 2))); + else + FAIL_IF(emit_op_imm(compiler, SLJIT_SUB | ARG2_IMM, SLJIT_LOCALS_REG, SLJIT_LOCALS_REG, local_size)); + } + + if (args >= 1) + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_SAVED_REG1, SLJIT_TEMPORARY_REG1))); + if (args >= 2) + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_SAVED_REG2, SLJIT_TEMPORARY_REG2))); + if (args >= 3) + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(SLJIT_SAVED_REG3, SLJIT_TEMPORARY_REG3))); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int size; + + CHECK_ERROR_VOID(); + check_sljit_set_context(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + size = (3 + saveds) * sizeof(sljit_uw); + local_size += size; + local_size = (local_size + 7) & ~7; + local_size -= size; + compiler->local_size = local_size; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + sljit_ins pop; + + CHECK_ERROR(); + check_sljit_emit_return(compiler, op, src, srcw); + + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + if (compiler->local_size > 0) { + if (compiler->local_size <= (127 << 2)) + FAIL_IF(push_inst16(compiler, ADD_SP | (compiler->local_size >> 2))); + else + FAIL_IF(emit_op_imm(compiler, SLJIT_ADD | ARG2_IMM, SLJIT_LOCALS_REG, SLJIT_LOCALS_REG, compiler->local_size)); + } + + pop = (1 << 4); + if (compiler->saveds >= 5) + pop |= 1 << 11; + if (compiler->saveds >= 4) + pop |= 1 << 10; + if (compiler->saveds >= 3) + pop |= 1 << 8; + if (compiler->saveds >= 2) + pop |= 1 << 7; + if (compiler->saveds >= 1) + pop |= 1 << 6; + if (compiler->temporaries >= 5) + pop |= 1 << 5; + return compiler->saveds >= 3 + ? push_inst32(compiler, POP_W | (1 << 15) | pop) + : push_inst16(compiler, POP | pop); +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) +extern unsigned int __aeabi_uidivmod(unsigned numerator, unsigned denominator); +extern unsigned int __aeabi_idivmod(unsigned numerator, unsigned denominator); +#else +#error "Software divmod functions are needed" +#endif + +#ifdef __cplusplus +} +#endif + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + CHECK_ERROR(); + check_sljit_emit_op0(compiler, op); + + op = GET_OPCODE(op); + switch (op) { + case SLJIT_BREAKPOINT: + push_inst16(compiler, BKPT); + break; + case SLJIT_NOP: + push_inst16(compiler, NOP); + break; + case SLJIT_UMUL: + case SLJIT_SMUL: + return push_inst32(compiler, (op == SLJIT_UMUL ? UMULL : SMULL) + | (reg_map[SLJIT_TEMPORARY_REG2] << 8) + | (reg_map[SLJIT_TEMPORARY_REG1] << 12) + | (reg_map[SLJIT_TEMPORARY_REG1] << 16) + | reg_map[SLJIT_TEMPORARY_REG2]); + case SLJIT_UDIV: + case SLJIT_SDIV: + if (compiler->temporaries >= 4) { + FAIL_IF(push_inst32(compiler, 0xf84d2d04 /* str r2, [sp, #-4]! */)); + FAIL_IF(push_inst32(compiler, 0xf84dcd04 /* str ip, [sp, #-4]! */)); + } else if (compiler->temporaries >= 3) + FAIL_IF(push_inst32(compiler, 0xf84d2d08 /* str r2, [sp, #-8]! */)); +#if defined(__GNUC__) + FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM, + (op == SLJIT_UDIV ? SLJIT_FUNC_OFFSET(__aeabi_uidivmod) : SLJIT_FUNC_OFFSET(__aeabi_idivmod)))); +#else +#error "Software divmod functions are needed" +#endif + if (compiler->temporaries >= 4) { + FAIL_IF(push_inst32(compiler, 0xf85dcb04 /* ldr ip, [sp], #4 */)); + return push_inst32(compiler, 0xf85d2b04 /* ldr r2, [sp], #4 */); + } else if (compiler->temporaries >= 3) + return push_inst32(compiler, 0xf85d2b08 /* ldr r2, [sp], #8 */); + return SLJIT_SUCCESS; + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int op_type, dst_r, flags; + + CHECK_ERROR(); + check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + op_type = GET_OPCODE(op); + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG1; + + if (op_type >= SLJIT_MOV && op_type <= SLJIT_MOVU_SI) { + switch (op_type) { + case SLJIT_MOV: + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: + flags = WORD_SIZE; + break; + case SLJIT_MOV_UB: + flags = BYTE_SIZE; + if (src & SLJIT_IMM) + srcw = (unsigned char)srcw; + break; + case SLJIT_MOV_SB: + flags = BYTE_SIZE | SIGNED; + if (src & SLJIT_IMM) + srcw = (signed char)srcw; + break; + case SLJIT_MOV_UH: + flags = HALF_SIZE; + if (src & SLJIT_IMM) + srcw = (unsigned short)srcw; + break; + case SLJIT_MOV_SH: + flags = HALF_SIZE | SIGNED; + if (src & SLJIT_IMM) + srcw = (signed short)srcw; + break; + case SLJIT_MOVU: + case SLJIT_MOVU_UI: + case SLJIT_MOVU_SI: + flags = WORD_SIZE | UPDATE; + break; + case SLJIT_MOVU_UB: + flags = BYTE_SIZE | UPDATE; + if (src & SLJIT_IMM) + srcw = (unsigned char)srcw; + break; + case SLJIT_MOVU_SB: + flags = BYTE_SIZE | SIGNED | UPDATE; + if (src & SLJIT_IMM) + srcw = (signed char)srcw; + break; + case SLJIT_MOVU_UH: + flags = HALF_SIZE | UPDATE; + if (src & SLJIT_IMM) + srcw = (unsigned short)srcw; + break; + case SLJIT_MOVU_SH: + flags = HALF_SIZE | SIGNED | UPDATE; + if (src & SLJIT_IMM) + srcw = (signed short)srcw; + break; + default: + SLJIT_ASSERT_STOP(); + flags = 0; + break; + } + + if (src & SLJIT_IMM) + FAIL_IF(emit_op_imm(compiler, SLJIT_MOV | ARG2_IMM, dst_r, TMP_REG1, srcw)); + else if (src & SLJIT_MEM) { + if (getput_arg_fast(compiler, flags, dst_r, src, srcw)) + FAIL_IF(compiler->error); + else + FAIL_IF(getput_arg(compiler, flags, dst_r, src, srcw, dst, dstw)); + } else { + if (dst_r != TMP_REG1) + return emit_op_imm(compiler, op_type, dst_r, TMP_REG1, src); + dst_r = src; + } + + if (dst & SLJIT_MEM) { + if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw)) + return compiler->error; + else + return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0); + } + return SLJIT_SUCCESS; + } + + if (op_type == SLJIT_NEG) { +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_op2(compiler, GET_FLAGS(op) | SLJIT_SUB, dst, dstw, SLJIT_IMM, 0, src, srcw); + } + + flags = (GET_FLAGS(op) ? SET_FLAGS : 0) | ((op & SLJIT_KEEP_FLAGS) ? KEEP_FLAGS : 0); + if (src & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG2, src, srcw)) + FAIL_IF(compiler->error); + else + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src, srcw, dst, dstw)); + src = TMP_REG2; + } + + if (src & SLJIT_IMM) + flags |= ARG2_IMM; + else + srcw = src; + + emit_op_imm(compiler, flags | op_type, dst_r, TMP_REG1, srcw); + + if (dst & SLJIT_MEM) { + if (getput_arg_fast(compiler, flags | STORE, dst_r, dst, dstw)) + return compiler->error; + else + return getput_arg(compiler, flags | STORE, dst_r, dst, dstw, 0, 0); + } + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int dst_r, flags; + + CHECK_ERROR(); + check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG1; + flags = (GET_FLAGS(op) ? SET_FLAGS : 0) | ((op & SLJIT_KEEP_FLAGS) ? KEEP_FLAGS : 0); + + if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, WORD_SIZE | STORE | ARG_TEST, TMP_REG1, dst, dstw)) + flags |= SLOW_DEST; + + if (src1 & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG1, src1, src1w)) + FAIL_IF(compiler->error); + else + flags |= SLOW_SRC1; + } + if (src2 & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG2, src2, src2w)) + FAIL_IF(compiler->error); + else + flags |= SLOW_SRC2; + } + + if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { + if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, dst, dstw)); + } + else { + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, dst, dstw)); + } + } + else if (flags & SLOW_SRC1) + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG1, src1, src1w, dst, dstw)); + else if (flags & SLOW_SRC2) + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src2, src2w, dst, dstw)); + + if (src1 & SLJIT_MEM) + src1 = TMP_REG1; + if (src2 & SLJIT_MEM) + src2 = TMP_REG2; + + if (src1 & SLJIT_IMM) + flags |= ARG1_IMM; + else + src1w = src1; + if (src2 & SLJIT_IMM) + flags |= ARG2_IMM; + else + src2w = src2; + + if (dst == SLJIT_UNUSED) + flags |= UNUSED_RETURN; + + if (GET_OPCODE(op) == SLJIT_MUL && (op & SLJIT_SET_O)) + flags |= SET_MULOV; + + emit_op_imm(compiler, flags | GET_OPCODE(op), dst_r, src1w, src2w); + + if (dst & SLJIT_MEM) { + if (!(flags & SLOW_DEST)) { + getput_arg_fast(compiler, WORD_SIZE | STORE, dst_r, dst, dstw); + return compiler->error; + } + return getput_arg(compiler, WORD_SIZE | STORE, TMP_REG1, dst, dstw, 0, 0); + } + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_register_index(int reg) +{ + check_sljit_get_register_index(reg); + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + CHECK_ERROR(); + check_sljit_emit_op_custom(compiler, instruction, size); + SLJIT_ASSERT(size == 2 || size == 4); + + if (size == 2) + return push_inst16(compiler, *(sljit_uh*)instruction); + return push_inst32(compiler, *(sljit_ins*)instruction); +} + +/* --------------------------------------------------------------------- */ +/* Floating point operators */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ + return 1; +} + +static int emit_fop_mem(struct sljit_compiler *compiler, int flags, int reg, int arg, sljit_w argw) +{ + sljit_w tmp; + sljit_w inst = VSTR | ((flags & STORE) ? 0 : 0x00100000); + + SLJIT_ASSERT(arg & SLJIT_MEM); + + /* Fast loads and stores. */ + if (SLJIT_UNLIKELY(arg & 0xf0)) { + FAIL_IF(push_inst32(compiler, ADD_W | RD4(TMP_REG2) | RN4(arg & 0xf) | RM4((arg & 0xf0) >> 4) | ((argw & 0x3) << 6))); + arg = SLJIT_MEM | TMP_REG2; + argw = 0; + } + + if (arg & 0xf) { + if (!(argw & ~0x3fc)) + return push_inst32(compiler, inst | 0x800000 | RN4(arg & 0xf) | DD4(reg) | (argw >> 2)); + if (!(-argw & ~0x3fc)) + return push_inst32(compiler, inst | RN4(arg & 0xf) | DD4(reg) | (-argw >> 2)); + } + + SLJIT_ASSERT(!(arg & 0xf0)); + if (compiler->cache_arg == arg) { + tmp = argw - compiler->cache_argw; + if (!(tmp & ~0x3fc)) + return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg) | (tmp >> 2)); + if (!(-tmp & ~0x3fc)) + return push_inst32(compiler, inst | RN4(TMP_REG3) | DD4(reg) | (-tmp >> 2)); + if (emit_set_delta(compiler, TMP_REG3, TMP_REG3, tmp) != SLJIT_ERR_UNSUPPORTED) { + FAIL_IF(compiler->error); + compiler->cache_argw = argw; + return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg)); + } + } + + compiler->cache_arg = arg; + compiler->cache_argw = argw; + + if (SLJIT_UNLIKELY(!(arg & 0xf))) + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + else if (emit_set_delta(compiler, TMP_REG3, arg & 0xf, argw) != SLJIT_ERR_UNSUPPORTED) + FAIL_IF(compiler->error); + else { + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + if (arg & 0xf) + FAIL_IF(push_inst16(compiler, ADD | SET_REGS44(TMP_REG3, (arg & 0xf)))); + } + return push_inst32(compiler, inst | 0x800000 | RN4(TMP_REG3) | DD4(reg)); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int dst_r; + + CHECK_ERROR(); + check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (GET_OPCODE(op) == SLJIT_FCMP) { + if (dst & SLJIT_MEM) { + emit_fop_mem(compiler, 0, TMP_FREG1, dst, dstw); + dst = TMP_FREG1; + } + if (src & SLJIT_MEM) { + emit_fop_mem(compiler, 0, TMP_FREG2, src, srcw); + src = TMP_FREG2; + } + FAIL_IF(push_inst32(compiler, VCMP_F64 | DD4(dst) | DM4(src))); + return push_inst32(compiler, VMRS); + } + + dst_r = (dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) ? dst : TMP_FREG1; + if (src & SLJIT_MEM) { + emit_fop_mem(compiler, 0, dst_r, src, srcw); + src = dst_r; + } + + switch (GET_OPCODE(op)) { + case SLJIT_FMOV: + if (src != dst_r) + FAIL_IF(push_inst32(compiler, VMOV_F64 | DD4(dst_r) | DM4(src))); + break; + case SLJIT_FNEG: + FAIL_IF(push_inst32(compiler, VNEG_F64 | DD4(dst_r) | DM4(src))); + break; + case SLJIT_FABS: + FAIL_IF(push_inst32(compiler, VABS_F64 | DD4(dst_r) | DM4(src))); + break; + } + + if (dst & SLJIT_MEM) + return emit_fop_mem(compiler, STORE, TMP_FREG1, dst, dstw); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int dst_r; + + CHECK_ERROR(); + check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + dst_r = (dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) ? dst : TMP_FREG1; + if (src1 & SLJIT_MEM) { + emit_fop_mem(compiler, 0, TMP_FREG1, src1, src1w); + src1 = TMP_FREG1; + } + if (src2 & SLJIT_MEM) { + emit_fop_mem(compiler, 0, TMP_FREG2, src2, src2w); + src2 = TMP_FREG2; + } + + switch (GET_OPCODE(op)) { + case SLJIT_FADD: + FAIL_IF(push_inst32(compiler, VADD_F64 | DD4(dst_r) | DN4(src1) | DM4(src2))); + break; + case SLJIT_FSUB: + FAIL_IF(push_inst32(compiler, VSUB_F64 | DD4(dst_r) | DN4(src1) | DM4(src2))); + break; + case SLJIT_FMUL: + FAIL_IF(push_inst32(compiler, VMUL_F64 | DD4(dst_r) | DN4(src1) | DM4(src2))); + break; + case SLJIT_FDIV: + FAIL_IF(push_inst32(compiler, VDIV_F64 | DD4(dst_r) | DN4(src1) | DM4(src2))); + break; + } + + if (dst & SLJIT_MEM) + return emit_fop_mem(compiler, STORE, TMP_FREG1, dst, dstw); + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Other instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + int size; + + CHECK_ERROR(); + check_sljit_emit_fast_enter(compiler, dst, dstw, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + size = (3 + saveds) * sizeof(sljit_uw); + local_size += size; + local_size = (local_size + 7) & ~7; + local_size -= size; + compiler->local_size = local_size; + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) + return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG3)); + else if (dst & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_SIZE | STORE, TMP_REG3, dst, dstw)) + return compiler->error; + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG2, TMP_REG3))); + compiler->cache_arg = 0; + compiler->cache_argw = 0; + return getput_arg(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw, 0, 0); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + CHECK_ERROR(); + check_sljit_emit_fast_return(compiler, src, srcw); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG3, src))); + else if (src & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_SIZE, TMP_REG3, src, srcw)) + FAIL_IF(compiler->error); + else { + compiler->cache_arg = 0; + compiler->cache_argw = 0; + FAIL_IF(getput_arg(compiler, WORD_SIZE, TMP_REG2, src, srcw, 0, 0)); + FAIL_IF(push_inst16(compiler, MOV | SET_REGS44(TMP_REG3, TMP_REG2))); + } + } + else if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, TMP_REG3, srcw)); + return push_inst16(compiler, BLX | RN3(TMP_REG3)); +} + +/* --------------------------------------------------------------------- */ +/* Conditional instructions */ +/* --------------------------------------------------------------------- */ + +static sljit_uw get_cc(int type) +{ + switch (type) { + case SLJIT_C_EQUAL: + case SLJIT_C_MUL_NOT_OVERFLOW: + case SLJIT_C_FLOAT_EQUAL: + return 0x0; + + case SLJIT_C_NOT_EQUAL: + case SLJIT_C_MUL_OVERFLOW: + case SLJIT_C_FLOAT_NOT_EQUAL: + return 0x1; + + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + return 0x3; + + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + return 0x2; + + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + return 0x8; + + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + return 0x9; + + case SLJIT_C_SIG_LESS: + return 0xb; + + case SLJIT_C_SIG_GREATER_EQUAL: + return 0xa; + + case SLJIT_C_SIG_GREATER: + return 0xc; + + case SLJIT_C_SIG_LESS_EQUAL: + return 0xd; + + case SLJIT_C_OVERFLOW: + case SLJIT_C_FLOAT_NAN: + return 0x6; + + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_FLOAT_NOT_NAN: + return 0x7; + + default: /* SLJIT_JUMP */ + return 0xe; + } +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + struct sljit_label *label; + + CHECK_ERROR_PTR(); + check_sljit_emit_label(compiler); + + if (compiler->last_label && compiler->last_label->size == compiler->size) + return compiler->last_label; + + label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); + PTR_FAIL_IF(!label); + set_label(label, compiler); + return label; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + struct sljit_jump *jump; + int cc; + + CHECK_ERROR_PTR(); + check_sljit_emit_jump(compiler, type); + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + /* In ARM, we don't need to touch the arguments. */ + PTR_FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0)); + if (type < SLJIT_JUMP) { + jump->flags |= IS_CONDITIONAL; + cc = get_cc(type); + jump->flags |= cc << 8; + PTR_FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); + } + + jump->addr = compiler->size; + if (type <= SLJIT_JUMP) + PTR_FAIL_IF(push_inst16(compiler, BX | RN3(TMP_REG1))); + else { + jump->flags |= IS_BL; + PTR_FAIL_IF(push_inst16(compiler, BLX | RN3(TMP_REG1))); + } + + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + struct sljit_jump *jump; + + CHECK_ERROR(); + check_sljit_emit_ijump(compiler, type, src, srcw); + + /* In ARM, we don't need to touch the arguments. */ + if (src & SLJIT_IMM) { + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + FAIL_IF(!jump); + set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0)); + jump->u.target = srcw; + + FAIL_IF(emit_imm32_const(compiler, TMP_REG1, 0)); + jump->addr = compiler->size; + FAIL_IF(push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(TMP_REG1))); + } + else { + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + return push_inst16(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RN3(src)); + + FAIL_IF(emit_op_mem(compiler, WORD_SIZE, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, src, srcw)); + if (type >= SLJIT_FAST_CALL) + return push_inst16(compiler, BLX | RN3(TMP_REG1)); + } + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + int dst_r; + sljit_uw cc; + + CHECK_ERROR(); + check_sljit_emit_cond_value(compiler, op, dst, dstw, type); + + if (dst == SLJIT_UNUSED) + return SLJIT_SUCCESS; + + cc = get_cc(type); + if (GET_OPCODE(op) == SLJIT_OR && dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | 0x8)); + FAIL_IF(push_inst32(compiler, ORRI | RN4(dst) | RD4(dst) | 0x1)); + if (op & SLJIT_SET_E) { + if (reg_map[dst] <= 7) + return push_inst16(compiler, ORRS | RD3(dst) | RN3(dst)); + return push_inst32(compiler, ORR_W | SET_FLAGS | RD4(TMP_REG1) | RN4(dst) | RM4(dst)); + } + return SLJIT_SUCCESS; + } + + dst_r = TMP_REG2; + if (op == SLJIT_MOV && dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS && reg_map[dst] <= 7) + dst_r = dst; + + FAIL_IF(push_inst16(compiler, IT | (cc << 4) | (((cc & 0x1) ^ 0x1) << 3) | 0x4)); + FAIL_IF(push_inst16(compiler, MOVSI | 0x1 | RDN3(dst_r))); + FAIL_IF(push_inst16(compiler, MOVSI | 0x0 | RDN3(dst_r))); + + if (dst_r == TMP_REG2) { + if (GET_OPCODE(op) == SLJIT_OR) { +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_op2(compiler, op, dst, dstw, dst, dstw, TMP_REG2, 0); + } + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, WORD_SIZE | STORE, TMP_REG2, dst, dstw); + else + return push_inst16(compiler, MOV | SET_REGS44(dst, TMP_REG2)); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w init_value) +{ + struct sljit_const *const_; + int dst_r; + + CHECK_ERROR_PTR(); + check_sljit_emit_const(compiler, dst, dstw, init_value); + + const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); + PTR_FAIL_IF(!const_); + set_const(const_, compiler); + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG1; + PTR_FAIL_IF(emit_imm32_const(compiler, dst_r, init_value)); + + if (dst & SLJIT_MEM) + PTR_FAIL_IF(emit_op_mem(compiler, WORD_SIZE | STORE, dst_r, dst, dstw)); + return const_; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ + inline_set_jump_addr(addr, new_addr, 1); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + sljit_uh* inst = (sljit_uh*)addr; + modify_imm32_const(inst, new_constant); + SLJIT_CACHE_FLUSH(inst, inst + 3); +} diff --git a/src/lib/pcre/sljit/sljitNativeARM_v5.c b/src/lib/pcre/sljit/sljitNativeARM_v5.c new file mode 100644 index 0000000..e3a5873 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeARM_v5.c @@ -0,0 +1,2424 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name() +{ +#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + return "ARMv7" SLJIT_CPUINFO; +#elif (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + return "ARMv5" SLJIT_CPUINFO; +#else +#error "Internal error: Unknown ARM architecture" +#endif +} + +/* Last register + 1. */ +#define TMP_REG1 (SLJIT_NO_REGISTERS + 1) +#define TMP_REG2 (SLJIT_NO_REGISTERS + 2) +#define TMP_REG3 (SLJIT_NO_REGISTERS + 3) +#define TMP_PC (SLJIT_NO_REGISTERS + 4) + +#define TMP_FREG1 (SLJIT_FLOAT_REG4 + 1) +#define TMP_FREG2 (SLJIT_FLOAT_REG4 + 2) + +/* In ARM instruction words. + Cache lines are usually 32 byte aligned. */ +#define CONST_POOL_ALIGNMENT 8 +#define CONST_POOL_EMPTY 0xffffffff + +#define ALIGN_INSTRUCTION(ptr) \ + (sljit_uw*)(((sljit_uw)(ptr) + (CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1) & ~((CONST_POOL_ALIGNMENT * sizeof(sljit_uw)) - 1)) +#define MAX_DIFFERENCE(max_diff) \ + (((max_diff) / (int)sizeof(sljit_uw)) - (CONST_POOL_ALIGNMENT - 1)) + +/* See sljit_emit_enter and sljit_emit_op0 if you want to change them. */ +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 5] = { + 0, 0, 1, 2, 10, 11, 4, 5, 6, 7, 8, 13, 3, 12, 14, 15 +}; + +#define RM(rm) (reg_map[rm]) +#define RD(rd) (reg_map[rd] << 12) +#define RN(rn) (reg_map[rn] << 16) + +/* --------------------------------------------------------------------- */ +/* Instrucion forms */ +/* --------------------------------------------------------------------- */ + +/* The instruction includes the AL condition. + INST_NAME - CONDITIONAL remove this flag. */ +#define COND_MASK 0xf0000000 +#define CONDITIONAL 0xe0000000 +#define PUSH_POOL 0xff000000 + +/* DP - Data Processing instruction (use with EMIT_DATA_PROCESS_INS). */ +#define ADC_DP 0x5 +#define ADD_DP 0x4 +#define AND_DP 0x0 +#define B 0xea000000 +#define BIC_DP 0xe +#define BL 0xeb000000 +#define BLX 0xe12fff30 +#define BX 0xe12fff10 +#define CLZ 0xe16f0f10 +#define CMP_DP 0xa +#define BKPT 0xe1200070 +#define EOR_DP 0x1 +#define MOV_DP 0xd +#define MUL 0xe0000090 +#define MVN_DP 0xf +#define NOP 0xe1a00000 +#define ORR_DP 0xc +#define PUSH 0xe92d0000 +#define POP 0xe8bd0000 +#define RSB_DP 0x3 +#define RSC_DP 0x7 +#define SBC_DP 0x6 +#define SMULL 0xe0c00090 +#define SUB_DP 0x2 +#define UMULL 0xe0800090 +#define VABS_F64 0xeeb00bc0 +#define VADD_F64 0xee300b00 +#define VCMP_F64 0xeeb40b40 +#define VDIV_F64 0xee800b00 +#define VMOV_F64 0xeeb00b40 +#define VMRS 0xeef1fa10 +#define VMUL_F64 0xee200b00 +#define VNEG_F64 0xeeb10b40 +#define VSTR 0xed000b00 +#define VSUB_F64 0xee300b40 + +#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) +/* Arm v7 specific instructions. */ +#define MOVW 0xe3000000 +#define MOVT 0xe3400000 +#define SXTB 0xe6af0070 +#define SXTH 0xe6bf0070 +#define UXTB 0xe6ef0070 +#define UXTH 0xe6ff0070 +#endif + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + +static int push_cpool(struct sljit_compiler *compiler) +{ + /* Pushing the constant pool into the instruction stream. */ + sljit_uw* inst; + sljit_uw* cpool_ptr; + sljit_uw* cpool_end; + int i; + + /* The label could point the address after the constant pool. */ + if (compiler->last_label && compiler->last_label->size == compiler->size) + compiler->last_label->size += compiler->cpool_fill + (CONST_POOL_ALIGNMENT - 1) + 1; + + SLJIT_ASSERT(compiler->cpool_fill > 0 && compiler->cpool_fill <= CPOOL_SIZE); + inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!inst); + compiler->size++; + *inst = 0xff000000 | compiler->cpool_fill; + + for (i = 0; i < CONST_POOL_ALIGNMENT - 1; i++) { + inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!inst); + compiler->size++; + *inst = 0; + } + + cpool_ptr = compiler->cpool; + cpool_end = cpool_ptr + compiler->cpool_fill; + while (cpool_ptr < cpool_end) { + inst = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!inst); + compiler->size++; + *inst = *cpool_ptr++; + } + compiler->cpool_diff = CONST_POOL_EMPTY; + compiler->cpool_fill = 0; + return SLJIT_SUCCESS; +} + +static int push_inst(struct sljit_compiler *compiler, sljit_uw inst) +{ + sljit_uw* ptr; + + if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092))) + FAIL_IF(push_cpool(compiler)); + + ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!ptr); + compiler->size++; + *ptr = inst; + return SLJIT_SUCCESS; +} + +static int push_inst_with_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal) +{ + sljit_uw* ptr; + sljit_uw cpool_index = CPOOL_SIZE; + sljit_uw* cpool_ptr; + sljit_uw* cpool_end; + sljit_ub* cpool_unique_ptr; + + if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092))) + FAIL_IF(push_cpool(compiler)); + else if (compiler->cpool_fill > 0) { + cpool_ptr = compiler->cpool; + cpool_end = cpool_ptr + compiler->cpool_fill; + cpool_unique_ptr = compiler->cpool_unique; + do { + if ((*cpool_ptr == literal) && !(*cpool_unique_ptr)) { + cpool_index = cpool_ptr - compiler->cpool; + break; + } + cpool_ptr++; + cpool_unique_ptr++; + } while (cpool_ptr < cpool_end); + } + + if (cpool_index == CPOOL_SIZE) { + /* Must allocate a new entry in the literal pool. */ + if (compiler->cpool_fill < CPOOL_SIZE) { + cpool_index = compiler->cpool_fill; + compiler->cpool_fill++; + } + else { + FAIL_IF(push_cpool(compiler)); + cpool_index = 0; + compiler->cpool_fill = 1; + } + } + + SLJIT_ASSERT((inst & 0xfff) == 0); + ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!ptr); + compiler->size++; + *ptr = inst | cpool_index; + + compiler->cpool[cpool_index] = literal; + compiler->cpool_unique[cpool_index] = 0; + if (compiler->cpool_diff == CONST_POOL_EMPTY) + compiler->cpool_diff = compiler->size; + return SLJIT_SUCCESS; +} + +static int push_inst_with_unique_literal(struct sljit_compiler *compiler, sljit_uw inst, sljit_uw literal) +{ + sljit_uw* ptr; + if (SLJIT_UNLIKELY((compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4092)) || compiler->cpool_fill >= CPOOL_SIZE)) + FAIL_IF(push_cpool(compiler)); + + SLJIT_ASSERT(compiler->cpool_fill < CPOOL_SIZE && (inst & 0xfff) == 0); + ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!ptr); + compiler->size++; + *ptr = inst | compiler->cpool_fill; + + compiler->cpool[compiler->cpool_fill] = literal; + compiler->cpool_unique[compiler->cpool_fill] = 1; + compiler->cpool_fill++; + if (compiler->cpool_diff == CONST_POOL_EMPTY) + compiler->cpool_diff = compiler->size; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int prepare_blx(struct sljit_compiler *compiler) +{ + /* Place for at least two instruction (doesn't matter whether the first has a literal). */ + if (SLJIT_UNLIKELY(compiler->cpool_diff != CONST_POOL_EMPTY && compiler->size - compiler->cpool_diff >= MAX_DIFFERENCE(4088))) + return push_cpool(compiler); + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_blx(struct sljit_compiler *compiler) +{ + /* Must follow tightly the previous instruction (to be able to convert it to bl instruction). */ + SLJIT_ASSERT(compiler->cpool_diff == CONST_POOL_EMPTY || compiler->size - compiler->cpool_diff < MAX_DIFFERENCE(4092)); + return push_inst(compiler, BLX | RM(TMP_REG1)); +} + +static sljit_uw patch_pc_relative_loads(sljit_uw *last_pc_patch, sljit_uw *code_ptr, sljit_uw* const_pool, sljit_uw cpool_size) +{ + sljit_uw diff; + sljit_uw ind; + sljit_uw counter = 0; + sljit_uw* clear_const_pool = const_pool; + sljit_uw* clear_const_pool_end = const_pool + cpool_size; + + SLJIT_ASSERT(const_pool - code_ptr <= CONST_POOL_ALIGNMENT); + /* Set unused flag for all literals in the constant pool. + I.e.: unused literals can belong to branches, which can be encoded as B or BL. + We can "compress" the constant pool by discarding these literals. */ + while (clear_const_pool < clear_const_pool_end) + *clear_const_pool++ = (sljit_uw)(-1); + + while (last_pc_patch < code_ptr) { + /* Data transfer instruction with Rn == r15. */ + if ((*last_pc_patch & 0x0c0f0000) == 0x040f0000) { + diff = const_pool - last_pc_patch; + ind = (*last_pc_patch) & 0xfff; + + /* Must be a load instruction with immediate offset. */ + SLJIT_ASSERT(ind < cpool_size && !(*last_pc_patch & (1 << 25)) && (*last_pc_patch & (1 << 20))); + if ((int)const_pool[ind] < 0) { + const_pool[ind] = counter; + ind = counter; + counter++; + } + else + ind = const_pool[ind]; + + SLJIT_ASSERT(diff >= 1); + if (diff >= 2 || ind > 0) { + diff = (diff + ind - 2) << 2; + SLJIT_ASSERT(diff <= 0xfff); + *last_pc_patch = (*last_pc_patch & ~0xfff) | diff; + } + else + *last_pc_patch = (*last_pc_patch & ~(0xfff | (1 << 23))) | 0x004; + } + last_pc_patch++; + } + return counter; +} + +/* In some rare ocasions we may need future patches. The probability is close to 0 in practice. */ +struct future_patch { + struct future_patch* next; + int index; + int value; +}; + +static SLJIT_INLINE int resolve_const_pool_index(struct future_patch **first_patch, sljit_uw cpool_current_index, sljit_uw *cpool_start_address, sljit_uw *buf_ptr) +{ + int value; + struct future_patch *curr_patch, *prev_patch; + + /* Using the values generated by patch_pc_relative_loads. */ + if (!*first_patch) + value = (int)cpool_start_address[cpool_current_index]; + else { + curr_patch = *first_patch; + prev_patch = 0; + while (1) { + if (!curr_patch) { + value = (int)cpool_start_address[cpool_current_index]; + break; + } + if ((sljit_uw)curr_patch->index == cpool_current_index) { + value = curr_patch->value; + if (prev_patch) + prev_patch->next = curr_patch->next; + else + *first_patch = curr_patch->next; + SLJIT_FREE(curr_patch); + break; + } + prev_patch = curr_patch; + curr_patch = curr_patch->next; + } + } + + if (value >= 0) { + if ((sljit_uw)value > cpool_current_index) { + curr_patch = (struct future_patch*)SLJIT_MALLOC(sizeof(struct future_patch)); + if (!curr_patch) { + while (*first_patch) { + curr_patch = *first_patch; + *first_patch = (*first_patch)->next; + SLJIT_FREE(curr_patch); + } + return SLJIT_ERR_ALLOC_FAILED; + } + curr_patch->next = *first_patch; + curr_patch->index = value; + curr_patch->value = cpool_start_address[value]; + *first_patch = curr_patch; + } + cpool_start_address[value] = *buf_ptr; + } + return SLJIT_SUCCESS; +} + +#else + +static int push_inst(struct sljit_compiler *compiler, sljit_uw inst) +{ + sljit_uw* ptr; + + ptr = (sljit_uw*)ensure_buf(compiler, sizeof(sljit_uw)); + FAIL_IF(!ptr); + compiler->size++; + *ptr = inst; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_imm(struct sljit_compiler *compiler, int reg, sljit_w imm) +{ + FAIL_IF(push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff))); + return push_inst(compiler, MOVT | RD(reg) | ((imm >> 12) & 0xf0000) | ((imm >> 16) & 0xfff)); +} + +#endif + +static SLJIT_INLINE int detect_jump_type(struct sljit_jump *jump, sljit_uw *code_ptr, sljit_uw *code) +{ + sljit_w diff; + + if (jump->flags & SLJIT_REWRITABLE_JUMP) + return 0; + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (jump->flags & IS_BL) + code_ptr--; + + if (jump->flags & JUMP_ADDR) + diff = ((sljit_w)jump->u.target - (sljit_w)(code_ptr + 2)); + else { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + diff = ((sljit_w)(code + jump->u.label->size) - (sljit_w)(code_ptr + 2)); + } + + /* Branch to Thumb code has not been optimized yet. */ + if (diff & 0x3) + return 0; + + diff >>= 2; + if (jump->flags & IS_BL) { + if (diff <= 0x01ffffff && diff >= -0x02000000) { + *code_ptr = (BL - CONDITIONAL) | (*(code_ptr + 1) & COND_MASK); + jump->flags |= PATCH_B; + return 1; + } + } + else { + if (diff <= 0x01ffffff && diff >= -0x02000000) { + *code_ptr = (B - CONDITIONAL) | (*code_ptr & COND_MASK); + jump->flags |= PATCH_B; + } + } +#else + if (jump->flags & JUMP_ADDR) + diff = ((sljit_w)jump->u.target - (sljit_w)code_ptr); + else { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + diff = ((sljit_w)(code + jump->u.label->size) - (sljit_w)code_ptr); + } + + /* Branch to Thumb code has not been optimized yet. */ + if (diff & 0x3) + return 0; + + diff >>= 2; + if (diff <= 0x01ffffff && diff >= -0x02000000) { + code_ptr -= 2; + *code_ptr = ((jump->flags & IS_BL) ? (BL - CONDITIONAL) : (B - CONDITIONAL)) | (code_ptr[2] & COND_MASK); + jump->flags |= PATCH_B; + return 1; + } +#endif + return 0; +} + +static SLJIT_INLINE void inline_set_jump_addr(sljit_uw addr, sljit_uw new_addr, int flush) +{ +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + sljit_uw *ptr = (sljit_uw*)addr; + sljit_uw *inst = (sljit_uw*)ptr[0]; + sljit_uw mov_pc = ptr[1]; + int bl = (mov_pc & 0x0000f000) != RD(TMP_PC); + sljit_w diff = (sljit_w)(((sljit_w)new_addr - (sljit_w)(inst + 2)) >> 2); + + if (diff <= 0x7fffff && diff >= -0x800000) { + /* Turn to branch. */ + if (!bl) { + inst[0] = (mov_pc & COND_MASK) | (B - CONDITIONAL) | (diff & 0xffffff); + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 1); + } + } else { + inst[0] = (mov_pc & COND_MASK) | (BL - CONDITIONAL) | (diff & 0xffffff); + inst[1] = NOP; + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 2); + } + } + } else { + /* Get the position of the constant. */ + if (mov_pc & (1 << 23)) + ptr = inst + ((mov_pc & 0xfff) >> 2) + 2; + else + ptr = inst + 1; + + if (*inst != mov_pc) { + inst[0] = mov_pc; + if (!bl) { + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 1); + } + } else { + inst[1] = BLX | RM(TMP_REG1); + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 2); + } + } + } + *ptr = new_addr; + } +#else + sljit_uw *inst = (sljit_uw*)addr; + SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT); + inst[0] = MOVW | (inst[0] & 0xf000) | ((new_addr << 4) & 0xf0000) | (new_addr & 0xfff); + inst[1] = MOVT | (inst[1] & 0xf000) | ((new_addr >> 12) & 0xf0000) | ((new_addr >> 16) & 0xfff); + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 2); + } +#endif +} + +static sljit_uw get_immediate(sljit_uw imm); + +static SLJIT_INLINE void inline_set_const(sljit_uw addr, sljit_w new_constant, int flush) +{ +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + sljit_uw *ptr = (sljit_uw*)addr; + sljit_uw *inst = (sljit_uw*)ptr[0]; + sljit_uw ldr_literal = ptr[1]; + sljit_uw src2; + + src2 = get_immediate(new_constant); + if (src2) { + *inst = 0xe3a00000 | (ldr_literal & 0xf000) | src2; + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 1); + } + return; + } + + src2 = get_immediate(~new_constant); + if (src2) { + *inst = 0xe3e00000 | (ldr_literal & 0xf000) | src2; + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 1); + } + return; + } + + if (ldr_literal & (1 << 23)) + ptr = inst + ((ldr_literal & 0xfff) >> 2) + 2; + else + ptr = inst + 1; + + if (*inst != ldr_literal) { + *inst = ldr_literal; + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 1); + } + } + *ptr = new_constant; +#else + sljit_uw *inst = (sljit_uw*)addr; + SLJIT_ASSERT((inst[0] & 0xfff00000) == MOVW && (inst[1] & 0xfff00000) == MOVT); + inst[0] = MOVW | (inst[0] & 0xf000) | ((new_constant << 4) & 0xf0000) | (new_constant & 0xfff); + inst[1] = MOVT | (inst[1] & 0xf000) | ((new_constant >> 12) & 0xf0000) | ((new_constant >> 16) & 0xfff); + if (flush) { + SLJIT_CACHE_FLUSH(inst, inst + 2); + } +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + sljit_uw *code; + sljit_uw *code_ptr; + sljit_uw *buf_ptr; + sljit_uw *buf_end; + sljit_uw size; + sljit_uw word_count; +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + sljit_uw cpool_size; + sljit_uw cpool_skip_alignment; + sljit_uw cpool_current_index; + sljit_uw *cpool_start_address; + sljit_uw *last_pc_patch; + struct future_patch *first_patch; +#endif + + struct sljit_label *label; + struct sljit_jump *jump; + struct sljit_const *const_; + + CHECK_ERROR_PTR(); + check_sljit_generate_code(compiler); + reverse_buf(compiler); + + /* Second code generation pass. */ +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + size = compiler->size + (compiler->patches << 1); + if (compiler->cpool_fill > 0) + size += compiler->cpool_fill + CONST_POOL_ALIGNMENT - 1; +#else + size = compiler->size; +#endif + code = (sljit_uw*)SLJIT_MALLOC_EXEC(size * sizeof(sljit_uw)); + PTR_FAIL_WITH_EXEC_IF(code); + buf = compiler->buf; + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + cpool_size = 0; + cpool_skip_alignment = 0; + cpool_current_index = 0; + cpool_start_address = NULL; + first_patch = NULL; + last_pc_patch = code; +#endif + + code_ptr = code; + word_count = 0; + + label = compiler->labels; + jump = compiler->jumps; + const_ = compiler->consts; + + if (label && label->size == 0) { + label->addr = (sljit_uw)code; + label->size = 0; + label = label->next; + } + + do { + buf_ptr = (sljit_uw*)buf->memory; + buf_end = buf_ptr + (buf->used_size >> 2); + do { + word_count++; +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (cpool_size > 0) { + if (cpool_skip_alignment > 0) { + buf_ptr++; + cpool_skip_alignment--; + } + else { + if (SLJIT_UNLIKELY(resolve_const_pool_index(&first_patch, cpool_current_index, cpool_start_address, buf_ptr))) { + SLJIT_FREE_EXEC(code); + compiler->error = SLJIT_ERR_ALLOC_FAILED; + return NULL; + } + buf_ptr++; + if (++cpool_current_index >= cpool_size) { + SLJIT_ASSERT(!first_patch); + cpool_size = 0; + if (label && label->size == word_count) { + /* Points after the current instruction. */ + label->addr = (sljit_uw)code_ptr; + label->size = code_ptr - code; + label = label->next; + } + } + } + } + else if ((*buf_ptr & 0xff000000) != PUSH_POOL) { +#endif + *code_ptr = *buf_ptr++; + /* These structures are ordered by their address. */ + SLJIT_ASSERT(!label || label->size >= word_count); + SLJIT_ASSERT(!jump || jump->addr >= word_count); + SLJIT_ASSERT(!const_ || const_->addr >= word_count); + if (jump && jump->addr == word_count) { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (detect_jump_type(jump, code_ptr, code)) + code_ptr--; + jump->addr = (sljit_uw)code_ptr; +#else + jump->addr = (sljit_uw)(code_ptr - 2); + if (detect_jump_type(jump, code_ptr, code)) + code_ptr -= 2; +#endif + jump = jump->next; + } + if (label && label->size == word_count) { + /* code_ptr can be affected above. */ + label->addr = (sljit_uw)(code_ptr + 1); + label->size = (code_ptr + 1) - code; + label = label->next; + } + if (const_ && const_->addr == word_count) { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + const_->addr = (sljit_uw)code_ptr; +#else + const_->addr = (sljit_uw)(code_ptr - 1); +#endif + const_ = const_->next; + } + code_ptr++; +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + } + else { + /* Fortunately, no need to shift. */ + cpool_size = *buf_ptr++ & ~PUSH_POOL; + SLJIT_ASSERT(cpool_size > 0); + cpool_start_address = ALIGN_INSTRUCTION(code_ptr + 1); + cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, cpool_size); + if (cpool_current_index > 0) { + /* Unconditional branch. */ + *code_ptr = B | (((cpool_start_address - code_ptr) + cpool_current_index - 2) & ~PUSH_POOL); + code_ptr = cpool_start_address + cpool_current_index; + } + cpool_skip_alignment = CONST_POOL_ALIGNMENT - 1; + cpool_current_index = 0; + last_pc_patch = code_ptr; + } +#endif + } while (buf_ptr < buf_end); + buf = buf->next; + } while (buf); + + SLJIT_ASSERT(!label); + SLJIT_ASSERT(!jump); + SLJIT_ASSERT(!const_); + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + SLJIT_ASSERT(cpool_size == 0); + if (compiler->cpool_fill > 0) { + cpool_start_address = ALIGN_INSTRUCTION(code_ptr); + cpool_current_index = patch_pc_relative_loads(last_pc_patch, code_ptr, cpool_start_address, compiler->cpool_fill); + if (cpool_current_index > 0) + code_ptr = cpool_start_address + cpool_current_index; + + buf_ptr = compiler->cpool; + buf_end = buf_ptr + compiler->cpool_fill; + cpool_current_index = 0; + while (buf_ptr < buf_end) { + if (SLJIT_UNLIKELY(resolve_const_pool_index(&first_patch, cpool_current_index, cpool_start_address, buf_ptr))) { + SLJIT_FREE_EXEC(code); + compiler->error = SLJIT_ERR_ALLOC_FAILED; + return NULL; + } + buf_ptr++; + cpool_current_index++; + } + SLJIT_ASSERT(!first_patch); + } +#endif + + jump = compiler->jumps; + while (jump) { + buf_ptr = (sljit_uw*)jump->addr; + + if (jump->flags & PATCH_B) { + if (!(jump->flags & JUMP_ADDR)) { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + SLJIT_ASSERT(((sljit_w)jump->u.label->addr - (sljit_w)(buf_ptr + 2)) <= 0x01ffffff && ((sljit_w)jump->u.label->addr - (sljit_w)(buf_ptr + 2)) >= -0x02000000); + *buf_ptr |= (((sljit_w)jump->u.label->addr - (sljit_w)(buf_ptr + 2)) >> 2) & 0x00ffffff; + } + else { + SLJIT_ASSERT(((sljit_w)jump->u.target - (sljit_w)(buf_ptr + 2)) <= 0x01ffffff && ((sljit_w)jump->u.target - (sljit_w)(buf_ptr + 2)) >= -0x02000000); + *buf_ptr |= (((sljit_w)jump->u.target - (sljit_w)(buf_ptr + 2)) >> 2) & 0x00ffffff; + } + } + else if (jump->flags & SLJIT_REWRITABLE_JUMP) { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + jump->addr = (sljit_uw)code_ptr; + code_ptr[0] = (sljit_uw)buf_ptr; + code_ptr[1] = *buf_ptr; + inline_set_jump_addr((sljit_uw)code_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); + code_ptr += 2; +#else + inline_set_jump_addr((sljit_uw)buf_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); +#endif + } + else { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (jump->flags & IS_BL) + buf_ptr--; + if (*buf_ptr & (1 << 23)) + buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2; + else + buf_ptr += 1; + *buf_ptr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; +#else + inline_set_jump_addr((sljit_uw)buf_ptr, (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target, 0); +#endif + } + jump = jump->next; + } + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + const_ = compiler->consts; + while (const_) { + buf_ptr = (sljit_uw*)const_->addr; + const_->addr = (sljit_uw)code_ptr; + + code_ptr[0] = (sljit_uw)buf_ptr; + code_ptr[1] = *buf_ptr; + if (*buf_ptr & (1 << 23)) + buf_ptr += ((*buf_ptr & 0xfff) >> 2) + 2; + else + buf_ptr += 1; + /* Set the value again (can be a simple constant). */ + inline_set_const((sljit_uw)code_ptr, *buf_ptr, 0); + code_ptr += 2; + + const_ = const_->next; + } +#endif + + SLJIT_ASSERT(code_ptr - code <= (int)size); + + SLJIT_CACHE_FLUSH(code, code_ptr); + compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_size = size * sizeof(sljit_uw); + return code; +} + +/* emit_op inp_flags. + WRITE_BACK must be the first, since it is a flag. */ +#define WRITE_BACK 0x01 +#define ALLOW_IMM 0x02 +#define ALLOW_INV_IMM 0x04 +#define ALLOW_ANY_IMM (ALLOW_IMM | ALLOW_INV_IMM) +#define ARG_TEST 0x08 + +/* Creates an index in data_transfer_insts array. */ +#define WORD_DATA 0x00 +#define BYTE_DATA 0x10 +#define HALF_DATA 0x20 +#define SIGNED_DATA 0x40 +#define LOAD_DATA 0x80 + +#define EMIT_INSTRUCTION(inst) \ + FAIL_IF(push_inst(compiler, (inst))) + +/* Condition: AL. */ +#define EMIT_DATA_PROCESS_INS(opcode, set_flags, dst, src1, src2) \ + (0xe0000000 | ((opcode) << 21) | (set_flags) | RD(dst) | RN(src1) | (src2)) + +static int emit_op(struct sljit_compiler *compiler, int op, int inp_flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w); + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int size; + sljit_uw push; + + CHECK_ERROR(); + check_sljit_emit_enter(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + /* Push saved registers, temporary registers + stmdb sp!, {..., lr} */ + push = PUSH | (1 << 14); + if (temporaries >= 5) + push |= 1 << 11; + if (temporaries >= 4) + push |= 1 << 10; + if (saveds >= 5) + push |= 1 << 8; + if (saveds >= 4) + push |= 1 << 7; + if (saveds >= 3) + push |= 1 << 6; + if (saveds >= 2) + push |= 1 << 5; + if (saveds >= 1) + push |= 1 << 4; + EMIT_INSTRUCTION(push); + + /* Stack must be aligned to 8 bytes: */ + size = (1 + saveds) * sizeof(sljit_uw); + if (temporaries >= 4) + size += (temporaries - 3) * sizeof(sljit_uw); + local_size += size; + local_size = (local_size + 7) & ~7; + local_size -= size; + compiler->local_size = local_size; + if (local_size > 0) + FAIL_IF(emit_op(compiler, SLJIT_SUB, ALLOW_IMM, SLJIT_LOCALS_REG, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, local_size)); + + if (args >= 1) + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_SAVED_REG1, SLJIT_UNUSED, RM(SLJIT_TEMPORARY_REG1))); + if (args >= 2) + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_SAVED_REG2, SLJIT_UNUSED, RM(SLJIT_TEMPORARY_REG2))); + if (args >= 3) + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, SLJIT_SAVED_REG3, SLJIT_UNUSED, RM(SLJIT_TEMPORARY_REG3))); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int size; + + CHECK_ERROR_VOID(); + check_sljit_set_context(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + size = (1 + saveds) * sizeof(sljit_uw); + if (temporaries >= 4) + size += (temporaries - 3) * sizeof(sljit_uw); + local_size += size; + local_size = (local_size + 7) & ~7; + local_size -= size; + compiler->local_size = local_size; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + sljit_uw pop; + + CHECK_ERROR(); + check_sljit_emit_return(compiler, op, src, srcw); + + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + if (compiler->local_size > 0) + FAIL_IF(emit_op(compiler, SLJIT_ADD, ALLOW_IMM, SLJIT_LOCALS_REG, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, compiler->local_size)); + + pop = POP | (1 << 15); + /* Push saved registers, temporary registers + ldmia sp!, {..., pc} */ + if (compiler->temporaries >= 5) + pop |= 1 << 11; + if (compiler->temporaries >= 4) + pop |= 1 << 10; + if (compiler->saveds >= 5) + pop |= 1 << 8; + if (compiler->saveds >= 4) + pop |= 1 << 7; + if (compiler->saveds >= 3) + pop |= 1 << 6; + if (compiler->saveds >= 2) + pop |= 1 << 5; + if (compiler->saveds >= 1) + pop |= 1 << 4; + + return push_inst(compiler, pop); +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +/* s/l - store/load (1 bit) + u/s - signed/unsigned (1 bit) + w/b/h/N - word/byte/half/NOT allowed (2 bit) + It contans 16 items, but not all are different. */ + +static sljit_w data_transfer_insts[16] = { +/* s u w */ 0xe5000000 /* str */, +/* s u b */ 0xe5400000 /* strb */, +/* s u h */ 0xe10000b0 /* strh */, +/* s u N */ 0x00000000 /* not allowed */, +/* s s w */ 0xe5000000 /* str */, +/* s s b */ 0xe5400000 /* strb */, +/* s s h */ 0xe10000b0 /* strh */, +/* s s N */ 0x00000000 /* not allowed */, + +/* l u w */ 0xe5100000 /* ldr */, +/* l u b */ 0xe5500000 /* ldrb */, +/* l u h */ 0xe11000b0 /* ldrh */, +/* l u N */ 0x00000000 /* not allowed */, +/* l s w */ 0xe5100000 /* ldr */, +/* l s b */ 0xe11000d0 /* ldrsb */, +/* l s h */ 0xe11000f0 /* ldrsh */, +/* l s N */ 0x00000000 /* not allowed */, +}; + +#define EMIT_DATA_TRANSFER(type, add, wb, target, base1, base2) \ + (data_transfer_insts[(type) >> 4] | ((add) << 23) | ((wb) << 21) | (reg_map[target] << 12) | (reg_map[base1] << 16) | (base2)) +/* Normal ldr/str instruction. + Type2: ldrsb, ldrh, ldrsh */ +#define IS_TYPE1_TRANSFER(type) \ + (data_transfer_insts[(type) >> 4] & 0x04000000) +#define TYPE2_TRANSFER_IMM(imm) \ + (((imm) & 0xf) | (((imm) & 0xf0) << 4) | (1 << 22)) + +/* flags: */ + /* Arguments are swapped. */ +#define ARGS_SWAPPED 0x01 + /* Inverted immediate. */ +#define INV_IMM 0x02 + /* Source and destination is register. */ +#define REG_DEST 0x04 +#define REG_SOURCE 0x08 + /* One instruction is enough. */ +#define FAST_DEST 0x10 + /* Multiple instructions are required. */ +#define SLOW_DEST 0x20 +/* SET_FLAGS must be (1 << 20) as it is also the value of S bit (can be used for optimization). */ +#define SET_FLAGS (1 << 20) +/* dst: reg + src1: reg + src2: reg or imm (if allowed) + SRC2_IMM must be (1 << 25) as it is also the value of I bit (can be used for optimization). */ +#define SRC2_IMM (1 << 25) + +#define EMIT_DATA_PROCESS_INS_AND_RETURN(opcode) \ + return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, (src2 & SRC2_IMM) ? src2 : RM(src2))) + +#define EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(opcode, dst, src1, src2) \ + return push_inst(compiler, EMIT_DATA_PROCESS_INS(opcode, flags & SET_FLAGS, dst, src1, src2)) + +#define EMIT_SHIFT_INS_AND_RETURN(opcode) \ + SLJIT_ASSERT(!(flags & INV_IMM) && !(src2 & SRC2_IMM)); \ + if (compiler->shift_imm != 0x20) { \ + SLJIT_ASSERT(src1 == TMP_REG1); \ + SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); \ + if (compiler->shift_imm != 0) \ + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (compiler->shift_imm << 7) | (opcode << 5) | reg_map[src2])); \ + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, reg_map[src2])); \ + } \ + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, flags & SET_FLAGS, dst, SLJIT_UNUSED, (reg_map[(flags & ARGS_SWAPPED) ? src1 : src2] << 8) | (opcode << 5) | 0x10 | ((flags & ARGS_SWAPPED) ? reg_map[src2] : reg_map[src1]))); + +static SLJIT_INLINE int emit_single_op(struct sljit_compiler *compiler, int op, int flags, + int dst, int src1, int src2) +{ + sljit_w mul_inst; + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + SLJIT_ASSERT(!(flags & INV_IMM)); + EMIT_DATA_PROCESS_INS_AND_RETURN(ADD_DP); + + case SLJIT_ADDC: + SLJIT_ASSERT(!(flags & INV_IMM)); + EMIT_DATA_PROCESS_INS_AND_RETURN(ADC_DP); + + case SLJIT_SUB: + SLJIT_ASSERT(!(flags & INV_IMM)); + if (!(flags & ARGS_SWAPPED)) + EMIT_DATA_PROCESS_INS_AND_RETURN(SUB_DP); + EMIT_DATA_PROCESS_INS_AND_RETURN(RSB_DP); + + case SLJIT_SUBC: + SLJIT_ASSERT(!(flags & INV_IMM)); + if (!(flags & ARGS_SWAPPED)) + EMIT_DATA_PROCESS_INS_AND_RETURN(SBC_DP); + EMIT_DATA_PROCESS_INS_AND_RETURN(RSC_DP); + + case SLJIT_MUL: + SLJIT_ASSERT(!(flags & INV_IMM)); + SLJIT_ASSERT(!(src2 & SRC2_IMM)); + if (SLJIT_UNLIKELY(op & SLJIT_SET_O)) + mul_inst = SMULL | (reg_map[TMP_REG3] << 16) | (reg_map[dst] << 12); + else + mul_inst = MUL | (reg_map[dst] << 16); + + if (dst != src2) + FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src1] << 8) | reg_map[src2])); + else if (dst != src1) + FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[src1])); + else { + /* Rm and Rd must not be the same register. */ + SLJIT_ASSERT(dst != TMP_REG1); + FAIL_IF(push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, reg_map[src2]))); + FAIL_IF(push_inst(compiler, mul_inst | (reg_map[src2] << 8) | reg_map[TMP_REG1])); + } + + if (!(op & SLJIT_SET_O)) + return SLJIT_SUCCESS; + + /* We need to use TMP_REG3. */ + compiler->cache_arg = 0; + compiler->cache_argw = 0; + /* cmp TMP_REG2, dst asr #31. */ + return push_inst(compiler, EMIT_DATA_PROCESS_INS(CMP_DP, SET_FLAGS, SLJIT_UNUSED, TMP_REG3, RM(dst) | 0xfc0)); + + case SLJIT_AND: + if (!(flags & INV_IMM)) + EMIT_DATA_PROCESS_INS_AND_RETURN(AND_DP); + EMIT_DATA_PROCESS_INS_AND_RETURN(BIC_DP); + + case SLJIT_OR: + SLJIT_ASSERT(!(flags & INV_IMM)); + EMIT_DATA_PROCESS_INS_AND_RETURN(ORR_DP); + + case SLJIT_XOR: + SLJIT_ASSERT(!(flags & INV_IMM)); + EMIT_DATA_PROCESS_INS_AND_RETURN(EOR_DP); + + case SLJIT_SHL: + EMIT_SHIFT_INS_AND_RETURN(0); + + case SLJIT_LSHR: + EMIT_SHIFT_INS_AND_RETURN(1); + + case SLJIT_ASHR: + EMIT_SHIFT_INS_AND_RETURN(2); + + case SLJIT_MOV: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); + if (dst != src2) { + if (src2 & SRC2_IMM) { + if (flags & INV_IMM) + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + } + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, reg_map[src2]); + } + return SLJIT_SUCCESS; + + case SLJIT_MOV_UB: + case SLJIT_MOV_SB: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); + if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (op == SLJIT_MOV_UB) + return push_inst(compiler, EMIT_DATA_PROCESS_INS(AND_DP, 0, dst, src2, SRC2_IMM | 0xff)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | reg_map[src2])); + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (24 << 7) | (op == SLJIT_MOV_UB ? 0x20 : 0x40) | reg_map[dst])); +#else + return push_inst(compiler, (op == SLJIT_MOV_UB ? UXTB : SXTB) | RD(dst) | RM(src2)); +#endif + } + else if (dst != src2) { + SLJIT_ASSERT(src2 & SRC2_IMM); + if (flags & INV_IMM) + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + } + return SLJIT_SUCCESS; + + case SLJIT_MOV_UH: + case SLJIT_MOV_SH: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & ARGS_SWAPPED)); + if ((flags & (REG_DEST | REG_SOURCE)) == (REG_DEST | REG_SOURCE)) { +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | reg_map[src2])); + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, (16 << 7) | (op == SLJIT_MOV_UH ? 0x20 : 0x40) | reg_map[dst])); +#else + return push_inst(compiler, (op == SLJIT_MOV_UH ? UXTH : SXTH) | RD(dst) | RM(src2)); +#endif + } + else if (dst != src2) { + SLJIT_ASSERT(src2 & SRC2_IMM); + if (flags & INV_IMM) + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + } + return SLJIT_SUCCESS; + + case SLJIT_NOT: + if (src2 & SRC2_IMM) { + if (flags & INV_IMM) + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MOV_DP, dst, SLJIT_UNUSED, src2); + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, src2); + } + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(MVN_DP, dst, SLJIT_UNUSED, RM(src2)); + + case SLJIT_CLZ: + SLJIT_ASSERT(!(flags & INV_IMM)); + SLJIT_ASSERT(!(src2 & SRC2_IMM)); + FAIL_IF(push_inst(compiler, CLZ | RD(dst) | RM(src2))); + if (flags & SET_FLAGS) + EMIT_FULL_DATA_PROCESS_INS_AND_RETURN(CMP_DP, SLJIT_UNUSED, dst, SRC2_IMM); + return SLJIT_SUCCESS; + } + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; +} + +#undef EMIT_DATA_PROCESS_INS_AND_RETURN +#undef EMIT_FULL_DATA_PROCESS_INS_AND_RETURN +#undef EMIT_SHIFT_INS_AND_RETURN + +/* Tests whether the immediate can be stored in the 12 bit imm field. + Returns with 0 if not possible. */ +static sljit_uw get_immediate(sljit_uw imm) +{ + int rol; + + if (imm <= 0xff) + return SRC2_IMM | imm; + + if (!(imm & 0xff000000)) { + imm <<= 8; + rol = 8; + } + else { + imm = (imm << 24) | (imm >> 8); + rol = 0; + } + + if (!(imm & 0xff000000)) { + imm <<= 8; + rol += 4; + } + + if (!(imm & 0xf0000000)) { + imm <<= 4; + rol += 2; + } + + if (!(imm & 0xc0000000)) { + imm <<= 2; + rol += 1; + } + + if (!(imm & 0x00ffffff)) + return SRC2_IMM | (imm >> 24) | (rol << 8); + else + return 0; +} + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) +static int generate_int(struct sljit_compiler *compiler, int reg, sljit_uw imm, int positive) +{ + sljit_uw mask; + sljit_uw imm1; + sljit_uw imm2; + int rol; + + /* Step1: Search a zero byte (8 continous zero bit). */ + mask = 0xff000000; + rol = 8; + while(1) { + if (!(imm & mask)) { + /* Rol imm by rol. */ + imm = (imm << rol) | (imm >> (32 - rol)); + /* Calculate arm rol. */ + rol = 4 + (rol >> 1); + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) { + /* rol by 8. */ + imm = (imm << 8) | (imm >> 24); + mask = 0xff00; + rol = 24; + while (1) { + if (!(imm & mask)) { + /* Rol imm by rol. */ + imm = (imm << rol) | (imm >> (32 - rol)); + /* Calculate arm rol. */ + rol = (rol >> 1) - 8; + break; + } + rol += 2; + mask >>= 2; + if (mask & 0x3) + return 0; + } + break; + } + } + + /* The low 8 bit must be zero. */ + SLJIT_ASSERT(!(imm & 0xff)); + + if (!(imm & 0xff000000)) { + imm1 = SRC2_IMM | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8); + imm2 = SRC2_IMM | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8); + } + else if (imm & 0xc0000000) { + imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if (!(imm & 0xff000000)) { + imm <<= 8; + rol += 4; + } + + if (!(imm & 0xf0000000)) { + imm <<= 4; + rol += 2; + } + + if (!(imm & 0xc0000000)) { + imm <<= 2; + rol += 1; + } + + if (!(imm & 0x00ffffff)) + imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } + else { + if (!(imm & 0xf0000000)) { + imm <<= 4; + rol += 2; + } + + if (!(imm & 0xc0000000)) { + imm <<= 2; + rol += 1; + } + + imm1 = SRC2_IMM | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8); + imm <<= 8; + rol += 4; + + if (!(imm & 0xf0000000)) { + imm <<= 4; + rol += 2; + } + + if (!(imm & 0xc0000000)) { + imm <<= 2; + rol += 1; + } + + if (!(imm & 0x00ffffff)) + imm2 = SRC2_IMM | (imm >> 24) | ((rol & 0xf) << 8); + else + return 0; + } + + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(positive ? MOV_DP : MVN_DP, 0, reg, SLJIT_UNUSED, imm1)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(positive ? ORR_DP : BIC_DP, 0, reg, reg, imm2)); + return 1; +} +#endif + +static int load_immediate(struct sljit_compiler *compiler, int reg, sljit_uw imm) +{ + sljit_uw tmp; + +#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + if (!(imm & ~0xffff)) + return push_inst(compiler, MOVW | RD(reg) | ((imm << 4) & 0xf0000) | (imm & 0xfff)); +#endif + + /* Create imm by 1 inst. */ + tmp = get_immediate(imm); + if (tmp) { + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, tmp)); + return SLJIT_SUCCESS; + } + + tmp = get_immediate(~imm); + if (tmp) { + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, tmp)); + return SLJIT_SUCCESS; + } + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + /* Create imm by 2 inst. */ + FAIL_IF(generate_int(compiler, reg, imm, 1)); + FAIL_IF(generate_int(compiler, reg, ~imm, 0)); + + /* Load integer. */ + return push_inst_with_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), imm); +#else + return emit_imm(compiler, reg, imm); +#endif +} + +/* Can perform an operation using at most 1 instruction. */ +static int getput_arg_fast(struct sljit_compiler *compiler, int inp_flags, int reg, int arg, sljit_w argw) +{ + sljit_uw imm; + + if (arg & SLJIT_IMM) { + imm = get_immediate(argw); + if (imm) { + if (inp_flags & ARG_TEST) + return 1; + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, imm)); + return -1; + } + imm = get_immediate(~argw); + if (imm) { + if (inp_flags & ARG_TEST) + return 1; + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MVN_DP, 0, reg, SLJIT_UNUSED, imm)); + return -1; + } + return (inp_flags & ARG_TEST) ? SLJIT_SUCCESS : 0; + } + + SLJIT_ASSERT(arg & SLJIT_MEM); + + /* Fast loads/stores. */ + if (arg & 0xf) { + if (!(arg & 0xf0)) { + if (IS_TYPE1_TRANSFER(inp_flags)) { + if (argw >= 0 && argw <= 0xfff) { + if (inp_flags & ARG_TEST) + return 1; + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & 0xf, argw)); + return -1; + } + if (argw < 0 && argw >= -0xfff) { + if (inp_flags & ARG_TEST) + return 1; + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & 0xf, -argw)); + return -1; + } + } + else { + if (argw >= 0 && argw <= 0xff) { + if (inp_flags & ARG_TEST) + return 1; + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & 0xf, TYPE2_TRANSFER_IMM(argw))); + return -1; + } + if (argw < 0 && argw >= -0xff) { + if (inp_flags & ARG_TEST) + return 1; + argw = -argw; + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 0, inp_flags & WRITE_BACK, reg, arg & 0xf, TYPE2_TRANSFER_IMM(argw))); + return -1; + } + } + } + else if ((argw & 0x3) == 0 || IS_TYPE1_TRANSFER(inp_flags)) { + if (inp_flags & ARG_TEST) + return 1; + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & 0xf, + RM((arg >> 4) & 0xf) | (IS_TYPE1_TRANSFER(inp_flags) ? SRC2_IMM : 0) | ((argw & 0x3) << 7))); + return -1; + } + } + + return (inp_flags & ARG_TEST) ? SLJIT_SUCCESS : 0; +} + +/* See getput_arg below. + Note: can_cache is called only for binary operators. Those + operators always uses word arguments without write back. */ +static int can_cache(int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + /* Immediate caching is not supported as it would be an operation on constant arguments. */ + if (arg & SLJIT_IMM) + return 0; + + /* Always a simple operation. */ + if (arg & 0xf0) + return 0; + + if (!(arg & 0xf)) { + /* Immediate access. */ + if ((next_arg & SLJIT_MEM) && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff)) + return 1; + return 0; + } + + if (argw <= 0xfffff && argw >= -0xfffff) + return 0; + + if (argw == next_argw && (next_arg & SLJIT_MEM)) + return 1; + + if (arg == next_arg && ((sljit_uw)argw - (sljit_uw)next_argw <= 0xfff || (sljit_uw)next_argw - (sljit_uw)argw <= 0xfff)) + return 1; + + return 0; +} + +#define GETPUT_ARG_DATA_TRANSFER(add, wb, target, base, imm) \ + if (max_delta & 0xf00) \ + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, imm))); \ + else \ + FAIL_IF(push_inst(compiler, EMIT_DATA_TRANSFER(inp_flags, add, wb, target, base, TYPE2_TRANSFER_IMM(imm)))); + +#define TEST_WRITE_BACK() \ + if (inp_flags & WRITE_BACK) { \ + tmp_r = arg & 0xf; \ + if (reg == tmp_r) { \ + /* This can only happen for stores */ \ + /* since ldr reg, [reg, ...]! has no meaning */ \ + SLJIT_ASSERT(!(inp_flags & LOAD_DATA)); \ + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(reg))); \ + reg = TMP_REG3; \ + } \ + } + +/* Emit the necessary instructions. See can_cache above. */ +static int getput_arg(struct sljit_compiler *compiler, int inp_flags, int reg, int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + int tmp_r; + sljit_w max_delta; + sljit_w sign; + + if (arg & SLJIT_IMM) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + return load_immediate(compiler, reg, argw); + } + + SLJIT_ASSERT(arg & SLJIT_MEM); + + tmp_r = (inp_flags & LOAD_DATA) ? reg : TMP_REG3; + max_delta = IS_TYPE1_TRANSFER(inp_flags) ? 0xfff : 0xff; + + if ((arg & 0xf) == SLJIT_UNUSED) { + /* Write back is not used. */ + if ((compiler->cache_arg & SLJIT_IMM) && (((sljit_uw)argw - (sljit_uw)compiler->cache_argw) <= (sljit_uw)max_delta || ((sljit_uw)compiler->cache_argw - (sljit_uw)argw) <= (sljit_uw)max_delta)) { + if (((sljit_uw)argw - (sljit_uw)compiler->cache_argw) <= (sljit_uw)max_delta) { + sign = 1; + argw = argw - compiler->cache_argw; + } + else { + sign = 0; + argw = compiler->cache_argw - argw; + } + + if (max_delta & 0xf00) { + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, sign, 0, reg, TMP_REG3, argw)); + } + else { + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, sign, 0, reg, TMP_REG3, TYPE2_TRANSFER_IMM(argw))); + } + return SLJIT_SUCCESS; + } + + /* With write back, we can create some sophisticated loads, but + it is hard to decide whether we should convert downward (0s) or upward (1s). */ + if ((next_arg & SLJIT_MEM) && ((sljit_uw)argw - (sljit_uw)next_argw <= (sljit_uw)max_delta || (sljit_uw)next_argw - (sljit_uw)argw <= (sljit_uw)max_delta)) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + + compiler->cache_arg = SLJIT_IMM; + compiler->cache_argw = argw; + tmp_r = TMP_REG3; + } + + FAIL_IF(load_immediate(compiler, tmp_r, argw)); + GETPUT_ARG_DATA_TRANSFER(1, 0, reg, tmp_r, 0); + return SLJIT_SUCCESS; + } + + /* Extended imm addressing for [reg+imm] format. */ + sign = (max_delta << 8) | 0xff; + if (!(arg & 0xf0) && argw <= sign && argw >= -sign) { + TEST_WRITE_BACK(); + if (argw >= 0) { + sign = 1; + } + else { + sign = 0; + argw = -argw; + } + + /* Optimization: add is 0x4, sub is 0x2. Sign is 1 for add and 0 for sub. */ + if (max_delta & 0xf00) + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(SUB_DP << sign, 0, tmp_r, arg & 0xf, SRC2_IMM | (argw >> 12) | 0xa00)); + else + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(SUB_DP << sign, 0, tmp_r, arg & 0xf, SRC2_IMM | (argw >> 8) | 0xc00)); + + argw &= max_delta; + GETPUT_ARG_DATA_TRANSFER(sign, inp_flags & WRITE_BACK, reg, tmp_r, argw); + return SLJIT_SUCCESS; + } + + if (arg & 0xf0) { + SLJIT_ASSERT((argw & 0x3) && !(max_delta & 0xf00)); + if (inp_flags & WRITE_BACK) + tmp_r = arg & 0xf; + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(ADD_DP, 0, tmp_r, arg & 0xf, RM((arg >> 4) & 0xf) | ((argw & 0x3) << 7))); + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, 0, reg, tmp_r, TYPE2_TRANSFER_IMM(0))); + return SLJIT_SUCCESS; + } + + if (compiler->cache_arg == arg && ((sljit_uw)argw - (sljit_uw)compiler->cache_argw) <= (sljit_uw)max_delta) { + SLJIT_ASSERT(!(inp_flags & WRITE_BACK)); + argw = argw - compiler->cache_argw; + GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, argw); + return SLJIT_SUCCESS; + } + + if (compiler->cache_arg == arg && ((sljit_uw)compiler->cache_argw - (sljit_uw)argw) <= (sljit_uw)max_delta) { + SLJIT_ASSERT(!(inp_flags & WRITE_BACK)); + argw = compiler->cache_argw - argw; + GETPUT_ARG_DATA_TRANSFER(0, 0, reg, TMP_REG3, argw); + return SLJIT_SUCCESS; + } + + if ((compiler->cache_arg & SLJIT_IMM) && compiler->cache_argw == argw) { + TEST_WRITE_BACK(); + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & 0xf, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0))); + return SLJIT_SUCCESS; + } + + if (argw == next_argw && (next_arg & SLJIT_MEM)) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + + compiler->cache_arg = SLJIT_IMM; + compiler->cache_argw = argw; + + TEST_WRITE_BACK(); + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & 0xf, RM(TMP_REG3) | (max_delta & 0xf00 ? SRC2_IMM : 0))); + return SLJIT_SUCCESS; + } + + if (arg == next_arg && !(inp_flags & WRITE_BACK) && ((sljit_uw)argw - (sljit_uw)next_argw <= (sljit_uw)max_delta || (sljit_uw)next_argw - (sljit_uw)argw <= (sljit_uw)max_delta)) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, TMP_REG3, reg_map[arg & 0xf])); + + compiler->cache_arg = arg; + compiler->cache_argw = argw; + + GETPUT_ARG_DATA_TRANSFER(1, 0, reg, TMP_REG3, 0); + return SLJIT_SUCCESS; + } + + if ((arg & 0xf) == tmp_r) { + compiler->cache_arg = SLJIT_IMM; + compiler->cache_argw = argw; + tmp_r = TMP_REG3; + } + + FAIL_IF(load_immediate(compiler, tmp_r, argw)); + EMIT_INSTRUCTION(EMIT_DATA_TRANSFER(inp_flags, 1, inp_flags & WRITE_BACK, reg, arg & 0xf, reg_map[tmp_r] | (max_delta & 0xf00 ? SRC2_IMM : 0))); + return SLJIT_SUCCESS; +} + +static int emit_op(struct sljit_compiler *compiler, int op, int inp_flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* arg1 goes to TMP_REG1 or src reg + arg2 goes to TMP_REG2, imm or src reg + TMP_REG3 can be used for caching + result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ + + /* We prefers register and simple consts. */ + int dst_r; + int src1_r; + int src2_r = 0; + int sugg_src2_r = TMP_REG2; + int flags = GET_FLAGS(op) ? SET_FLAGS : 0; + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + /* Destination check. */ + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REG3) { + dst_r = dst; + flags |= REG_DEST; + if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI) + sugg_src2_r = dst_r; + } + else if (dst == SLJIT_UNUSED) { + if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI && !(src2 & SLJIT_MEM)) + return SLJIT_SUCCESS; + dst_r = TMP_REG2; + } + else { + SLJIT_ASSERT(dst & SLJIT_MEM); + if (getput_arg_fast(compiler, inp_flags | ARG_TEST, TMP_REG2, dst, dstw)) { + flags |= FAST_DEST; + dst_r = TMP_REG2; + } + else { + flags |= SLOW_DEST; + dst_r = 0; + } + } + + /* Source 1. */ + if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= TMP_REG3) + src1_r = src1; + else if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= TMP_REG3) { + flags |= ARGS_SWAPPED; + src1_r = src2; + src2 = src1; + src2w = src1w; + } + else { + if ((inp_flags & ALLOW_ANY_IMM) && (src1 & SLJIT_IMM)) { + /* The second check will generate a hit. */ + src2_r = get_immediate(src1w); + if (src2_r) { + flags |= ARGS_SWAPPED; + src1 = src2; + src1w = src2w; + } + if (inp_flags & ALLOW_INV_IMM) { + src2_r = get_immediate(~src1w); + if (src2_r) { + flags |= ARGS_SWAPPED | INV_IMM; + src1 = src2; + src1w = src2w; + } + } + } + + src1_r = 0; + if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w)) { + FAIL_IF(compiler->error); + src1_r = TMP_REG1; + } + } + + /* Source 2. */ + if (src2_r == 0) { + if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= TMP_REG3) { + src2_r = src2; + flags |= REG_SOURCE; + if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_SI) + dst_r = src2_r; + } + else do { /* do { } while(0) is used because of breaks. */ + if ((inp_flags & ALLOW_ANY_IMM) && (src2 & SLJIT_IMM)) { + src2_r = get_immediate(src2w); + if (src2_r) + break; + if (inp_flags & ALLOW_INV_IMM) { + src2_r = get_immediate(~src2w); + if (src2_r) { + flags |= INV_IMM; + break; + } + } + } + + /* src2_r is 0. */ + if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) { + FAIL_IF(compiler->error); + src2_r = sugg_src2_r; + } + } while (0); + } + + /* src1_r, src2_r and dst_r can be zero (=unprocessed) or non-zero. + If they are zero, they must not be registers. */ + if (src1_r == 0 && src2_r == 0 && dst_r == 0) { + if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { + SLJIT_ASSERT(!(flags & ARGS_SWAPPED)); + flags |= ARGS_SWAPPED; + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src1, src1w, dst, dstw)); + } + else { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw)); + } + src1_r = TMP_REG1; + src2_r = TMP_REG2; + } + else if (src1_r == 0 && src2_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); + src1_r = TMP_REG1; + } + else if (src1_r == 0 && dst_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); + src1_r = TMP_REG1; + } + else if (src2_r == 0 && dst_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw)); + src2_r = sugg_src2_r; + } + + if (dst_r == 0) + dst_r = TMP_REG2; + + if (src1_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0)); + src1_r = TMP_REG1; + } + + if (src2_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0)); + src2_r = sugg_src2_r; + } + + FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); + + if (flags & (FAST_DEST | SLOW_DEST)) { + if (flags & FAST_DEST) + FAIL_IF(getput_arg_fast(compiler, inp_flags, dst_r, dst, dstw)); + else + FAIL_IF(getput_arg(compiler, inp_flags, dst_r, dst, dstw, 0, 0)); + } + return SLJIT_SUCCESS; +} + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) +extern unsigned int __aeabi_uidivmod(unsigned numerator, unsigned denominator); +extern unsigned int __aeabi_idivmod(unsigned numerator, unsigned denominator); +#else +#error "Software divmod functions are needed" +#endif + +#ifdef __cplusplus +} +#endif + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + CHECK_ERROR(); + check_sljit_emit_op0(compiler, op); + + op = GET_OPCODE(op); + switch (op) { + case SLJIT_BREAKPOINT: + EMIT_INSTRUCTION(BKPT); + break; + case SLJIT_NOP: + EMIT_INSTRUCTION(NOP); + break; + case SLJIT_UMUL: + case SLJIT_SMUL: +#if (defined SLJIT_CONFIG_ARM_V7 && SLJIT_CONFIG_ARM_V7) + return push_inst(compiler, (op == SLJIT_UMUL ? UMULL : SMULL) + | (reg_map[SLJIT_TEMPORARY_REG2] << 16) + | (reg_map[SLJIT_TEMPORARY_REG1] << 12) + | (reg_map[SLJIT_TEMPORARY_REG1] << 8) + | reg_map[SLJIT_TEMPORARY_REG2]); +#else + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, RM(SLJIT_TEMPORARY_REG2))); + return push_inst(compiler, (op == SLJIT_UMUL ? UMULL : SMULL) + | (reg_map[SLJIT_TEMPORARY_REG2] << 16) + | (reg_map[SLJIT_TEMPORARY_REG1] << 12) + | (reg_map[SLJIT_TEMPORARY_REG1] << 8) + | reg_map[TMP_REG1]); +#endif + case SLJIT_UDIV: + case SLJIT_SDIV: + if (compiler->temporaries >= 3) + EMIT_INSTRUCTION(0xe52d2008 /* str r2, [sp, #-8]! */); +#if defined(__GNUC__) + FAIL_IF(sljit_emit_ijump(compiler, SLJIT_FAST_CALL, SLJIT_IMM, + (op == SLJIT_UDIV ? SLJIT_FUNC_OFFSET(__aeabi_uidivmod) : SLJIT_FUNC_OFFSET(__aeabi_idivmod)))); +#else +#error "Software divmod functions are needed" +#endif + if (compiler->temporaries >= 3) + return push_inst(compiler, 0xe49d2008 /* ldr r2, [sp], #8 */); + return SLJIT_SUCCESS; + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + CHECK_ERROR(); + check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw); + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: + return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_UB: + return emit_op(compiler, SLJIT_MOV_UB, ALLOW_ANY_IMM | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw); + + case SLJIT_MOV_SB: + return emit_op(compiler, SLJIT_MOV_SB, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw); + + case SLJIT_MOV_UH: + return emit_op(compiler, SLJIT_MOV_UH, ALLOW_ANY_IMM | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw); + + case SLJIT_MOV_SH: + return emit_op(compiler, SLJIT_MOV_SH, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw); + + case SLJIT_MOVU: + case SLJIT_MOVU_UI: + case SLJIT_MOVU_SI: + return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_UB: + return emit_op(compiler, SLJIT_MOV_UB, ALLOW_ANY_IMM | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw); + + case SLJIT_MOVU_SB: + return emit_op(compiler, SLJIT_MOV_SB, ALLOW_ANY_IMM | SIGNED_DATA | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw); + + case SLJIT_MOVU_UH: + return emit_op(compiler, SLJIT_MOV_UH, ALLOW_ANY_IMM | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw); + + case SLJIT_MOVU_SH: + return emit_op(compiler, SLJIT_MOV_SH, ALLOW_ANY_IMM | SIGNED_DATA | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw); + + case SLJIT_NOT: + return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_NEG: +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_op2(compiler, SLJIT_SUB | GET_FLAGS(op), dst, dstw, SLJIT_IMM, 0, src, srcw); + + case SLJIT_CLZ: + return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src, srcw); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + CHECK_ERROR(); + check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + case SLJIT_ADDC: + case SLJIT_SUB: + case SLJIT_SUBC: + case SLJIT_OR: + case SLJIT_XOR: + return emit_op(compiler, op, ALLOW_IMM, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_MUL: + return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_AND: + return emit_op(compiler, op, ALLOW_ANY_IMM, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SHL: + case SLJIT_LSHR: + case SLJIT_ASHR: + if (src2 & SLJIT_IMM) { + compiler->shift_imm = src2w & 0x1f; + return emit_op(compiler, op, 0, dst, dstw, TMP_REG1, 0, src1, src1w); + } + else { + compiler->shift_imm = 0x20; + return emit_op(compiler, op, 0, dst, dstw, src1, src1w, src2, src2w); + } + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_register_index(int reg) +{ + check_sljit_get_register_index(reg); + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + CHECK_ERROR(); + check_sljit_emit_op_custom(compiler, instruction, size); + SLJIT_ASSERT(size == 4); + + return push_inst(compiler, *(sljit_uw*)instruction); +} + +/* --------------------------------------------------------------------- */ +/* Floating point operators */ +/* --------------------------------------------------------------------- */ + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + +/* 0 - no fpu + 1 - vfp */ +static int arm_fpu_type = -1; + +static void init_compiler() +{ + if (arm_fpu_type != -1) + return; + + /* TODO: Only the OS can help to determine the correct fpu type. */ + arm_fpu_type = 1; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ + if (arm_fpu_type == -1) + init_compiler(); + return arm_fpu_type; +} + +#else + +#define arm_fpu_type 1 + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ + /* Always available. */ + return 1; +} + +#endif + +#define EMIT_FPU_DATA_TRANSFER(add, load, base, freg, offs) \ + (VSTR | ((add) << 23) | ((load) << 20) | (reg_map[base] << 16) | (freg << 12) | (offs)) +#define EMIT_FPU_OPERATION(opcode, dst, src1, src2) \ + ((opcode) | ((dst) << 12) | (src1) | ((src2) << 16)) + +static int emit_fpu_data_transfer(struct sljit_compiler *compiler, int fpu_reg, int load, int arg, sljit_w argw) +{ + SLJIT_ASSERT(arg & SLJIT_MEM); + + /* Fast loads and stores. */ + if ((arg & 0xf) && !(arg & 0xf0) && (argw & 0x3) == 0) { + if (argw >= 0 && argw <= 0x3ff) { + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(1, load, arg & 0xf, fpu_reg, argw >> 2)); + return SLJIT_SUCCESS; + } + if (argw < 0 && argw >= -0x3ff) { + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(0, load, arg & 0xf, fpu_reg, (-argw) >> 2)); + return SLJIT_SUCCESS; + } + if (argw >= 0 && argw <= 0x3ffff) { + SLJIT_ASSERT(get_immediate(argw & 0x3fc00)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & 0xf, get_immediate(argw & 0x3fc00))); + argw &= 0x3ff; + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(1, load, TMP_REG1, fpu_reg, argw >> 2)); + return SLJIT_SUCCESS; + } + if (argw < 0 && argw >= -0x3ffff) { + argw = -argw; + SLJIT_ASSERT(get_immediate(argw & 0x3fc00)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(SUB_DP, 0, TMP_REG1, arg & 0xf, get_immediate(argw & 0x3fc00))); + argw &= 0x3ff; + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(0, load, TMP_REG1, fpu_reg, argw >> 2)); + return SLJIT_SUCCESS; + } + } + + if (arg & 0xf0) { + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG1, arg & 0xf, RM((arg >> 4) & 0xf) | ((argw & 0x3) << 7))); + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(1, load, TMP_REG1, fpu_reg, 0)); + return SLJIT_SUCCESS; + } + + if (compiler->cache_arg == arg && ((argw - compiler->cache_argw) & 0x3) == 0) { + if (((sljit_uw)argw - (sljit_uw)compiler->cache_argw) <= 0x3ff) { + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(1, load, TMP_REG3, fpu_reg, (argw - compiler->cache_argw) >> 2)); + return SLJIT_SUCCESS; + } + if (((sljit_uw)compiler->cache_argw - (sljit_uw)argw) <= 0x3ff) { + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(0, load, TMP_REG3, fpu_reg, (compiler->cache_argw - argw) >> 2)); + return SLJIT_SUCCESS; + } + } + + compiler->cache_arg = arg; + compiler->cache_argw = argw; + if (arg & 0xf) { + FAIL_IF(load_immediate(compiler, TMP_REG1, argw)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(ADD_DP, 0, TMP_REG3, arg & 0xf, reg_map[TMP_REG1])); + } + else + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + + EMIT_INSTRUCTION(EMIT_FPU_DATA_TRANSFER(1, load, TMP_REG3, fpu_reg, 0)); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int dst_freg; + + CHECK_ERROR(); + check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (GET_OPCODE(op) == SLJIT_FCMP) { + if (dst > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, dst, dstw)); + dst = TMP_FREG1; + } + if (src > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src, srcw)); + src = TMP_FREG2; + } + EMIT_INSTRUCTION(VCMP_F64 | (dst << 12) | src); + EMIT_INSTRUCTION(VMRS); + return SLJIT_SUCCESS; + } + + dst_freg = (dst > SLJIT_FLOAT_REG4) ? TMP_FREG1 : dst; + + if (src > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, dst_freg, 1, src, srcw)); + src = dst_freg; + } + + switch (op) { + case SLJIT_FMOV: + if (src != dst_freg && dst_freg != TMP_FREG1) + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VMOV_F64, dst_freg, src, 0)); + break; + case SLJIT_FNEG: + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VNEG_F64, dst_freg, src, 0)); + break; + case SLJIT_FABS: + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VABS_F64, dst_freg, src, 0)); + break; + } + + if (dst_freg == TMP_FREG1) + FAIL_IF(emit_fpu_data_transfer(compiler, src, 0, dst, dstw)); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int dst_freg; + + CHECK_ERROR(); + check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + dst_freg = (dst > SLJIT_FLOAT_REG4) ? TMP_FREG1 : dst; + + if (src2 > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src2, src2w)); + src2 = TMP_FREG2; + } + + if (src1 > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, src1, src1w)); + src1 = TMP_FREG1; + } + + switch (op) { + case SLJIT_FADD: + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VADD_F64, dst_freg, src2, src1)); + break; + + case SLJIT_FSUB: + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VSUB_F64, dst_freg, src2, src1)); + break; + + case SLJIT_FMUL: + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VMUL_F64, dst_freg, src2, src1)); + break; + + case SLJIT_FDIV: + EMIT_INSTRUCTION(EMIT_FPU_OPERATION(VDIV_F64, dst_freg, src2, src1)); + break; + } + + if (dst_freg == TMP_FREG1) + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 0, dst, dstw)); + + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Other instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + int size; + + CHECK_ERROR(); + check_sljit_emit_fast_enter(compiler, dst, dstw, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + size = (1 + saveds) * sizeof(sljit_uw); + if (temporaries >= 4) + size += (temporaries - 3) * sizeof(sljit_uw); + local_size += size; + local_size = (local_size + 7) & ~7; + local_size -= size; + compiler->local_size = local_size; + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, 0, dst, SLJIT_UNUSED, RM(TMP_REG3))); + else if (dst & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_DATA, TMP_REG3, dst, dstw)) + return compiler->error; + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG2, SLJIT_UNUSED, RM(TMP_REG3))); + compiler->cache_arg = 0; + compiler->cache_argw = 0; + return getput_arg(compiler, WORD_DATA, TMP_REG2, dst, dstw, 0, 0); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + CHECK_ERROR(); + check_sljit_emit_fast_return(compiler, src, srcw); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(src))); + else if (src & SLJIT_MEM) { + if (getput_arg_fast(compiler, WORD_DATA | LOAD_DATA, TMP_REG3, src, srcw)) + FAIL_IF(compiler->error); + else { + compiler->cache_arg = 0; + compiler->cache_argw = 0; + FAIL_IF(getput_arg(compiler, WORD_DATA | LOAD_DATA, TMP_REG2, src, srcw, 0, 0)); + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG3, SLJIT_UNUSED, RM(TMP_REG2))); + } + } + else if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, TMP_REG3, srcw)); + return push_inst(compiler, BLX | RM(TMP_REG3)); +} + +/* --------------------------------------------------------------------- */ +/* Conditional instructions */ +/* --------------------------------------------------------------------- */ + +static sljit_uw get_cc(int type) +{ + switch (type) { + case SLJIT_C_EQUAL: + case SLJIT_C_MUL_NOT_OVERFLOW: + case SLJIT_C_FLOAT_EQUAL: + return 0x00000000; + + case SLJIT_C_NOT_EQUAL: + case SLJIT_C_MUL_OVERFLOW: + case SLJIT_C_FLOAT_NOT_EQUAL: + return 0x10000000; + + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + return 0x30000000; + + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + return 0x20000000; + + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + return 0x80000000; + + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + return 0x90000000; + + case SLJIT_C_SIG_LESS: + return 0xb0000000; + + case SLJIT_C_SIG_GREATER_EQUAL: + return 0xa0000000; + + case SLJIT_C_SIG_GREATER: + return 0xc0000000; + + case SLJIT_C_SIG_LESS_EQUAL: + return 0xd0000000; + + case SLJIT_C_OVERFLOW: + case SLJIT_C_FLOAT_NAN: + return 0x60000000; + + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_FLOAT_NOT_NAN: + return 0x70000000; + + default: /* SLJIT_JUMP */ + return 0xe0000000; + } +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + struct sljit_label *label; + + CHECK_ERROR_PTR(); + check_sljit_emit_label(compiler); + + if (compiler->last_label && compiler->last_label->size == compiler->size) + return compiler->last_label; + + label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); + PTR_FAIL_IF(!label); + set_label(label, compiler); + return label; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + struct sljit_jump *jump; + + CHECK_ERROR_PTR(); + check_sljit_emit_jump(compiler, type); + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + /* In ARM, we don't need to touch the arguments. */ +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (type >= SLJIT_FAST_CALL) + PTR_FAIL_IF(prepare_blx(compiler)); + PTR_FAIL_IF(push_inst_with_unique_literal(compiler, ((EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, + type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0)) & ~COND_MASK) | get_cc(type), 0)); + + if (jump->flags & SLJIT_REWRITABLE_JUMP) { + jump->addr = compiler->size; + compiler->patches++; + } + + if (type >= SLJIT_FAST_CALL) { + jump->flags |= IS_BL; + PTR_FAIL_IF(emit_blx(compiler)); + } + + if (!(jump->flags & SLJIT_REWRITABLE_JUMP)) + jump->addr = compiler->size; +#else + if (type >= SLJIT_FAST_CALL) + jump->flags |= IS_BL; + PTR_FAIL_IF(emit_imm(compiler, TMP_REG1, 0)); + PTR_FAIL_IF(push_inst(compiler, (((type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1)) & ~COND_MASK) | get_cc(type))); + jump->addr = compiler->size; +#endif + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + struct sljit_jump *jump; + + CHECK_ERROR(); + check_sljit_emit_ijump(compiler, type, src, srcw); + + /* In ARM, we don't need to touch the arguments. */ + if (src & SLJIT_IMM) { + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + FAIL_IF(!jump); + set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_BL : 0)); + jump->u.target = srcw; + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + if (type >= SLJIT_FAST_CALL) + FAIL_IF(prepare_blx(compiler)); + FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, type <= SLJIT_JUMP ? TMP_PC : TMP_REG1, TMP_PC, 0), 0)); + if (type >= SLJIT_FAST_CALL) + FAIL_IF(emit_blx(compiler)); +#else + FAIL_IF(emit_imm(compiler, TMP_REG1, 0)); + FAIL_IF(push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG1))); +#endif + jump->addr = compiler->size; + } + else { + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(src)); + + SLJIT_ASSERT(src & SLJIT_MEM); + FAIL_IF(emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + return push_inst(compiler, (type <= SLJIT_JUMP ? BX : BLX) | RM(TMP_REG2)); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + int reg; + sljit_uw cc; + + CHECK_ERROR(); + check_sljit_emit_cond_value(compiler, op, dst, dstw, type); + + if (dst == SLJIT_UNUSED) + return SLJIT_SUCCESS; + + cc = get_cc(type); + if (GET_OPCODE(op) == SLJIT_OR) { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + EMIT_INSTRUCTION((EMIT_DATA_PROCESS_INS(ORR_DP, 0, dst, dst, SRC2_IMM | 1) & ~COND_MASK) | cc); + if (op & SLJIT_SET_E) + return push_inst(compiler, EMIT_DATA_PROCESS_INS(MOV_DP, SET_FLAGS, TMP_REG1, SLJIT_UNUSED, RM(dst))); + return SLJIT_SUCCESS; + } + + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, SRC2_IMM | 0)); + EMIT_INSTRUCTION((EMIT_DATA_PROCESS_INS(MOV_DP, 0, TMP_REG1, SLJIT_UNUSED, SRC2_IMM | 1) & ~COND_MASK) | cc); +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return emit_op(compiler, op, ALLOW_IMM, dst, dstw, TMP_REG1, 0, dst, dstw); + } + + reg = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG2; + + EMIT_INSTRUCTION(EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, SRC2_IMM | 0)); + EMIT_INSTRUCTION((EMIT_DATA_PROCESS_INS(MOV_DP, 0, reg, SLJIT_UNUSED, SRC2_IMM | 1) & ~COND_MASK) | cc); + + if (reg == TMP_REG2) + return emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w init_value) +{ + struct sljit_const *const_; + int reg; + + CHECK_ERROR_PTR(); + check_sljit_emit_const(compiler, dst, dstw, init_value); + + const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); + PTR_FAIL_IF(!const_); + + reg = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG2; + +#if (defined SLJIT_CONFIG_ARM_V5 && SLJIT_CONFIG_ARM_V5) + PTR_FAIL_IF(push_inst_with_unique_literal(compiler, EMIT_DATA_TRANSFER(WORD_DATA | LOAD_DATA, 1, 0, reg, TMP_PC, 0), init_value)); + compiler->patches++; +#else + PTR_FAIL_IF(emit_imm(compiler, reg, init_value)); +#endif + set_const(const_, compiler); + + if (reg == TMP_REG2 && dst != SLJIT_UNUSED) + if (emit_op(compiler, SLJIT_MOV, ALLOW_ANY_IMM, dst, dstw, TMP_REG1, 0, TMP_REG2, 0)) + return NULL; + return const_; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ + inline_set_jump_addr(addr, new_addr, 1); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + inline_set_const(addr, new_constant, 1); +} diff --git a/src/lib/pcre/sljit/sljitNativeMIPS_32.c b/src/lib/pcre/sljit/sljitNativeMIPS_32.c new file mode 100644 index 0000000..c0cc8b5 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeMIPS_32.c @@ -0,0 +1,405 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +/* mips 32-bit arch dependent functions. */ + +static int load_immediate(struct sljit_compiler *compiler, int dst_ar, sljit_w imm) +{ + if (!(imm & ~0xffff)) + return push_inst(compiler, ORI | SA(0) | TA(dst_ar) | IMM(imm), dst_ar); + + if (imm < 0 && imm >= SIMM_MIN) + return push_inst(compiler, ADDIU | SA(0) | TA(dst_ar) | IMM(imm), dst_ar); + + FAIL_IF(push_inst(compiler, LUI | TA(dst_ar) | IMM(imm >> 16), dst_ar)); + return (imm & 0xffff) ? push_inst(compiler, ORI | SA(dst_ar) | TA(dst_ar) | IMM(imm), dst_ar) : SLJIT_SUCCESS; +} + +#define EMIT_LOGICAL(op_imm, op_norm) \ + if (flags & SRC2_IMM) { \ + if (op & SLJIT_SET_E) \ + FAIL_IF(push_inst(compiler, op_imm | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); \ + if (CHECK_FLAGS(SLJIT_SET_E)) \ + FAIL_IF(push_inst(compiler, op_imm | S(src1) | T(dst) | IMM(src2), DR(dst))); \ + } \ + else { \ + if (op & SLJIT_SET_E) \ + FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ + if (CHECK_FLAGS(SLJIT_SET_E)) \ + FAIL_IF(push_inst(compiler, op_norm | S(src1) | T(src2) | D(dst), DR(dst))); \ + } + +#define EMIT_SHIFT(op_imm, op_norm) \ + if (flags & SRC2_IMM) { \ + if (op & SLJIT_SET_E) \ + FAIL_IF(push_inst(compiler, op_imm | T(src1) | DA(EQUAL_FLAG) | SH_IMM(src2), EQUAL_FLAG)); \ + if (CHECK_FLAGS(SLJIT_SET_E)) \ + FAIL_IF(push_inst(compiler, op_imm | T(src1) | D(dst) | SH_IMM(src2), DR(dst))); \ + } \ + else { \ + if (op & SLJIT_SET_E) \ + FAIL_IF(push_inst(compiler, op_norm | S(src2) | T(src1) | DA(EQUAL_FLAG), EQUAL_FLAG)); \ + if (CHECK_FLAGS(SLJIT_SET_E)) \ + FAIL_IF(push_inst(compiler, op_norm | S(src2) | T(src1) | D(dst), DR(dst))); \ + } + +static SLJIT_INLINE int emit_single_op(struct sljit_compiler *compiler, int op, int flags, + int dst, int src1, sljit_w src2) +{ + int overflow_ra = 0; + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, SRL | T(src1) | DA(TMP_EREG1) | SH_IMM(31), TMP_EREG1)); + if (src2 < 0) + FAIL_IF(push_inst(compiler, XORI | SA(TMP_EREG1) | TA(TMP_EREG1) | IMM(1), TMP_EREG1)); + } + if (op & SLJIT_SET_E) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(src2), EQUAL_FLAG)); + if (op & SLJIT_SET_C) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); + else { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, OR | S(src1) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); + } + } + /* dst may be the same as src1 or src2. */ + if (CHECK_FLAGS(SLJIT_SET_E)) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, SRL | T(dst) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG)); + if (src2 < 0) + FAIL_IF(push_inst(compiler, XORI | SA(OVERFLOW_FLAG) | TA(OVERFLOW_FLAG) | IMM(1), OVERFLOW_FLAG)); + } + } + else { + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(TMP_EREG1), TMP_EREG1)); + FAIL_IF(push_inst(compiler, SRL | TA(TMP_EREG1) | DA(TMP_EREG1) | SH_IMM(31), TMP_EREG1)); + if (src1 != dst) + overflow_ra = DR(src1); + else if (src2 != dst) + overflow_ra = DR(src2); + else { + /* Rare ocasion. */ + FAIL_IF(push_inst(compiler, ADDU | S(src1) | TA(0) | DA(TMP_EREG2), TMP_EREG2)); + overflow_ra = TMP_EREG2; + } + } + if (op & SLJIT_SET_E) + FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG)); + /* dst may be the same as src1 or src2. */ + if (CHECK_FLAGS(SLJIT_SET_E)) + FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(overflow_ra) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + FAIL_IF(push_inst(compiler, SRL | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG)); + } + } + + /* a + b >= a | b (otherwise, the carry should be set to 1). */ + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(ULESS_FLAG) | DA(ULESS_FLAG), ULESS_FLAG)); + if (op & SLJIT_SET_O) + return push_inst(compiler, MOVN | SA(0) | TA(TMP_EREG1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG); + return SLJIT_SUCCESS; + + case SLJIT_ADDC: + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_C) { + if (src2 >= 0) + FAIL_IF(push_inst(compiler, ORI | S(src1) | TA(TMP_EREG1) | IMM(src2), TMP_EREG1)); + else { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | TA(TMP_EREG1) | IMM(src2), TMP_EREG1)); + FAIL_IF(push_inst(compiler, OR | S(src1) | TA(TMP_EREG1) | DA(TMP_EREG1), TMP_EREG1)); + } + } + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(src2), DR(dst))); + } else { + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, OR | S(src1) | T(src2) | DA(TMP_EREG1), TMP_EREG1)); + /* dst may be the same as src1 or src2. */ + FAIL_IF(push_inst(compiler, ADDU | S(src1) | T(src2) | D(dst), DR(dst))); + } + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, SLTU | S(dst) | TA(TMP_EREG1) | DA(TMP_EREG1), TMP_EREG1)); + + FAIL_IF(push_inst(compiler, ADDU | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst))); + if (!(op & SLJIT_SET_C)) + return SLJIT_SUCCESS; + + /* Set TMP_EREG2 (dst == 0) && (ULESS_FLAG == 1). */ + FAIL_IF(push_inst(compiler, SLTIU | S(dst) | TA(TMP_EREG2) | IMM(1), TMP_EREG2)); + FAIL_IF(push_inst(compiler, AND | SA(TMP_EREG2) | TA(ULESS_FLAG) | DA(TMP_EREG2), TMP_EREG2)); + /* Set carry flag. */ + return push_inst(compiler, OR | SA(TMP_EREG2) | TA(TMP_EREG1) | DA(ULESS_FLAG), ULESS_FLAG); + + case SLJIT_SUB: + if ((flags & SRC2_IMM) && ((op & (SLJIT_SET_S | SLJIT_SET_U)) || src2 == SIMM_MIN)) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, SRL | T(src1) | DA(TMP_EREG1) | SH_IMM(31), TMP_EREG1)); + if (src2 < 0) + FAIL_IF(push_inst(compiler, XORI | SA(TMP_EREG1) | TA(TMP_EREG1) | IMM(1), TMP_EREG1)); + if (src1 != dst) + overflow_ra = DR(src1); + else { + /* Rare ocasion. */ + FAIL_IF(push_inst(compiler, ADDU | S(src1) | TA(0) | DA(TMP_EREG2), TMP_EREG2)); + overflow_ra = TMP_EREG2; + } + } + if (op & SLJIT_SET_E) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | TA(EQUAL_FLAG) | IMM(-src2), EQUAL_FLAG)); + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(ULESS_FLAG) | IMM(src2), ULESS_FLAG)); + /* dst may be the same as src1 or src2. */ + if (CHECK_FLAGS(SLJIT_SET_E)) + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); + } + else { + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, XOR | S(src1) | T(src2) | DA(TMP_EREG1), TMP_EREG1)); + FAIL_IF(push_inst(compiler, SRL | TA(TMP_EREG1) | DA(TMP_EREG1) | SH_IMM(31), TMP_EREG1)); + if (src1 != dst) + overflow_ra = DR(src1); + else { + /* Rare ocasion. */ + FAIL_IF(push_inst(compiler, ADDU | S(src1) | TA(0) | DA(TMP_EREG2), TMP_EREG2)); + overflow_ra = TMP_EREG2; + } + } + if (op & SLJIT_SET_E) + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (op & (SLJIT_SET_U | SLJIT_SET_C)) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(ULESS_FLAG), ULESS_FLAG)); + if (op & SLJIT_SET_U) + FAIL_IF(push_inst(compiler, SLTU | S(src2) | T(src1) | DA(UGREATER_FLAG), UGREATER_FLAG)); + if (op & SLJIT_SET_S) { + FAIL_IF(push_inst(compiler, SLT | S(src1) | T(src2) | DA(LESS_FLAG), LESS_FLAG)); + FAIL_IF(push_inst(compiler, SLT | S(src2) | T(src1) | DA(GREATER_FLAG), GREATER_FLAG)); + } + /* dst may be the same as src1 or src2. */ + if (CHECK_FLAGS(SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_C)) + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); + } + + if (op & SLJIT_SET_O) { + FAIL_IF(push_inst(compiler, XOR | S(dst) | TA(overflow_ra) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG)); + FAIL_IF(push_inst(compiler, SRL | TA(OVERFLOW_FLAG) | DA(OVERFLOW_FLAG) | SH_IMM(31), OVERFLOW_FLAG)); + return push_inst(compiler, MOVZ | SA(0) | TA(TMP_EREG1) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG); + } + return SLJIT_SUCCESS; + + case SLJIT_SUBC: + if ((flags & SRC2_IMM) && src2 == SIMM_MIN) { + FAIL_IF(push_inst(compiler, ADDIU | SA(0) | T(TMP_REG2) | IMM(src2), DR(TMP_REG2))); + src2 = TMP_REG2; + flags &= ~SRC2_IMM; + } + + if (flags & SRC2_IMM) { + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, SLTIU | S(src1) | TA(TMP_EREG1) | IMM(-src2), TMP_EREG1)); + /* dst may be the same as src1 or src2. */ + FAIL_IF(push_inst(compiler, ADDIU | S(src1) | T(dst) | IMM(-src2), DR(dst))); + } + else { + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, SLTU | S(src1) | T(src2) | DA(TMP_EREG1), TMP_EREG1)); + /* dst may be the same as src1 or src2. */ + FAIL_IF(push_inst(compiler, SUBU | S(src1) | T(src2) | D(dst), DR(dst))); + } + + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, MOVZ | SA(ULESS_FLAG) | T(dst) | DA(TMP_EREG1), TMP_EREG1)); + + FAIL_IF(push_inst(compiler, SUBU | S(dst) | TA(ULESS_FLAG) | D(dst), DR(dst))); + + if (op & SLJIT_SET_C) + FAIL_IF(push_inst(compiler, ADDU | SA(TMP_EREG1) | TA(0) | DA(ULESS_FLAG), ULESS_FLAG)); + + return SLJIT_SUCCESS; + + case SLJIT_MUL: + SLJIT_ASSERT(!(flags & SRC2_IMM)); + if (!(op & SLJIT_SET_O)) { +#if (defined SLJIT_MIPS_32_64 && SLJIT_MIPS_32_64) + return push_inst(compiler, MUL | S(src1) | T(src2) | D(dst), DR(dst)); +#else + FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); + return push_inst(compiler, MFLO | D(dst), DR(dst)); +#endif + } + FAIL_IF(push_inst(compiler, MULT | S(src1) | T(src2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, MFHI | DA(TMP_EREG1), TMP_EREG1)); + FAIL_IF(push_inst(compiler, MFLO | D(dst), DR(dst))); + FAIL_IF(push_inst(compiler, SRA | T(dst) | DA(TMP_EREG2) | SH_IMM(31), TMP_EREG2)); + return push_inst(compiler, SUBU | SA(TMP_EREG1) | TA(TMP_EREG2) | DA(OVERFLOW_FLAG), OVERFLOW_FLAG); + + case SLJIT_AND: + EMIT_LOGICAL(ANDI, AND); + return SLJIT_SUCCESS; + + case SLJIT_OR: + EMIT_LOGICAL(ORI, OR); + return SLJIT_SUCCESS; + + case SLJIT_XOR: + EMIT_LOGICAL(XORI, XOR); + return SLJIT_SUCCESS; + + case SLJIT_SHL: + EMIT_SHIFT(SLL, SLLV); + return SLJIT_SUCCESS; + + case SLJIT_LSHR: + EMIT_SHIFT(SRL, SRLV); + return SLJIT_SUCCESS; + + case SLJIT_ASHR: + EMIT_SHIFT(SRA, SRAV); + return SLJIT_SUCCESS; + + case SLJIT_MOV: + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: + SLJIT_ASSERT(src1 == TMP_REG1); + if (dst != src2) + return push_inst(compiler, ADDU | S(src2) | TA(0) | D(dst), DR(dst)); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UB: + case SLJIT_MOV_SB: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SB) { +#if (defined SLJIT_MIPS_32_64 && SLJIT_MIPS_32_64) + return push_inst(compiler, SEB | T(src2) | D(dst), DR(dst)); +#else + FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(24), DR(dst))); + return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(24), DR(dst)); +#endif + } + return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xff), DR(dst)); + } + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UH: + case SLJIT_MOV_SH: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SH) { +#if (defined SLJIT_MIPS_32_64 && SLJIT_MIPS_32_64) + return push_inst(compiler, SEH | T(src2) | D(dst), DR(dst)); +#else + FAIL_IF(push_inst(compiler, SLL | T(src2) | D(dst) | SH_IMM(16), DR(dst))); + return push_inst(compiler, SRA | T(dst) | D(dst) | SH_IMM(16), DR(dst)); +#endif + } + return push_inst(compiler, ANDI | S(src2) | T(dst) | IMM(0xffff), DR(dst)); + } + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_NOT: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); + if (op & SLJIT_SET_E) + FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (CHECK_FLAGS(SLJIT_SET_E)) + FAIL_IF(push_inst(compiler, NOR | S(src2) | T(src2) | D(dst), DR(dst))); + return SLJIT_SUCCESS; + + case SLJIT_CLZ: + SLJIT_ASSERT(src1 == TMP_REG1 && !(flags & SRC2_IMM)); +#if (defined SLJIT_MIPS_32_64 && SLJIT_MIPS_32_64) + if (op & SLJIT_SET_E) + FAIL_IF(push_inst(compiler, CLZ | S(src2) | TA(EQUAL_FLAG) | DA(EQUAL_FLAG), EQUAL_FLAG)); + if (CHECK_FLAGS(SLJIT_SET_E)) + FAIL_IF(push_inst(compiler, CLZ | S(src2) | T(dst) | D(dst), DR(dst))); +#else + if (SLJIT_UNLIKELY(flags & UNUSED_DEST)) { + FAIL_IF(push_inst(compiler, SRL | T(src2) | DA(EQUAL_FLAG) | SH_IMM(31), EQUAL_FLAG)); + return push_inst(compiler, XORI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG); + } + /* Nearly all instructions are unmovable in the following sequence. */ + FAIL_IF(push_inst(compiler, ADDU_W | S(src2) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + /* Check zero. */ + FAIL_IF(push_inst(compiler, BEQ | S(TMP_REG1) | TA(0) | IMM(6), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM(32), UNMOVABLE_INS)); + /* Check sign bit. */ + FAIL_IF(push_inst(compiler, BLTZ | S(TMP_REG1) | IMM(4), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, ORI | SA(0) | T(dst) | IMM(0), UNMOVABLE_INS)); + /* Loop for searching the highest bit. */ + FAIL_IF(push_inst(compiler, SLL | T(TMP_REG1) | D(TMP_REG1) | SH_IMM(1), DR(TMP_REG1))); + FAIL_IF(push_inst(compiler, BGEZ | S(TMP_REG1) | IMM(-2), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, ADDIU_W | S(dst) | T(dst) | IMM(1), UNMOVABLE_INS)); + if (op & SLJIT_SET_E) + return push_inst(compiler, ADDU_W | S(dst) | TA(0) | DA(EQUAL_FLAG), EQUAL_FLAG); +#endif + return SLJIT_SUCCESS; + } + + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_const(struct sljit_compiler *compiler, int reg, sljit_w init_value) +{ + FAIL_IF(push_inst(compiler, LUI | T(reg) | IMM(init_value >> 16), DR(reg))); + return push_inst(compiler, ORI | S(reg) | T(reg) | IMM(init_value), DR(reg)); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ + sljit_ins *inst = (sljit_ins*)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_addr & 0xffff); + SLJIT_CACHE_FLUSH(inst, inst + 2); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + sljit_ins *inst = (sljit_ins*)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_constant & 0xffff); + SLJIT_CACHE_FLUSH(inst, inst + 2); +} diff --git a/src/lib/pcre/sljit/sljitNativeMIPS_common.c b/src/lib/pcre/sljit/sljitNativeMIPS_common.c new file mode 100644 index 0000000..3c6ee66 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeMIPS_common.c @@ -0,0 +1,1829 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name() +{ + return "MIPS" SLJIT_CPUINFO; +} + +/* Latest MIPS architecture. */ +/* Detect SLJIT_MIPS_32_64 */ + +/* Length of an instruction word + Both for mips-32 and mips-64 */ +typedef sljit_ui sljit_ins; + +#define TMP_REG1 (SLJIT_NO_REGISTERS + 1) +#define TMP_REG2 (SLJIT_NO_REGISTERS + 2) +#define TMP_REG3 (SLJIT_NO_REGISTERS + 3) +#define REAL_STACK_PTR (SLJIT_NO_REGISTERS + 4) + +/* For position independent code, t9 must contain the function address. */ +#define PIC_ADDR_REG TMP_REG2 + +/* TMP_EREG1 is used mainly for literal encoding on 64 bit. */ +#define TMP_EREG1 15 +#define TMP_EREG2 24 +/* Floating point status register. */ +#define FCSR_REG 31 +/* Return address register. */ +#define RETURN_ADDR_REG 31 + +/* Flags are keept in volatile registers. */ +#define EQUAL_FLAG 7 +/* And carry flag as well. */ +#define ULESS_FLAG 10 +#define UGREATER_FLAG 11 +#define LESS_FLAG 12 +#define GREATER_FLAG 13 +#define OVERFLOW_FLAG 14 + +#define TMP_FREG1 (SLJIT_FLOAT_REG4 + 1) +#define TMP_FREG2 (SLJIT_FLOAT_REG4 + 2) + +/* --------------------------------------------------------------------- */ +/* Instrucion forms */ +/* --------------------------------------------------------------------- */ + +#define S(s) (reg_map[s] << 21) +#define T(t) (reg_map[t] << 16) +#define D(d) (reg_map[d] << 11) +/* Absolute registers. */ +#define SA(s) ((s) << 21) +#define TA(t) ((t) << 16) +#define DA(d) ((d) << 11) +#define FT(t) ((t) << (16 + 1)) +#define FS(s) ((s) << (11 + 1)) +#define FD(d) ((d) << (6 + 1)) +#define IMM(imm) ((imm) & 0xffff) +#define SH_IMM(imm) ((imm & 0x1f) << 6) + +#define DR(dr) (reg_map[dr]) +#define HI(opcode) ((opcode) << 26) +#define LO(opcode) (opcode) +#define FMT_D (17 << 21) + +#define ABS_D (HI(17) | FMT_D | LO(5)) +#define ADD_D (HI(17) | FMT_D | LO(0)) +#define ADDU (HI(0) | LO(33)) +#define ADDIU (HI(9)) +#define AND (HI(0) | LO(36)) +#define ANDI (HI(12)) +#define B (HI(4)) +#define BAL (HI(1) | (17 << 16)) +#define BC1F (HI(17) | (8 << 21)) +#define BC1T (HI(17) | (8 << 21) | (1 << 16)) +#define BEQ (HI(4)) +#define BGEZ (HI(1) | (1 << 16)) +#define BGTZ (HI(7)) +#define BLEZ (HI(6)) +#define BLTZ (HI(1) | (0 << 16)) +#define BNE (HI(5)) +#define BREAK (HI(0) | LO(13)) +#define C_UN_D (HI(17) | FMT_D | LO(49)) +#define C_UEQ_D (HI(17) | FMT_D | LO(51)) +#define C_ULE_D (HI(17) | FMT_D | LO(55)) +#define C_ULT_D (HI(17) | FMT_D | LO(53)) +#define DIV (HI(0) | LO(26)) +#define DIVU (HI(0) | LO(27)) +#define DIV_D (HI(17) | FMT_D | LO(3)) +#define J (HI(2)) +#define JAL (HI(3)) +#define JALR (HI(0) | LO(9)) +#define JR (HI(0) | LO(8)) +#define LD (HI(55)) +#define LDC1 (HI(53)) +#define LUI (HI(15)) +#define LW (HI(35)) +#define NEG_D (HI(17) | FMT_D | LO(7)) +#define MFHI (HI(0) | LO(16)) +#define MFLO (HI(0) | LO(18)) +#define MOV_D (HI(17) | FMT_D | LO(6)) +#define CFC1 (HI(17) | (2 << 21)) +#define MOVN (HI(0) | LO(11)) +#define MOVZ (HI(0) | LO(10)) +#define MUL_D (HI(17) | FMT_D | LO(2)) +#define MULT (HI(0) | LO(24)) +#define MULTU (HI(0) | LO(25)) +#define NOP (HI(0) | LO(0)) +#define NOR (HI(0) | LO(39)) +#define OR (HI(0) | LO(37)) +#define ORI (HI(13)) +#define SD (HI(63)) +#define SDC1 (HI(61)) +#define SLT (HI(0) | LO(42)) +#define SLTI (HI(10)) +#define SLTIU (HI(11)) +#define SLTU (HI(0) | LO(43)) +#define SLL (HI(0) | LO(0)) +#define SLLV (HI(0) | LO(4)) +#define SRL (HI(0) | LO(2)) +#define SRLV (HI(0) | LO(6)) +#define SRA (HI(0) | LO(3)) +#define SRAV (HI(0) | LO(7)) +#define SUB_D (HI(17) | FMT_D | LO(1)) +#define SUBU (HI(0) | LO(35)) +#define SW (HI(43)) +#define XOR (HI(0) | LO(38)) +#define XORI (HI(14)) + +#if (defined SLJIT_MIPS_32_64 && SLJIT_MIPS_32_64) +#define CLZ (HI(28) | LO(32)) +#define MUL (HI(28) | LO(2)) +#define SEB (HI(31) | (16 << 6) | LO(32)) +#define SEH (HI(31) | (24 << 6) | LO(32)) +#endif + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#define ADDU_W ADDU +#define ADDIU_W ADDIU +#define SLL_W SLL +#define SUBU_W SUBU +#else +#define ADDU_W DADDU +#define ADDIU_W DADDIU +#define SLL_W DSLL +#define SUBU_W DSUBU +#endif + +#define SIMM_MAX (0x7fff) +#define SIMM_MIN (-0x8000) +#define UIMM_MAX (0xffff) + +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 6] = { + 0, 2, 5, 6, 3, 8, 17, 18, 19, 20, 21, 16, 4, 25, 9, 29 +}; + +/* dest_reg is the absolute name of the register + Useful for reordering instructions in the delay slot. */ +static int push_inst(struct sljit_compiler *compiler, sljit_ins ins, int delay_slot) +{ + sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins)); + FAIL_IF(!ptr); + *ptr = ins; + compiler->size++; + compiler->delay_slot = delay_slot; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE sljit_ins invert_branch(int flags) +{ + return (flags & IS_BIT26_COND) ? (1 << 26) : (1 << 16); +} + +static SLJIT_INLINE sljit_ins* optimize_jump(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code) +{ + sljit_w diff; + sljit_uw target_addr; + sljit_ins *inst; + sljit_ins saved_inst; + + if (jump->flags & SLJIT_REWRITABLE_JUMP) + return code_ptr; + + if (jump->flags & JUMP_ADDR) + target_addr = jump->u.target; + else { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + target_addr = (sljit_uw)(code + jump->u.label->size); + } + inst = (sljit_ins*)jump->addr; + if (jump->flags & IS_COND) + inst--; + + /* B instructions. */ + if (jump->flags & IS_MOVABLE) { + diff = ((sljit_w)target_addr - (sljit_w)(inst)) >> 2; + if (diff <= SIMM_MAX && diff >= SIMM_MIN) { + jump->flags |= PATCH_B; + + if (!(jump->flags & IS_COND)) { + inst[0] = inst[-1]; + inst[-1] = (jump->flags & IS_JAL) ? BAL : B; + jump->addr -= sizeof(sljit_ins); + return inst; + } + saved_inst = inst[0]; + inst[0] = inst[-1]; + inst[-1] = saved_inst ^ invert_branch(jump->flags); + jump->addr -= 2 * sizeof(sljit_ins); + return inst; + } + } + + diff = ((sljit_w)target_addr - (sljit_w)(inst + 1)) >> 2; + if (diff <= SIMM_MAX && diff >= SIMM_MIN) { + jump->flags |= PATCH_B; + + if (!(jump->flags & IS_COND)) { + inst[0] = (jump->flags & IS_JAL) ? BAL : B; + inst[1] = NOP; + return inst + 1; + } + inst[0] = inst[0] ^ invert_branch(jump->flags); + inst[1] = NOP; + jump->addr -= sizeof(sljit_ins); + return inst + 1; + } + + if (jump->flags & IS_COND) { + if ((target_addr & ~0xfffffff) == ((jump->addr + 3 * sizeof(sljit_ins)) & ~0xfffffff)) { + jump->flags |= PATCH_J; + inst[0] = (inst[0] & 0xffff0000) | 3; + inst[1] = NOP; + inst[2] = J; + inst[3] = NOP; + jump->addr += sizeof(sljit_ins); + return inst + 3; + } + return code_ptr; + } + + /* J instuctions. */ + if (jump->flags & IS_MOVABLE) { + if ((target_addr & ~0xfffffff) == (jump->addr & ~0xfffffff)) { + jump->flags |= PATCH_J; + inst[0] = inst[-1]; + inst[-1] = (jump->flags & IS_JAL) ? JAL : J; + jump->addr -= sizeof(sljit_ins); + return inst; + } + } + + if ((target_addr & ~0xfffffff) == ((jump->addr + sizeof(sljit_ins)) & ~0xfffffff)) { + jump->flags |= PATCH_J; + inst[0] = (jump->flags & IS_JAL) ? JAL : J; + inst[1] = NOP; + return inst + 1; + } + + return code_ptr; +} + +#ifdef __GNUC__ +static __attribute__ ((noinline)) void sljit_cache_flush(void* code, void* code_ptr) +{ + SLJIT_CACHE_FLUSH(code, code_ptr); +} +#endif + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + sljit_ins *code; + sljit_ins *code_ptr; + sljit_ins *buf_ptr; + sljit_ins *buf_end; + sljit_uw word_count; + sljit_uw addr; + + struct sljit_label *label; + struct sljit_jump *jump; + struct sljit_const *const_; + + CHECK_ERROR_PTR(); + check_sljit_generate_code(compiler); + reverse_buf(compiler); + + code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins)); + PTR_FAIL_WITH_EXEC_IF(code); + buf = compiler->buf; + + code_ptr = code; + word_count = 0; + label = compiler->labels; + jump = compiler->jumps; + const_ = compiler->consts; + do { + buf_ptr = (sljit_ins*)buf->memory; + buf_end = buf_ptr + (buf->used_size >> 2); + do { + *code_ptr = *buf_ptr++; + SLJIT_ASSERT(!label || label->size >= word_count); + SLJIT_ASSERT(!jump || jump->addr >= word_count); + SLJIT_ASSERT(!const_ || const_->addr >= word_count); + /* These structures are ordered by their address. */ + if (label && label->size == word_count) { + /* Just recording the address. */ + label->addr = (sljit_uw)code_ptr; + label->size = code_ptr - code; + label = label->next; + } + if (jump && jump->addr == word_count) { +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + jump->addr = (sljit_uw)(code_ptr - 3); +#else + jump->addr = (sljit_uw)(code_ptr - 6); +#endif + code_ptr = optimize_jump(jump, code_ptr, code); + jump = jump->next; + } + if (const_ && const_->addr == word_count) { + /* Just recording the address. */ + const_->addr = (sljit_uw)code_ptr; + const_ = const_->next; + } + code_ptr ++; + word_count ++; + } while (buf_ptr < buf_end); + + buf = buf->next; + } while (buf); + + if (label && label->size == word_count) { + label->addr = (sljit_uw)code_ptr; + label->size = code_ptr - code; + label = label->next; + } + + SLJIT_ASSERT(!label); + SLJIT_ASSERT(!jump); + SLJIT_ASSERT(!const_); + SLJIT_ASSERT(code_ptr - code <= (int)compiler->size); + + jump = compiler->jumps; + while (jump) { + do { + addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; + buf_ptr = (sljit_ins*)jump->addr; + + if (jump->flags & PATCH_B) { + addr = (sljit_w)(addr - (jump->addr + sizeof(sljit_ins))) >> 2; + SLJIT_ASSERT((sljit_w)addr <= SIMM_MAX && (sljit_w)addr >= SIMM_MIN); + buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | (addr & 0xffff); + break; + } + if (jump->flags & PATCH_J) { + SLJIT_ASSERT((addr & ~0xfffffff) == ((jump->addr + sizeof(sljit_ins)) & ~0xfffffff)); + buf_ptr[0] |= (addr >> 2) & 0x03ffffff; + break; + } + + /* Set the fields of immediate loads. */ +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((addr >> 16) & 0xffff); + buf_ptr[1] = (buf_ptr[1] & 0xffff0000) | (addr & 0xffff); +#else + buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((addr >> 48) & 0xffff); + buf_ptr[1] = (buf_ptr[1] & 0xffff0000) | ((addr >> 32) & 0xffff); + buf_ptr[3] = (buf_ptr[3] & 0xffff0000) | ((addr >> 16) & 0xffff); + buf_ptr[4] = (buf_ptr[4] & 0xffff0000) | (addr & 0xffff); +#endif + } while (0); + jump = jump->next; + } + + compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_size = compiler->size * sizeof(sljit_ins); +#ifndef __GNUC__ + SLJIT_CACHE_FLUSH(code, code_ptr); +#else + /* GCC workaround for invalid code generation with -O2. */ + sljit_cache_flush(code, code_ptr); +#endif + return code; +} + +/* Creates an index in data_transfer_insts array. */ +#define WORD_DATA 0x00 +#define BYTE_DATA 0x01 +#define HALF_DATA 0x02 +#define INT_DATA 0x03 +#define SIGNED_DATA 0x04 +#define LOAD_DATA 0x08 + +#define MEM_MASK 0x0f + +#define WRITE_BACK 0x00010 +#define ARG_TEST 0x00020 +#define CUMULATIVE_OP 0x00040 +#define LOGICAL_OP 0x00080 +#define IMM_OP 0x00100 +#define SRC2_IMM 0x00200 + +#define UNUSED_DEST 0x00400 +#define REG_DEST 0x00800 +#define REG1_SOURCE 0x01000 +#define REG2_SOURCE 0x02000 +#define SLOW_SRC1 0x04000 +#define SLOW_SRC2 0x08000 +#define SLOW_DEST 0x10000 + +/* Only these flags are set. UNUSED_DEST is not set when no flags should be set. */ +#define CHECK_FLAGS(list) \ + (!(flags & UNUSED_DEST) || (op & GET_FLAGS(~(list)))) + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#include "sljitNativeMIPS_32.c" +#else +#include "sljitNativeMIPS_64.c" +#endif + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#define STACK_STORE SW +#define STACK_LOAD LW +#else +#define STACK_STORE SD +#define STACK_LOAD LD +#endif + +static int emit_op(struct sljit_compiler *compiler, int op, int inp_flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w); + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + sljit_ins base; + + CHECK_ERROR(); + check_sljit_emit_enter(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + compiler->has_locals = local_size > 0; + local_size += (saveds + 2 + 4) * sizeof(sljit_w); + local_size = (local_size + 15) & ~0xf; + compiler->local_size = local_size; + + if (local_size <= SIMM_MAX) { + /* Frequent case. */ + FAIL_IF(push_inst(compiler, ADDIU_W | S(REAL_STACK_PTR) | T(REAL_STACK_PTR) | IMM(-local_size), DR(REAL_STACK_PTR))); + base = S(REAL_STACK_PTR); + } + else { + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), local_size)); + FAIL_IF(push_inst(compiler, ADDU_W | S(REAL_STACK_PTR) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); + FAIL_IF(push_inst(compiler, SUBU_W | S(REAL_STACK_PTR) | T(TMP_REG1) | D(REAL_STACK_PTR), DR(REAL_STACK_PTR))); + base = S(TMP_REG2); + local_size = 0; + } + + FAIL_IF(push_inst(compiler, STACK_STORE | base | TA(RETURN_ADDR_REG) | IMM(local_size - 1 * (int)sizeof(sljit_w)), MOVABLE_INS)); + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, STACK_STORE | base | T(SLJIT_LOCALS_REG) | IMM(local_size - 2 * (int)sizeof(sljit_w)), MOVABLE_INS)); + if (saveds >= 1) + FAIL_IF(push_inst(compiler, STACK_STORE | base | T(SLJIT_SAVED_REG1) | IMM(local_size - 3 * (int)sizeof(sljit_w)), MOVABLE_INS)); + if (saveds >= 2) + FAIL_IF(push_inst(compiler, STACK_STORE | base | T(SLJIT_SAVED_REG2) | IMM(local_size - 4 * (int)sizeof(sljit_w)), MOVABLE_INS)); + if (saveds >= 3) + FAIL_IF(push_inst(compiler, STACK_STORE | base | T(SLJIT_SAVED_REG3) | IMM(local_size - 5 * (int)sizeof(sljit_w)), MOVABLE_INS)); + if (saveds >= 4) + FAIL_IF(push_inst(compiler, STACK_STORE | base | T(SLJIT_SAVED_EREG1) | IMM(local_size - 6 * (int)sizeof(sljit_w)), MOVABLE_INS)); + if (saveds >= 5) + FAIL_IF(push_inst(compiler, STACK_STORE | base | T(SLJIT_SAVED_EREG2) | IMM(local_size - 7 * (int)sizeof(sljit_w)), MOVABLE_INS)); + + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, ADDIU_W | S(REAL_STACK_PTR) | T(SLJIT_LOCALS_REG) | IMM(4 * sizeof(sljit_w)), DR(SLJIT_LOCALS_REG))); + + if (args >= 1) + FAIL_IF(push_inst(compiler, ADDU_W | SA(4) | TA(0) | D(SLJIT_SAVED_REG1), DR(SLJIT_SAVED_REG1))); + if (args >= 2) + FAIL_IF(push_inst(compiler, ADDU_W | SA(5) | TA(0) | D(SLJIT_SAVED_REG2), DR(SLJIT_SAVED_REG2))); + if (args >= 3) + FAIL_IF(push_inst(compiler, ADDU_W | SA(6) | TA(0) | D(SLJIT_SAVED_REG3), DR(SLJIT_SAVED_REG3))); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + CHECK_ERROR_VOID(); + check_sljit_set_context(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + compiler->has_locals = local_size > 0; + local_size += (saveds + 2 + 4) * sizeof(sljit_w); + compiler->local_size = (local_size + 15) & ~0xf; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + int local_size; + sljit_ins base; + + CHECK_ERROR(); + check_sljit_emit_return(compiler, op, src, srcw); + + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + local_size = compiler->local_size; + if (local_size <= SIMM_MAX) + base = S(REAL_STACK_PTR); + else { + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), local_size)); + FAIL_IF(push_inst(compiler, ADDU_W | S(REAL_STACK_PTR) | T(TMP_REG1) | D(TMP_REG1), DR(TMP_REG1))); + base = S(TMP_REG1); + local_size = 0; + } + + FAIL_IF(push_inst(compiler, STACK_LOAD | base | TA(RETURN_ADDR_REG) | IMM(local_size - 1 * (int)sizeof(sljit_w)), RETURN_ADDR_REG)); + if (compiler->saveds >= 5) + FAIL_IF(push_inst(compiler, STACK_LOAD | base | T(SLJIT_SAVED_EREG2) | IMM(local_size - 7 * (int)sizeof(sljit_w)), DR(SLJIT_SAVED_EREG2))); + if (compiler->saveds >= 4) + FAIL_IF(push_inst(compiler, STACK_LOAD | base | T(SLJIT_SAVED_EREG1) | IMM(local_size - 6 * (int)sizeof(sljit_w)), DR(SLJIT_SAVED_EREG1))); + if (compiler->saveds >= 3) + FAIL_IF(push_inst(compiler, STACK_LOAD | base | T(SLJIT_SAVED_REG3) | IMM(local_size - 5 * (int)sizeof(sljit_w)), DR(SLJIT_SAVED_REG3))); + if (compiler->saveds >= 2) + FAIL_IF(push_inst(compiler, STACK_LOAD | base | T(SLJIT_SAVED_REG2) | IMM(local_size - 4 * (int)sizeof(sljit_w)), DR(SLJIT_SAVED_REG2))); + if (compiler->saveds >= 1) + FAIL_IF(push_inst(compiler, STACK_LOAD | base | T(SLJIT_SAVED_REG1) | IMM(local_size - 3 * (int)sizeof(sljit_w)), DR(SLJIT_SAVED_REG1))); + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, STACK_LOAD | base | T(SLJIT_LOCALS_REG) | IMM(local_size - 2 * (int)sizeof(sljit_w)), DR(SLJIT_LOCALS_REG))); + + FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS)); + if (compiler->local_size <= SIMM_MAX) + return push_inst(compiler, ADDIU_W | S(REAL_STACK_PTR) | T(REAL_STACK_PTR) | IMM(compiler->local_size), UNMOVABLE_INS); + else + return push_inst(compiler, ADDU_W | S(TMP_REG1) | TA(0) | D(REAL_STACK_PTR), UNMOVABLE_INS); +} + +#undef STACK_STORE +#undef STACK_LOAD + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#define ARCH_DEPEND(a, b) a +#else +#define ARCH_DEPEND(a, b) b +#endif + +static SLJIT_CONST sljit_ins data_transfer_insts[16] = { +/* s u w */ ARCH_DEPEND(HI(43) /* sw */, HI(63) /* sd */), +/* s u b */ HI(40) /* sb */, +/* s u h */ HI(41) /* sh*/, +/* s u i */ HI(43) /* sw */, + +/* s s w */ ARCH_DEPEND(HI(43) /* sw */, HI(63) /* sd */), +/* s s b */ HI(40) /* sb */, +/* s s h */ HI(41) /* sh*/, +/* s s i */ HI(43) /* sw */, + +/* l u w */ ARCH_DEPEND(HI(35) /* lw */, HI(55) /* ld */), +/* l u b */ HI(36) /* lbu */, +/* l u h */ HI(37) /* lhu */, +/* l u i */ ARCH_DEPEND(HI(35) /* lw */, HI(39) /* lwu */), + +/* l s w */ ARCH_DEPEND(HI(35) /* lw */, HI(55) /* ld */), +/* l s b */ HI(32) /* lb */, +/* l s h */ HI(33) /* lh */, +/* l s i */ HI(35) /* lw */, +}; + +/* reg_ar is an absoulute register! */ + +/* Can perform an operation using at most 1 instruction. */ +static int getput_arg_fast(struct sljit_compiler *compiler, int flags, int reg_ar, int arg, sljit_w argw) +{ + SLJIT_ASSERT(arg & SLJIT_MEM); + + if (!(flags & WRITE_BACK) && !(arg & 0xf0) && argw <= SIMM_MAX && argw >= SIMM_MIN) { + /* Works for both absoulte and relative addresses. */ + if (SLJIT_UNLIKELY(flags & ARG_TEST)) + return 1; + FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(arg & 0xf) | TA(reg_ar) | IMM(argw), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS)); + return -1; + } + return (flags & ARG_TEST) ? SLJIT_SUCCESS : 0; +} + +/* See getput_arg below. + Note: can_cache is called only for binary operators. Those + operators always uses word arguments without write back. */ +static int can_cache(int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + if (!(next_arg & SLJIT_MEM)) + return 0; + + /* Simple operation except for updates. */ + if (arg & 0xf0) { + argw &= 0x3; + next_argw &= 0x3; + if (argw && argw == next_argw && (arg == next_arg || (arg & 0xf0) == (next_arg & 0xf0))) + return 1; + return 0; + } + + if (arg == next_arg) { + if (((sljit_uw)(next_argw - argw) <= SIMM_MAX && (sljit_uw)(next_argw - argw) >= SIMM_MIN)) + return 1; + return 0; + } + + return 0; +} + +/* Emit the necessary instructions. See can_cache above. */ +static int getput_arg(struct sljit_compiler *compiler, int flags, int reg_ar, int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + int tmp_ar; + int base; + + SLJIT_ASSERT(arg & SLJIT_MEM); + if (!(next_arg & SLJIT_MEM)) { + next_arg = 0; + next_argw = 0; + } + + tmp_ar = (flags & LOAD_DATA) ? reg_ar : DR(TMP_REG3); + base = arg & 0xf; + + if (SLJIT_UNLIKELY(arg & 0xf0)) { + argw &= 0x3; + if ((flags & WRITE_BACK) && reg_ar == DR(base)) { + SLJIT_ASSERT(!(flags & LOAD_DATA) && DR(TMP_REG1) != reg_ar); + FAIL_IF(push_inst(compiler, ADDU_W | SA(reg_ar) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + reg_ar = DR(TMP_REG1); + } + + /* Using the cache. */ + if (argw == compiler->cache_argw) { + if (!(flags & WRITE_BACK)) { + if (arg == compiler->cache_arg) + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + if ((SLJIT_MEM | (arg & 0xf0)) == compiler->cache_arg) { + if (arg == next_arg && argw == (next_argw & 0x3)) { + compiler->cache_arg = arg; + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(TMP_REG3), DR(TMP_REG3))); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | DA(tmp_ar), tmp_ar)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + } + else { + if ((SLJIT_MEM | (arg & 0xf0)) == compiler->cache_arg) { + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base))); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + } + } + + if (SLJIT_UNLIKELY(argw)) { + compiler->cache_arg = SLJIT_MEM | (arg & 0xf0); + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, SLL_W | T((arg >> 4) & 0xf) | D(TMP_REG3) | SH_IMM(argw), DR(TMP_REG3))); + } + + if (!(flags & WRITE_BACK)) { + if (arg == next_arg && argw == (next_argw & 0x3)) { + compiler->cache_arg = arg; + compiler->cache_argw = argw; + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? ((arg >> 4) & 0xf) : TMP_REG3) | D(TMP_REG3), DR(TMP_REG3))); + tmp_ar = DR(TMP_REG3); + } + else + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? ((arg >> 4) & 0xf) : TMP_REG3) | DA(tmp_ar), tmp_ar)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(!argw ? ((arg >> 4) & 0xf) : TMP_REG3) | D(base), DR(base))); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + + if (SLJIT_UNLIKELY(flags & WRITE_BACK) && base) { + /* Update only applies if a base register exists. */ + if (reg_ar == DR(base)) { + SLJIT_ASSERT(!(flags & LOAD_DATA) && DR(TMP_REG1) != reg_ar); + if (argw <= SIMM_MAX && argw >= SIMM_MIN) { + FAIL_IF(push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar) | IMM(argw), MOVABLE_INS)); + if (argw) + return push_inst(compiler, ADDIU_W | S(base) | T(base) | IMM(argw), DR(base)); + return SLJIT_SUCCESS; + } + FAIL_IF(push_inst(compiler, ADDU_W | SA(reg_ar) | TA(0) | D(TMP_REG1), DR(TMP_REG1))); + reg_ar = DR(TMP_REG1); + } + + if (argw <= SIMM_MAX && argw >= SIMM_MIN) { + if (argw) + FAIL_IF(push_inst(compiler, ADDIU_W | S(base) | T(base) | IMM(argw), DR(base))); + } + else { + if (compiler->cache_arg == SLJIT_MEM && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { + if (argw != compiler->cache_argw) { + FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); + compiler->cache_argw = argw; + } + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base))); + } + else { + compiler->cache_arg = SLJIT_MEM; + compiler->cache_argw = argw; + FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw)); + FAIL_IF(push_inst(compiler, ADDU_W | S(base) | T(TMP_REG3) | D(base), DR(base))); + } + } + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(base) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + + if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { + if (argw != compiler->cache_argw) { + FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); + compiler->cache_argw = argw; + } + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + + if (compiler->cache_arg == SLJIT_MEM && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) { + if (argw != compiler->cache_argw) + FAIL_IF(push_inst(compiler, ADDIU_W | S(TMP_REG3) | T(TMP_REG3) | IMM(argw - compiler->cache_argw), DR(TMP_REG3))); + } + else { + compiler->cache_arg = SLJIT_MEM; + FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw)); + } + compiler->cache_argw = argw; + + if (!base) + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + + if (arg == next_arg && next_argw - argw <= SIMM_MAX && next_argw - argw >= SIMM_MIN) { + compiler->cache_arg = arg; + FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | D(TMP_REG3), DR(TMP_REG3))); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | S(TMP_REG3) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); + } + + FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(base) | DA(tmp_ar), tmp_ar)); + return push_inst(compiler, data_transfer_insts[flags & MEM_MASK] | SA(tmp_ar) | TA(reg_ar), (flags & LOAD_DATA) ? reg_ar : MOVABLE_INS); +} + +static SLJIT_INLINE int emit_op_mem(struct sljit_compiler *compiler, int flags, int reg_ar, int arg, sljit_w argw) +{ + if (getput_arg_fast(compiler, flags, reg_ar, arg, argw)) + return compiler->error; + compiler->cache_arg = 0; + compiler->cache_argw = 0; + return getput_arg(compiler, flags, reg_ar, arg, argw, 0, 0); +} + +static int emit_op(struct sljit_compiler *compiler, int op, int flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* arg1 goes to TMP_REG1 or src reg + arg2 goes to TMP_REG2, imm or src reg + TMP_REG3 can be used for caching + result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ + int dst_r = TMP_REG2; + int src1_r; + sljit_w src2_r = 0; + int sugg_src2_r = TMP_REG2; + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REG3) { + dst_r = dst; + flags |= REG_DEST; + if (GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_MOVU_SI) + sugg_src2_r = dst_r; + } + else if (dst == SLJIT_UNUSED) { + if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI && !(src2 & SLJIT_MEM)) + return SLJIT_SUCCESS; + if (GET_FLAGS(op)) + flags |= UNUSED_DEST; + } + else if ((dst & SLJIT_MEM) && !getput_arg_fast(compiler, flags | ARG_TEST, DR(TMP_REG1), dst, dstw)) + flags |= SLOW_DEST; + + if (flags & IMM_OP) { + if ((src2 & SLJIT_IMM) && src2w) { + if ((!(flags & LOGICAL_OP) && (src2w <= SIMM_MAX && src2w >= SIMM_MIN)) + || ((flags & LOGICAL_OP) && !(src2w & ~UIMM_MAX))) { + flags |= SRC2_IMM; + src2_r = src2w; + } + } + if ((src1 & SLJIT_IMM) && src1w && (flags & CUMULATIVE_OP) && !(flags & SRC2_IMM)) { + if ((!(flags & LOGICAL_OP) && (src1w <= SIMM_MAX && src1w >= SIMM_MIN)) + || ((flags & LOGICAL_OP) && !(src1w & ~UIMM_MAX))) { + flags |= SRC2_IMM; + src2_r = src1w; + + /* And swap arguments. */ + src1 = src2; + src1w = src2w; + src2 = SLJIT_IMM; + /* src2w = src2_r unneeded. */ + } + } + } + + /* Source 1. */ + if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= TMP_REG3) { + src1_r = src1; + flags |= REG1_SOURCE; + } + else if (src1 & SLJIT_IMM) { + if (src1w) { + FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w)); + src1_r = TMP_REG1; + } + else + src1_r = 0; + } + else { + if (getput_arg_fast(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w)) + FAIL_IF(compiler->error); + else + flags |= SLOW_SRC1; + src1_r = TMP_REG1; + } + + /* Source 2. */ + if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= TMP_REG3) { + src2_r = src2; + flags |= REG2_SOURCE; + if (!(flags & REG_DEST) && GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_MOVU_SI) + dst_r = src2_r; + } + else if (src2 & SLJIT_IMM) { + if (!(flags & SRC2_IMM)) { + if (src2w || (GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_MOVU_SI)) { + FAIL_IF(load_immediate(compiler, DR(sugg_src2_r), src2w)); + src2_r = sugg_src2_r; + } + else + src2_r = 0; + } + } + else { + if (getput_arg_fast(compiler, flags | LOAD_DATA, DR(sugg_src2_r), src2, src2w)) + FAIL_IF(compiler->error); + else + flags |= SLOW_SRC2; + src2_r = sugg_src2_r; + } + + if ((flags & (SLOW_SRC1 | SLOW_SRC2)) == (SLOW_SRC1 | SLOW_SRC2)) { + SLJIT_ASSERT(src2_r == TMP_REG2); + if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG2), src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, dst, dstw)); + } + else { + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG2), src2, src2w, dst, dstw)); + } + } + else if (flags & SLOW_SRC1) + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(TMP_REG1), src1, src1w, dst, dstw)); + else if (flags & SLOW_SRC2) + FAIL_IF(getput_arg(compiler, flags | LOAD_DATA, DR(sugg_src2_r), src2, src2w, dst, dstw)); + + FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); + + if (dst & SLJIT_MEM) { + if (!(flags & SLOW_DEST)) { + getput_arg_fast(compiler, flags, DR(dst_r), dst, dstw); + return compiler->error; + } + return getput_arg(compiler, flags, DR(dst_r), dst, dstw, 0, 0); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + CHECK_ERROR(); + check_sljit_emit_op0(compiler, op); + + op = GET_OPCODE(op); + switch (op) { + case SLJIT_BREAKPOINT: + return push_inst(compiler, BREAK, UNMOVABLE_INS); + case SLJIT_NOP: + return push_inst(compiler, NOP, UNMOVABLE_INS); + case SLJIT_UMUL: + case SLJIT_SMUL: + FAIL_IF(push_inst(compiler, (op == SLJIT_UMUL ? MULTU : MULT) | S(SLJIT_TEMPORARY_REG1) | T(SLJIT_TEMPORARY_REG2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, MFLO | D(SLJIT_TEMPORARY_REG1), DR(SLJIT_TEMPORARY_REG1))); + return push_inst(compiler, MFHI | D(SLJIT_TEMPORARY_REG2), DR(SLJIT_TEMPORARY_REG2)); + case SLJIT_UDIV: + case SLJIT_SDIV: +#if !(defined SLJIT_MIPS_32_64 && SLJIT_MIPS_32_64) + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); +#endif + FAIL_IF(push_inst(compiler, (op == SLJIT_UDIV ? DIVU : DIV) | S(SLJIT_TEMPORARY_REG1) | T(SLJIT_TEMPORARY_REG2), MOVABLE_INS)); + FAIL_IF(push_inst(compiler, MFLO | D(SLJIT_TEMPORARY_REG1), DR(SLJIT_TEMPORARY_REG1))); + return push_inst(compiler, MFHI | D(SLJIT_TEMPORARY_REG2), DR(SLJIT_TEMPORARY_REG2)); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + #define inp_flags 0 +#endif + + CHECK_ERROR(); + check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw); + + SLJIT_COMPILE_ASSERT(SLJIT_MOV + 7 == SLJIT_MOVU, movu_offset); + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: + return emit_op(compiler, SLJIT_MOV, inp_flags | WORD_DATA, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_UI: + return emit_op(compiler, SLJIT_MOV_UI, inp_flags | INT_DATA, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_SI: + return emit_op(compiler, SLJIT_MOV_SI, inp_flags | INT_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_UB: + return emit_op(compiler, SLJIT_MOV_UB, inp_flags | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw); + + case SLJIT_MOV_SB: + return emit_op(compiler, SLJIT_MOV_SB, inp_flags | BYTE_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw); + + case SLJIT_MOV_UH: + return emit_op(compiler, SLJIT_MOV_UH, inp_flags | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw); + + case SLJIT_MOV_SH: + return emit_op(compiler, SLJIT_MOV_SH, inp_flags | HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw); + + case SLJIT_MOVU: + return emit_op(compiler, SLJIT_MOV, inp_flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_UI: + return emit_op(compiler, SLJIT_MOV_UI, inp_flags | INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_SI: + return emit_op(compiler, SLJIT_MOV_SI, inp_flags | INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_UB: + return emit_op(compiler, SLJIT_MOV_UB, inp_flags | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw); + + case SLJIT_MOVU_SB: + return emit_op(compiler, SLJIT_MOV_SB, inp_flags | BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw); + + case SLJIT_MOVU_UH: + return emit_op(compiler, SLJIT_MOV_UH, inp_flags | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw); + + case SLJIT_MOVU_SH: + return emit_op(compiler, SLJIT_MOV_SH, inp_flags | HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw); + + case SLJIT_NOT: + return emit_op(compiler, op, inp_flags, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_NEG: + return emit_op(compiler, SLJIT_SUB | GET_ALL_FLAGS(op), inp_flags | IMM_OP, dst, dstw, SLJIT_IMM, 0, src, srcw); + + case SLJIT_CLZ: + return emit_op(compiler, op, inp_flags, dst, dstw, TMP_REG1, 0, src, srcw); + } + + return SLJIT_SUCCESS; +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + #undef inp_flags +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + #define inp_flags 0 +#endif + + CHECK_ERROR(); + check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + case SLJIT_ADDC: + return emit_op(compiler, op, inp_flags | CUMULATIVE_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SUB: + case SLJIT_SUBC: + return emit_op(compiler, op, inp_flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_MUL: + return emit_op(compiler, op, inp_flags | CUMULATIVE_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_AND: + case SLJIT_OR: + case SLJIT_XOR: + return emit_op(compiler, op, inp_flags | CUMULATIVE_OP | LOGICAL_OP | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SHL: + case SLJIT_LSHR: + case SLJIT_ASHR: +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + if (src2 & SLJIT_IMM) + src2w &= 0x1f; +#else + if (src2 & SLJIT_IMM) + src2w &= 0x3f; +#endif + return emit_op(compiler, op, inp_flags | IMM_OP, dst, dstw, src1, src1w, src2, src2w); + } + + return SLJIT_SUCCESS; +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) + #undef inp_flags +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_register_index(int reg) +{ + check_sljit_get_register_index(reg); + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + CHECK_ERROR(); + check_sljit_emit_op_custom(compiler, instruction, size); + SLJIT_ASSERT(size == 4); + + return push_inst(compiler, *(sljit_ins*)instruction, UNMOVABLE_INS); +} + +/* --------------------------------------------------------------------- */ +/* Floating point operators */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ +#if (defined SLJIT_QEMU && SLJIT_QEMU) + /* Qemu says fir is 0 by default. */ + return 1; +#elif defined(__GNUC__) + sljit_w fir; + asm ("cfc1 %0, $0" : "=r"(fir)); + return (fir >> 22) & 0x1; +#else +#error "FIR check is not implemented for this architecture" +#endif +} + +static int emit_fpu_data_transfer(struct sljit_compiler *compiler, int fpu_reg, int load, int arg, sljit_w argw) +{ + int hi_reg; + + SLJIT_ASSERT(arg & SLJIT_MEM); + + /* Fast loads and stores. */ + if (!(arg & 0xf0)) { + /* Both for (arg & 0xf) == SLJIT_UNUSED and (arg & 0xf) != SLJIT_UNUSED. */ + if (argw <= SIMM_MAX && argw >= SIMM_MIN) + return push_inst(compiler, (load ? LDC1 : SDC1) | S(arg & 0xf) | FT(fpu_reg) | IMM(argw), MOVABLE_INS); + } + + if (arg & 0xf0) { + argw &= 0x3; + hi_reg = (arg >> 4) & 0xf; + if (argw) { + FAIL_IF(push_inst(compiler, SLL_W | T(hi_reg) | D(TMP_REG1) | SH_IMM(argw), DR(TMP_REG1))); + hi_reg = TMP_REG1; + } + FAIL_IF(push_inst(compiler, ADDU_W | S(hi_reg) | T(arg & 0xf) | D(TMP_REG1), DR(TMP_REG1))); + return push_inst(compiler, (load ? LDC1 : SDC1) | S(TMP_REG1) | FT(fpu_reg) | IMM(0), MOVABLE_INS); + } + + /* Use cache. */ + if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) + return push_inst(compiler, (load ? LDC1 : SDC1) | S(TMP_REG3) | FT(fpu_reg) | IMM(argw - compiler->cache_argw), MOVABLE_INS); + + /* Put value to cache. */ + compiler->cache_arg = arg; + compiler->cache_argw = argw; + + FAIL_IF(load_immediate(compiler, DR(TMP_REG3), argw)); + if (arg & 0xf) + FAIL_IF(push_inst(compiler, ADDU_W | S(TMP_REG3) | T(arg & 0xf) | D(TMP_REG3), DR(TMP_REG3))); + return push_inst(compiler, (load ? LDC1 : SDC1) | S(TMP_REG3) | FT(fpu_reg) | IMM(0), MOVABLE_INS); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int dst_fr; + + CHECK_ERROR(); + check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (GET_OPCODE(op) == SLJIT_FCMP) { + if (dst > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, dst, dstw)); + dst = TMP_FREG1; + } + if (src > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src, srcw)); + src = TMP_FREG2; + } + + /* src and dst are swapped. */ + if (op & SLJIT_SET_E) { + FAIL_IF(push_inst(compiler, C_UEQ_D | FT(src) | FS(dst), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, CFC1 | TA(EQUAL_FLAG) | DA(FCSR_REG), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, SRL | TA(EQUAL_FLAG) | DA(EQUAL_FLAG) | SH_IMM(23), EQUAL_FLAG)); + FAIL_IF(push_inst(compiler, ANDI | SA(EQUAL_FLAG) | TA(EQUAL_FLAG) | IMM(1), EQUAL_FLAG)); + } + if (op & SLJIT_SET_S) { + /* Mixing the instructions for the two checks. */ + FAIL_IF(push_inst(compiler, C_ULT_D | FT(src) | FS(dst), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, CFC1 | TA(ULESS_FLAG) | DA(FCSR_REG), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, C_ULT_D | FT(dst) | FS(src), UNMOVABLE_INS)); + FAIL_IF(push_inst(compiler, SRL | TA(ULESS_FLAG) | DA(ULESS_FLAG) | SH_IMM(23), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, ANDI | SA(ULESS_FLAG) | TA(ULESS_FLAG) | IMM(1), ULESS_FLAG)); + FAIL_IF(push_inst(compiler, CFC1 | TA(UGREATER_FLAG) | DA(FCSR_REG), UGREATER_FLAG)); + FAIL_IF(push_inst(compiler, SRL | TA(UGREATER_FLAG) | DA(UGREATER_FLAG) | SH_IMM(23), UGREATER_FLAG)); + FAIL_IF(push_inst(compiler, ANDI | SA(UGREATER_FLAG) | TA(UGREATER_FLAG) | IMM(1), UGREATER_FLAG)); + } + return push_inst(compiler, C_UN_D | FT(src) | FS(dst), FCSR_FCC); + } + + dst_fr = (dst > SLJIT_FLOAT_REG4) ? TMP_FREG1 : dst; + + if (src > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, dst_fr, 1, src, srcw)); + src = dst_fr; + } + + switch (op) { + case SLJIT_FMOV: + if (src != dst_fr && dst_fr != TMP_FREG1) + FAIL_IF(push_inst(compiler, MOV_D | FS(src) | FD(dst_fr), MOVABLE_INS)); + break; + case SLJIT_FNEG: + FAIL_IF(push_inst(compiler, NEG_D | FS(src) | FD(dst_fr), MOVABLE_INS)); + break; + case SLJIT_FABS: + FAIL_IF(push_inst(compiler, ABS_D | FS(src) | FD(dst_fr), MOVABLE_INS)); + break; + } + + if (dst_fr == TMP_FREG1) + FAIL_IF(emit_fpu_data_transfer(compiler, src, 0, dst, dstw)); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int dst_fr; + + CHECK_ERROR(); + check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + dst_fr = (dst > SLJIT_FLOAT_REG4) ? TMP_FREG1 : dst; + + if (src2 > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src2, src2w)); + src2 = TMP_FREG2; + } + + if (src1 > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, src1, src1w)); + src1 = TMP_FREG1; + } + + switch (op) { + case SLJIT_FADD: + FAIL_IF(push_inst(compiler, ADD_D | FT(src2) | FS(src1) | FD(dst_fr), MOVABLE_INS)); + break; + + case SLJIT_FSUB: + FAIL_IF(push_inst(compiler, SUB_D | FT(src2) | FS(src1) | FD(dst_fr), MOVABLE_INS)); + break; + + case SLJIT_FMUL: + FAIL_IF(push_inst(compiler, MUL_D | FT(src2) | FS(src1) | FD(dst_fr), MOVABLE_INS)); + break; + + case SLJIT_FDIV: + FAIL_IF(push_inst(compiler, DIV_D | FT(src2) | FS(src1) | FD(dst_fr), MOVABLE_INS)); + break; + } + + if (dst_fr == TMP_FREG1) + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 0, dst, dstw)); + + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Other instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + CHECK_ERROR(); + check_sljit_emit_fast_enter(compiler, dst, dstw, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + compiler->has_locals = local_size > 0; + local_size += (saveds + 2 + 4) * sizeof(sljit_w); + compiler->local_size = (local_size + 15) & ~0xf; + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) + return push_inst(compiler, ADDU_W | SA(RETURN_ADDR_REG) | TA(0) | D(dst), DR(dst)); + else if (dst & SLJIT_MEM) + return emit_op_mem(compiler, WORD_DATA, RETURN_ADDR_REG, dst, dstw); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + CHECK_ERROR(); + check_sljit_emit_fast_return(compiler, src, srcw); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | DA(RETURN_ADDR_REG), RETURN_ADDR_REG)); + else if (src & SLJIT_MEM) + FAIL_IF(emit_op_mem(compiler, WORD_DATA | LOAD_DATA, RETURN_ADDR_REG, src, srcw)); + else if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, RETURN_ADDR_REG, srcw)); + + FAIL_IF(push_inst(compiler, JR | SA(RETURN_ADDR_REG), UNMOVABLE_INS)); + return push_inst(compiler, NOP, UNMOVABLE_INS); +} + +/* --------------------------------------------------------------------- */ +/* Conditional instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + struct sljit_label *label; + + CHECK_ERROR_PTR(); + check_sljit_emit_label(compiler); + + if (compiler->last_label && compiler->last_label->size == compiler->size) + return compiler->last_label; + + label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); + PTR_FAIL_IF(!label); + set_label(label, compiler); + compiler->delay_slot = UNMOVABLE_INS; + return label; +} + +#if (defined SLJIT_CONFIG_MIPS_32 && SLJIT_CONFIG_MIPS_32) +#define JUMP_LENGTH 4 +#else +#define JUMP_LENGTH 7 +#endif + +#define BR_Z(src) \ + inst = BEQ | SA(src) | TA(0) | JUMP_LENGTH; \ + flags = IS_BIT26_COND; \ + delay_check = src; + +#define BR_NZ(src) \ + inst = BNE | SA(src) | TA(0) | JUMP_LENGTH; \ + flags = IS_BIT26_COND; \ + delay_check = src; + +#define BR_T() \ + inst = BC1T | JUMP_LENGTH; \ + flags = IS_BIT16_COND; \ + delay_check = FCSR_FCC; + +#define BR_F() \ + inst = BC1F | JUMP_LENGTH; \ + flags = IS_BIT16_COND; \ + delay_check = FCSR_FCC; + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + struct sljit_jump *jump; + sljit_ins inst; + int flags = 0; + int delay_check = UNMOVABLE_INS; + + CHECK_ERROR_PTR(); + check_sljit_emit_jump(compiler, type); + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + switch (type) { + case SLJIT_C_EQUAL: + case SLJIT_C_FLOAT_NOT_EQUAL: + BR_NZ(EQUAL_FLAG); + break; + case SLJIT_C_NOT_EQUAL: + case SLJIT_C_FLOAT_EQUAL: + BR_Z(EQUAL_FLAG); + break; + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + BR_Z(ULESS_FLAG); + break; + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + BR_NZ(ULESS_FLAG); + break; + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + BR_Z(UGREATER_FLAG); + break; + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + BR_NZ(UGREATER_FLAG); + break; + case SLJIT_C_SIG_LESS: + BR_Z(LESS_FLAG); + break; + case SLJIT_C_SIG_GREATER_EQUAL: + BR_NZ(LESS_FLAG); + break; + case SLJIT_C_SIG_GREATER: + BR_Z(GREATER_FLAG); + break; + case SLJIT_C_SIG_LESS_EQUAL: + BR_NZ(GREATER_FLAG); + break; + case SLJIT_C_OVERFLOW: + case SLJIT_C_MUL_OVERFLOW: + BR_Z(OVERFLOW_FLAG); + break; + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_MUL_NOT_OVERFLOW: + BR_NZ(OVERFLOW_FLAG); + break; + case SLJIT_C_FLOAT_NAN: + BR_F(); + break; + case SLJIT_C_FLOAT_NOT_NAN: + BR_T(); + break; + default: + /* Not conditional branch. */ + inst = 0; + break; + } + + jump->flags |= flags; + if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != delay_check)) + jump->flags |= IS_MOVABLE; + + if (inst) + PTR_FAIL_IF(push_inst(compiler, inst, UNMOVABLE_INS)); + + PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); + if (type <= SLJIT_JUMP) { + PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + } else { + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); + /* Cannot be optimized out if type is >= CALL0. */ + jump->flags |= IS_JAL | (type >= SLJIT_CALL0 ? SLJIT_REWRITABLE_JUMP : 0); + PTR_FAIL_IF(push_inst(compiler, JALR | S(TMP_REG2) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + jump->addr = compiler->size; + /* A NOP if type < CALL1. */ + PTR_FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_TEMPORARY_REG1) | TA(0) | DA(4), UNMOVABLE_INS)); + } + return jump; +} + +#define RESOLVE_IMM1() \ + if (src1 & SLJIT_IMM) { \ + if (src1w) { \ + PTR_FAIL_IF(load_immediate(compiler, DR(TMP_REG1), src1w)); \ + src1 = TMP_REG1; \ + } \ + else \ + src1 = 0; \ + } + +#define RESOLVE_IMM2() \ + if (src2 & SLJIT_IMM) { \ + if (src2w) { \ + PTR_FAIL_IF(load_immediate(compiler, DR(TMP_REG2), src2w)); \ + src2 = TMP_REG2; \ + } \ + else \ + src2 = 0; \ + } + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_cmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + struct sljit_jump *jump; + int flags; + sljit_ins inst; + + CHECK_ERROR_PTR(); + check_sljit_emit_cmp(compiler, type, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + flags = ((type & SLJIT_INT_OP) ? INT_DATA : WORD_DATA) | LOAD_DATA; + if (src1 & SLJIT_MEM) { + if (getput_arg_fast(compiler, flags, DR(TMP_REG1), src1, src1w)) + PTR_FAIL_IF(compiler->error); + else + PTR_FAIL_IF(getput_arg(compiler, flags, DR(TMP_REG1), src1, src1w, src2, src2w)); + src1 = TMP_REG1; + } + if (src2 & SLJIT_MEM) { + if (getput_arg_fast(compiler, flags, DR(TMP_REG2), src2, src2w)) + PTR_FAIL_IF(compiler->error); + else + PTR_FAIL_IF(getput_arg(compiler, flags, DR(TMP_REG2), src2, src2w, 0, 0)); + src2 = TMP_REG2; + } + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + if (type <= SLJIT_C_NOT_EQUAL) { + RESOLVE_IMM1(); + RESOLVE_IMM2(); + jump->flags |= IS_BIT26_COND; + if (compiler->delay_slot == MOVABLE_INS || (compiler->delay_slot != UNMOVABLE_INS && compiler->delay_slot != DR(src1) && compiler->delay_slot != DR(src2))) + jump->flags |= IS_MOVABLE; + PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_C_EQUAL ? BNE : BEQ) | S(src1) | T(src2) | JUMP_LENGTH, UNMOVABLE_INS)); + } + else if (type >= SLJIT_C_SIG_LESS && (((src1 & SLJIT_IMM) && (src1w == 0)) || ((src2 & SLJIT_IMM) && (src2w == 0)))) { + inst = NOP; + if ((src1 & SLJIT_IMM) && (src1w == 0)) { + RESOLVE_IMM2(); + switch (type) { + case SLJIT_C_SIG_LESS: + inst = BLEZ; + jump->flags |= IS_BIT26_COND; + break; + case SLJIT_C_SIG_GREATER_EQUAL: + inst = BGTZ; + jump->flags |= IS_BIT26_COND; + break; + case SLJIT_C_SIG_GREATER: + inst = BGEZ; + jump->flags |= IS_BIT16_COND; + break; + case SLJIT_C_SIG_LESS_EQUAL: + inst = BLTZ; + jump->flags |= IS_BIT16_COND; + break; + } + src1 = src2; + } + else { + RESOLVE_IMM1(); + switch (type) { + case SLJIT_C_SIG_LESS: + inst = BGEZ; + jump->flags |= IS_BIT16_COND; + break; + case SLJIT_C_SIG_GREATER_EQUAL: + inst = BLTZ; + jump->flags |= IS_BIT16_COND; + break; + case SLJIT_C_SIG_GREATER: + inst = BLEZ; + jump->flags |= IS_BIT26_COND; + break; + case SLJIT_C_SIG_LESS_EQUAL: + inst = BGTZ; + jump->flags |= IS_BIT26_COND; + break; + } + } + PTR_FAIL_IF(push_inst(compiler, inst | S(src1) | JUMP_LENGTH, UNMOVABLE_INS)); + } + else { + if (type == SLJIT_C_LESS || type == SLJIT_C_GREATER_EQUAL || type == SLJIT_C_SIG_LESS || type == SLJIT_C_SIG_GREATER_EQUAL) { + RESOLVE_IMM1(); + if ((src2 & SLJIT_IMM) && src2w <= SIMM_MAX && src2w >= SIMM_MIN) + PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_C_LESS_EQUAL ? SLTIU : SLTI) | S(src1) | T(TMP_REG1) | IMM(src2w), DR(TMP_REG1))); + else { + RESOLVE_IMM2(); + PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_C_LESS_EQUAL ? SLTU : SLT) | S(src1) | T(src2) | D(TMP_REG1), DR(TMP_REG1))); + } + type = (type == SLJIT_C_LESS || type == SLJIT_C_SIG_LESS) ? SLJIT_C_NOT_EQUAL : SLJIT_C_EQUAL; + } + else { + RESOLVE_IMM2(); + if ((src1 & SLJIT_IMM) && src1w <= SIMM_MAX && src1w >= SIMM_MIN) + PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_C_LESS_EQUAL ? SLTIU : SLTI) | S(src2) | T(TMP_REG1) | IMM(src1w), DR(TMP_REG1))); + else { + RESOLVE_IMM1(); + PTR_FAIL_IF(push_inst(compiler, (type <= SLJIT_C_LESS_EQUAL ? SLTU : SLT) | S(src2) | T(src1) | D(TMP_REG1), DR(TMP_REG1))); + } + type = (type == SLJIT_C_GREATER || type == SLJIT_C_SIG_GREATER) ? SLJIT_C_NOT_EQUAL : SLJIT_C_EQUAL; + } + + jump->flags |= IS_BIT26_COND; + PTR_FAIL_IF(push_inst(compiler, (type == SLJIT_C_EQUAL ? BNE : BEQ) | S(TMP_REG1) | TA(0) | JUMP_LENGTH, UNMOVABLE_INS)); + } + + PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); + PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + return jump; +} + +#undef RESOLVE_IMM1 +#undef RESOLVE_IMM2 + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_fcmp(struct sljit_compiler *compiler, int type, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + struct sljit_jump *jump; + sljit_ins inst; + int if_true; + + CHECK_ERROR_PTR(); + check_sljit_emit_fcmp(compiler, type, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (src1 > SLJIT_FLOAT_REG4) { + PTR_FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, src1, src1w)); + src1 = TMP_FREG1; + } + if (src2 > SLJIT_FLOAT_REG4) { + PTR_FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src2, src2w)); + src2 = TMP_FREG2; + } + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + jump->flags |= IS_BIT16_COND; + type &= 0xff; + + switch (type) { + case SLJIT_C_FLOAT_EQUAL: + inst = C_UEQ_D; + if_true = 1; + break; + case SLJIT_C_FLOAT_NOT_EQUAL: + inst = C_UEQ_D; + if_true = 0; + break; + case SLJIT_C_FLOAT_LESS: + inst = C_ULT_D; + if_true = 1; + break; + case SLJIT_C_FLOAT_GREATER_EQUAL: + inst = C_ULT_D; + if_true = 0; + break; + case SLJIT_C_FLOAT_GREATER: + inst = C_ULE_D; + if_true = 0; + break; + case SLJIT_C_FLOAT_LESS_EQUAL: + inst = C_ULE_D; + if_true = 1; + break; + case SLJIT_C_FLOAT_NAN: + inst = C_UN_D; + if_true = 1; + break; + case SLJIT_C_FLOAT_NOT_NAN: + default: /* Make compilers happy. */ + inst = C_UN_D; + if_true = 0; + break; + } + + PTR_FAIL_IF(push_inst(compiler, inst | FT(src2) | FS(src1), UNMOVABLE_INS)); + /* Intentionally the other opcode. */ + PTR_FAIL_IF(push_inst(compiler, (if_true ? BC1F : BC1T) | JUMP_LENGTH, UNMOVABLE_INS)); + PTR_FAIL_IF(emit_const(compiler, TMP_REG2, 0)); + PTR_FAIL_IF(push_inst(compiler, JR | S(TMP_REG2), UNMOVABLE_INS)); + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + return jump; +} + +#undef JUMP_LENGTH +#undef BR_Z +#undef BR_NZ +#undef BR_T +#undef BR_F + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + int src_r = TMP_REG2; + struct sljit_jump *jump = NULL; + + CHECK_ERROR(); + check_sljit_emit_ijump(compiler, type, src, srcw); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) { + if (DR(src) != 4) + src_r = src; + else + FAIL_IF(push_inst(compiler, ADDU_W | S(src) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); + } + + if (type >= SLJIT_CALL0) { + SLJIT_ASSERT(DR(PIC_ADDR_REG) == 25 && PIC_ADDR_REG == TMP_REG2); + if (src & (SLJIT_IMM | SLJIT_MEM)) { + if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, DR(PIC_ADDR_REG), srcw)); + else { + SLJIT_ASSERT(src_r == TMP_REG2 && (src & SLJIT_MEM)); + FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + } + FAIL_IF(push_inst(compiler, JALR | S(PIC_ADDR_REG) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + /* We need an extra instruction in any case. */ + return push_inst(compiler, ADDU_W | S(SLJIT_TEMPORARY_REG1) | TA(0) | DA(4), UNMOVABLE_INS); + } + + /* Register input. */ + if (type >= SLJIT_CALL1) + FAIL_IF(push_inst(compiler, ADDU_W | S(SLJIT_TEMPORARY_REG1) | TA(0) | DA(4), 4)); + FAIL_IF(push_inst(compiler, JALR | S(src_r) | DA(RETURN_ADDR_REG), UNMOVABLE_INS)); + return push_inst(compiler, ADDU_W | S(src_r) | TA(0) | D(PIC_ADDR_REG), UNMOVABLE_INS); + } + + if (src & SLJIT_IMM) { + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + FAIL_IF(!jump); + set_jump(jump, compiler, JUMP_ADDR | ((type >= SLJIT_FAST_CALL) ? IS_JAL : 0)); + jump->u.target = srcw; + + if (compiler->delay_slot != UNMOVABLE_INS) + jump->flags |= IS_MOVABLE; + + FAIL_IF(emit_const(compiler, TMP_REG2, 0)); + } + else if (src & SLJIT_MEM) + FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + + FAIL_IF(push_inst(compiler, JR | S(src_r), UNMOVABLE_INS)); + if (jump) + jump->addr = compiler->size; + FAIL_IF(push_inst(compiler, NOP, UNMOVABLE_INS)); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + int sugg_dst_ar, dst_ar; + + CHECK_ERROR(); + check_sljit_emit_cond_value(compiler, op, dst, dstw, type); + + if (dst == SLJIT_UNUSED) + return SLJIT_SUCCESS; + + sugg_dst_ar = DR((op == SLJIT_MOV && dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG2); + + switch (type) { + case SLJIT_C_EQUAL: + case SLJIT_C_NOT_EQUAL: + FAIL_IF(push_inst(compiler, SLTIU | SA(EQUAL_FLAG) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); + dst_ar = sugg_dst_ar; + break; + case SLJIT_C_LESS: + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_LESS: + case SLJIT_C_FLOAT_GREATER_EQUAL: + dst_ar = ULESS_FLAG; + break; + case SLJIT_C_GREATER: + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_GREATER: + case SLJIT_C_FLOAT_LESS_EQUAL: + dst_ar = UGREATER_FLAG; + break; + case SLJIT_C_SIG_LESS: + case SLJIT_C_SIG_GREATER_EQUAL: + dst_ar = LESS_FLAG; + break; + case SLJIT_C_SIG_GREATER: + case SLJIT_C_SIG_LESS_EQUAL: + dst_ar = GREATER_FLAG; + break; + case SLJIT_C_OVERFLOW: + case SLJIT_C_NOT_OVERFLOW: + dst_ar = OVERFLOW_FLAG; + break; + case SLJIT_C_MUL_OVERFLOW: + case SLJIT_C_MUL_NOT_OVERFLOW: + FAIL_IF(push_inst(compiler, SLTIU | SA(OVERFLOW_FLAG) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); + dst_ar = sugg_dst_ar; + type ^= 0x1; /* Flip type bit for the XORI below. */ + break; + case SLJIT_C_FLOAT_EQUAL: + case SLJIT_C_FLOAT_NOT_EQUAL: + dst_ar = EQUAL_FLAG; + break; + + case SLJIT_C_FLOAT_NAN: + case SLJIT_C_FLOAT_NOT_NAN: + FAIL_IF(push_inst(compiler, CFC1 | TA(sugg_dst_ar) | DA(FCSR_REG), sugg_dst_ar)); + FAIL_IF(push_inst(compiler, SRL | TA(sugg_dst_ar) | DA(sugg_dst_ar) | SH_IMM(23), sugg_dst_ar)); + FAIL_IF(push_inst(compiler, ANDI | SA(sugg_dst_ar) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); + dst_ar = sugg_dst_ar; + break; + + default: + SLJIT_ASSERT_STOP(); + dst_ar = sugg_dst_ar; + break; + } + + if (type & 0x1) { + FAIL_IF(push_inst(compiler, XORI | SA(dst_ar) | TA(sugg_dst_ar) | IMM(1), sugg_dst_ar)); + dst_ar = sugg_dst_ar; + } + + if (GET_OPCODE(op) == SLJIT_OR) { + if (DR(TMP_REG2) != dst_ar) + FAIL_IF(push_inst(compiler, ADDU_W | SA(dst_ar) | TA(0) | D(TMP_REG2), DR(TMP_REG2))); + return emit_op(compiler, op, CUMULATIVE_OP | LOGICAL_OP | IMM_OP, dst, dstw, dst, dstw, TMP_REG2, 0); + } + + if (dst & SLJIT_MEM) + return emit_op_mem(compiler, WORD_DATA, dst_ar, dst, dstw); + + if (sugg_dst_ar != dst_ar) + return push_inst(compiler, ADDU_W | SA(dst_ar) | TA(0) | DA(sugg_dst_ar), sugg_dst_ar); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w init_value) +{ + struct sljit_const *const_; + int reg; + + CHECK_ERROR_PTR(); + check_sljit_emit_const(compiler, dst, dstw, init_value); + + const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); + PTR_FAIL_IF(!const_); + set_const(const_, compiler); + + reg = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG2; + + PTR_FAIL_IF(emit_const(compiler, reg, init_value)); + + if (dst & SLJIT_MEM) + PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0)); + return const_; +} diff --git a/src/lib/pcre/sljit/sljitNativePPC_32.c b/src/lib/pcre/sljit/sljitNativePPC_32.c new file mode 100644 index 0000000..82d0508 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativePPC_32.c @@ -0,0 +1,262 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +/* ppc 32-bit arch dependent functions. */ + +static int load_immediate(struct sljit_compiler *compiler, int reg, sljit_w imm) +{ + if (imm <= SIMM_MAX && imm >= SIMM_MIN) + return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm)); + + if (!(imm & ~0xffff)) + return push_inst(compiler, ORI | S(ZERO_REG) | A(reg) | IMM(imm)); + + FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16))); + return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS; +} + +#define INS_CLEAR_LEFT(dst, src, from) \ + (RLWINM | S(src) | A(dst) | ((from) << 6) | (31 << 1)) + +static SLJIT_INLINE int emit_single_op(struct sljit_compiler *compiler, int op, int flags, + int dst, int src1, int src2) +{ + switch (op) { + case SLJIT_ADD: + if (flags & ALT_FORM1) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm); + } + if (flags & ALT_FORM2) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + } + if (flags & ALT_FORM3) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm); + } + if (flags & ALT_FORM4) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff))); + return push_inst(compiler, ADDIS | D(dst) | A(dst) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1))); + } + if (!(flags & ALT_SET_FLAGS)) + return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)); + return push_inst(compiler, ADDC | OERC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); + + case SLJIT_ADDC: + if (flags & ALT_FORM1) { + FAIL_IF(push_inst(compiler, MFXER | S(0))); + FAIL_IF(push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2))); + return push_inst(compiler, MTXER | S(0)); + } + return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2)); + + case SLJIT_SUB: + if (flags & ALT_FORM1) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm); + } + if (flags & (ALT_FORM2 | ALT_FORM3)) { + SLJIT_ASSERT(src2 == TMP_REG2); + if (flags & ALT_FORM2) + FAIL_IF(push_inst(compiler, CMPI | CRD(0) | A(src1) | compiler->imm)); + if (flags & ALT_FORM3) + return push_inst(compiler, CMPLI | CRD(4) | A(src1) | compiler->imm); + return SLJIT_SUCCESS; + } + if (flags & (ALT_FORM4 | ALT_FORM5)) { + if (flags & ALT_FORM4) + FAIL_IF(push_inst(compiler, CMPL | CRD(4) | A(src1) | B(src2))); + if (flags & ALT_FORM5) + FAIL_IF(push_inst(compiler, CMP | CRD(0) | A(src1) | B(src2))); + return SLJIT_SUCCESS; + } + if (!(flags & ALT_SET_FLAGS)) + return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1)); + if (flags & ALT_FORM6) + FAIL_IF(push_inst(compiler, CMPL | CRD(4) | A(src1) | B(src2))); + return push_inst(compiler, SUBFC | OERC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + + case SLJIT_SUBC: + if (flags & ALT_FORM1) { + FAIL_IF(push_inst(compiler, MFXER | S(0))); + FAIL_IF(push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1))); + return push_inst(compiler, MTXER | S(0)); + } + return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1)); + + case SLJIT_MUL: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm); + } + return push_inst(compiler, MULLW | OERC(flags) | D(dst) | A(src2) | B(src1)); + + case SLJIT_AND: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM2) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm); + } + return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_OR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM2) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM3) { + SLJIT_ASSERT(src2 == TMP_REG2); + FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(compiler->imm))); + return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + } + return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_XOR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM2) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM3) { + SLJIT_ASSERT(src2 == TMP_REG2); + FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(compiler->imm))); + return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + } + return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_SHL: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + compiler->imm &= 0x1f; + return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11) | ((31 - compiler->imm) << 1)); + } + return push_inst(compiler, SLW | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_LSHR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + compiler->imm &= 0x1f; + return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (((32 - compiler->imm) & 0x1f) << 11) | (compiler->imm << 6) | (31 << 1)); + } + return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_ASHR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + compiler->imm &= 0x1f; + return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)); + } + return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_MOV: + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: + SLJIT_ASSERT(src1 == TMP_REG1); + if (dst != src2) + return push_inst(compiler, OR | S(src2) | A(dst) | B(src2)); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UB: + case SLJIT_MOV_SB: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SB) + return push_inst(compiler, EXTSB | S(src2) | A(dst)); + return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 24)); + } + else if ((flags & REG_DEST) && op == SLJIT_MOV_SB) + return push_inst(compiler, EXTSB | S(src2) | A(dst)); + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UH: + case SLJIT_MOV_SH: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SH) + return push_inst(compiler, EXTSH | S(src2) | A(dst)); + return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 16)); + } + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_NOT: + SLJIT_ASSERT(src1 == TMP_REG1); + return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2)); + + case SLJIT_NEG: + SLJIT_ASSERT(src1 == TMP_REG1); + return push_inst(compiler, NEG | OERC(flags) | D(dst) | A(src2)); + + case SLJIT_CLZ: + SLJIT_ASSERT(src1 == TMP_REG1); + return push_inst(compiler, CNTLZW | RC(flags) | S(src2) | A(dst)); + } + + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_const(struct sljit_compiler *compiler, int reg, sljit_w init_value) +{ + FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 16))); + return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value)); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ + sljit_ins *inst = (sljit_ins*)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_addr & 0xffff); + SLJIT_CACHE_FLUSH(inst, inst + 2); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + sljit_ins *inst = (sljit_ins*)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 16) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | (new_constant & 0xffff); + SLJIT_CACHE_FLUSH(inst, inst + 2); +} diff --git a/src/lib/pcre/sljit/sljitNativePPC_64.c b/src/lib/pcre/sljit/sljitNativePPC_64.c new file mode 100644 index 0000000..cc2ae37 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativePPC_64.c @@ -0,0 +1,428 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +/* ppc 64-bit arch dependent functions. */ + +#ifdef __GNUC__ +#define ASM_SLJIT_CLZ(src, dst) \ + asm volatile ( "cntlzd %0, %1" : "=r"(dst) : "r"(src) ) +#else +#error "Must implement count leading zeroes" +#endif + +#define RLDI(dst, src, sh, mb, type) \ + (HI(30) | S(src) | A(dst) | ((type) << 2) | (((sh) & 0x1f) << 11) | (((sh) & 0x20) >> 4) | (((mb) & 0x1f) << 6) | ((mb) & 0x20)) + +#define PUSH_RLDICR(reg, shift) \ + push_inst(compiler, RLDI(reg, reg, 63 - shift, shift, 1)) + +static int load_immediate(struct sljit_compiler *compiler, int reg, sljit_w imm) +{ + sljit_uw tmp; + sljit_uw shift; + sljit_uw tmp2; + sljit_uw shift2; + + if (imm <= SIMM_MAX && imm >= SIMM_MIN) + return push_inst(compiler, ADDI | D(reg) | A(0) | IMM(imm)); + + if (!(imm & ~0xffff)) + return push_inst(compiler, ORI | S(ZERO_REG) | A(reg) | IMM(imm)); + + if (imm <= SLJIT_W(0x7fffffff) && imm >= SLJIT_W(-0x80000000)) { + FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 16))); + return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)) : SLJIT_SUCCESS; + } + + /* Count leading zeroes. */ + tmp = (imm >= 0) ? imm : ~imm; + ASM_SLJIT_CLZ(tmp, shift); + SLJIT_ASSERT(shift > 0); + shift--; + tmp = (imm << shift); + + if ((tmp & ~0xffff000000000000ul) == 0) { + FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48))); + shift += 15; + return PUSH_RLDICR(reg, shift); + } + + if ((tmp & ~0xffffffff00000000ul) == 0) { + FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(tmp >> 48))); + FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp >> 32))); + shift += 31; + return PUSH_RLDICR(reg, shift); + } + + /* Cut out the 16 bit from immediate. */ + shift += 15; + tmp2 = imm & ((1ul << (63 - shift)) - 1); + + if (tmp2 <= 0xffff) { + FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48))); + FAIL_IF(PUSH_RLDICR(reg, shift)); + return push_inst(compiler, ORI | S(reg) | A(reg) | tmp2); + } + + if (tmp2 <= 0xffffffff) { + FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48))); + FAIL_IF(PUSH_RLDICR(reg, shift)); + FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | (tmp2 >> 16))); + return (imm & 0xffff) ? push_inst(compiler, ORI | S(reg) | A(reg) | IMM(tmp2)) : SLJIT_SUCCESS; + } + + ASM_SLJIT_CLZ(tmp2, shift2); + tmp2 <<= shift2; + + if ((tmp2 & ~0xffff000000000000ul) == 0) { + FAIL_IF(push_inst(compiler, ADDI | D(reg) | A(0) | IMM(tmp >> 48))); + shift2 += 15; + shift += (63 - shift2); + FAIL_IF(PUSH_RLDICR(reg, shift)); + FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | (tmp2 >> 48))); + return PUSH_RLDICR(reg, shift2); + } + + /* The general version. */ + FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(imm >> 48))); + FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm >> 32))); + FAIL_IF(PUSH_RLDICR(reg, 31)); + FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(imm >> 16))); + return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(imm)); +} + +/* Simplified mnemonics: clrldi. */ +#define INS_CLEAR_LEFT(dst, src, from) \ + (RLDICL | S(src) | A(dst) | ((from) << 6) | (1 << 5)) + +/* Sign extension for integer operations. */ +#define UN_EXTS() \ + if ((flags & (ALT_SIGN_EXT | REG2_SOURCE)) == (ALT_SIGN_EXT | REG2_SOURCE)) { \ + FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \ + src2 = TMP_REG2; \ + } + +#define BIN_EXTS() \ + if (flags & ALT_SIGN_EXT) { \ + if (flags & REG1_SOURCE) { \ + FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \ + src1 = TMP_REG1; \ + } \ + if (flags & REG2_SOURCE) { \ + FAIL_IF(push_inst(compiler, EXTSW | S(src2) | A(TMP_REG2))); \ + src2 = TMP_REG2; \ + } \ + } + +#define BIN_IMM_EXTS() \ + if ((flags & (ALT_SIGN_EXT | REG1_SOURCE)) == (ALT_SIGN_EXT | REG1_SOURCE)) { \ + FAIL_IF(push_inst(compiler, EXTSW | S(src1) | A(TMP_REG1))); \ + src1 = TMP_REG1; \ + } + +static SLJIT_INLINE int emit_single_op(struct sljit_compiler *compiler, int op, int flags, + int dst, int src1, int src2) +{ + switch (op) { + case SLJIT_ADD: + if (flags & ALT_FORM1) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ADDI | D(dst) | A(src1) | compiler->imm); + } + if (flags & ALT_FORM2) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ADDIS | D(dst) | A(src1) | compiler->imm); + } + if (flags & ALT_FORM3) { + SLJIT_ASSERT(src2 == TMP_REG2); + BIN_IMM_EXTS(); + return push_inst(compiler, ADDIC | D(dst) | A(src1) | compiler->imm); + } + if (flags & ALT_FORM4) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + FAIL_IF(push_inst(compiler, ADDI | D(dst) | A(src1) | (compiler->imm & 0xffff))); + return push_inst(compiler, ADDIS | D(dst) | A(dst) | (((compiler->imm >> 16) & 0xffff) + ((compiler->imm >> 15) & 0x1))); + } + if (!(flags & ALT_SET_FLAGS)) + return push_inst(compiler, ADD | D(dst) | A(src1) | B(src2)); + BIN_EXTS(); + return push_inst(compiler, ADDC | OERC(ALT_SET_FLAGS) | D(dst) | A(src1) | B(src2)); + + case SLJIT_ADDC: + if (flags & ALT_FORM1) { + FAIL_IF(push_inst(compiler, MFXER | S(0))); + FAIL_IF(push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2))); + return push_inst(compiler, MTXER | S(0)); + } + BIN_EXTS(); + return push_inst(compiler, ADDE | D(dst) | A(src1) | B(src2)); + + case SLJIT_SUB: + if (flags & ALT_FORM1) { + /* Flags does not set: BIN_IMM_EXTS unnecessary. */ + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, SUBFIC | D(dst) | A(src1) | compiler->imm); + } + if (flags & (ALT_FORM2 | ALT_FORM3)) { + SLJIT_ASSERT(src2 == TMP_REG2); + if (flags & ALT_FORM2) + FAIL_IF(push_inst(compiler, CMPI | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm)); + if (flags & ALT_FORM3) + return push_inst(compiler, CMPLI | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | compiler->imm); + return SLJIT_SUCCESS; + } + if (flags & (ALT_FORM4 | ALT_FORM5)) { + if (flags & ALT_FORM4) + FAIL_IF(push_inst(compiler, CMPL | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2))); + if (flags & ALT_FORM5) + return push_inst(compiler, CMP | CRD(0 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2)); + return SLJIT_SUCCESS; + } + if (!(flags & ALT_SET_FLAGS)) + return push_inst(compiler, SUBF | D(dst) | A(src2) | B(src1)); + BIN_EXTS(); + if (flags & ALT_FORM6) + FAIL_IF(push_inst(compiler, CMPL | CRD(4 | ((flags & ALT_SIGN_EXT) ? 0 : 1)) | A(src1) | B(src2))); + return push_inst(compiler, SUBFC | OERC(ALT_SET_FLAGS) | D(dst) | A(src2) | B(src1)); + + case SLJIT_SUBC: + if (flags & ALT_FORM1) { + FAIL_IF(push_inst(compiler, MFXER | S(0))); + FAIL_IF(push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1))); + return push_inst(compiler, MTXER | S(0)); + } + BIN_EXTS(); + return push_inst(compiler, SUBFE | D(dst) | A(src2) | B(src1)); + + case SLJIT_MUL: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, MULLI | D(dst) | A(src1) | compiler->imm); + } + BIN_EXTS(); + if (flags & ALT_FORM2) + return push_inst(compiler, MULLW | OERC(flags) | D(dst) | A(src2) | B(src1)); + return push_inst(compiler, MULLD | OERC(flags) | D(dst) | A(src2) | B(src1)); + + case SLJIT_AND: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ANDI | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM2) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ANDIS | S(src1) | A(dst) | compiler->imm); + } + return push_inst(compiler, AND | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_OR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ORI | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM2) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, ORIS | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM3) { + SLJIT_ASSERT(src2 == TMP_REG2); + FAIL_IF(push_inst(compiler, ORI | S(src1) | A(dst) | IMM(compiler->imm))); + return push_inst(compiler, ORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + } + return push_inst(compiler, OR | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_XOR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, XORI | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM2) { + SLJIT_ASSERT(src2 == TMP_REG2); + return push_inst(compiler, XORIS | S(src1) | A(dst) | compiler->imm); + } + if (flags & ALT_FORM3) { + SLJIT_ASSERT(src2 == TMP_REG2); + FAIL_IF(push_inst(compiler, XORI | S(src1) | A(dst) | IMM(compiler->imm))); + return push_inst(compiler, XORIS | S(dst) | A(dst) | IMM(compiler->imm >> 16)); + } + return push_inst(compiler, XOR | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_SHL: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + if (flags & ALT_FORM2) { + compiler->imm &= 0x1f; + return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11) | ((31 - compiler->imm) << 1)); + } + else { + compiler->imm &= 0x3f; + return push_inst(compiler, RLDI(dst, src1, compiler->imm, 63 - compiler->imm, 1) | RC(flags)); + } + } + if (flags & ALT_FORM2) + return push_inst(compiler, SLW | RC(flags) | S(src1) | A(dst) | B(src2)); + return push_inst(compiler, SLD | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_LSHR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + if (flags & ALT_FORM2) { + compiler->imm &= 0x1f; + return push_inst(compiler, RLWINM | RC(flags) | S(src1) | A(dst) | (((32 - compiler->imm) & 0x1f) << 11) | (compiler->imm << 6) | (31 << 1)); + } + else { + compiler->imm &= 0x3f; + return push_inst(compiler, RLDI(dst, src1, 64 - compiler->imm, compiler->imm, 0) | RC(flags)); + } + } + if (flags & ALT_FORM2) + return push_inst(compiler, SRW | RC(flags) | S(src1) | A(dst) | B(src2)); + return push_inst(compiler, SRD | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_ASHR: + if (flags & ALT_FORM1) { + SLJIT_ASSERT(src2 == TMP_REG2); + if (flags & ALT_FORM2) { + compiler->imm &= 0x1f; + return push_inst(compiler, SRAWI | RC(flags) | S(src1) | A(dst) | (compiler->imm << 11)); + } + else { + compiler->imm &= 0x3f; + return push_inst(compiler, SRADI | RC(flags) | S(src1) | A(dst) | ((compiler->imm & 0x1f) << 11) | ((compiler->imm & 0x20) >> 4)); + } + } + if (flags & ALT_FORM2) + return push_inst(compiler, SRAW | RC(flags) | S(src1) | A(dst) | B(src2)); + return push_inst(compiler, SRAD | RC(flags) | S(src1) | A(dst) | B(src2)); + + case SLJIT_MOV: + SLJIT_ASSERT(src1 == TMP_REG1); + if (dst != src2) + return push_inst(compiler, OR | S(src2) | A(dst) | B(src2)); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SI) + return push_inst(compiler, EXTSW | S(src2) | A(dst)); + return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 0)); + } + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UB: + case SLJIT_MOV_SB: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SB) + return push_inst(compiler, EXTSB | S(src2) | A(dst)); + return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 24)); + } + else if ((flags & REG_DEST) && op == SLJIT_MOV_SB) + return push_inst(compiler, EXTSB | S(src2) | A(dst)); + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_MOV_UH: + case SLJIT_MOV_SH: + SLJIT_ASSERT(src1 == TMP_REG1); + if ((flags & (REG_DEST | REG2_SOURCE)) == (REG_DEST | REG2_SOURCE)) { + if (op == SLJIT_MOV_SH) + return push_inst(compiler, EXTSH | S(src2) | A(dst)); + return push_inst(compiler, INS_CLEAR_LEFT(dst, src2, 16)); + } + else if (dst != src2) + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; + + case SLJIT_NOT: + SLJIT_ASSERT(src1 == TMP_REG1); + UN_EXTS(); + return push_inst(compiler, NOR | RC(flags) | S(src2) | A(dst) | B(src2)); + + case SLJIT_NEG: + SLJIT_ASSERT(src1 == TMP_REG1); + UN_EXTS(); + return push_inst(compiler, NEG | OERC(flags) | D(dst) | A(src2)); + + case SLJIT_CLZ: + SLJIT_ASSERT(src1 == TMP_REG1); + if (flags & ALT_FORM1) + return push_inst(compiler, CNTLZW | RC(flags) | S(src2) | A(dst)); + return push_inst(compiler, CNTLZD | RC(flags) | S(src2) | A(dst)); + } + + SLJIT_ASSERT_STOP(); + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_const(struct sljit_compiler *compiler, int reg, sljit_w init_value) +{ + FAIL_IF(push_inst(compiler, ADDIS | D(reg) | A(0) | IMM(init_value >> 48))); + FAIL_IF(push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value >> 32))); + FAIL_IF(PUSH_RLDICR(reg, 31)); + FAIL_IF(push_inst(compiler, ORIS | S(reg) | A(reg) | IMM(init_value >> 16))); + return push_inst(compiler, ORI | S(reg) | A(reg) | IMM(init_value)); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ + sljit_ins *inst = (sljit_ins*)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_addr >> 48) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | ((new_addr >> 32) & 0xffff); + inst[3] = (inst[3] & 0xffff0000) | ((new_addr >> 16) & 0xffff); + inst[4] = (inst[4] & 0xffff0000) | (new_addr & 0xffff); + SLJIT_CACHE_FLUSH(inst, inst + 5); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + sljit_ins *inst = (sljit_ins*)addr; + + inst[0] = (inst[0] & 0xffff0000) | ((new_constant >> 48) & 0xffff); + inst[1] = (inst[1] & 0xffff0000) | ((new_constant >> 32) & 0xffff); + inst[3] = (inst[3] & 0xffff0000) | ((new_constant >> 16) & 0xffff); + inst[4] = (inst[4] & 0xffff0000) | (new_constant & 0xffff); + SLJIT_CACHE_FLUSH(inst, inst + 5); +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_function_context(void** func_ptr, struct sljit_function_context* context, sljit_w addr, void* func) +{ + sljit_w* ptrs; + if (func_ptr) + *func_ptr = (void*)context; + ptrs = (sljit_w*)func; + context->addr = addr ? addr : ptrs[0]; + context->r2 = ptrs[1]; + context->r11 = ptrs[2]; +} diff --git a/src/lib/pcre/sljit/sljitNativePPC_common.c b/src/lib/pcre/sljit/sljitNativePPC_common.c new file mode 100644 index 0000000..f0f191d --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativePPC_common.c @@ -0,0 +1,1872 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name() +{ + return "PowerPC" SLJIT_CPUINFO; +} + +/* Length of an instruction word. + Both for ppc-32 and ppc-64. */ +typedef sljit_ui sljit_ins; + +static void ppc_cache_flush(sljit_ins *from, sljit_ins *to) +{ + while (from < to) { +#ifdef __GNUC__ + asm volatile ( "icbi 0, %0" : : "r"(from) ); +#else +#error "Must implement icbi" +#endif + from++; + } +} + +#define TMP_REG1 (SLJIT_NO_REGISTERS + 1) +#define TMP_REG2 (SLJIT_NO_REGISTERS + 2) +#define TMP_REG3 (SLJIT_NO_REGISTERS + 3) +#define ZERO_REG (SLJIT_NO_REGISTERS + 4) +#define REAL_STACK_PTR (SLJIT_NO_REGISTERS + 5) + +#define TMP_FREG1 (SLJIT_FLOAT_REG4 + 1) +#define TMP_FREG2 (SLJIT_FLOAT_REG4 + 2) + +/* --------------------------------------------------------------------- */ +/* Instrucion forms */ +/* --------------------------------------------------------------------- */ +#define D(d) (reg_map[d] << 21) +#define S(s) (reg_map[s] << 21) +#define A(a) (reg_map[a] << 16) +#define B(b) (reg_map[b] << 11) +#define C(c) (reg_map[c] << 6) +#define FD(fd) ((fd) << 21) +#define FA(fa) ((fa) << 16) +#define FB(fb) ((fb) << 11) +#define FC(fc) ((fc) << 6) +#define IMM(imm) ((imm) & 0xffff) +#define CRD(d) ((d) << 21) + +/* Instruction bit sections. + OE and Rc flag (see ALT_SET_FLAGS). */ +#define OERC(flags) (((flags & ALT_SET_FLAGS) >> 10) | (flags & ALT_SET_FLAGS)) +/* Rc flag (see ALT_SET_FLAGS). */ +#define RC(flags) ((flags & ALT_SET_FLAGS) >> 10) +#define HI(opcode) ((opcode) << 26) +#define LO(opcode) ((opcode) << 1) + +#define ADD (HI(31) | LO(266)) +#define ADDC (HI(31) | LO(10)) +#define ADDE (HI(31) | LO(138)) +#define ADDI (HI(14)) +#define ADDIC (HI(13)) +#define ADDIS (HI(15)) +#define ADDME (HI(31) | LO(234)) +#define AND (HI(31) | LO(28)) +#define ANDI (HI(28)) +#define ANDIS (HI(29)) +#define Bx (HI(18)) +#define BCx (HI(16)) +#define BCCTR (HI(19) | LO(528) | (3 << 11)) +#define BLR (HI(19) | LO(16) | (0x14 << 21)) +#define CNTLZD (HI(31) | LO(58)) +#define CNTLZW (HI(31) | LO(26)) +#define CMP (HI(31) | LO(0)) +#define CMPI (HI(11)) +#define CMPL (HI(31) | LO(32)) +#define CMPLI (HI(10)) +#define CROR (HI(19) | LO(449)) +#define DIVD (HI(31) | LO(489)) +#define DIVDU (HI(31) | LO(457)) +#define DIVW (HI(31) | LO(491)) +#define DIVWU (HI(31) | LO(459)) +#define EXTSB (HI(31) | LO(954)) +#define EXTSH (HI(31) | LO(922)) +#define EXTSW (HI(31) | LO(986)) +#define FABS (HI(63) | LO(264)) +#define FADD (HI(63) | LO(21)) +#define FCMPU (HI(63) | LO(0)) +#define FDIV (HI(63) | LO(18)) +#define FMR (HI(63) | LO(72)) +#define FMUL (HI(63) | LO(25)) +#define FNEG (HI(63) | LO(40)) +#define FSUB (HI(63) | LO(20)) +#define LD (HI(58) | 0) +#define LFD (HI(50)) +#define LFDUX (HI(31) | LO(631)) +#define LFDX (HI(31) | LO(599)) +#define LWZ (HI(32)) +#define MFCR (HI(31) | LO(19)) +#define MFLR (HI(31) | LO(339) | 0x80000) +#define MFXER (HI(31) | LO(339) | 0x10000) +#define MTCTR (HI(31) | LO(467) | 0x90000) +#define MTLR (HI(31) | LO(467) | 0x80000) +#define MTXER (HI(31) | LO(467) | 0x10000) +#define MULHD (HI(31) | LO(73)) +#define MULHDU (HI(31) | LO(9)) +#define MULHW (HI(31) | LO(75)) +#define MULHWU (HI(31) | LO(11)) +#define MULLD (HI(31) | LO(233)) +#define MULLI (HI(7)) +#define MULLW (HI(31) | LO(235)) +#define NEG (HI(31) | LO(104)) +#define NOP (HI(24)) +#define NOR (HI(31) | LO(124)) +#define OR (HI(31) | LO(444)) +#define ORI (HI(24)) +#define ORIS (HI(25)) +#define RLDICL (HI(30)) +#define RLWINM (HI(21)) +#define SLD (HI(31) | LO(27)) +#define SLW (HI(31) | LO(24)) +#define SRAD (HI(31) | LO(794)) +#define SRADI (HI(31) | LO(413 << 1)) +#define SRAW (HI(31) | LO(792)) +#define SRAWI (HI(31) | LO(824)) +#define SRD (HI(31) | LO(539)) +#define SRW (HI(31) | LO(536)) +#define STD (HI(62) | 0) +#define STDU (HI(62) | 1) +#define STDUX (HI(31) | LO(181)) +#define STFD (HI(54)) +#define STFDUX (HI(31) | LO(759)) +#define STFDX (HI(31) | LO(727)) +#define STW (HI(36)) +#define STWU (HI(37)) +#define STWUX (HI(31) | LO(183)) +#define SUBF (HI(31) | LO(40)) +#define SUBFC (HI(31) | LO(8)) +#define SUBFE (HI(31) | LO(136)) +#define SUBFIC (HI(8)) +#define XOR (HI(31) | LO(316)) +#define XORI (HI(26)) +#define XORIS (HI(27)) + +#define SIMM_MAX (0x7fff) +#define SIMM_MIN (-0x8000) +#define UIMM_MAX (0xffff) + +/* SLJIT_LOCALS_REG is not the real stack register, since it must + point to the head of the stack chain. */ +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 6] = { + 0, 3, 4, 5, 6, 7, 29, 28, 27, 26, 25, 31, 8, 9, 10, 30, 1 +}; + +static int push_inst(struct sljit_compiler *compiler, sljit_ins ins) +{ + sljit_ins *ptr = (sljit_ins*)ensure_buf(compiler, sizeof(sljit_ins)); + FAIL_IF(!ptr); + *ptr = ins; + compiler->size++; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int optimize_jump(struct sljit_jump *jump, sljit_ins *code_ptr, sljit_ins *code) +{ + sljit_w diff; + sljit_uw target_addr; + + if (jump->flags & SLJIT_REWRITABLE_JUMP) + return 0; + + if (jump->flags & JUMP_ADDR) + target_addr = jump->u.target; + else { + SLJIT_ASSERT(jump->flags & JUMP_LABEL); + target_addr = (sljit_uw)(code + jump->u.label->size); + } + diff = ((sljit_w)target_addr - (sljit_w)(code_ptr)) & ~0x3l; + + if (jump->flags & UNCOND_B) { + if (diff <= 0x01ffffff && diff >= -0x02000000) { + jump->flags |= PATCH_B; + return 1; + } + if (target_addr <= 0x03ffffff) { + jump->flags |= PATCH_B | ABSOLUTE_B; + return 1; + } + } + else { + if (diff <= 0x7fff && diff >= -0x8000) { + jump->flags |= PATCH_B; + return 1; + } + if (target_addr <= 0xffff) { + jump->flags |= PATCH_B | ABSOLUTE_B; + return 1; + } + } + return 0; +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + sljit_ins *code; + sljit_ins *code_ptr; + sljit_ins *buf_ptr; + sljit_ins *buf_end; + sljit_uw word_count; + sljit_uw addr; + + struct sljit_label *label; + struct sljit_jump *jump; + struct sljit_const *const_; + + CHECK_ERROR_PTR(); + check_sljit_generate_code(compiler); + reverse_buf(compiler); + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + compiler->size += (compiler->size & 0x1) + (sizeof(struct sljit_function_context) / sizeof(sljit_ins)); +#endif + code = (sljit_ins*)SLJIT_MALLOC_EXEC(compiler->size * sizeof(sljit_ins)); + PTR_FAIL_WITH_EXEC_IF(code); + buf = compiler->buf; + + code_ptr = code; + word_count = 0; + label = compiler->labels; + jump = compiler->jumps; + const_ = compiler->consts; + do { + buf_ptr = (sljit_ins*)buf->memory; + buf_end = buf_ptr + (buf->used_size >> 2); + do { + *code_ptr = *buf_ptr++; + SLJIT_ASSERT(!label || label->size >= word_count); + SLJIT_ASSERT(!jump || jump->addr >= word_count); + SLJIT_ASSERT(!const_ || const_->addr >= word_count); + /* These structures are ordered by their address. */ + if (label && label->size == word_count) { + /* Just recording the address. */ + label->addr = (sljit_uw)code_ptr; + label->size = code_ptr - code; + label = label->next; + } + if (jump && jump->addr == word_count) { +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + jump->addr = (sljit_uw)(code_ptr - 3); +#else + jump->addr = (sljit_uw)(code_ptr - 6); +#endif + if (optimize_jump(jump, code_ptr, code)) { +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + code_ptr[-3] = code_ptr[0]; + code_ptr -= 3; +#else + code_ptr[-6] = code_ptr[0]; + code_ptr -= 6; +#endif + } + jump = jump->next; + } + if (const_ && const_->addr == word_count) { + /* Just recording the address. */ + const_->addr = (sljit_uw)code_ptr; + const_ = const_->next; + } + code_ptr ++; + word_count ++; + } while (buf_ptr < buf_end); + + buf = buf->next; + } while (buf); + + if (label && label->size == word_count) { + label->addr = (sljit_uw)code_ptr; + label->size = code_ptr - code; + label = label->next; + } + + SLJIT_ASSERT(!label); + SLJIT_ASSERT(!jump); + SLJIT_ASSERT(!const_); +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + SLJIT_ASSERT(code_ptr - code <= (int)compiler->size - ((compiler->size & 0x1) ? 3 : 2)); +#else + SLJIT_ASSERT(code_ptr - code <= (int)compiler->size); +#endif + + jump = compiler->jumps; + while (jump) { + do { + addr = (jump->flags & JUMP_LABEL) ? jump->u.label->addr : jump->u.target; + buf_ptr = (sljit_ins*)jump->addr; + if (jump->flags & PATCH_B) { + if (jump->flags & UNCOND_B) { + if (!(jump->flags & ABSOLUTE_B)) { + addr = addr - jump->addr; + SLJIT_ASSERT((sljit_w)addr <= 0x01ffffff && (sljit_w)addr >= -0x02000000); + *buf_ptr = Bx | (addr & 0x03fffffc) | ((*buf_ptr) & 0x1); + } + else { + SLJIT_ASSERT(addr <= 0x03ffffff); + *buf_ptr = Bx | (addr & 0x03fffffc) | 0x2 | ((*buf_ptr) & 0x1); + } + } + else { + if (!(jump->flags & ABSOLUTE_B)) { + addr = addr - jump->addr; + SLJIT_ASSERT((sljit_w)addr <= 0x7fff && (sljit_w)addr >= -0x8000); + *buf_ptr = BCx | (addr & 0xfffc) | ((*buf_ptr) & 0x03ff0001); + } + else { + addr = addr & ~0x3l; + SLJIT_ASSERT(addr <= 0xffff); + *buf_ptr = BCx | (addr & 0xfffc) | 0x2 | ((*buf_ptr) & 0x03ff0001); + } + + } + break; + } + /* Set the fields of immediate loads. */ +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((addr >> 16) & 0xffff); + buf_ptr[1] = (buf_ptr[1] & 0xffff0000) | (addr & 0xffff); +#else + buf_ptr[0] = (buf_ptr[0] & 0xffff0000) | ((addr >> 48) & 0xffff); + buf_ptr[1] = (buf_ptr[1] & 0xffff0000) | ((addr >> 32) & 0xffff); + buf_ptr[3] = (buf_ptr[3] & 0xffff0000) | ((addr >> 16) & 0xffff); + buf_ptr[4] = (buf_ptr[4] & 0xffff0000) | (addr & 0xffff); +#endif + } while (0); + jump = jump->next; + } + + SLJIT_CACHE_FLUSH(code, code_ptr); + compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_size = compiler->size * sizeof(sljit_ins); + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (((sljit_w)code_ptr) & 0x4) + code_ptr++; + sljit_set_function_context(NULL, (struct sljit_function_context*)code_ptr, (sljit_w)code, sljit_generate_code); + return code_ptr; +#else + return code; +#endif +} + +/* inp_flags: */ + +/* Creates an index in data_transfer_insts array. */ +#define WORD_DATA 0x00 +#define BYTE_DATA 0x01 +#define HALF_DATA 0x02 +#define INT_DATA 0x03 +#define SIGNED_DATA 0x04 +#define LOAD_DATA 0x08 +#define WRITE_BACK 0x10 +#define INDEXED 0x20 + +#define MEM_MASK 0x3f + +/* Other inp_flags. */ + +#define ARG_TEST 0x000100 +/* Integer opertion and set flags -> requires exts on 64 bit systems. */ +#define ALT_SIGN_EXT 0x000200 +/* This flag affects the RC() and OERC() macros. */ +#define ALT_SET_FLAGS 0x000400 +#define ALT_FORM1 0x010000 +#define ALT_FORM2 0x020000 +#define ALT_FORM3 0x040000 +#define ALT_FORM4 0x080000 +#define ALT_FORM5 0x100000 +#define ALT_FORM6 0x200000 + +/* Source and destination is register. */ +#define REG_DEST 0x000001 +#define REG1_SOURCE 0x000002 +#define REG2_SOURCE 0x000004 +/* getput_arg_fast returned true. */ +#define FAST_DEST 0x000008 +/* Multiple instructions are required. */ +#define SLOW_DEST 0x000010 +/* +ALT_SIGN_EXT 0x000200 +ALT_SET_FLAGS 0x000400 +ALT_FORM1 0x010000 +... +ALT_FORM6 0x200000 */ + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) +#include "sljitNativePPC_32.c" +#else +#include "sljitNativePPC_64.c" +#endif + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) +#define STACK_STORE STW +#define STACK_LOAD LWZ +#else +#define STACK_STORE STD +#define STACK_LOAD LD +#endif + +static int emit_op(struct sljit_compiler *compiler, int op, int inp_flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w); + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + CHECK_ERROR(); + check_sljit_emit_enter(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + compiler->has_locals = local_size > 0; + + FAIL_IF(push_inst(compiler, MFLR | D(0))); + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, STACK_STORE | S(SLJIT_LOCALS_REG) | A(REAL_STACK_PTR) | IMM(-(int)(sizeof(sljit_w))) )); + FAIL_IF(push_inst(compiler, STACK_STORE | S(ZERO_REG) | A(REAL_STACK_PTR) | IMM(-2 * (int)(sizeof(sljit_w))) )); + if (saveds >= 1) + FAIL_IF(push_inst(compiler, STACK_STORE | S(SLJIT_SAVED_REG1) | A(REAL_STACK_PTR) | IMM(-3 * (int)(sizeof(sljit_w))) )); + if (saveds >= 2) + FAIL_IF(push_inst(compiler, STACK_STORE | S(SLJIT_SAVED_REG2) | A(REAL_STACK_PTR) | IMM(-4 * (int)(sizeof(sljit_w))) )); + if (saveds >= 3) + FAIL_IF(push_inst(compiler, STACK_STORE | S(SLJIT_SAVED_REG3) | A(REAL_STACK_PTR) | IMM(-5 * (int)(sizeof(sljit_w))) )); + if (saveds >= 4) + FAIL_IF(push_inst(compiler, STACK_STORE | S(SLJIT_SAVED_EREG1) | A(REAL_STACK_PTR) | IMM(-6 * (int)(sizeof(sljit_w))) )); + if (saveds >= 5) + FAIL_IF(push_inst(compiler, STACK_STORE | S(SLJIT_SAVED_EREG2) | A(REAL_STACK_PTR) | IMM(-7 * (int)(sizeof(sljit_w))) )); + FAIL_IF(push_inst(compiler, STACK_STORE | S(0) | A(REAL_STACK_PTR) | IMM(sizeof(sljit_w)) )); + + FAIL_IF(push_inst(compiler, ADDI | D(ZERO_REG) | A(0) | 0)); + if (args >= 1) + FAIL_IF(push_inst(compiler, OR | S(SLJIT_TEMPORARY_REG1) | A(SLJIT_SAVED_REG1) | B(SLJIT_TEMPORARY_REG1))); + if (args >= 2) + FAIL_IF(push_inst(compiler, OR | S(SLJIT_TEMPORARY_REG2) | A(SLJIT_SAVED_REG2) | B(SLJIT_TEMPORARY_REG2))); + if (args >= 3) + FAIL_IF(push_inst(compiler, OR | S(SLJIT_TEMPORARY_REG3) | A(SLJIT_SAVED_REG3) | B(SLJIT_TEMPORARY_REG3))); + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + compiler->local_size = (2 + saveds + 2) * sizeof(sljit_w) + local_size; +#else + compiler->local_size = (2 + saveds + 7 + 8) * sizeof(sljit_w) + local_size; +#endif + compiler->local_size = (compiler->local_size + 15) & ~0xf; + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + if (compiler->local_size <= SIMM_MAX) + FAIL_IF(push_inst(compiler, STWU | S(REAL_STACK_PTR) | A(REAL_STACK_PTR) | IMM(-compiler->local_size))); + else { + FAIL_IF(load_immediate(compiler, 0, -compiler->local_size)); + FAIL_IF(push_inst(compiler, STWUX | S(REAL_STACK_PTR) | A(REAL_STACK_PTR) | B(0))); + } + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, ADDI | D(SLJIT_LOCALS_REG) | A(REAL_STACK_PTR) | IMM(2 * sizeof(sljit_w)))); +#else + if (compiler->local_size <= SIMM_MAX) + FAIL_IF(push_inst(compiler, STDU | S(REAL_STACK_PTR) | A(REAL_STACK_PTR) | IMM(-compiler->local_size))); + else { + FAIL_IF(load_immediate(compiler, 0, -compiler->local_size)); + FAIL_IF(push_inst(compiler, STDUX | S(REAL_STACK_PTR) | A(REAL_STACK_PTR) | B(0))); + } + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, ADDI | D(SLJIT_LOCALS_REG) | A(REAL_STACK_PTR) | IMM((7 + 8) * sizeof(sljit_w)))); +#endif + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + CHECK_ERROR_VOID(); + check_sljit_set_context(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + compiler->has_locals = local_size > 0; +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + compiler->local_size = (2 + saveds + 2) * sizeof(sljit_w) + local_size; +#else + compiler->local_size = (2 + saveds + 7 + 8) * sizeof(sljit_w) + local_size; +#endif + compiler->local_size = (compiler->local_size + 15) & ~0xf; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + CHECK_ERROR(); + check_sljit_emit_return(compiler, op, src, srcw); + + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + if (compiler->local_size <= SIMM_MAX) + FAIL_IF(push_inst(compiler, ADDI | D(REAL_STACK_PTR) | A(REAL_STACK_PTR) | IMM(compiler->local_size))); + else { + FAIL_IF(load_immediate(compiler, 0, compiler->local_size)); + FAIL_IF(push_inst(compiler, ADD | D(REAL_STACK_PTR) | A(REAL_STACK_PTR) | B(0))); + } + + FAIL_IF(push_inst(compiler, STACK_LOAD | D(0) | A(REAL_STACK_PTR) | IMM(sizeof(sljit_w)))); + if (compiler->saveds >= 5) + FAIL_IF(push_inst(compiler, STACK_LOAD | D(SLJIT_SAVED_EREG2) | A(REAL_STACK_PTR) | IMM(-7 * (int)(sizeof(sljit_w))) )); + if (compiler->saveds >= 4) + FAIL_IF(push_inst(compiler, STACK_LOAD | D(SLJIT_SAVED_EREG1) | A(REAL_STACK_PTR) | IMM(-6 * (int)(sizeof(sljit_w))) )); + if (compiler->saveds >= 3) + FAIL_IF(push_inst(compiler, STACK_LOAD | D(SLJIT_SAVED_REG3) | A(REAL_STACK_PTR) | IMM(-5 * (int)(sizeof(sljit_w))) )); + if (compiler->saveds >= 2) + FAIL_IF(push_inst(compiler, STACK_LOAD | D(SLJIT_SAVED_REG2) | A(REAL_STACK_PTR) | IMM(-4 * (int)(sizeof(sljit_w))) )); + if (compiler->saveds >= 1) + FAIL_IF(push_inst(compiler, STACK_LOAD | D(SLJIT_SAVED_REG1) | A(REAL_STACK_PTR) | IMM(-3 * (int)(sizeof(sljit_w))) )); + FAIL_IF(push_inst(compiler, STACK_LOAD | D(ZERO_REG) | A(REAL_STACK_PTR) | IMM(-2 * (int)(sizeof(sljit_w))) )); + if (compiler->has_locals) + FAIL_IF(push_inst(compiler, STACK_LOAD | D(SLJIT_LOCALS_REG) | A(REAL_STACK_PTR) | IMM(-(int)(sizeof(sljit_w))) )); + + FAIL_IF(push_inst(compiler, MTLR | S(0))); + FAIL_IF(push_inst(compiler, BLR)); + + return SLJIT_SUCCESS; +} + +#undef STACK_STORE +#undef STACK_LOAD + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +/* i/x - immediate/indexed form + n/w - no write-back / write-back (1 bit) + s/l - store/load (1 bit) + u/s - signed/unsigned (1 bit) + w/b/h/i - word/byte/half/int allowed (2 bit) + It contans 32 items, but not all are different. */ + +/* 64 bit only: [reg+imm] must be aligned to 4 bytes. */ +#define ADDR_MODE2 0x10000 +/* 64-bit only: there is no lwau instruction. */ +#define UPDATE_REQ 0x20000 + +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) +#define ARCH_DEPEND(a, b) a +#define GET_INST_CODE(inst) (inst) +#else +#define ARCH_DEPEND(a, b) b +#define GET_INST_CODE(index) ((inst) & ~(ADDR_MODE2 | UPDATE_REQ)) +#endif + +static SLJIT_CONST sljit_ins data_transfer_insts[64] = { + +/* No write-back. */ + +/* i n s u w */ ARCH_DEPEND(HI(36) /* stw */, HI(62) | ADDR_MODE2 | 0x0 /* std */), +/* i n s u b */ HI(38) /* stb */, +/* i n s u h */ HI(44) /* sth*/, +/* i n s u i */ HI(36) /* stw */, + +/* i n s s w */ ARCH_DEPEND(HI(36) /* stw */, HI(62) | ADDR_MODE2 | 0x0 /* std */), +/* i n s s b */ HI(38) /* stb */, +/* i n s s h */ HI(44) /* sth*/, +/* i n s s i */ HI(36) /* stw */, + +/* i n l u w */ ARCH_DEPEND(HI(32) /* lwz */, HI(58) | ADDR_MODE2 | 0x0 /* ld */), +/* i n l u b */ HI(34) /* lbz */, +/* i n l u h */ HI(40) /* lhz */, +/* i n l u i */ HI(32) /* lwz */, + +/* i n l s w */ ARCH_DEPEND(HI(32) /* lwz */, HI(58) | ADDR_MODE2 | 0x0 /* ld */), +/* i n l s b */ HI(34) /* lbz */ /* EXTS_REQ */, +/* i n l s h */ HI(42) /* lha */, +/* i n l s i */ ARCH_DEPEND(HI(32) /* lwz */, HI(58) | ADDR_MODE2 | 0x2 /* lwa */), + +/* Write-back. */ + +/* i w s u w */ ARCH_DEPEND(HI(37) /* stwu */, HI(62) | ADDR_MODE2 | 0x1 /* stdu */), +/* i w s u b */ HI(39) /* stbu */, +/* i w s u h */ HI(45) /* sthu */, +/* i w s u i */ HI(37) /* stwu */, + +/* i w s s w */ ARCH_DEPEND(HI(37) /* stwu */, HI(62) | ADDR_MODE2 | 0x1 /* stdu */), +/* i w s s b */ HI(39) /* stbu */, +/* i w s s h */ HI(45) /* sthu */, +/* i w s s i */ HI(37) /* stwu */, + +/* i w l u w */ ARCH_DEPEND(HI(33) /* lwzu */, HI(58) | ADDR_MODE2 | 0x1 /* ldu */), +/* i w l u b */ HI(35) /* lbzu */, +/* i w l u h */ HI(41) /* lhzu */, +/* i w l u i */ HI(33) /* lwzu */, + +/* i w l s w */ ARCH_DEPEND(HI(33) /* lwzu */, HI(58) | ADDR_MODE2 | 0x1 /* ldu */), +/* i w l s b */ HI(35) /* lbzu */ /* EXTS_REQ */, +/* i w l s h */ HI(43) /* lhau */, +/* i w l s i */ ARCH_DEPEND(HI(33) /* lwzu */, HI(58) | ADDR_MODE2 | UPDATE_REQ | 0x2 /* lwa */), + +/* ---------- */ +/* Indexed */ +/* ---------- */ + +/* No write-back. */ + +/* x n s u w */ ARCH_DEPEND(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */), +/* x n s u b */ HI(31) | LO(215) /* stbx */, +/* x n s u h */ HI(31) | LO(407) /* sthx */, +/* x n s u i */ HI(31) | LO(151) /* stwx */, + +/* x n s s w */ ARCH_DEPEND(HI(31) | LO(151) /* stwx */, HI(31) | LO(149) /* stdx */), +/* x n s s b */ HI(31) | LO(215) /* stbx */, +/* x n s s h */ HI(31) | LO(407) /* sthx */, +/* x n s s i */ HI(31) | LO(151) /* stwx */, + +/* x n l u w */ ARCH_DEPEND(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */), +/* x n l u b */ HI(31) | LO(87) /* lbzx */, +/* x n l u h */ HI(31) | LO(279) /* lhzx */, +/* x n l u i */ HI(31) | LO(23) /* lwzx */, + +/* x n l s w */ ARCH_DEPEND(HI(31) | LO(23) /* lwzx */, HI(31) | LO(21) /* ldx */), +/* x n l s b */ HI(31) | LO(87) /* lbzx */ /* EXTS_REQ */, +/* x n l s h */ HI(31) | LO(343) /* lhax */, +/* x n l s i */ ARCH_DEPEND(HI(31) | LO(23) /* lwzx */, HI(31) | LO(341) /* lwax */), + +/* Write-back. */ + +/* x w s u w */ ARCH_DEPEND(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */), +/* x w s u b */ HI(31) | LO(247) /* stbux */, +/* x w s u h */ HI(31) | LO(439) /* sthux */, +/* x w s u i */ HI(31) | LO(183) /* stwux */, + +/* x w s s w */ ARCH_DEPEND(HI(31) | LO(183) /* stwux */, HI(31) | LO(181) /* stdux */), +/* x w s s b */ HI(31) | LO(247) /* stbux */, +/* x w s s h */ HI(31) | LO(439) /* sthux */, +/* x w s s i */ HI(31) | LO(183) /* stwux */, + +/* x w l u w */ ARCH_DEPEND(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */), +/* x w l u b */ HI(31) | LO(119) /* lbzux */, +/* x w l u h */ HI(31) | LO(311) /* lhzux */, +/* x w l u i */ HI(31) | LO(55) /* lwzux */, + +/* x w l s w */ ARCH_DEPEND(HI(31) | LO(55) /* lwzux */, HI(31) | LO(53) /* ldux */), +/* x w l s b */ HI(31) | LO(119) /* lbzux */ /* EXTS_REQ */, +/* x w l s h */ HI(31) | LO(375) /* lhaux */, +/* x w l s i */ ARCH_DEPEND(HI(31) | LO(55) /* lwzux */, HI(31) | LO(373) /* lwaux */) + +}; + +#undef ARCH_DEPEND + +/* Simple cases, (no caching is required). */ +static int getput_arg_fast(struct sljit_compiler *compiler, int inp_flags, int reg, int arg, sljit_w argw) +{ + sljit_ins inst; +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + int tmp_reg; +#endif + + SLJIT_ASSERT(arg & SLJIT_MEM); + if (!(arg & 0xf)) { +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + if (argw <= SIMM_MAX && argw >= SIMM_MIN) { + if (inp_flags & ARG_TEST) + return 1; + + inst = data_transfer_insts[(inp_flags & ~WRITE_BACK) & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + push_inst(compiler, GET_INST_CODE(inst) | D(reg) | IMM(argw)); + return -1; + } +#else + inst = data_transfer_insts[(inp_flags & ~WRITE_BACK) & MEM_MASK]; + if (argw <= SIMM_MAX && argw >= SIMM_MIN && + (!(inst & ADDR_MODE2) || (argw & 0x3) == 0)) { + if (inp_flags & ARG_TEST) + return 1; + + push_inst(compiler, GET_INST_CODE(inst) | D(reg) | IMM(argw)); + return -1; + } +#endif + return (inp_flags & ARG_TEST) ? SLJIT_SUCCESS : 0; + } + + if (!(arg & 0xf0)) { +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + if (argw <= SIMM_MAX && argw >= SIMM_MIN) { + if (inp_flags & ARG_TEST) + return 1; + + inst = data_transfer_insts[inp_flags & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | IMM(argw)); + return -1; + } +#else + inst = data_transfer_insts[inp_flags & MEM_MASK]; + if (argw <= SIMM_MAX && argw >= SIMM_MIN && (!(inst & ADDR_MODE2) || (argw & 0x3) == 0)) { + if (inp_flags & ARG_TEST) + return 1; + + if ((inp_flags & WRITE_BACK) && (inst & UPDATE_REQ)) { + tmp_reg = (inp_flags & LOAD_DATA) ? (arg & 0xf) : TMP_REG3; + if (push_inst(compiler, ADDI | D(tmp_reg) | A(arg & 0xf) | IMM(argw))) + return -1; + arg = tmp_reg | SLJIT_MEM; + argw = 0; + } + push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | IMM(argw)); + return -1; + } +#endif + } + else if (!(argw & 0x3)) { + if (inp_flags & ARG_TEST) + return 1; + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | B((arg >> 4) & 0xf)); + return -1; + } + return (inp_flags & ARG_TEST) ? SLJIT_SUCCESS : 0; +} + +/* See getput_arg below. + Note: can_cache is called only for binary operators. Those operator always + uses word arguments without write back. */ +static int can_cache(int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + SLJIT_ASSERT(arg & SLJIT_MEM); + SLJIT_ASSERT(next_arg & SLJIT_MEM); + + if (!(arg & 0xf)) { + if ((next_arg & SLJIT_MEM) && ((sljit_uw)argw - (sljit_uw)next_argw <= SIMM_MAX || (sljit_uw)next_argw - (sljit_uw)argw <= SIMM_MAX)) + return 1; + return 0; + } + + if (arg & 0xf0) + return 0; + + if (argw <= SIMM_MAX && argw >= SIMM_MIN) { + if (arg == next_arg && (next_argw >= SIMM_MAX && next_argw <= SIMM_MIN)) + return 1; + } + + if (arg == next_arg && ((sljit_uw)argw - (sljit_uw)next_argw <= SIMM_MAX || (sljit_uw)next_argw - (sljit_uw)argw <= SIMM_MAX)) + return 1; + + return 0; +} + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define ADJUST_CACHED_IMM(imm) \ + if ((inst & ADDR_MODE2) && (imm & 0x3)) { \ + /* Adjust cached value. Fortunately this is really a rare case */ \ + compiler->cache_argw += imm & 0x3; \ + FAIL_IF(push_inst(compiler, ADDI | D(TMP_REG3) | A(TMP_REG3) | (imm & 0x3))); \ + imm &= ~0x3; \ + } +#else +#define ADJUST_CACHED_IMM(imm) +#endif + +/* Emit the necessary instructions. See can_cache above. */ +static int getput_arg(struct sljit_compiler *compiler, int inp_flags, int reg, int arg, sljit_w argw, int next_arg, sljit_w next_argw) +{ + int tmp_r; + sljit_ins inst; + + SLJIT_ASSERT(arg & SLJIT_MEM); + + tmp_r = (inp_flags & LOAD_DATA) ? reg : TMP_REG3; + if ((arg & 0xf) == tmp_r) { + /* Special case for "mov reg, [reg, ... ]". + Caching would not happen anyway. */ + tmp_r = TMP_REG3; + compiler->cache_arg = 0; + compiler->cache_argw = 0; + } + + if (!(arg & 0xf)) { + inst = data_transfer_insts[(inp_flags & ~WRITE_BACK) & MEM_MASK]; + if ((compiler->cache_arg & SLJIT_IMM) && (((sljit_uw)argw - (sljit_uw)compiler->cache_argw) <= SIMM_MAX || ((sljit_uw)compiler->cache_argw - (sljit_uw)argw) <= SIMM_MAX)) { + argw = argw - compiler->cache_argw; + ADJUST_CACHED_IMM(argw); + SLJIT_ASSERT(!(inst & UPDATE_REQ)); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(TMP_REG3) | IMM(argw)); + } + + if ((next_arg & SLJIT_MEM) && (argw - next_argw <= SIMM_MAX || next_argw - argw <= SIMM_MAX)) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + + compiler->cache_arg = SLJIT_IMM; + compiler->cache_argw = argw; + tmp_r = TMP_REG3; + } + + FAIL_IF(load_immediate(compiler, tmp_r, argw)); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(tmp_r)); + } + + if (SLJIT_UNLIKELY(arg & 0xf0)) { + argw &= 0x3; + /* Otherwise getput_arg_fast would capture it. */ + SLJIT_ASSERT(argw); +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + FAIL_IF(push_inst(compiler, RLWINM | S((arg >> 4) & 0xf) | A(tmp_r) | (argw << 11) | ((31 - argw) << 1))); +#else + FAIL_IF(push_inst(compiler, RLDI(tmp_r, (arg >> 4) & 0xf, argw, 63 - argw, 1))); +#endif + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | B(tmp_r)); + } + + inst = data_transfer_insts[inp_flags & MEM_MASK]; + + if (compiler->cache_arg == arg && ((sljit_uw)argw - (sljit_uw)compiler->cache_argw <= SIMM_MAX || (sljit_uw)compiler->cache_argw - (sljit_uw)argw <= SIMM_MAX)) { + SLJIT_ASSERT(!(inp_flags & WRITE_BACK)); + argw = argw - compiler->cache_argw; + ADJUST_CACHED_IMM(argw); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(TMP_REG3) | IMM(argw)); + } + + if ((compiler->cache_arg & SLJIT_IMM) && compiler->cache_argw == argw) { + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | B(TMP_REG3)); + } + + if (argw == next_argw && (next_arg & SLJIT_MEM)) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + + compiler->cache_arg = SLJIT_IMM; + compiler->cache_argw = argw; + + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | B(TMP_REG3)); + } + + if (arg == next_arg && !(inp_flags & WRITE_BACK) && ((sljit_uw)argw - (sljit_uw)next_argw <= SIMM_MAX || (sljit_uw)next_argw - (sljit_uw)argw <= SIMM_MAX)) { + SLJIT_ASSERT(inp_flags & LOAD_DATA); + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + FAIL_IF(push_inst(compiler, ADD | D(TMP_REG3) | A(TMP_REG3) | B(arg & 0xf))); + + compiler->cache_arg = arg; + compiler->cache_argw = argw; + + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(TMP_REG3)); + } + + /* Get the indexed version instead of the normal one. */ + inst = data_transfer_insts[(inp_flags | INDEXED) & MEM_MASK]; + SLJIT_ASSERT(!(inst & (ADDR_MODE2 | UPDATE_REQ))); + FAIL_IF(load_immediate(compiler, tmp_r, argw)); + return push_inst(compiler, GET_INST_CODE(inst) | D(reg) | A(arg & 0xf) | B(tmp_r)); +} + +static int emit_op(struct sljit_compiler *compiler, int op, int inp_flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* arg1 goes to TMP_REG1 or src reg + arg2 goes to TMP_REG2, imm or src reg + TMP_REG3 can be used for caching + result goes to TMP_REG2, so put result can use TMP_REG1 and TMP_REG3. */ + int dst_r; + int src1_r; + int src2_r; + int sugg_src2_r = TMP_REG2; + int flags = inp_flags & (ALT_FORM1 | ALT_FORM2 | ALT_FORM3 | ALT_FORM4 | ALT_FORM5 | ALT_FORM6 | ALT_SIGN_EXT | ALT_SET_FLAGS); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + /* Destination check. */ + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= ZERO_REG) { + dst_r = dst; + flags |= REG_DEST; + if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI) + sugg_src2_r = dst_r; + } + else if (dst == SLJIT_UNUSED) { + if (op >= SLJIT_MOV && op <= SLJIT_MOVU_SI && !(src2 & SLJIT_MEM)) + return SLJIT_SUCCESS; + dst_r = TMP_REG2; + } + else { + SLJIT_ASSERT(dst & SLJIT_MEM); + if (getput_arg_fast(compiler, inp_flags | ARG_TEST, TMP_REG2, dst, dstw)) { + flags |= FAST_DEST; + dst_r = TMP_REG2; + } + else { + flags |= SLOW_DEST; + dst_r = 0; + } + } + + /* Source 1. */ + if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= ZERO_REG) { + src1_r = src1; + flags |= REG1_SOURCE; + } + else if (src1 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if ((inp_flags & 0x3) == INT_DATA) { + if (inp_flags & SIGNED_DATA) + src1w = (signed int)src1w; + else + src1w = (unsigned int)src1w; + } +#endif + FAIL_IF(load_immediate(compiler, TMP_REG1, src1w)); + src1_r = TMP_REG1; + } + else if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w)) { + FAIL_IF(compiler->error); + src1_r = TMP_REG1; + } + else + src1_r = 0; + + /* Source 2. */ + if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= ZERO_REG) { + src2_r = src2; + flags |= REG2_SOURCE; + if (!(flags & REG_DEST) && op >= SLJIT_MOV && op <= SLJIT_MOVU_SI) + dst_r = src2_r; + } + else if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if ((inp_flags & 0x3) == INT_DATA) { + if (inp_flags & SIGNED_DATA) + src2w = (signed int)src2w; + else + src2w = (unsigned int)src2w; + } +#endif + FAIL_IF(load_immediate(compiler, sugg_src2_r, src2w)); + src2_r = sugg_src2_r; + } + else if (getput_arg_fast(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w)) { + FAIL_IF(compiler->error); + src2_r = sugg_src2_r; + } + else + src2_r = 0; + + /* src1_r, src2_r and dst_r can be zero (=unprocessed). + All arguments are complex addressing modes, and it is a binary operator. */ + if (src1_r == 0 && src2_r == 0 && dst_r == 0) { + if (!can_cache(src1, src1w, src2, src2w) && can_cache(src1, src1w, dst, dstw)) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, src1, src1w)); + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); + } + else { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG2, src2, src2w, dst, dstw)); + } + src1_r = TMP_REG1; + src2_r = TMP_REG2; + } + else if (src1_r == 0 && src2_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, src2, src2w)); + src1_r = TMP_REG1; + } + else if (src1_r == 0 && dst_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, dst, dstw)); + src1_r = TMP_REG1; + } + else if (src2_r == 0 && dst_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, dst, dstw)); + src2_r = sugg_src2_r; + } + + if (dst_r == 0) + dst_r = TMP_REG2; + + if (src1_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, TMP_REG1, src1, src1w, 0, 0)); + src1_r = TMP_REG1; + } + + if (src2_r == 0) { + FAIL_IF(getput_arg(compiler, inp_flags | LOAD_DATA, sugg_src2_r, src2, src2w, 0, 0)); + src2_r = sugg_src2_r; + } + + FAIL_IF(emit_single_op(compiler, op, flags, dst_r, src1_r, src2_r)); + + if (flags & (FAST_DEST | SLOW_DEST)) { + if (flags & FAST_DEST) + FAIL_IF(getput_arg_fast(compiler, inp_flags, dst_r, dst, dstw)); + else + FAIL_IF(getput_arg(compiler, inp_flags, dst_r, dst, dstw, 0, 0)); + } + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + CHECK_ERROR(); + check_sljit_emit_op0(compiler, op); + + switch (GET_OPCODE(op)) { + case SLJIT_BREAKPOINT: + case SLJIT_NOP: + return push_inst(compiler, NOP); + break; + case SLJIT_UMUL: + case SLJIT_SMUL: + FAIL_IF(push_inst(compiler, OR | S(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG1))); +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + FAIL_IF(push_inst(compiler, MULLD | D(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2))); + return push_inst(compiler, (GET_OPCODE(op) == SLJIT_UMUL ? MULHDU : MULHD) | D(SLJIT_TEMPORARY_REG2) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2)); +#else + FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2))); + return push_inst(compiler, (GET_OPCODE(op) == SLJIT_UMUL ? MULHWU : MULHW) | D(SLJIT_TEMPORARY_REG2) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2)); +#endif + case SLJIT_UDIV: + case SLJIT_SDIV: + FAIL_IF(push_inst(compiler, OR | S(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG1))); +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (op & SLJIT_INT_OP) { + FAIL_IF(push_inst(compiler, (GET_OPCODE(op) == SLJIT_UDIV ? DIVWU : DIVW) | D(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2))); + FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_TEMPORARY_REG2) | A(SLJIT_TEMPORARY_REG1) | B(SLJIT_TEMPORARY_REG2))); + return push_inst(compiler, SUBF | D(SLJIT_TEMPORARY_REG2) | A(SLJIT_TEMPORARY_REG2) | B(TMP_REG1)); + } + FAIL_IF(push_inst(compiler, (GET_OPCODE(op) == SLJIT_UDIV ? DIVDU : DIVD) | D(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2))); + FAIL_IF(push_inst(compiler, MULLD | D(SLJIT_TEMPORARY_REG2) | A(SLJIT_TEMPORARY_REG1) | B(SLJIT_TEMPORARY_REG2))); + return push_inst(compiler, SUBF | D(SLJIT_TEMPORARY_REG2) | A(SLJIT_TEMPORARY_REG2) | B(TMP_REG1)); +#else + FAIL_IF(push_inst(compiler, (GET_OPCODE(op) == SLJIT_UDIV ? DIVWU : DIVW) | D(SLJIT_TEMPORARY_REG1) | A(TMP_REG1) | B(SLJIT_TEMPORARY_REG2))); + FAIL_IF(push_inst(compiler, MULLW | D(SLJIT_TEMPORARY_REG2) | A(SLJIT_TEMPORARY_REG1) | B(SLJIT_TEMPORARY_REG2))); + return push_inst(compiler, SUBF | D(SLJIT_TEMPORARY_REG2) | A(SLJIT_TEMPORARY_REG2) | B(TMP_REG1)); +#endif + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int inp_flags = GET_FLAGS(op) ? ALT_SET_FLAGS : 0; + + CHECK_ERROR(); + check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw); + + if ((src & SLJIT_IMM) && srcw == 0) + src = ZERO_REG; + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (op & SLJIT_INT_OP) { + inp_flags |= INT_DATA | SIGNED_DATA; + if (src & SLJIT_IMM) + srcw = (int)srcw; + } +#endif + if (op & SLJIT_SET_O) + FAIL_IF(push_inst(compiler, MTXER | S(ZERO_REG))); + + switch (GET_OPCODE(op)) { + case SLJIT_MOV: + return emit_op(compiler, SLJIT_MOV, inp_flags | WORD_DATA, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_UI: + return emit_op(compiler, SLJIT_MOV_UI, inp_flags | INT_DATA, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_SI: + return emit_op(compiler, SLJIT_MOV_SI, inp_flags | INT_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOV_UB: + return emit_op(compiler, SLJIT_MOV_UB, inp_flags | BYTE_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw); + + case SLJIT_MOV_SB: + return emit_op(compiler, SLJIT_MOV_SB, inp_flags | BYTE_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw); + + case SLJIT_MOV_UH: + return emit_op(compiler, SLJIT_MOV_UH, inp_flags | HALF_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw); + + case SLJIT_MOV_SH: + return emit_op(compiler, SLJIT_MOV_SH, inp_flags | HALF_DATA | SIGNED_DATA, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw); + + case SLJIT_MOVU: + return emit_op(compiler, SLJIT_MOV, inp_flags | WORD_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_UI: + return emit_op(compiler, SLJIT_MOV_UI, inp_flags | INT_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_SI: + return emit_op(compiler, SLJIT_MOV_SI, inp_flags | INT_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_MOVU_UB: + return emit_op(compiler, SLJIT_MOV_UB, inp_flags | BYTE_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw); + + case SLJIT_MOVU_SB: + return emit_op(compiler, SLJIT_MOV_SB, inp_flags | BYTE_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw); + + case SLJIT_MOVU_UH: + return emit_op(compiler, SLJIT_MOV_UH, inp_flags | HALF_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw); + + case SLJIT_MOVU_SH: + return emit_op(compiler, SLJIT_MOV_SH, inp_flags | HALF_DATA | SIGNED_DATA | WRITE_BACK, dst, dstw, TMP_REG1, 0, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw); + + case SLJIT_NOT: + return emit_op(compiler, SLJIT_NOT, inp_flags, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_NEG: + return emit_op(compiler, SLJIT_NEG, inp_flags, dst, dstw, TMP_REG1, 0, src, srcw); + + case SLJIT_CLZ: +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + return emit_op(compiler, SLJIT_CLZ, inp_flags | (!(op & SLJIT_INT_OP) ? 0 : ALT_FORM1), dst, dstw, TMP_REG1, 0, src, srcw); +#else + return emit_op(compiler, SLJIT_CLZ, inp_flags, dst, dstw, TMP_REG1, 0, src, srcw); +#endif + } + + return SLJIT_SUCCESS; +} + +#define TEST_SL_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && (srcw) <= SIMM_MAX && (srcw) >= SIMM_MIN) + +#define TEST_UL_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && !((srcw) & ~0xffff)) + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define TEST_SH_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && !((srcw) & 0xffff) && (srcw) <= SLJIT_W(0x7fffffff) && (srcw) >= SLJIT_W(-0x80000000)) +#else +#define TEST_SH_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && !((srcw) & 0xffff)) +#endif + +#define TEST_UH_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && !((srcw) & ~0xffff0000)) + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define TEST_ADD_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && (srcw) <= SLJIT_W(0x7fff7fff) && (srcw) >= SLJIT_W(-0x80000000)) +#else +#define TEST_ADD_IMM(src, srcw) \ + ((src) & SLJIT_IMM) +#endif + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) +#define TEST_UI_IMM(src, srcw) \ + (((src) & SLJIT_IMM) && !((srcw) & ~0xffffffff)) +#else +#define TEST_UI_IMM(src, srcw) \ + ((src) & SLJIT_IMM) +#endif + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int inp_flags = GET_FLAGS(op) ? ALT_SET_FLAGS : 0; + + CHECK_ERROR(); + check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + if ((src1 & SLJIT_IMM) && src1w == 0) + src1 = ZERO_REG; + if ((src2 & SLJIT_IMM) && src2w == 0) + src2 = ZERO_REG; + +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (op & SLJIT_INT_OP) { + inp_flags |= INT_DATA | SIGNED_DATA; + if (src1 & SLJIT_IMM) + src1w = (src1w << 32) >> 32; + if (src2 & SLJIT_IMM) + src2w = (src2w << 32) >> 32; + if (GET_FLAGS(op)) + inp_flags |= ALT_SIGN_EXT; + } +#endif + if (op & SLJIT_SET_O) + FAIL_IF(push_inst(compiler, MTXER | S(ZERO_REG))); + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + if (!GET_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) { + if (TEST_SL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_SL_IMM(src1, src1w)) { + compiler->imm = src1w & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); + } + if (TEST_SH_IMM(src2, src2w)) { + compiler->imm = (src2w >> 16) & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_SH_IMM(src1, src1w)) { + compiler->imm = (src1w >> 16) & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); + } + /* Range between -1 and -32768 is covered above. */ + if (TEST_ADD_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffffffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_ADD_IMM(src1, src1w)) { + compiler->imm = src1w & 0xffffffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM4, dst, dstw, src2, src2w, TMP_REG2, 0); + } + } + if (!(GET_FLAGS(op) & (SLJIT_SET_E | SLJIT_SET_O))) { + if (TEST_SL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_SL_IMM(src1, src1w)) { + compiler->imm = src1w & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0); + } + } + return emit_op(compiler, SLJIT_ADD, inp_flags, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_ADDC: + return emit_op(compiler, SLJIT_ADDC, inp_flags | (!(op & SLJIT_KEEP_FLAGS) ? 0 : ALT_FORM1), dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SUB: + if (!GET_FLAGS(op) && ((src1 | src2) & SLJIT_IMM)) { + if (TEST_SL_IMM(src2, -src2w)) { + compiler->imm = (-src2w) & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_SL_IMM(src1, src1w)) { + compiler->imm = src1w & 0xffff; + return emit_op(compiler, SLJIT_SUB, inp_flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); + } + if (TEST_SH_IMM(src2, -src2w)) { + compiler->imm = ((-src2w) >> 16) & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + } + /* Range between -1 and -32768 is covered above. */ + if (TEST_ADD_IMM(src2, -src2w)) { + compiler->imm = -src2w & 0xffffffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM4, dst, dstw, src1, src1w, TMP_REG2, 0); + } + } + if (dst == SLJIT_UNUSED && (op & (SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U)) && !(op & (SLJIT_SET_O | SLJIT_SET_C))) { + if (!(op & SLJIT_SET_U)) { + /* We know ALT_SIGN_EXT is set if it is an SLJIT_INT_OP on 64 bit systems. */ + if (TEST_SL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_SUB, inp_flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (GET_FLAGS(op) == SLJIT_SET_E && TEST_SL_IMM(src1, src1w)) { + compiler->imm = src1w & 0xffff; + return emit_op(compiler, SLJIT_SUB, inp_flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); + } + } + if (!(op & (SLJIT_SET_E | SLJIT_SET_S))) { + /* We know ALT_SIGN_EXT is set if it is an SLJIT_INT_OP on 64 bit systems. */ + if (TEST_UL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_SUB, inp_flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); + } + return emit_op(compiler, SLJIT_SUB, inp_flags | ALT_FORM4, dst, dstw, src1, src1w, src2, src2w); + } + if ((src2 & SLJIT_IMM) && src2w >= 0 && src2w <= 0x7fff) { + compiler->imm = src2w; + return emit_op(compiler, SLJIT_SUB, inp_flags | ALT_FORM2 | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); + } + return emit_op(compiler, SLJIT_SUB, inp_flags | ((op & SLJIT_SET_U) ? ALT_FORM4 : 0) | ((op & (SLJIT_SET_E | SLJIT_SET_S)) ? ALT_FORM5 : 0), dst, dstw, src1, src1w, src2, src2w); + } + if (!(op & (SLJIT_SET_E | SLJIT_SET_S | SLJIT_SET_U | SLJIT_SET_O))) { + if (TEST_SL_IMM(src2, -src2w)) { + compiler->imm = (-src2w) & 0xffff; + return emit_op(compiler, SLJIT_ADD, inp_flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); + } + } + /* We know ALT_SIGN_EXT is set if it is an SLJIT_INT_OP on 64 bit systems. */ + return emit_op(compiler, SLJIT_SUB, inp_flags | (!(op & SLJIT_SET_U) ? 0 : ALT_FORM6), dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SUBC: + return emit_op(compiler, SLJIT_SUBC, inp_flags | (!(op & SLJIT_KEEP_FLAGS) ? 0 : ALT_FORM1), dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_MUL: +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (op & SLJIT_INT_OP) + inp_flags |= ALT_FORM2; +#endif + if (!GET_FLAGS(op)) { + if (TEST_SL_IMM(src2, src2w)) { + compiler->imm = src2w & 0xffff; + return emit_op(compiler, SLJIT_MUL, inp_flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_SL_IMM(src1, src1w)) { + compiler->imm = src1w & 0xffff; + return emit_op(compiler, SLJIT_MUL, inp_flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); + } + } + return emit_op(compiler, SLJIT_MUL, inp_flags, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_AND: + case SLJIT_OR: + case SLJIT_XOR: + /* Commutative unsigned operations. */ + if (!GET_FLAGS(op) || GET_OPCODE(op) == SLJIT_AND) { + if (TEST_UL_IMM(src2, src2w)) { + compiler->imm = src2w; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_UL_IMM(src1, src1w)) { + compiler->imm = src1w; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM1, dst, dstw, src2, src2w, TMP_REG2, 0); + } + if (TEST_UH_IMM(src2, src2w)) { + compiler->imm = (src2w >> 16) & 0xffff; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM2, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_UH_IMM(src1, src1w)) { + compiler->imm = (src1w >> 16) & 0xffff; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM2, dst, dstw, src2, src2w, TMP_REG2, 0); + } + } + if (!GET_FLAGS(op) && GET_OPCODE(op) != SLJIT_AND) { + if (TEST_UI_IMM(src2, src2w)) { + compiler->imm = src2w; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM3, dst, dstw, src1, src1w, TMP_REG2, 0); + } + if (TEST_UI_IMM(src1, src1w)) { + compiler->imm = src1w; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM3, dst, dstw, src2, src2w, TMP_REG2, 0); + } + } + return emit_op(compiler, GET_OPCODE(op), inp_flags, dst, dstw, src1, src1w, src2, src2w); + + case SLJIT_SHL: + case SLJIT_LSHR: + case SLJIT_ASHR: +#if (defined SLJIT_CONFIG_PPC_64 && SLJIT_CONFIG_PPC_64) + if (op & SLJIT_INT_OP) + inp_flags |= ALT_FORM2; +#endif + if (src2 & SLJIT_IMM) { + compiler->imm = src2w; + return emit_op(compiler, GET_OPCODE(op), inp_flags | ALT_FORM1, dst, dstw, src1, src1w, TMP_REG2, 0); + } + return emit_op(compiler, GET_OPCODE(op), inp_flags, dst, dstw, src1, src1w, src2, src2w); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_register_index(int reg) +{ + check_sljit_get_register_index(reg); + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + CHECK_ERROR(); + check_sljit_emit_op_custom(compiler, instruction, size); + SLJIT_ASSERT(size == 4); + + return push_inst(compiler, *(sljit_ins*)instruction); +} + +/* --------------------------------------------------------------------- */ +/* Floating point operators */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ + /* Always available. */ + return 1; +} + +static int emit_fpu_data_transfer(struct sljit_compiler *compiler, int fpu_reg, int load, int arg, sljit_w argw) +{ + SLJIT_ASSERT(arg & SLJIT_MEM); + + /* Fast loads and stores. */ + if (!(arg & 0xf0)) { + /* Both for (arg & 0xf) == SLJIT_UNUSED and (arg & 0xf) != SLJIT_UNUSED. */ + if (argw <= SIMM_MAX && argw >= SIMM_MIN) + return push_inst(compiler, (load ? LFD : STFD) | FD(fpu_reg) | A(arg & 0xf) | IMM(argw)); + } + + if (arg & 0xf0) { + argw &= 0x3; + if (argw) { +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + FAIL_IF(push_inst(compiler, RLWINM | S((arg >> 4) & 0xf) | A(TMP_REG2) | (argw << 11) | ((31 - argw) << 1))); +#else + FAIL_IF(push_inst(compiler, RLDI(TMP_REG2, (arg >> 4) & 0xf, argw, 63 - argw, 1))); +#endif + return push_inst(compiler, (load ? LFDX : STFDX) | FD(fpu_reg) | A(arg & 0xf) | B(TMP_REG2)); + } + return push_inst(compiler, (load ? LFDX : STFDX) | FD(fpu_reg) | A(arg & 0xf) | B((arg >> 4) & 0xf)); + } + + /* Use cache. */ + if (compiler->cache_arg == arg && argw - compiler->cache_argw <= SIMM_MAX && argw - compiler->cache_argw >= SIMM_MIN) + return push_inst(compiler, (load ? LFD : STFD) | FD(fpu_reg) | A(TMP_REG3) | IMM(argw - compiler->cache_argw)); + + /* Put value to cache. */ + compiler->cache_arg = arg; + compiler->cache_argw = argw; + + FAIL_IF(load_immediate(compiler, TMP_REG3, argw)); + if (!(arg & 0xf)) + return push_inst(compiler, (load ? LFDX : STFDX) | FD(fpu_reg) | A(0) | B(TMP_REG3)); + return push_inst(compiler, (load ? LFDUX : STFDUX) | FD(fpu_reg) | A(TMP_REG3) | B(arg & 0xf)); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int dst_fr; + + CHECK_ERROR(); + check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + if (GET_OPCODE(op) == SLJIT_FCMP) { + if (dst > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, dst, dstw)); + dst = TMP_FREG1; + } + if (src > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src, srcw)); + src = TMP_FREG2; + } + return push_inst(compiler, FCMPU | CRD(4) | FA(dst) | FB(src)); + } + + dst_fr = (dst > SLJIT_FLOAT_REG4) ? TMP_FREG1 : dst; + + if (src > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, dst_fr, 1, src, srcw)); + src = dst_fr; + } + + switch (op) { + case SLJIT_FMOV: + if (src != dst_fr && dst_fr != TMP_FREG1) + FAIL_IF(push_inst(compiler, FMR | FD(dst_fr) | FB(src))); + break; + case SLJIT_FNEG: + FAIL_IF(push_inst(compiler, FNEG | FD(dst_fr) | FB(src))); + break; + case SLJIT_FABS: + FAIL_IF(push_inst(compiler, FABS | FD(dst_fr) | FB(src))); + break; + } + + if (dst_fr == TMP_FREG1) + FAIL_IF(emit_fpu_data_transfer(compiler, src, 0, dst, dstw)); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int dst_fr; + + CHECK_ERROR(); + check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + + compiler->cache_arg = 0; + compiler->cache_argw = 0; + + dst_fr = (dst > SLJIT_FLOAT_REG4) ? TMP_FREG1 : dst; + + if (src2 > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG2, 1, src2, src2w)); + src2 = TMP_FREG2; + } + + if (src1 > SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 1, src1, src1w)); + src1 = TMP_FREG1; + } + + switch (op) { + case SLJIT_FADD: + FAIL_IF(push_inst(compiler, FADD | FD(dst_fr) | FA(src1) | FB(src2))); + break; + + case SLJIT_FSUB: + FAIL_IF(push_inst(compiler, FSUB | FD(dst_fr) | FA(src1) | FB(src2))); + break; + + case SLJIT_FMUL: + FAIL_IF(push_inst(compiler, FMUL | FD(dst_fr) | FA(src1) | FC(src2) /* FMUL use FC as src2 */)); + break; + + case SLJIT_FDIV: + FAIL_IF(push_inst(compiler, FDIV | FD(dst_fr) | FA(src1) | FB(src2))); + break; + } + + if (dst_fr == TMP_FREG1) + FAIL_IF(emit_fpu_data_transfer(compiler, TMP_FREG1, 0, dst, dstw)); + + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Other instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + CHECK_ERROR(); + check_sljit_emit_fast_enter(compiler, dst, dstw, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + + compiler->has_locals = local_size > 0; +#if (defined SLJIT_CONFIG_PPC_32 && SLJIT_CONFIG_PPC_32) + compiler->local_size = (2 + saveds + 2) * sizeof(sljit_w) + local_size; +#else + compiler->local_size = (2 + saveds + 7 + 8) * sizeof(sljit_w) + local_size; +#endif + compiler->local_size = (compiler->local_size + 15) & ~0xf; + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) + return push_inst(compiler, MFLR | D(dst)); + else if (dst & SLJIT_MEM) { + FAIL_IF(push_inst(compiler, MFLR | D(TMP_REG2))); + return emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + CHECK_ERROR(); + check_sljit_emit_fast_return(compiler, src, srcw); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + FAIL_IF(push_inst(compiler, MTLR | S(src))); + else { + if (src & SLJIT_MEM) + FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + else if (src & SLJIT_IMM) + FAIL_IF(load_immediate(compiler, TMP_REG2, srcw)); + FAIL_IF(push_inst(compiler, MTLR | S(TMP_REG2))); + } + return push_inst(compiler, BLR); +} + +/* --------------------------------------------------------------------- */ +/* Conditional instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + struct sljit_label *label; + + CHECK_ERROR_PTR(); + check_sljit_emit_label(compiler); + + if (compiler->last_label && compiler->last_label->size == compiler->size) + return compiler->last_label; + + label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); + PTR_FAIL_IF(!label); + set_label(label, compiler); + return label; +} + +static sljit_ins get_bo_bi_flags(struct sljit_compiler *compiler, int type) +{ + switch (type) { + case SLJIT_C_EQUAL: + return (12 << 21) | (2 << 16); + + case SLJIT_C_NOT_EQUAL: + return (4 << 21) | (2 << 16); + + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + return (12 << 21) | ((4 + 0) << 16); + + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + return (4 << 21) | ((4 + 0) << 16); + + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + return (12 << 21) | ((4 + 1) << 16); + + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + return (4 << 21) | ((4 + 1) << 16); + + case SLJIT_C_SIG_LESS: + return (12 << 21) | (0 << 16); + + case SLJIT_C_SIG_GREATER_EQUAL: + return (4 << 21) | (0 << 16); + + case SLJIT_C_SIG_GREATER: + return (12 << 21) | (1 << 16); + + case SLJIT_C_SIG_LESS_EQUAL: + return (4 << 21) | (1 << 16); + + case SLJIT_C_OVERFLOW: + case SLJIT_C_MUL_OVERFLOW: + return (12 << 21) | (3 << 16); + + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_MUL_NOT_OVERFLOW: + return (4 << 21) | (3 << 16); + + case SLJIT_C_FLOAT_EQUAL: + return (12 << 21) | ((4 + 2) << 16); + + case SLJIT_C_FLOAT_NOT_EQUAL: + return (4 << 21) | ((4 + 2) << 16); + + case SLJIT_C_FLOAT_NAN: + return (12 << 21) | ((4 + 3) << 16); + + case SLJIT_C_FLOAT_NOT_NAN: + return (4 << 21) | ((4 + 3) << 16); + + default: + SLJIT_ASSERT(type >= SLJIT_JUMP && type <= SLJIT_CALL3); + return (20 << 21); + } +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + struct sljit_jump *jump; + sljit_ins bo_bi_flags; + + CHECK_ERROR_PTR(); + check_sljit_emit_jump(compiler, type); + + bo_bi_flags = get_bo_bi_flags(compiler, type & 0xff); + if (!bo_bi_flags) + return NULL; + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF(!jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + /* In PPC, we don't need to touch the arguments. */ + if (type >= SLJIT_JUMP) + jump->flags |= UNCOND_B; + + PTR_FAIL_IF(emit_const(compiler, TMP_REG1, 0)); + PTR_FAIL_IF(push_inst(compiler, MTCTR | S(TMP_REG1))); + jump->addr = compiler->size; + PTR_FAIL_IF(push_inst(compiler, BCCTR | bo_bi_flags | (type >= SLJIT_FAST_CALL ? 1 : 0))); + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + sljit_ins bo_bi_flags; + struct sljit_jump *jump = NULL; + int src_r; + + CHECK_ERROR(); + check_sljit_emit_ijump(compiler, type, src, srcw); + + bo_bi_flags = get_bo_bi_flags(compiler, type); + FAIL_IF(!bo_bi_flags); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) + src_r = src; + else if (src & SLJIT_IMM) { + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + FAIL_IF(!jump); + set_jump(jump, compiler, JUMP_ADDR | UNCOND_B); + jump->u.target = srcw; + + FAIL_IF(emit_const(compiler, TMP_REG2, 0)); + src_r = TMP_REG2; + } + else { + FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, TMP_REG2, 0, TMP_REG1, 0, src, srcw)); + src_r = TMP_REG2; + } + + FAIL_IF(push_inst(compiler, MTCTR | S(src_r))); + if (jump) + jump->addr = compiler->size; + return push_inst(compiler, BCCTR | bo_bi_flags | (type >= SLJIT_FAST_CALL ? 1 : 0)); +} + +/* Get a bit from CR, all other bits are zeroed. */ +#define GET_CR_BIT(bit, dst) \ + FAIL_IF(push_inst(compiler, MFCR | D(dst))); \ + FAIL_IF(push_inst(compiler, RLWINM | S(dst) | A(dst) | ((1 + (bit)) << 11) | (31 << 6) | (31 << 1))); + +#define INVERT_BIT(dst) \ + FAIL_IF(push_inst(compiler, XORI | S(dst) | A(dst) | 0x1)); + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + int reg; + + CHECK_ERROR(); + check_sljit_emit_cond_value(compiler, op, dst, dstw, type); + + if (dst == SLJIT_UNUSED) + return SLJIT_SUCCESS; + + reg = (op == SLJIT_MOV && dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG2; + + switch (type) { + case SLJIT_C_EQUAL: + GET_CR_BIT(2, reg); + break; + + case SLJIT_C_NOT_EQUAL: + GET_CR_BIT(2, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + GET_CR_BIT(4 + 0, reg); + break; + + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + GET_CR_BIT(4 + 0, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + GET_CR_BIT(4 + 1, reg); + break; + + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + GET_CR_BIT(4 + 1, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_SIG_LESS: + GET_CR_BIT(0, reg); + break; + + case SLJIT_C_SIG_GREATER_EQUAL: + GET_CR_BIT(0, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_SIG_GREATER: + GET_CR_BIT(1, reg); + break; + + case SLJIT_C_SIG_LESS_EQUAL: + GET_CR_BIT(1, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_OVERFLOW: + case SLJIT_C_MUL_OVERFLOW: + GET_CR_BIT(3, reg); + break; + + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_MUL_NOT_OVERFLOW: + GET_CR_BIT(3, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_FLOAT_EQUAL: + GET_CR_BIT(4 + 2, reg); + break; + + case SLJIT_C_FLOAT_NOT_EQUAL: + GET_CR_BIT(4 + 2, reg); + INVERT_BIT(reg); + break; + + case SLJIT_C_FLOAT_NAN: + GET_CR_BIT(4 + 3, reg); + break; + + case SLJIT_C_FLOAT_NOT_NAN: + GET_CR_BIT(4 + 3, reg); + INVERT_BIT(reg); + break; + + default: + SLJIT_ASSERT_STOP(); + break; + } + + if (GET_OPCODE(op) == SLJIT_OR) + return emit_op(compiler, GET_OPCODE(op), GET_FLAGS(op) ? ALT_SET_FLAGS : 0, dst, dstw, dst, dstw, TMP_REG2, 0); + + if (reg == TMP_REG2) + return emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w init_value) +{ + struct sljit_const *const_; + int reg; + + CHECK_ERROR_PTR(); + check_sljit_emit_const(compiler, dst, dstw, init_value); + + const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); + PTR_FAIL_IF(!const_); + set_const(const_, compiler); + + reg = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REG2; + + PTR_FAIL_IF(emit_const(compiler, reg, init_value)); + + if (dst & SLJIT_MEM) + PTR_FAIL_IF(emit_op(compiler, SLJIT_MOV, WORD_DATA, dst, dstw, TMP_REG1, 0, TMP_REG2, 0)); + return const_; +} diff --git a/src/lib/pcre/sljit/sljitNativeX86_32.c b/src/lib/pcre/sljit/sljitNativeX86_32.c new file mode 100644 index 0000000..68bca84 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeX86_32.c @@ -0,0 +1,517 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +/* x86 32-bit arch dependent functions. */ + +static int emit_do_imm(struct sljit_compiler *compiler, sljit_ub opcode, sljit_w imm) +{ + sljit_ub *buf; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1 + sizeof(sljit_w)); + FAIL_IF(!buf); + INC_SIZE(1 + sizeof(sljit_w)); + *buf++ = opcode; + *(sljit_w*)buf = imm; + return SLJIT_SUCCESS; +} + +static sljit_ub* generate_far_jump_code(struct sljit_jump *jump, sljit_ub *code_ptr, int type) +{ + if (type == SLJIT_JUMP) { + *code_ptr++ = 0xe9; + jump->addr++; + } + else if (type >= SLJIT_FAST_CALL) { + *code_ptr++ = 0xe8; + jump->addr++; + } + else { + *code_ptr++ = 0x0f; + *code_ptr++ = get_jump_code(type); + jump->addr += 2; + } + + if (jump->flags & JUMP_LABEL) + jump->flags |= PATCH_MW; + else + *(sljit_w*)code_ptr = jump->u.target - (jump->addr + 4); + code_ptr += 4; + + return code_ptr; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int size; + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_enter(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + compiler->args = args; + compiler->flags_saved = 0; + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + size = 1 + (saveds <= 3 ? saveds : 3) + (args > 0 ? (args * 2) : 0) + (args > 2 ? 2 : 0); +#else + size = 1 + (saveds <= 3 ? saveds : 3) + (args > 0 ? (2 + args * 3) : 0); +#endif + buf = (sljit_ub*)ensure_buf(compiler, 1 + size); + FAIL_IF(!buf); + + INC_SIZE(size); + PUSH_REG(reg_map[TMP_REGISTER]); +#if !(defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (args > 0) { + *buf++ = 0x8b; + *buf++ = 0xc4 | (reg_map[TMP_REGISTER] << 3); + } +#endif + if (saveds > 2) + PUSH_REG(reg_map[SLJIT_SAVED_REG3]); + if (saveds > 1) + PUSH_REG(reg_map[SLJIT_SAVED_REG2]); + if (saveds > 0) + PUSH_REG(reg_map[SLJIT_SAVED_REG1]); + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (args > 0) { + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_SAVED_REG1] << 3) | reg_map[SLJIT_TEMPORARY_REG3]; + } + if (args > 1) { + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_SAVED_REG2] << 3) | reg_map[SLJIT_TEMPORARY_REG2]; + } + if (args > 2) { + *buf++ = 0x8b; + *buf++ = 0x44 | (reg_map[SLJIT_SAVED_REG3] << 3); + *buf++ = 0x24; + *buf++ = sizeof(sljit_w) * (3 + 2); /* saveds >= 3 as well. */ + } +#else + if (args > 0) { + *buf++ = 0x8b; + *buf++ = 0x40 | (reg_map[SLJIT_SAVED_REG1] << 3) | reg_map[TMP_REGISTER]; + *buf++ = sizeof(sljit_w) * 2; + } + if (args > 1) { + *buf++ = 0x8b; + *buf++ = 0x40 | (reg_map[SLJIT_SAVED_REG2] << 3) | reg_map[TMP_REGISTER]; + *buf++ = sizeof(sljit_w) * 3; + } + if (args > 2) { + *buf++ = 0x8b; + *buf++ = 0x40 | (reg_map[SLJIT_SAVED_REG3] << 3) | reg_map[TMP_REGISTER]; + *buf++ = sizeof(sljit_w) * 4; + } +#endif + + local_size = (local_size + sizeof(sljit_uw) - 1) & ~(sizeof(sljit_uw) - 1); + compiler->temporaries_start = local_size; + if (temporaries > 3) + local_size += (temporaries - 3) * sizeof(sljit_uw); + compiler->saveds_start = local_size; + if (saveds > 3) + local_size += (saveds - 3) * sizeof(sljit_uw); + +#ifdef _WIN32 + if (local_size > 1024) { + FAIL_IF(emit_do_imm(compiler, 0xb8 + reg_map[SLJIT_TEMPORARY_REG1], local_size)); + FAIL_IF(sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_touch_stack))); + } +#endif + + compiler->local_size = local_size; + if (local_size > 0) + return emit_non_cum_binary(compiler, 0x2b, 0x29, 0x5 << 3, 0x2d, + SLJIT_LOCALS_REG, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, local_size); + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + CHECK_ERROR_VOID(); + check_sljit_set_context(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + compiler->args = args; + compiler->local_size = (local_size + sizeof(sljit_uw) - 1) & ~(sizeof(sljit_uw) - 1); + compiler->temporaries_start = compiler->local_size; + if (temporaries > 3) + compiler->local_size += (temporaries - 3) * sizeof(sljit_uw); + compiler->saveds_start = compiler->local_size; + if (saveds > 3) + compiler->local_size += (saveds - 3) * sizeof(sljit_uw); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + int size; + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_return(compiler, op, src, srcw); + SLJIT_ASSERT(compiler->args >= 0); + + compiler->flags_saved = 0; + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + if (compiler->local_size > 0) + FAIL_IF(emit_cum_binary(compiler, 0x03, 0x01, 0x0 << 3, 0x05, + SLJIT_LOCALS_REG, 0, SLJIT_LOCALS_REG, 0, SLJIT_IMM, compiler->local_size)); + + size = 2 + (compiler->saveds <= 3 ? compiler->saveds : 3); +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (compiler->args > 2) + size += 2; +#else + if (compiler->args > 0) + size += 2; +#endif + buf = (sljit_ub*)ensure_buf(compiler, 1 + size); + FAIL_IF(!buf); + + INC_SIZE(size); + + if (compiler->saveds > 0) + POP_REG(reg_map[SLJIT_SAVED_REG1]); + if (compiler->saveds > 1) + POP_REG(reg_map[SLJIT_SAVED_REG2]); + if (compiler->saveds > 2) + POP_REG(reg_map[SLJIT_SAVED_REG3]); + POP_REG(reg_map[TMP_REGISTER]); +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (compiler->args > 2) + RETN(sizeof(sljit_w)); + else + RET(); +#else + if (compiler->args > 0) + RETN(compiler->args * sizeof(sljit_w)); + else + RET(); +#endif + + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +/* Size contains the flags as well. */ +static sljit_ub* emit_x86_instruction(struct sljit_compiler *compiler, int size, + /* The register or immediate operand. */ + int a, sljit_w imma, + /* The general operand (not immediate). */ + int b, sljit_w immb) +{ + sljit_ub *buf; + sljit_ub *buf_ptr; + int flags = size & ~0xf; + int inst_size; + + /* Both cannot be switched on. */ + SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS)); + /* Size flags not allowed for typed instructions. */ + SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0); + /* Both size flags cannot be switched on. */ + SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG)); +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + /* SSE2 and immediate is not possible. */ + SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2)); +#endif + + size &= 0xf; + inst_size = size; + +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + if (flags & EX86_PREF_F2) + inst_size++; +#endif + if (flags & EX86_PREF_66) + inst_size++; + + /* Calculate size of b. */ + inst_size += 1; /* mod r/m byte. */ + if (b & SLJIT_MEM) { + if ((b & 0x0f) == SLJIT_UNUSED) + inst_size += sizeof(sljit_w); + else if (immb != 0 && !(b & 0xf0)) { + /* Immediate operand. */ + if (immb <= 127 && immb >= -128) + inst_size += sizeof(sljit_b); + else + inst_size += sizeof(sljit_w); + } + + if ((b & 0xf) == SLJIT_LOCALS_REG && !(b & 0xf0)) + b |= SLJIT_LOCALS_REG << 4; + + if ((b & 0xf0) != SLJIT_UNUSED) + inst_size += 1; /* SIB byte. */ + } + + /* Calculate size of a. */ + if (a & SLJIT_IMM) { + if (flags & EX86_BIN_INS) { + if (imma <= 127 && imma >= -128) { + inst_size += 1; + flags |= EX86_BYTE_ARG; + } else + inst_size += 4; + } + else if (flags & EX86_SHIFT_INS) { + imma &= 0x1f; + if (imma != 1) { + inst_size ++; + flags |= EX86_BYTE_ARG; + } + } else if (flags & EX86_BYTE_ARG) + inst_size++; + else if (flags & EX86_HALF_ARG) + inst_size += sizeof(short); + else + inst_size += sizeof(sljit_w); + } + else + SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG); + + buf = (sljit_ub*)ensure_buf(compiler, 1 + inst_size); + PTR_FAIL_IF(!buf); + + /* Encoding the byte. */ + INC_SIZE(inst_size); +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + if (flags & EX86_PREF_F2) + *buf++ = 0xf2; +#endif + if (flags & EX86_PREF_66) + *buf++ = 0x66; + + buf_ptr = buf + size; + + /* Encode mod/rm byte. */ + if (!(flags & EX86_SHIFT_INS)) { + if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM)) + *buf = (flags & EX86_BYTE_ARG) ? 0x83 : 0x81; + + if ((a & SLJIT_IMM) || (a == 0)) + *buf_ptr = 0; +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + else if (!(flags & EX86_SSE2)) + *buf_ptr = reg_map[a] << 3; + else + *buf_ptr = a << 3; +#else + else + *buf_ptr = reg_map[a] << 3; +#endif + } + else { + if (a & SLJIT_IMM) { + if (imma == 1) + *buf = 0xd1; + else + *buf = 0xc1; + } else + *buf = 0xd3; + *buf_ptr = 0; + } + + if (!(b & SLJIT_MEM)) +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + *buf_ptr++ |= 0xc0 + ((!(flags & EX86_SSE2)) ? reg_map[b] : b); +#else + *buf_ptr++ |= 0xc0 + reg_map[b]; +#endif + else if ((b & 0x0f) != SLJIT_UNUSED) { + if ((b & 0xf0) == SLJIT_UNUSED || (b & 0xf0) == (SLJIT_LOCALS_REG << 4)) { + if (immb != 0) { + if (immb <= 127 && immb >= -128) + *buf_ptr |= 0x40; + else + *buf_ptr |= 0x80; + } + + if ((b & 0xf0) == SLJIT_UNUSED) + *buf_ptr++ |= reg_map[b & 0x0f]; + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = reg_map[b & 0x0f] | (reg_map[(b >> 4) & 0x0f] << 3); + } + + if (immb != 0) { + if (immb <= 127 && immb >= -128) + *buf_ptr++ = immb; /* 8 bit displacement. */ + else { + *(sljit_w*)buf_ptr = immb; /* 32 bit displacement. */ + buf_ptr += sizeof(sljit_w); + } + } + } + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = reg_map[b & 0x0f] | (reg_map[(b >> 4) & 0x0f] << 3) | (immb << 6); + } + } + else { + *buf_ptr++ |= 0x05; + *(sljit_w*)buf_ptr = immb; /* 32 bit displacement. */ + buf_ptr += sizeof(sljit_w); + } + + if (a & SLJIT_IMM) { + if (flags & EX86_BYTE_ARG) + *buf_ptr = imma; + else if (flags & EX86_HALF_ARG) + *(short*)buf_ptr = imma; + else if (!(flags & EX86_SHIFT_INS)) + *(sljit_w*)buf_ptr = imma; + } + + return !(flags & EX86_SHIFT_INS) ? buf : (buf + 1); +} + +/* --------------------------------------------------------------------- */ +/* Call / return instructions */ +/* --------------------------------------------------------------------- */ + +static SLJIT_INLINE int call_with_args(struct sljit_compiler *compiler, int type) +{ + sljit_ub *buf; + +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + buf = (sljit_ub*)ensure_buf(compiler, type >= SLJIT_CALL3 ? 1 + 2 + 1 : 1 + 2); + FAIL_IF(!buf); + INC_SIZE(type >= SLJIT_CALL3 ? 2 + 1 : 2); + + if (type >= SLJIT_CALL3) + PUSH_REG(reg_map[SLJIT_TEMPORARY_REG3]); + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_TEMPORARY_REG3] << 3) | reg_map[SLJIT_TEMPORARY_REG1]; +#else + buf = (sljit_ub*)ensure_buf(compiler, type - SLJIT_CALL0 + 1); + FAIL_IF(!buf); + INC_SIZE(type - SLJIT_CALL0); + if (type >= SLJIT_CALL3) + PUSH_REG(reg_map[SLJIT_TEMPORARY_REG3]); + if (type >= SLJIT_CALL2) + PUSH_REG(reg_map[SLJIT_TEMPORARY_REG2]); + PUSH_REG(reg_map[SLJIT_TEMPORARY_REG1]); +#endif + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_fast_enter(compiler, dst, dstw, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + compiler->args = args; + compiler->local_size = (local_size + sizeof(sljit_uw) - 1) & ~(sizeof(sljit_uw) - 1); + compiler->temporaries_start = compiler->local_size; + if (temporaries > 3) + compiler->local_size += (temporaries - 3) * sizeof(sljit_uw); + compiler->saveds_start = compiler->local_size; + if (saveds > 3) + compiler->local_size += (saveds - 3) * sizeof(sljit_uw); + + CHECK_EXTRA_REGS(dst, dstw, (void)0); + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + + INC_SIZE(1); + POP_REG(reg_map[dst]); + return SLJIT_SUCCESS; + } + else if (dst & SLJIT_MEM) { + buf = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + FAIL_IF(!buf); + *buf++ = 0x8f; + return SLJIT_SUCCESS; + } + + /* For UNUSED dst. Uncommon, but possible. */ + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + + INC_SIZE(1); + POP_REG(reg_map[TMP_REGISTER]); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_fast_return(compiler, src, srcw); + + CHECK_EXTRA_REGS(src, srcw, (void)0); + + if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1 + 1); + FAIL_IF(!buf); + + INC_SIZE(1 + 1); + PUSH_REG(reg_map[src]); + } + else if (src & SLJIT_MEM) { + buf = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); + FAIL_IF(!buf); + *buf++ = 0xff; + *buf |= 6 << 3; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + INC_SIZE(1); + } + else { + /* SLJIT_IMM. */ + buf = (sljit_ub*)ensure_buf(compiler, 1 + 5 + 1); + FAIL_IF(!buf); + + INC_SIZE(5 + 1); + *buf++ = 0x68; + *(sljit_w*)buf = srcw; + buf += sizeof(sljit_w); + } + + RET(); + return SLJIT_SUCCESS; +} diff --git a/src/lib/pcre/sljit/sljitNativeX86_64.c b/src/lib/pcre/sljit/sljitNativeX86_64.c new file mode 100644 index 0000000..40d875b --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeX86_64.c @@ -0,0 +1,842 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +/* x86 64-bit arch dependent functions. */ + +static int emit_load_imm64(struct sljit_compiler *compiler, int reg, sljit_w imm) +{ + sljit_ub *buf; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2 + sizeof(sljit_w)); + FAIL_IF(!buf); + INC_SIZE(2 + sizeof(sljit_w)); + *buf++ = REX_W | ((reg_map[reg] <= 7) ? 0 : REX_B); + *buf++ = 0xb8 + (reg_map[reg] & 0x7); + *(sljit_w*)buf = imm; + return SLJIT_SUCCESS; +} + +static sljit_ub* generate_far_jump_code(struct sljit_jump *jump, sljit_ub *code_ptr, int type) +{ + if (type < SLJIT_JUMP) { + *code_ptr++ = get_jump_code(type ^ 0x1) - 0x10; + *code_ptr++ = 10 + 3; + } + + SLJIT_COMPILE_ASSERT(reg_map[TMP_REG3] == 9, tmp3_is_9_first); + *code_ptr++ = REX_W | REX_B; + *code_ptr++ = 0xb8 + 1; + jump->addr = (sljit_uw)code_ptr; + + if (jump->flags & JUMP_LABEL) + jump->flags |= PATCH_MD; + else + *(sljit_w*)code_ptr = jump->u.target; + + code_ptr += sizeof(sljit_w); + *code_ptr++ = REX_B; + *code_ptr++ = 0xff; + *code_ptr++ = (type >= SLJIT_FAST_CALL) ? 0xd1 /* call */ : 0xe1 /* jmp */; + + return code_ptr; +} + +static sljit_ub* generate_fixed_jump(sljit_ub *code_ptr, sljit_w addr, int type) +{ + sljit_w delta = addr - ((sljit_w)code_ptr + 1 + sizeof(sljit_hw)); + + if (delta <= SLJIT_W(0x7fffffff) && delta >= SLJIT_W(-0x80000000)) { + *code_ptr++ = (type == 2) ? 0xe8 /* call */ : 0xe9 /* jmp */; + *(sljit_w*)code_ptr = delta; + } + else { + SLJIT_COMPILE_ASSERT(reg_map[TMP_REG3] == 9, tmp3_is_9_second); + *code_ptr++ = REX_W | REX_B; + *code_ptr++ = 0xb8 + 1; + *(sljit_w*)code_ptr = addr; + code_ptr += sizeof(sljit_w); + *code_ptr++ = REX_B; + *code_ptr++ = 0xff; + *code_ptr++ = (type == 2) ? 0xd1 /* call */ : 0xe1 /* jmp */; + } + + return code_ptr; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_enter(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int size, pushed_size; + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_enter(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + compiler->flags_saved = 0; + + size = saveds; + /* Including the return address saved by the call instruction. */ + pushed_size = (saveds + 1) * sizeof(sljit_w); +#ifndef _WIN64 + if (saveds >= 2) + size += saveds - 1; +#else + /* Saving the virtual stack pointer. */ + compiler->has_locals = local_size > 0; + if (local_size > 0) { + size += 2; + pushed_size += sizeof(sljit_w); + } + if (saveds >= 4) + size += saveds - 3; + if (temporaries >= 5) { + size += (5 - 4) * 2; + pushed_size += sizeof(sljit_w); + } +#endif + size += args * 3; + if (size > 0) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + size); + FAIL_IF(!buf); + + INC_SIZE(size); + if (saveds >= 5) { + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_EREG2] >= 8, saved_ereg2_is_hireg); + *buf++ = REX_B; + PUSH_REG(reg_lmap[SLJIT_SAVED_EREG2]); + } + if (saveds >= 4) { + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_EREG1] >= 8, saved_ereg1_is_hireg); + *buf++ = REX_B; + PUSH_REG(reg_lmap[SLJIT_SAVED_EREG1]); + } + if (saveds >= 3) { +#ifndef _WIN64 + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_REG3] >= 8, saved_reg3_is_hireg); + *buf++ = REX_B; +#else + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_REG3] < 8, saved_reg3_is_loreg); +#endif + PUSH_REG(reg_lmap[SLJIT_SAVED_REG3]); + } + if (saveds >= 2) { +#ifndef _WIN64 + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_REG2] >= 8, saved_reg2_is_hireg); + *buf++ = REX_B; +#else + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_REG2] < 8, saved_reg2_is_loreg); +#endif + PUSH_REG(reg_lmap[SLJIT_SAVED_REG2]); + } + if (saveds >= 1) { + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_SAVED_REG1] < 8, saved_reg1_is_loreg); + PUSH_REG(reg_lmap[SLJIT_SAVED_REG1]); + } +#ifdef _WIN64 + if (temporaries >= 5) { + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_TEMPORARY_EREG2] >= 8, temporary_ereg2_is_hireg); + *buf++ = REX_B; + PUSH_REG(reg_lmap[SLJIT_TEMPORARY_EREG2]); + } + if (local_size > 0) { + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_LOCALS_REG] >= 8, locals_reg_is_hireg); + *buf++ = REX_B; + PUSH_REG(reg_lmap[SLJIT_LOCALS_REG]); + } +#endif + +#ifndef _WIN64 + if (args > 0) { + *buf++ = REX_W; + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_SAVED_REG1] << 3) | 0x7; + } + if (args > 1) { + *buf++ = REX_W | REX_R; + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_lmap[SLJIT_SAVED_REG2] << 3) | 0x6; + } + if (args > 2) { + *buf++ = REX_W | REX_R; + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_lmap[SLJIT_SAVED_REG3] << 3) | 0x2; + } +#else + if (args > 0) { + *buf++ = REX_W; + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_SAVED_REG1] << 3) | 0x1; + } + if (args > 1) { + *buf++ = REX_W; + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_SAVED_REG2] << 3) | 0x2; + } + if (args > 2) { + *buf++ = REX_W | REX_B; + *buf++ = 0x8b; + *buf++ = 0xc0 | (reg_map[SLJIT_SAVED_REG3] << 3) | 0x0; + } +#endif + } + + local_size = ((local_size + pushed_size + 16 - 1) & ~(16 - 1)) - pushed_size; +#ifdef _WIN64 + local_size += 4 * sizeof(sljit_w); + compiler->local_size = local_size; + if (local_size > 1024) { + /* Allocate the stack for the function itself. */ + buf = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!buf); + INC_SIZE(4); + *buf++ = REX_W; + *buf++ = 0x83; + *buf++ = 0xc0 | (5 << 3) | 4; + /* Pushed size must be divisible by 8. */ + SLJIT_ASSERT(!(pushed_size & 0x7)); + if (pushed_size & 0x8) { + *buf++ = 5 * sizeof(sljit_w); + local_size -= 5 * sizeof(sljit_w); + } else { + *buf++ = 4 * sizeof(sljit_w); + local_size -= 4 * sizeof(sljit_w); + } + FAIL_IF(emit_load_imm64(compiler, SLJIT_TEMPORARY_REG1, local_size)); + FAIL_IF(sljit_emit_ijump(compiler, SLJIT_CALL1, SLJIT_IMM, SLJIT_FUNC_OFFSET(sljit_touch_stack))); + } +#else + compiler->local_size = local_size; + if (local_size > 0) { +#endif + /* In case of Win64, local_size is always > 4 * sizeof(sljit_w) */ + if (local_size <= 127) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!buf); + INC_SIZE(4); + *buf++ = REX_W; + *buf++ = 0x83; + *buf++ = 0xc0 | (5 << 3) | 4; + *buf++ = local_size; + } + else { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 7); + FAIL_IF(!buf); + INC_SIZE(7); + *buf++ = REX_W; + *buf++ = 0x81; + *buf++ = 0xc0 | (5 << 3) | 4; + *(sljit_hw*)buf = local_size; + buf += sizeof(sljit_hw); + } +#ifndef _WIN64 + } +#endif + +#ifdef _WIN64 + if (compiler->has_locals) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 5); + FAIL_IF(!buf); + INC_SIZE(5); + *buf++ = REX_W | REX_R; + *buf++ = 0x8d; + *buf++ = 0x40 | (reg_lmap[SLJIT_LOCALS_REG] << 3) | 0x4; + *buf++ = 0x24; + *buf = 4 * sizeof(sljit_w); + } +#endif + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_context(struct sljit_compiler *compiler, int args, int temporaries, int saveds, int local_size) +{ + int pushed_size; + + CHECK_ERROR_VOID(); + check_sljit_set_context(compiler, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + /* Including the return address saved by the call instruction. */ + pushed_size = (saveds + 1) * sizeof(sljit_w); +#ifdef _WIN64 + compiler->has_locals = local_size > 0; + if (local_size > 0) + pushed_size += sizeof(sljit_w); + if (temporaries >= 5) + pushed_size += sizeof(sljit_w); +#endif + compiler->local_size = ((local_size + pushed_size + 16 - 1) & ~(16 - 1)) - pushed_size; +#ifdef _WIN64 + compiler->local_size += 4 * sizeof(sljit_w); +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_return(struct sljit_compiler *compiler, int op, int src, sljit_w srcw) +{ + int size; + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_return(compiler, op, src, srcw); + + compiler->flags_saved = 0; + FAIL_IF(emit_mov_before_return(compiler, op, src, srcw)); + + if (compiler->local_size > 0) { + if (compiler->local_size <= 127) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!buf); + INC_SIZE(4); + *buf++ = REX_W; + *buf++ = 0x83; + *buf++ = 0xc0 | (0 << 3) | 4; + *buf = compiler->local_size; + } + else { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 7); + FAIL_IF(!buf); + INC_SIZE(7); + *buf++ = REX_W; + *buf++ = 0x81; + *buf++ = 0xc0 | (0 << 3) | 4; + *(sljit_hw*)buf = compiler->local_size; + } + } + + size = 1 + compiler->saveds; +#ifndef _WIN64 + if (compiler->saveds >= 2) + size += compiler->saveds - 1; +#else + if (compiler->has_locals) + size += 2; + if (compiler->saveds >= 4) + size += compiler->saveds - 3; + if (compiler->temporaries >= 5) + size += (5 - 4) * 2; +#endif + buf = (sljit_ub*)ensure_buf(compiler, 1 + size); + FAIL_IF(!buf); + + INC_SIZE(size); + +#ifdef _WIN64 + if (compiler->has_locals) { + *buf++ = REX_B; + POP_REG(reg_lmap[SLJIT_LOCALS_REG]); + } + if (compiler->temporaries >= 5) { + *buf++ = REX_B; + POP_REG(reg_lmap[SLJIT_TEMPORARY_EREG2]); + } +#endif + if (compiler->saveds >= 1) + POP_REG(reg_map[SLJIT_SAVED_REG1]); + if (compiler->saveds >= 2) { +#ifndef _WIN64 + *buf++ = REX_B; +#endif + POP_REG(reg_lmap[SLJIT_SAVED_REG2]); + } + if (compiler->saveds >= 3) { +#ifndef _WIN64 + *buf++ = REX_B; +#endif + POP_REG(reg_lmap[SLJIT_SAVED_REG3]); + } + if (compiler->saveds >= 4) { + *buf++ = REX_B; + POP_REG(reg_lmap[SLJIT_SAVED_EREG1]); + } + if (compiler->saveds >= 5) { + *buf++ = REX_B; + POP_REG(reg_lmap[SLJIT_SAVED_EREG2]); + } + + RET(); + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +static int emit_do_imm32(struct sljit_compiler *compiler, sljit_ub rex, sljit_ub opcode, sljit_w imm) +{ + sljit_ub *buf; + + if (rex != 0) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2 + sizeof(sljit_hw)); + FAIL_IF(!buf); + INC_SIZE(2 + sizeof(sljit_hw)); + *buf++ = rex; + *buf++ = opcode; + *(sljit_hw*)buf = imm; + } + else { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1 + sizeof(sljit_hw)); + FAIL_IF(!buf); + INC_SIZE(1 + sizeof(sljit_hw)); + *buf++ = opcode; + *(sljit_hw*)buf = imm; + } + return SLJIT_SUCCESS; +} + +static sljit_ub* emit_x86_instruction(struct sljit_compiler *compiler, int size, + /* The register or immediate operand. */ + int a, sljit_w imma, + /* The general operand (not immediate). */ + int b, sljit_w immb) +{ + sljit_ub *buf; + sljit_ub *buf_ptr; + sljit_ub rex = 0; + int flags = size & ~0xf; + int inst_size; + + /* The immediate operand must be 32 bit. */ + SLJIT_ASSERT(!(a & SLJIT_IMM) || compiler->mode32 || IS_HALFWORD(imma)); + /* Both cannot be switched on. */ + SLJIT_ASSERT((flags & (EX86_BIN_INS | EX86_SHIFT_INS)) != (EX86_BIN_INS | EX86_SHIFT_INS)); + /* Size flags not allowed for typed instructions. */ + SLJIT_ASSERT(!(flags & (EX86_BIN_INS | EX86_SHIFT_INS)) || (flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) == 0); + /* Both size flags cannot be switched on. */ + SLJIT_ASSERT((flags & (EX86_BYTE_ARG | EX86_HALF_ARG)) != (EX86_BYTE_ARG | EX86_HALF_ARG)); +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + /* SSE2 and immediate is not possible. */ + SLJIT_ASSERT(!(a & SLJIT_IMM) || !(flags & EX86_SSE2)); +#endif + + size &= 0xf; + inst_size = size; + + if ((b & SLJIT_MEM) && !(b & 0xf0) && NOT_HALFWORD(immb)) { + if (emit_load_imm64(compiler, TMP_REG3, immb)) + return NULL; + immb = 0; + if (b & 0xf) + b |= TMP_REG3 << 4; + else + b |= TMP_REG3; + } + + if (!compiler->mode32 && !(flags & EX86_NO_REXW)) + rex |= REX_W; + else if (flags & EX86_REX) + rex |= REX; + +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + if (flags & EX86_PREF_F2) + inst_size++; +#endif + if (flags & EX86_PREF_66) + inst_size++; + + /* Calculate size of b. */ + inst_size += 1; /* mod r/m byte. */ + if (b & SLJIT_MEM) { + if ((b & 0x0f) == SLJIT_UNUSED) + inst_size += 1 + sizeof(sljit_hw); /* SIB byte required to avoid RIP based addressing. */ + else { + if (reg_map[b & 0x0f] >= 8) + rex |= REX_B; + if (immb != 0 && !(b & 0xf0)) { + /* Immediate operand. */ + if (immb <= 127 && immb >= -128) + inst_size += sizeof(sljit_b); + else + inst_size += sizeof(sljit_hw); + } + } + +#ifndef _WIN64 + if ((b & 0xf) == SLJIT_LOCALS_REG && (b & 0xf0) == 0) + b |= SLJIT_LOCALS_REG << 4; +#endif + + if ((b & 0xf0) != SLJIT_UNUSED) { + inst_size += 1; /* SIB byte. */ + if (reg_map[(b >> 4) & 0x0f] >= 8) + rex |= REX_X; + } + } +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + else if (!(flags & EX86_SSE2) && reg_map[b] >= 8) + rex |= REX_B; +#else + else if (reg_map[b] >= 8) + rex |= REX_B; +#endif + + if (a & SLJIT_IMM) { + if (flags & EX86_BIN_INS) { + if (imma <= 127 && imma >= -128) { + inst_size += 1; + flags |= EX86_BYTE_ARG; + } else + inst_size += 4; + } + else if (flags & EX86_SHIFT_INS) { + imma &= compiler->mode32 ? 0x1f : 0x3f; + if (imma != 1) { + inst_size ++; + flags |= EX86_BYTE_ARG; + } + } else if (flags & EX86_BYTE_ARG) + inst_size++; + else if (flags & EX86_HALF_ARG) + inst_size += sizeof(short); + else + inst_size += sizeof(sljit_hw); + } + else { + SLJIT_ASSERT(!(flags & EX86_SHIFT_INS) || a == SLJIT_PREF_SHIFT_REG); + /* reg_map[SLJIT_PREF_SHIFT_REG] is less than 8. */ +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + if (!(flags & EX86_SSE2) && reg_map[a] >= 8) + rex |= REX_R; +#else + if (reg_map[a] >= 8) + rex |= REX_R; +#endif + } + + if (rex) + inst_size++; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + inst_size); + PTR_FAIL_IF(!buf); + + /* Encoding the byte. */ + INC_SIZE(inst_size); +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + if (flags & EX86_PREF_F2) + *buf++ = 0xf2; +#endif + if (flags & EX86_PREF_66) + *buf++ = 0x66; + if (rex) + *buf++ = rex; + buf_ptr = buf + size; + + /* Encode mod/rm byte. */ + if (!(flags & EX86_SHIFT_INS)) { + if ((flags & EX86_BIN_INS) && (a & SLJIT_IMM)) + *buf = (flags & EX86_BYTE_ARG) ? 0x83 : 0x81; + + if ((a & SLJIT_IMM) || (a == 0)) + *buf_ptr = 0; +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + else if (!(flags & EX86_SSE2)) + *buf_ptr = reg_lmap[a] << 3; + else + *buf_ptr = a << 3; +#else + else + *buf_ptr = reg_lmap[a] << 3; +#endif + } + else { + if (a & SLJIT_IMM) { + if (imma == 1) + *buf = 0xd1; + else + *buf = 0xc1; + } else + *buf = 0xd3; + *buf_ptr = 0; + } + + if (!(b & SLJIT_MEM)) +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + *buf_ptr++ |= 0xc0 + ((!(flags & EX86_SSE2)) ? reg_lmap[b] : b); +#else + *buf_ptr++ |= 0xc0 + reg_lmap[b]; +#endif + else if ((b & 0x0f) != SLJIT_UNUSED) { +#ifdef _WIN64 + SLJIT_ASSERT((b & 0xf0) != (SLJIT_LOCALS_REG << 4)); +#endif + if ((b & 0xf0) == SLJIT_UNUSED || (b & 0xf0) == (SLJIT_LOCALS_REG << 4)) { + if (immb != 0) { + if (immb <= 127 && immb >= -128) + *buf_ptr |= 0x40; + else + *buf_ptr |= 0x80; + } + + if ((b & 0xf0) == SLJIT_UNUSED) + *buf_ptr++ |= reg_lmap[b & 0x0f]; + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = reg_lmap[b & 0x0f] | (reg_lmap[(b >> 4) & 0x0f] << 3); + } + + if (immb != 0) { + if (immb <= 127 && immb >= -128) + *buf_ptr++ = immb; /* 8 bit displacement. */ + else { + *(sljit_hw*)buf_ptr = immb; /* 32 bit displacement. */ + buf_ptr += sizeof(sljit_hw); + } + } + } + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = reg_lmap[b & 0x0f] | (reg_lmap[(b >> 4) & 0x0f] << 3) | (immb << 6); + } + } + else { + *buf_ptr++ |= 0x04; + *buf_ptr++ = 0x25; + *(sljit_hw*)buf_ptr = immb; /* 32 bit displacement. */ + buf_ptr += sizeof(sljit_hw); + } + + if (a & SLJIT_IMM) { + if (flags & EX86_BYTE_ARG) + *buf_ptr = imma; + else if (flags & EX86_HALF_ARG) + *(short*)buf_ptr = imma; + else if (!(flags & EX86_SHIFT_INS)) + *(sljit_hw*)buf_ptr = imma; + } + + return !(flags & EX86_SHIFT_INS) ? buf : (buf + 1); +} + +/* --------------------------------------------------------------------- */ +/* Call / return instructions */ +/* --------------------------------------------------------------------- */ + +static SLJIT_INLINE int call_with_args(struct sljit_compiler *compiler, int type) +{ + sljit_ub *buf; + +#ifndef _WIN64 + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_TEMPORARY_REG2] == 6 && reg_map[SLJIT_TEMPORARY_REG1] < 8 && reg_map[SLJIT_TEMPORARY_REG3] < 8, args_registers); + + buf = (sljit_ub*)ensure_buf(compiler, 1 + ((type < SLJIT_CALL3) ? 3 : 6)); + FAIL_IF(!buf); + INC_SIZE((type < SLJIT_CALL3) ? 3 : 6); + if (type >= SLJIT_CALL3) { + *buf++ = REX_W; + *buf++ = 0x8b; + *buf++ = 0xc0 | (0x2 << 3) | reg_lmap[SLJIT_TEMPORARY_REG3]; + } + *buf++ = REX_W; + *buf++ = 0x8b; + *buf++ = 0xc0 | (0x7 << 3) | reg_lmap[SLJIT_TEMPORARY_REG1]; +#else + SLJIT_COMPILE_ASSERT(reg_map[SLJIT_TEMPORARY_REG2] == 2 && reg_map[SLJIT_TEMPORARY_REG1] < 8 && reg_map[SLJIT_TEMPORARY_REG3] < 8, args_registers); + + buf = (sljit_ub*)ensure_buf(compiler, 1 + ((type < SLJIT_CALL3) ? 3 : 6)); + FAIL_IF(!buf); + INC_SIZE((type < SLJIT_CALL3) ? 3 : 6); + if (type >= SLJIT_CALL3) { + *buf++ = REX_W | REX_R; + *buf++ = 0x8b; + *buf++ = 0xc0 | (0x0 << 3) | reg_lmap[SLJIT_TEMPORARY_REG3]; + } + *buf++ = REX_W; + *buf++ = 0x8b; + *buf++ = 0xc0 | (0x1 << 3) | reg_lmap[SLJIT_TEMPORARY_REG1]; +#endif + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_enter(struct sljit_compiler *compiler, int dst, sljit_w dstw, int args, int temporaries, int saveds, int local_size) +{ + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_fast_enter(compiler, dst, dstw, args, temporaries, saveds, local_size); + + compiler->temporaries = temporaries; + compiler->saveds = saveds; + compiler->local_size = (local_size + sizeof(sljit_uw) - 1) & ~(sizeof(sljit_uw) - 1); +#ifdef _WIN64 + compiler->local_size += 4 * sizeof(sljit_w); +#endif + + /* For UNUSED dst. Uncommon, but possible. */ + if (dst == SLJIT_UNUSED) + dst = TMP_REGISTER; + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) { + if (reg_map[dst] < 8) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + + INC_SIZE(1); + POP_REG(reg_lmap[dst]); + } + else { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!buf); + + INC_SIZE(2); + *buf++ = REX_B; + POP_REG(reg_lmap[dst]); + } + } + else if (dst & SLJIT_MEM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + /* REX_W is not necessary (src is not immediate). */ + compiler->mode32 = 1; +#endif + buf = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + FAIL_IF(!buf); + *buf++ = 0x8f; + } + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fast_return(struct sljit_compiler *compiler, int src, sljit_w srcw) +{ + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_fast_return(compiler, src, srcw); + + CHECK_EXTRA_REGS(src, srcw, (void)0); + + if ((src & SLJIT_IMM) && NOT_HALFWORD(srcw)) { + FAIL_IF(emit_load_imm64(compiler, TMP_REGISTER, srcw)); + src = TMP_REGISTER; + } + + if (src >= SLJIT_TEMPORARY_REG1 && src <= TMP_REGISTER) { + if (reg_map[src] < 8) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1 + 1); + FAIL_IF(!buf); + + INC_SIZE(1 + 1); + PUSH_REG(reg_lmap[src]); + } + else { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2 + 1); + FAIL_IF(!buf); + + INC_SIZE(2 + 1); + *buf++ = REX_B; + PUSH_REG(reg_lmap[src]); + } + } + else if (src & SLJIT_MEM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + /* REX_W is not necessary (src is not immediate). */ + compiler->mode32 = 1; +#endif + buf = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); + FAIL_IF(!buf); + *buf++ = 0xff; + *buf |= 6 << 3; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + INC_SIZE(1); + } + else { + SLJIT_ASSERT(IS_HALFWORD(srcw)); + /* SLJIT_IMM. */ + buf = (sljit_ub*)ensure_buf(compiler, 1 + 5 + 1); + FAIL_IF(!buf); + + INC_SIZE(5 + 1); + *buf++ = 0x68; + *(sljit_hw*)buf = srcw; + buf += sizeof(sljit_hw); + } + + RET(); + return SLJIT_SUCCESS; +} + + +/* --------------------------------------------------------------------- */ +/* Extend input */ +/* --------------------------------------------------------------------- */ + +static int emit_mov_int(struct sljit_compiler *compiler, int sign, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + int dst_r; + + compiler->mode32 = 0; + + if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM)) + return SLJIT_SUCCESS; /* Empty instruction. */ + + if (src & SLJIT_IMM) { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + if (sign || ((sljit_uw)srcw <= 0x7fffffff)) { + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, (sljit_w)(sljit_i)srcw, dst, dstw); + FAIL_IF(!code); + *code = 0xc7; + return SLJIT_SUCCESS; + } + return emit_load_imm64(compiler, dst, srcw); + } + compiler->mode32 = 1; + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, (sljit_w)(sljit_i)srcw, dst, dstw); + FAIL_IF(!code); + *code = 0xc7; + compiler->mode32 = 0; + return SLJIT_SUCCESS; + } + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_SAVED_REG3) ? dst : TMP_REGISTER; + + if ((dst & SLJIT_MEM) && (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_SAVED_REG3)) + dst_r = src; + else { + if (sign) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src, srcw); + FAIL_IF(!code); + *code++ = 0x63; + } else { + compiler->mode32 = 1; + FAIL_IF(emit_mov(compiler, dst_r, 0, src, srcw)); + compiler->mode32 = 0; + } + } + + if (dst & SLJIT_MEM) { + compiler->mode32 = 1; + code = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x89; + compiler->mode32 = 0; + } + + return SLJIT_SUCCESS; +} diff --git a/src/lib/pcre/sljit/sljitNativeX86_common.c b/src/lib/pcre/sljit/sljitNativeX86_common.c new file mode 100644 index 0000000..0a44163 --- /dev/null +++ b/src/lib/pcre/sljit/sljitNativeX86_common.c @@ -0,0 +1,2858 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +SLJIT_API_FUNC_ATTRIBUTE SLJIT_CONST char* sljit_get_platform_name() +{ + return "x86" SLJIT_CPUINFO; +} + +/* + 32b register indexes: + 0 - EAX + 1 - ECX + 2 - EDX + 3 - EBX + 4 - none + 5 - EBP + 6 - ESI + 7 - EDI +*/ + +/* + 64b register indexes: + 0 - RAX + 1 - RCX + 2 - RDX + 3 - RBX + 4 - none + 5 - RBP + 6 - RSI + 7 - RDI + 8 - R8 - From now on REX prefix is required + 9 - R9 + 10 - R10 + 11 - R11 + 12 - R12 + 13 - R13 + 14 - R14 + 15 - R15 +*/ + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + +/* Last register + 1. */ +#define TMP_REGISTER (SLJIT_NO_REGISTERS + 1) + +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 2] = { + 0, 0, 2, 1, 0, 0, 3, 6, 7, 0, 0, 4, 5 +}; + +#define CHECK_EXTRA_REGS(p, w, do) \ + if (p >= SLJIT_TEMPORARY_EREG1 && p <= SLJIT_TEMPORARY_EREG2) { \ + w = compiler->temporaries_start + (p - SLJIT_TEMPORARY_EREG1) * sizeof(sljit_w); \ + p = SLJIT_MEM1(SLJIT_LOCALS_REG); \ + do; \ + } \ + else if (p >= SLJIT_SAVED_EREG1 && p <= SLJIT_SAVED_EREG2) { \ + w = compiler->saveds_start + (p - SLJIT_SAVED_EREG1) * sizeof(sljit_w); \ + p = SLJIT_MEM1(SLJIT_LOCALS_REG); \ + do; \ + } + +#else /* SLJIT_CONFIG_X86_32 */ + +/* Last register + 1. */ +#define TMP_REGISTER (SLJIT_NO_REGISTERS + 1) +#define TMP_REG2 (SLJIT_NO_REGISTERS + 2) +#define TMP_REG3 (SLJIT_NO_REGISTERS + 3) + +/* Note: r12 & 0x7 == 0b100, which decoded as SIB byte present + Note: avoid to use r12 and r13 for memory addessing + therefore r12 is better for SAVED_EREG than SAVED_REG. */ +#ifndef _WIN64 +/* 1st passed in rdi, 2nd argument passed in rsi, 3rd in rdx. */ +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 4] = { + 0, 0, 6, 1, 8, 11, 3, 15, 14, 13, 12, 4, 2, 7, 9 +}; +/* low-map. reg_map & 0x7. */ +static SLJIT_CONST sljit_ub reg_lmap[SLJIT_NO_REGISTERS + 4] = { + 0, 0, 6, 1, 0, 3, 3, 7, 6, 5, 4, 4, 2, 7, 1 +}; +#else +/* 1st passed in rcx, 2nd argument passed in rdx, 3rd in r8. */ +static SLJIT_CONST sljit_ub reg_map[SLJIT_NO_REGISTERS + 4] = { + 0, 0, 2, 1, 11, 13, 3, 6, 7, 14, 12, 15, 10, 8, 9 +}; +/* low-map. reg_map & 0x7. */ +static SLJIT_CONST sljit_ub reg_lmap[SLJIT_NO_REGISTERS + 4] = { + 0, 0, 2, 1, 3, 5, 3, 6, 7, 6, 4, 7, 2, 0, 1 +}; +#endif + +#define REX_W 0x48 +#define REX_R 0x44 +#define REX_X 0x42 +#define REX_B 0x41 +#define REX 0x40 + +typedef unsigned int sljit_uhw; +typedef int sljit_hw; + +#define IS_HALFWORD(x) ((x) <= 0x7fffffffll && (x) >= -0x80000000ll) +#define NOT_HALFWORD(x) ((x) > 0x7fffffffll || (x) < -0x80000000ll) + +#define CHECK_EXTRA_REGS(p, w, do) + +#endif /* SLJIT_CONFIG_X86_32 */ + +#if (defined SLJIT_SSE2 && SLJIT_SSE2) +#define TMP_FREG (SLJIT_FLOAT_REG4 + 1) +#endif + +/* Size flags for emit_x86_instruction: */ +#define EX86_BIN_INS 0x0010 +#define EX86_SHIFT_INS 0x0020 +#define EX86_REX 0x0040 +#define EX86_NO_REXW 0x0080 +#define EX86_BYTE_ARG 0x0100 +#define EX86_HALF_ARG 0x0200 +#define EX86_PREF_66 0x0400 + +#if (defined SLJIT_SSE2 && SLJIT_SSE2) +#define EX86_PREF_F2 0x0800 +#define EX86_SSE2 0x1000 +#endif + +#define INC_SIZE(s) (*buf++ = (s), compiler->size += (s)) +#define INC_CSIZE(s) (*code++ = (s), compiler->size += (s)) + +#define PUSH_REG(r) (*buf++ = (0x50 + (r))) +#define POP_REG(r) (*buf++ = (0x58 + (r))) +#define RET() (*buf++ = (0xc3)) +#define RETN(n) (*buf++ = (0xc2), *buf++ = n, *buf++ = 0) +/* r32, r/m32 */ +#define MOV_RM(mod, reg, rm) (*buf++ = (0x8b), *buf++ = (mod) << 6 | (reg) << 3 | (rm)) + +static sljit_ub get_jump_code(int type) +{ + switch (type) { + case SLJIT_C_EQUAL: + case SLJIT_C_FLOAT_EQUAL: + return 0x84; + + case SLJIT_C_NOT_EQUAL: + case SLJIT_C_FLOAT_NOT_EQUAL: + return 0x85; + + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + return 0x82; + + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + return 0x83; + + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + return 0x87; + + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + return 0x86; + + case SLJIT_C_SIG_LESS: + return 0x8c; + + case SLJIT_C_SIG_GREATER_EQUAL: + return 0x8d; + + case SLJIT_C_SIG_GREATER: + return 0x8f; + + case SLJIT_C_SIG_LESS_EQUAL: + return 0x8e; + + case SLJIT_C_OVERFLOW: + case SLJIT_C_MUL_OVERFLOW: + return 0x80; + + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_MUL_NOT_OVERFLOW: + return 0x81; + + case SLJIT_C_FLOAT_NAN: + return 0x8a; + + case SLJIT_C_FLOAT_NOT_NAN: + return 0x8b; + } + return 0; +} + +static sljit_ub* generate_far_jump_code(struct sljit_jump *jump, sljit_ub *code_ptr, int type); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +static sljit_ub* generate_fixed_jump(sljit_ub *code_ptr, sljit_w addr, int type); +#endif + +static sljit_ub* generate_near_jump_code(struct sljit_jump *jump, sljit_ub *code_ptr, sljit_ub *code, int type) +{ + int short_jump; + sljit_uw label_addr; + + if (jump->flags & JUMP_LABEL) + label_addr = (sljit_uw)(code + jump->u.label->size); + else + label_addr = jump->u.target; + short_jump = (sljit_w)(label_addr - (jump->addr + 2)) >= -128 && (sljit_w)(label_addr - (jump->addr + 2)) <= 127; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((sljit_w)(label_addr - (jump->addr + 1)) > 0x7fffffffll || (sljit_w)(label_addr - (jump->addr + 1)) < -0x80000000ll) + return generate_far_jump_code(jump, code_ptr, type); +#endif + + if (type == SLJIT_JUMP) { + if (short_jump) + *code_ptr++ = 0xeb; + else + *code_ptr++ = 0xe9; + jump->addr++; + } + else if (type >= SLJIT_FAST_CALL) { + short_jump = 0; + *code_ptr++ = 0xe8; + jump->addr++; + } + else if (short_jump) { + *code_ptr++ = get_jump_code(type) - 0x10; + jump->addr++; + } + else { + *code_ptr++ = 0x0f; + *code_ptr++ = get_jump_code(type); + jump->addr += 2; + } + + if (short_jump) { + jump->flags |= PATCH_MB; + code_ptr += sizeof(sljit_b); + } else { + jump->flags |= PATCH_MW; +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + code_ptr += sizeof(sljit_w); +#else + code_ptr += sizeof(sljit_hw); +#endif + } + + return code_ptr; +} + +SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler) +{ + struct sljit_memory_fragment *buf; + sljit_ub *code; + sljit_ub *code_ptr; + sljit_ub *buf_ptr; + sljit_ub *buf_end; + sljit_ub len; + + struct sljit_label *label; + struct sljit_jump *jump; + struct sljit_const *const_; + + CHECK_ERROR_PTR(); + check_sljit_generate_code(compiler); + reverse_buf(compiler); + + /* Second code generation pass. */ + code = (sljit_ub*)SLJIT_MALLOC_EXEC(compiler->size); + PTR_FAIL_WITH_EXEC_IF(code); + buf = compiler->buf; + + code_ptr = code; + label = compiler->labels; + jump = compiler->jumps; + const_ = compiler->consts; + do { + buf_ptr = buf->memory; + buf_end = buf_ptr + buf->used_size; + do { + len = *buf_ptr++; + if (len > 0) { + /* The code is already generated. */ + SLJIT_MEMMOVE(code_ptr, buf_ptr, len); + code_ptr += len; + buf_ptr += len; + } + else { + if (*buf_ptr >= 4) { + jump->addr = (sljit_uw)code_ptr; + if (!(jump->flags & SLJIT_REWRITABLE_JUMP)) + code_ptr = generate_near_jump_code(jump, code_ptr, code, *buf_ptr - 4); + else + code_ptr = generate_far_jump_code(jump, code_ptr, *buf_ptr - 4); + jump = jump->next; + } + else if (*buf_ptr == 0) { + label->addr = (sljit_uw)code_ptr; + label->size = code_ptr - code; + label = label->next; + } + else if (*buf_ptr == 1) { + const_->addr = ((sljit_uw)code_ptr) - sizeof(sljit_w); + const_ = const_->next; + } + else { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + *code_ptr++ = (*buf_ptr == 2) ? 0xe8 /* call */ : 0xe9 /* jmp */; + buf_ptr++; + *(sljit_w*)code_ptr = *(sljit_w*)buf_ptr - ((sljit_w)code_ptr + sizeof(sljit_w)); + code_ptr += sizeof(sljit_w); + buf_ptr += sizeof(sljit_w) - 1; +#else + code_ptr = generate_fixed_jump(code_ptr, *(sljit_w*)(buf_ptr + 1), *buf_ptr); + buf_ptr += sizeof(sljit_w); +#endif + } + buf_ptr++; + } + } while (buf_ptr < buf_end); + SLJIT_ASSERT(buf_ptr == buf_end); + buf = buf->next; + } while (buf); + + SLJIT_ASSERT(!label); + SLJIT_ASSERT(!jump); + SLJIT_ASSERT(!const_); + + jump = compiler->jumps; + while (jump) { + if (jump->flags & PATCH_MB) { + SLJIT_ASSERT((sljit_w)(jump->u.label->addr - (jump->addr + sizeof(sljit_b))) >= -128 && (sljit_w)(jump->u.label->addr - (jump->addr + sizeof(sljit_b))) <= 127); + *(sljit_ub*)jump->addr = (sljit_ub)(jump->u.label->addr - (jump->addr + sizeof(sljit_b))); + } else if (jump->flags & PATCH_MW) { + if (jump->flags & JUMP_LABEL) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + *(sljit_w*)jump->addr = (sljit_w)(jump->u.label->addr - (jump->addr + sizeof(sljit_w))); +#else + SLJIT_ASSERT((sljit_w)(jump->u.label->addr - (jump->addr + sizeof(sljit_hw))) >= -0x80000000ll && (sljit_w)(jump->u.label->addr - (jump->addr + sizeof(sljit_hw))) <= 0x7fffffffll); + *(sljit_hw*)jump->addr = (sljit_hw)(jump->u.label->addr - (jump->addr + sizeof(sljit_hw))); +#endif + } + else { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + *(sljit_w*)jump->addr = (sljit_w)(jump->u.target - (jump->addr + sizeof(sljit_w))); +#else + SLJIT_ASSERT((sljit_w)(jump->u.target - (jump->addr + sizeof(sljit_hw))) >= -0x80000000ll && (sljit_w)(jump->u.target - (jump->addr + sizeof(sljit_hw))) <= 0x7fffffffll); + *(sljit_hw*)jump->addr = (sljit_hw)(jump->u.target - (jump->addr + sizeof(sljit_hw))); +#endif + } + } +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + else if (jump->flags & PATCH_MD) + *(sljit_w*)jump->addr = jump->u.label->addr; +#endif + + jump = jump->next; + } + + /* Maybe we waste some space because of short jumps. */ + SLJIT_ASSERT(code_ptr <= code + compiler->size); + compiler->error = SLJIT_ERR_COMPILED; + compiler->executable_size = compiler->size; + return (void*)code; +} + +/* --------------------------------------------------------------------- */ +/* Operators */ +/* --------------------------------------------------------------------- */ + +static int emit_cum_binary(struct sljit_compiler *compiler, + sljit_ub op_rm, sljit_ub op_mr, sljit_ub op_imm, sljit_ub op_eax_imm, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w); + +static int emit_non_cum_binary(struct sljit_compiler *compiler, + sljit_ub op_rm, sljit_ub op_mr, sljit_ub op_imm, sljit_ub op_eax_imm, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w); + +static int emit_mov(struct sljit_compiler *compiler, + int dst, sljit_w dstw, + int src, sljit_w srcw); + +static SLJIT_INLINE int emit_save_flags(struct sljit_compiler *compiler) +{ + sljit_ub *buf; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + buf = (sljit_ub*)ensure_buf(compiler, 1 + 5); + FAIL_IF(!buf); + INC_SIZE(5); + *buf++ = 0x9c; /* pushfd */ +#else + buf = (sljit_ub*)ensure_buf(compiler, 1 + 6); + FAIL_IF(!buf); + INC_SIZE(6); + *buf++ = 0x9c; /* pushfq */ + *buf++ = 0x48; +#endif + *buf++ = 0x8d; /* lea esp/rsp, [esp/rsp + sizeof(sljit_w)] */ + *buf++ = 0x64; + *buf++ = 0x24; + *buf++ = sizeof(sljit_w); + compiler->flags_saved = 1; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_restore_flags(struct sljit_compiler *compiler, int keep_flags) +{ + sljit_ub *buf; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + buf = (sljit_ub*)ensure_buf(compiler, 1 + 5); + FAIL_IF(!buf); + INC_SIZE(5); +#else + buf = (sljit_ub*)ensure_buf(compiler, 1 + 6); + FAIL_IF(!buf); + INC_SIZE(6); + *buf++ = 0x48; +#endif + *buf++ = 0x8d; /* lea esp/rsp, [esp/rsp - sizeof(sljit_w)] */ + *buf++ = 0x64; + *buf++ = 0x24; + *buf++ = (sljit_ub)-(int)sizeof(sljit_w); + *buf++ = 0x9d; /* popfd / popfq */ + compiler->flags_saved = keep_flags; + return SLJIT_SUCCESS; +} + +#ifdef _WIN32 +#include + +static void SLJIT_CALL sljit_touch_stack(sljit_w local_size) +{ + /* Workaround for calling _chkstk. */ + alloca(local_size); +} +#endif + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) +#include "sljitNativeX86_32.c" +#else +#include "sljitNativeX86_64.c" +#endif + +static int emit_mov(struct sljit_compiler *compiler, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + + if (dst == SLJIT_UNUSED) { + /* No destination, doesn't need to setup flags. */ + if (src & SLJIT_MEM) { + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src, srcw); + FAIL_IF(!code); + *code = 0x8b; + } + return SLJIT_SUCCESS; + } + if (src >= SLJIT_TEMPORARY_REG1 && src <= TMP_REGISTER) { + code = emit_x86_instruction(compiler, 1, src, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x89; + return SLJIT_SUCCESS; + } + if (src & SLJIT_IMM) { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + return emit_do_imm(compiler, 0xb8 + reg_map[dst], srcw); +#else + if (!compiler->mode32) { + if (NOT_HALFWORD(srcw)) + return emit_load_imm64(compiler, dst, srcw); + } + else + return emit_do_imm32(compiler, (reg_map[dst] >= 8) ? REX_B : 0, 0xb8 + reg_lmap[dst], srcw); +#endif + } +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (!compiler->mode32 && NOT_HALFWORD(srcw)) { + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, srcw)); + code = emit_x86_instruction(compiler, 1, TMP_REG2, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x89; + return SLJIT_SUCCESS; + } +#endif + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, srcw, dst, dstw); + FAIL_IF(!code); + *code = 0xc7; + return SLJIT_SUCCESS; + } + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) { + code = emit_x86_instruction(compiler, 1, dst, 0, src, srcw); + FAIL_IF(!code); + *code = 0x8b; + return SLJIT_SUCCESS; + } + + /* Memory to memory move. Requires two instruction. */ + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src, srcw); + FAIL_IF(!code); + *code = 0x8b; + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x89; + return SLJIT_SUCCESS; +} + +#define EMIT_MOV(compiler, dst, dstw, src, srcw) \ + FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw)); + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op0(struct sljit_compiler *compiler, int op) +{ + sljit_ub *buf; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + int size; +#endif + + CHECK_ERROR(); + check_sljit_emit_op0(compiler, op); + + switch (GET_OPCODE(op)) { + case SLJIT_BREAKPOINT: + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + INC_SIZE(1); + *buf = 0xcc; + break; + case SLJIT_NOP: + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + INC_SIZE(1); + *buf = 0x90; + break; + case SLJIT_UMUL: + case SLJIT_SMUL: + case SLJIT_UDIV: + case SLJIT_SDIV: + compiler->flags_saved = 0; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) +#ifdef _WIN64 + SLJIT_COMPILE_ASSERT( + reg_map[SLJIT_TEMPORARY_REG1] == 0 + && reg_map[SLJIT_TEMPORARY_REG2] == 2 + && reg_map[TMP_REGISTER] > 7, + invalid_register_assignment_for_div_mul); +#else + SLJIT_COMPILE_ASSERT( + reg_map[SLJIT_TEMPORARY_REG1] == 0 + && reg_map[SLJIT_TEMPORARY_REG2] < 7 + && reg_map[TMP_REGISTER] == 2, + invalid_register_assignment_for_div_mul); +#endif + compiler->mode32 = op & SLJIT_INT_OP; +#endif + + op = GET_OPCODE(op); + if (op == SLJIT_UDIV) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || defined(_WIN64) + EMIT_MOV(compiler, TMP_REGISTER, 0, SLJIT_TEMPORARY_REG2, 0); + buf = emit_x86_instruction(compiler, 1, SLJIT_TEMPORARY_REG2, 0, SLJIT_TEMPORARY_REG2, 0); +#else + buf = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, TMP_REGISTER, 0); +#endif + FAIL_IF(!buf); + *buf = 0x33; + } + + if (op == SLJIT_SDIV) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) || defined(_WIN64) + EMIT_MOV(compiler, TMP_REGISTER, 0, SLJIT_TEMPORARY_REG2, 0); +#endif + + /* CDQ instruction */ +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + INC_SIZE(1); + *buf = 0x99; +#else + if (compiler->mode32) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!buf); + INC_SIZE(1); + *buf = 0x99; + } else { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!buf); + INC_SIZE(2); + *buf++ = REX_W; + *buf = 0x99; + } +#endif + } + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!buf); + INC_SIZE(2); + *buf++ = 0xf7; + *buf = 0xc0 | ((op >= SLJIT_UDIV) ? reg_map[TMP_REGISTER] : reg_map[SLJIT_TEMPORARY_REG2]); +#else +#ifdef _WIN64 + size = (!compiler->mode32 || op >= SLJIT_UDIV) ? 3 : 2; +#else + size = (!compiler->mode32) ? 3 : 2; +#endif + buf = (sljit_ub*)ensure_buf(compiler, 1 + size); + FAIL_IF(!buf); + INC_SIZE(size); +#ifdef _WIN64 + if (!compiler->mode32) + *buf++ = REX_W | ((op >= SLJIT_UDIV) ? REX_B : 0); + else if (op >= SLJIT_UDIV) + *buf++ = REX_B; + *buf++ = 0xf7; + *buf = 0xc0 | ((op >= SLJIT_UDIV) ? reg_lmap[TMP_REGISTER] : reg_lmap[SLJIT_TEMPORARY_REG2]); +#else + if (!compiler->mode32) + *buf++ = REX_W; + *buf++ = 0xf7; + *buf = 0xc0 | reg_map[SLJIT_TEMPORARY_REG2]; +#endif +#endif + switch (op) { + case SLJIT_UMUL: + *buf |= 4 << 3; + break; + case SLJIT_SMUL: + *buf |= 5 << 3; + break; + case SLJIT_UDIV: + *buf |= 6 << 3; + break; + case SLJIT_SDIV: + *buf |= 7 << 3; + break; + } +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && !defined(_WIN64) + EMIT_MOV(compiler, SLJIT_TEMPORARY_REG2, 0, TMP_REGISTER, 0); +#endif + break; + } + + return SLJIT_SUCCESS; +} + +#define ENCODE_PREFIX(prefix) \ + do { \ + code = (sljit_ub*)ensure_buf(compiler, 1 + 1); \ + FAIL_IF(!code); \ + INC_CSIZE(1); \ + *code = (prefix); \ + } while (0) + +static int emit_mov_byte(struct sljit_compiler *compiler, int sign, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + int dst_r; +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + int work_r; +#endif + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif + + if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM)) + return SLJIT_SUCCESS; /* Empty instruction. */ + + if (src & SLJIT_IMM) { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + return emit_do_imm(compiler, 0xb8 + reg_map[dst], srcw); +#else + return emit_load_imm64(compiler, dst, srcw); +#endif + } + code = emit_x86_instruction(compiler, 1 | EX86_BYTE_ARG | EX86_NO_REXW, SLJIT_IMM, srcw, dst, dstw); + FAIL_IF(!code); + *code = 0xc6; + return SLJIT_SUCCESS; + } + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) ? dst : TMP_REGISTER; + + if ((dst & SLJIT_MEM) && src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (reg_map[src] >= 4) { + SLJIT_ASSERT(dst_r == TMP_REGISTER); + EMIT_MOV(compiler, TMP_REGISTER, 0, src, 0); + } else + dst_r = src; +#else + dst_r = src; +#endif + } +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + else if (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS && reg_map[src] >= 4) { + /* src, dst are registers. */ + SLJIT_ASSERT(dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER); + if (reg_map[dst] < 4) { + if (dst != src) + EMIT_MOV(compiler, dst, 0, src, 0); + code = emit_x86_instruction(compiler, 2, dst, 0, dst, 0); + FAIL_IF(!code); + *code++ = 0x0f; + *code = sign ? 0xbe : 0xb6; + } + else { + if (dst != src) + EMIT_MOV(compiler, dst, 0, src, 0); + if (sign) { + /* shl reg, 24 */ + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 24, dst, 0); + FAIL_IF(!code); + *code |= 0x4 << 3; + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 24, dst, 0); + FAIL_IF(!code); + /* shr/sar reg, 24 */ + *code |= 0x7 << 3; + } + else { + /* and dst, 0xff */ + code = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 255, dst, 0); + FAIL_IF(!code); + *(code + 1) |= 0x4 << 3; + } + } + return SLJIT_SUCCESS; + } +#endif + else { + /* src can be memory addr or reg_map[src] < 4 on x86_32 architectures. */ + code = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw); + FAIL_IF(!code); + *code++ = 0x0f; + *code = sign ? 0xbe : 0xb6; + } + + if (dst & SLJIT_MEM) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (dst_r == TMP_REGISTER) { + /* Find a non-used register, whose reg_map[src] < 4. */ + if ((dst & 0xf) == SLJIT_TEMPORARY_REG1) { + if ((dst & 0xf0) == (SLJIT_TEMPORARY_REG2 << 4)) + work_r = SLJIT_TEMPORARY_REG3; + else + work_r = SLJIT_TEMPORARY_REG2; + } + else { + if ((dst & 0xf0) != (SLJIT_TEMPORARY_REG1 << 4)) + work_r = SLJIT_TEMPORARY_REG1; + else if ((dst & 0xf) == SLJIT_TEMPORARY_REG2) + work_r = SLJIT_TEMPORARY_REG3; + else + work_r = SLJIT_TEMPORARY_REG2; + } + + if (work_r == SLJIT_TEMPORARY_REG1) { + ENCODE_PREFIX(0x90 + reg_map[TMP_REGISTER]); + } + else { + code = emit_x86_instruction(compiler, 1, work_r, 0, dst_r, 0); + FAIL_IF(!code); + *code = 0x87; + } + + code = emit_x86_instruction(compiler, 1, work_r, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x88; + + if (work_r == SLJIT_TEMPORARY_REG1) { + ENCODE_PREFIX(0x90 + reg_map[TMP_REGISTER]); + } + else { + code = emit_x86_instruction(compiler, 1, work_r, 0, dst_r, 0); + FAIL_IF(!code); + *code = 0x87; + } + } + else { + code = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x88; + } +#else + code = emit_x86_instruction(compiler, 1 | EX86_REX | EX86_NO_REXW, dst_r, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x88; +#endif + } + + return SLJIT_SUCCESS; +} + +static int emit_mov_half(struct sljit_compiler *compiler, int sign, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + int dst_r; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif + + if (dst == SLJIT_UNUSED && !(src & SLJIT_MEM)) + return SLJIT_SUCCESS; /* Empty instruction. */ + + if (src & SLJIT_IMM) { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + return emit_do_imm(compiler, 0xb8 + reg_map[dst], srcw); +#else + return emit_load_imm64(compiler, dst, srcw); +#endif + } + code = emit_x86_instruction(compiler, 1 | EX86_HALF_ARG | EX86_NO_REXW | EX86_PREF_66, SLJIT_IMM, srcw, dst, dstw); + FAIL_IF(!code); + *code = 0xc7; + return SLJIT_SUCCESS; + } + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) ? dst : TMP_REGISTER; + + if ((dst & SLJIT_MEM) && (src >= SLJIT_TEMPORARY_REG1 && src <= SLJIT_NO_REGISTERS)) + dst_r = src; + else { + code = emit_x86_instruction(compiler, 2, dst_r, 0, src, srcw); + FAIL_IF(!code); + *code++ = 0x0f; + *code = sign ? 0xbf : 0xb7; + } + + if (dst & SLJIT_MEM) { + code = emit_x86_instruction(compiler, 1 | EX86_NO_REXW | EX86_PREF_66, dst_r, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x89; + } + + return SLJIT_SUCCESS; +} + +static int emit_unary(struct sljit_compiler *compiler, int un_index, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + + if (dst == SLJIT_UNUSED) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= (un_index) << 3; + return SLJIT_SUCCESS; + } + if (dst == src && dstw == srcw) { + /* Same input and output */ + code = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= (un_index) << 3; + return SLJIT_SUCCESS; + } + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + EMIT_MOV(compiler, dst, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= (un_index) << 3; + return SLJIT_SUCCESS; + } + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= (un_index) << 3; + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + return SLJIT_SUCCESS; +} + +static int emit_not_with_flags(struct sljit_compiler *compiler, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + + if (dst == SLJIT_UNUSED) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= 0x2 << 3; + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code = 0x0b; + return SLJIT_SUCCESS; + } + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + EMIT_MOV(compiler, dst, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, dst, dstw); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= 0x2 << 3; + code = emit_x86_instruction(compiler, 1, dst, 0, dst, 0); + FAIL_IF(!code); + *code = 0x0b; + return SLJIT_SUCCESS; + } + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= 0x2 << 3; + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code = 0x0b; + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + return SLJIT_SUCCESS; +} + +static int emit_clz(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + int dst_r; + + SLJIT_UNUSED_ARG(op); + if (SLJIT_UNLIKELY(dst == SLJIT_UNUSED)) { + /* Just set the zero flag. */ + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + code = emit_x86_instruction(compiler, 1, 0, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code++ = 0xf7; + *code |= 0x2 << 3; +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, 31, TMP_REGISTER, 0); +#else + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_IMM, !(op & SLJIT_INT_OP) ? 63 : 31, TMP_REGISTER, 0); +#endif + FAIL_IF(!code); + *code |= 0x5 << 3; + return SLJIT_SUCCESS; + } + + if (SLJIT_UNLIKELY(src & SLJIT_IMM)) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + src = TMP_REGISTER; + srcw = 0; + } + + code = emit_x86_instruction(compiler, 2, TMP_REGISTER, 0, src, srcw); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0xbd; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) + dst_r = dst; + else { + /* Find an unused temporary register. */ + if ((dst & 0xf) != SLJIT_TEMPORARY_REG1 && (dst & 0xf0) != (SLJIT_TEMPORARY_REG1 << 4)) + dst_r = SLJIT_TEMPORARY_REG1; + else if ((dst & 0xf) != SLJIT_TEMPORARY_REG2 && (dst & 0xf0) != (SLJIT_TEMPORARY_REG2 << 4)) + dst_r = SLJIT_TEMPORARY_REG2; + else + dst_r = SLJIT_TEMPORARY_REG3; + EMIT_MOV(compiler, dst, dstw, dst_r, 0); + } + EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, 32 + 31); +#else + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= TMP_REGISTER) ? dst : TMP_REG2; + compiler->mode32 = 0; + EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, !(op & SLJIT_INT_OP) ? 64 + 63 : 32 + 31); + compiler->mode32 = op & SLJIT_INT_OP; +#endif + + code = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0x45; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + code = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, 31, dst_r, 0); +#else + code = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, !(op & SLJIT_INT_OP) ? 63 : 31, dst_r, 0); +#endif + FAIL_IF(!code); + *(code + 1) |= 0x6 << 3; + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (dst & SLJIT_MEM) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x87; + } +#else + if (dst & SLJIT_MEM) + EMIT_MOV(compiler, dst, dstw, TMP_REG2, 0); +#endif + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + sljit_ub* code; + int update = 0; +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + int dst_is_ereg = 0; + int src_is_ereg = 0; +#else + #define src_is_ereg 0 +#endif + + CHECK_ERROR(); + check_sljit_emit_op1(compiler, op, dst, dstw, src, srcw); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = op & SLJIT_INT_OP; +#endif + CHECK_EXTRA_REGS(dst, dstw, dst_is_ereg = 1); + CHECK_EXTRA_REGS(src, srcw, src_is_ereg = 1); + + if (GET_OPCODE(op) >= SLJIT_MOV && GET_OPCODE(op) <= SLJIT_MOVU_SI) { + op = GET_OPCODE(op); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; +#endif + + SLJIT_COMPILE_ASSERT(SLJIT_MOV + 7 == SLJIT_MOVU, movu_offset); + if (op >= SLJIT_MOVU) { + update = 1; + op -= 7; + } + + if (src & SLJIT_IMM) { + switch (op) { + case SLJIT_MOV_UB: + srcw = (unsigned char)srcw; + break; + case SLJIT_MOV_SB: + srcw = (signed char)srcw; + break; + case SLJIT_MOV_UH: + srcw = (unsigned short)srcw; + break; + case SLJIT_MOV_SH: + srcw = (signed short)srcw; + break; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + case SLJIT_MOV_UI: + srcw = (unsigned int)srcw; + break; + case SLJIT_MOV_SI: + srcw = (signed int)srcw; + break; +#endif + } +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (SLJIT_UNLIKELY(dst_is_ereg)) + return emit_mov(compiler, dst, dstw, src, srcw); +#endif + } + + if (SLJIT_UNLIKELY(update) && (src & SLJIT_MEM) && !src_is_ereg && (src & 0xf) && (srcw != 0 || (src & 0xf0) != 0)) { + code = emit_x86_instruction(compiler, 1, src & 0xf, 0, src, srcw); + FAIL_IF(!code); + *code = 0x8d; + src &= SLJIT_MEM | 0xf; + srcw = 0; + } + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (SLJIT_UNLIKELY(dst_is_ereg) && (!(op == SLJIT_MOV || op == SLJIT_MOV_UI || op == SLJIT_MOV_SI) || (src & SLJIT_MEM))) { + SLJIT_ASSERT(dst == SLJIT_MEM1(SLJIT_LOCALS_REG)); + dst = TMP_REGISTER; + } +#endif + + switch (op) { + case SLJIT_MOV: +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + case SLJIT_MOV_UI: + case SLJIT_MOV_SI: +#endif + FAIL_IF(emit_mov(compiler, dst, dstw, src, srcw)); + break; + case SLJIT_MOV_UB: + FAIL_IF(emit_mov_byte(compiler, 0, dst, dstw, src, (src & SLJIT_IMM) ? (unsigned char)srcw : srcw)); + break; + case SLJIT_MOV_SB: + FAIL_IF(emit_mov_byte(compiler, 1, dst, dstw, src, (src & SLJIT_IMM) ? (signed char)srcw : srcw)); + break; + case SLJIT_MOV_UH: + FAIL_IF(emit_mov_half(compiler, 0, dst, dstw, src, (src & SLJIT_IMM) ? (unsigned short)srcw : srcw)); + break; + case SLJIT_MOV_SH: + FAIL_IF(emit_mov_half(compiler, 1, dst, dstw, src, (src & SLJIT_IMM) ? (signed short)srcw : srcw)); + break; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + case SLJIT_MOV_UI: + FAIL_IF(emit_mov_int(compiler, 0, dst, dstw, src, (src & SLJIT_IMM) ? (unsigned int)srcw : srcw)); + break; + case SLJIT_MOV_SI: + FAIL_IF(emit_mov_int(compiler, 1, dst, dstw, src, (src & SLJIT_IMM) ? (signed int)srcw : srcw)); + break; +#endif + } + +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (SLJIT_UNLIKELY(dst_is_ereg) && dst == TMP_REGISTER) + return emit_mov(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), dstw, TMP_REGISTER, 0); +#endif + + if (SLJIT_UNLIKELY(update) && (dst & SLJIT_MEM) && (dst & 0xf) && (dstw != 0 || (dst & 0xf0) != 0)) { + code = emit_x86_instruction(compiler, 1, dst & 0xf, 0, dst, dstw); + FAIL_IF(!code); + *code = 0x8d; + } + return SLJIT_SUCCESS; + } + + if (SLJIT_UNLIKELY(GET_FLAGS(op))) + compiler->flags_saved = 0; + + switch (GET_OPCODE(op)) { + case SLJIT_NOT: + if (SLJIT_UNLIKELY(op & SLJIT_SET_E)) + return emit_not_with_flags(compiler, dst, dstw, src, srcw); + return emit_unary(compiler, 0x2, dst, dstw, src, srcw); + + case SLJIT_NEG: + if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) + FAIL_IF(emit_save_flags(compiler)); + return emit_unary(compiler, 0x3, dst, dstw, src, srcw); + + case SLJIT_CLZ: + if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) + FAIL_IF(emit_save_flags(compiler)); + return emit_clz(compiler, op, dst, dstw, src, srcw); + } + + return SLJIT_SUCCESS; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + #undef src_is_ereg +#endif +} + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + +#define BINARY_IMM(_op_imm_, _op_mr_, immw, arg, argw) \ + if (IS_HALFWORD(immw) || compiler->mode32) { \ + code = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, immw, arg, argw); \ + FAIL_IF(!code); \ + *(code + 1) |= (_op_imm_); \ + } \ + else { \ + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, immw)); \ + code = emit_x86_instruction(compiler, 1, TMP_REG2, 0, arg, argw); \ + FAIL_IF(!code); \ + *code = (_op_mr_); \ + } + +#define BINARY_EAX_IMM(_op_eax_imm_, immw) \ + FAIL_IF(emit_do_imm32(compiler, (!compiler->mode32) ? REX_W : 0, (_op_eax_imm_), immw)) + +#else + +#define BINARY_IMM(_op_imm_, _op_mr_, immw, arg, argw) \ + code = emit_x86_instruction(compiler, 1 | EX86_BIN_INS, SLJIT_IMM, immw, arg, argw); \ + FAIL_IF(!code); \ + *(code + 1) |= (_op_imm_); + +#define BINARY_EAX_IMM(_op_eax_imm_, immw) \ + FAIL_IF(emit_do_imm(compiler, (_op_eax_imm_), immw)) + +#endif + +static int emit_cum_binary(struct sljit_compiler *compiler, + sljit_ub op_rm, sljit_ub op_mr, sljit_ub op_imm, sljit_ub op_eax_imm, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + + if (dst == SLJIT_UNUSED) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + if (src2 & SLJIT_IMM) { + BINARY_IMM(op_imm, op_mr, src2w, TMP_REGISTER, 0); + } + else { + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + return SLJIT_SUCCESS; + } + + if (dst == src1 && dstw == src1w) { + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((dst == SLJIT_TEMPORARY_REG1) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) { +#else + if ((dst == SLJIT_TEMPORARY_REG1) && (src2w > 127 || src2w < -128)) { +#endif + BINARY_EAX_IMM(op_eax_imm, src2w); + } + else { + BINARY_IMM(op_imm, op_mr, src2w, dst, dstw); + } + } + else if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + code = emit_x86_instruction(compiler, 1, dst, dstw, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + else if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= TMP_REGISTER) { + /* Special exception for sljit_emit_cond_value. */ + code = emit_x86_instruction(compiler, 1, src2, src2w, dst, dstw); + FAIL_IF(!code); + *code = op_mr; + } + else { + EMIT_MOV(compiler, TMP_REGISTER, 0, src2, src2w); + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, dst, dstw); + FAIL_IF(!code); + *code = op_mr; + } + return SLJIT_SUCCESS; + } + + /* Only for cumulative operations. */ + if (dst == src2 && dstw == src2w) { + if (src1 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((dst == SLJIT_TEMPORARY_REG1) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) { +#else + if ((dst == SLJIT_TEMPORARY_REG1) && (src1w > 127 || src1w < -128)) { +#endif + BINARY_EAX_IMM(op_eax_imm, src1w); + } + else { + BINARY_IMM(op_imm, op_mr, src1w, dst, dstw); + } + } + else if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + code = emit_x86_instruction(compiler, 1, dst, dstw, src1, src1w); + FAIL_IF(!code); + *code = op_rm; + } + else if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= SLJIT_NO_REGISTERS) { + code = emit_x86_instruction(compiler, 1, src1, src1w, dst, dstw); + FAIL_IF(!code); + *code = op_mr; + } + else { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, dst, dstw); + FAIL_IF(!code); + *code = op_mr; + } + return SLJIT_SUCCESS; + } + + /* General version. */ + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + EMIT_MOV(compiler, dst, 0, src1, src1w); + if (src2 & SLJIT_IMM) { + BINARY_IMM(op_imm, op_mr, src2w, dst, 0); + } + else { + code = emit_x86_instruction(compiler, 1, dst, 0, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + } + else { + /* This version requires less memory writing. */ + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + if (src2 & SLJIT_IMM) { + BINARY_IMM(op_imm, op_mr, src2w, TMP_REGISTER, 0); + } + else { + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + } + + return SLJIT_SUCCESS; +} + +static int emit_non_cum_binary(struct sljit_compiler *compiler, + sljit_ub op_rm, sljit_ub op_mr, sljit_ub op_imm, sljit_ub op_eax_imm, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + + if (dst == SLJIT_UNUSED) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + if (src2 & SLJIT_IMM) { + BINARY_IMM(op_imm, op_mr, src2w, TMP_REGISTER, 0); + } + else { + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + return SLJIT_SUCCESS; + } + + if (dst == src1 && dstw == src1w) { + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((dst == SLJIT_TEMPORARY_REG1) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) { +#else + if ((dst == SLJIT_TEMPORARY_REG1) && (src2w > 127 || src2w < -128)) { +#endif + BINARY_EAX_IMM(op_eax_imm, src2w); + } + else { + BINARY_IMM(op_imm, op_mr, src2w, dst, dstw); + } + } + else if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + code = emit_x86_instruction(compiler, 1, dst, dstw, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + else if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= SLJIT_NO_REGISTERS) { + code = emit_x86_instruction(compiler, 1, src2, src2w, dst, dstw); + FAIL_IF(!code); + *code = op_mr; + } + else { + EMIT_MOV(compiler, TMP_REGISTER, 0, src2, src2w); + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, dst, dstw); + FAIL_IF(!code); + *code = op_mr; + } + return SLJIT_SUCCESS; + } + + /* General version. */ + if ((dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) && dst != src2) { + EMIT_MOV(compiler, dst, 0, src1, src1w); + if (src2 & SLJIT_IMM) { + BINARY_IMM(op_imm, op_mr, src2w, dst, 0); + } + else { + code = emit_x86_instruction(compiler, 1, dst, 0, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + } + else { + /* This version requires less memory writing. */ + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + if (src2 & SLJIT_IMM) { + BINARY_IMM(op_imm, op_mr, src2w, TMP_REGISTER, 0); + } + else { + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src2, src2w); + FAIL_IF(!code); + *code = op_rm; + } + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + } + + return SLJIT_SUCCESS; +} + +static int emit_mul(struct sljit_compiler *compiler, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + int dst_r; + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REGISTER; + + /* Register destination. */ + if (dst_r == src1 && !(src2 & SLJIT_IMM)) { + code = emit_x86_instruction(compiler, 2, dst_r, 0, src2, src2w); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0xaf; + } + else if (dst_r == src2 && !(src1 & SLJIT_IMM)) { + code = emit_x86_instruction(compiler, 2, dst_r, 0, src1, src1w); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0xaf; + } + else if (src1 & SLJIT_IMM) { + if (src2 & SLJIT_IMM) { + EMIT_MOV(compiler, dst_r, 0, SLJIT_IMM, src2w); + src2 = dst_r; + src2w = 0; + } + + if (src1w <= 127 && src1w >= -128) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x6b; + code = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!code); + INC_CSIZE(1); + *code = (sljit_b)src1w; + } +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + else { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x69; + code = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!code); + INC_CSIZE(4); + *(sljit_w*)code = src1w; + } +#else + else if (IS_HALFWORD(src1w)) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x69; + code = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!code); + INC_CSIZE(4); + *(sljit_hw*)code = (sljit_hw)src1w; + } + else { + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, src1w); + if (dst_r != src2) + EMIT_MOV(compiler, dst_r, 0, src2, src2w); + code = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0xaf; + } +#endif + } + else if (src2 & SLJIT_IMM) { + /* Note: src1 is NOT immediate. */ + + if (src2w <= 127 && src2w >= -128) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w); + FAIL_IF(!code); + *code = 0x6b; + code = (sljit_ub*)ensure_buf(compiler, 1 + 1); + FAIL_IF(!code); + INC_CSIZE(1); + *code = (sljit_b)src2w; + } +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + else { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w); + FAIL_IF(!code); + *code = 0x69; + code = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!code); + INC_CSIZE(4); + *(sljit_w*)code = src2w; + } +#else + else if (IS_HALFWORD(src2w)) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, src1, src1w); + FAIL_IF(!code); + *code = 0x69; + code = (sljit_ub*)ensure_buf(compiler, 1 + 4); + FAIL_IF(!code); + INC_CSIZE(4); + *(sljit_hw*)code = (sljit_hw)src2w; + } + else { + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_IMM, src1w); + if (dst_r != src1) + EMIT_MOV(compiler, dst_r, 0, src1, src1w); + code = emit_x86_instruction(compiler, 2, dst_r, 0, TMP_REG2, 0); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0xaf; + } +#endif + } + else { + /* Neither argument is immediate. */ + if (ADDRESSING_DEPENDS_ON(src2, dst_r)) + dst_r = TMP_REGISTER; + EMIT_MOV(compiler, dst_r, 0, src1, src1w); + code = emit_x86_instruction(compiler, 2, dst_r, 0, src2, src2w); + FAIL_IF(!code); + *code++ = 0x0f; + *code = 0xaf; + } + + if (dst_r == TMP_REGISTER) + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + + return SLJIT_SUCCESS; +} + +static int emit_lea_binary(struct sljit_compiler *compiler, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + int dst_r, done = 0; + + /* These cases better be left to handled by normal way. */ + if (dst == src1 && dstw == src1w) + return SLJIT_ERR_UNSUPPORTED; + if (dst == src2 && dstw == src2w) + return SLJIT_ERR_UNSUPPORTED; + + dst_r = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REGISTER; + + if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= SLJIT_NO_REGISTERS) { + if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= SLJIT_NO_REGISTERS) { + /* It is not possible to be both SLJIT_LOCALS_REG. */ + if (src1 != SLJIT_LOCALS_REG || src2 != SLJIT_LOCALS_REG) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM2(src1, src2), 0); + FAIL_IF(!code); + *code = 0x8d; + done = 1; + } + } +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((src2 & SLJIT_IMM) && (compiler->mode32 || IS_HALFWORD(src2w))) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src1), (int)src2w); +#else + if (src2 & SLJIT_IMM) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src1), src2w); +#endif + FAIL_IF(!code); + *code = 0x8d; + done = 1; + } + } + else if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= SLJIT_NO_REGISTERS) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((src1 & SLJIT_IMM) && (compiler->mode32 || IS_HALFWORD(src1w))) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src2), (int)src1w); +#else + if (src1 & SLJIT_IMM) { + code = emit_x86_instruction(compiler, 1, dst_r, 0, SLJIT_MEM1(src2), src1w); +#endif + FAIL_IF(!code); + *code = 0x8d; + done = 1; + } + } + + if (done) { + if (dst_r == TMP_REGISTER) + return emit_mov(compiler, dst, dstw, TMP_REGISTER, 0); + return SLJIT_SUCCESS; + } + return SLJIT_ERR_UNSUPPORTED; +} + +static int emit_cmp_binary(struct sljit_compiler *compiler, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (src1 == SLJIT_TEMPORARY_REG1 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) { +#else + if (src1 == SLJIT_TEMPORARY_REG1 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128)) { +#endif + BINARY_EAX_IMM(0x3d, src2w); + return SLJIT_SUCCESS; + } + + if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= SLJIT_NO_REGISTERS) { + if (src2 & SLJIT_IMM) { + BINARY_IMM(0x7 << 3, 0x39, src2w, src1, 0); + } + else { + code = emit_x86_instruction(compiler, 1, src1, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x3b; + } + return SLJIT_SUCCESS; + } + + if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= SLJIT_NO_REGISTERS && !(src1 & SLJIT_IMM)) { + code = emit_x86_instruction(compiler, 1, src2, 0, src1, src1w); + FAIL_IF(!code); + *code = 0x39; + return SLJIT_SUCCESS; + } + + if (src2 & SLJIT_IMM) { + if (src1 & SLJIT_IMM) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + src1 = TMP_REGISTER; + src1w = 0; + } + BINARY_IMM(0x7 << 3, 0x39, src2w, src1, src1w); + } + else { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x3b; + } + return SLJIT_SUCCESS; +} + +static int emit_test_binary(struct sljit_compiler *compiler, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (src1 == SLJIT_TEMPORARY_REG1 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128) && (compiler->mode32 || IS_HALFWORD(src2w))) { +#else + if (src1 == SLJIT_TEMPORARY_REG1 && (src2 & SLJIT_IMM) && (src2w > 127 || src2w < -128)) { +#endif + BINARY_EAX_IMM(0xa9, src2w); + return SLJIT_SUCCESS; + } + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (src2 == SLJIT_TEMPORARY_REG1 && (src2 & SLJIT_IMM) && (src1w > 127 || src1w < -128) && (compiler->mode32 || IS_HALFWORD(src1w))) { +#else + if (src2 == SLJIT_TEMPORARY_REG1 && (src1 & SLJIT_IMM) && (src1w > 127 || src1w < -128)) { +#endif + BINARY_EAX_IMM(0xa9, src1w); + return SLJIT_SUCCESS; + } + + if (src1 >= SLJIT_TEMPORARY_REG1 && src1 <= SLJIT_NO_REGISTERS) { + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (IS_HALFWORD(src2w) || compiler->mode32) { + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, src1, 0); + FAIL_IF(!code); + *code = 0xf7; + } + else { + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w)); + code = emit_x86_instruction(compiler, 1, TMP_REG2, 0, src1, 0); + FAIL_IF(!code); + *code = 0x85; + } +#else + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, src1, 0); + FAIL_IF(!code); + *code = 0xf7; +#endif + } + else { + code = emit_x86_instruction(compiler, 1, src1, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x85; + } + return SLJIT_SUCCESS; + } + + if (src2 >= SLJIT_TEMPORARY_REG1 && src2 <= SLJIT_NO_REGISTERS) { + if (src1 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (IS_HALFWORD(src1w) || compiler->mode32) { + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, src1w, src2, 0); + FAIL_IF(!code); + *code = 0xf7; + } + else { + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src1w)); + code = emit_x86_instruction(compiler, 1, TMP_REG2, 0, src2, 0); + FAIL_IF(!code); + *code = 0x85; + } +#else + code = emit_x86_instruction(compiler, 1, src1, src1w, src2, 0); + FAIL_IF(!code); + *code = 0xf7; +#endif + } + else { + code = emit_x86_instruction(compiler, 1, src2, 0, src1, src1w); + FAIL_IF(!code); + *code = 0x85; + } + return SLJIT_SUCCESS; + } + + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (IS_HALFWORD(src2w) || compiler->mode32) { + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, TMP_REGISTER, 0); + FAIL_IF(!code); + *code = 0xf7; + } + else { + FAIL_IF(emit_load_imm64(compiler, TMP_REG2, src2w)); + code = emit_x86_instruction(compiler, 1, TMP_REG2, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code = 0x85; + } +#else + code = emit_x86_instruction(compiler, 1, SLJIT_IMM, src2w, TMP_REGISTER, 0); + FAIL_IF(!code); + *code = 0xf7; +#endif + } + else { + code = emit_x86_instruction(compiler, 1, TMP_REGISTER, 0, src2, src2w); + FAIL_IF(!code); + *code = 0x85; + } + return SLJIT_SUCCESS; +} + +static int emit_shift(struct sljit_compiler *compiler, + sljit_ub mode, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + sljit_ub* code; + + if ((src2 & SLJIT_IMM) || (src2 == SLJIT_PREF_SHIFT_REG)) { + if (dst == src1 && dstw == src1w) { + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, dst, dstw); + FAIL_IF(!code); + *code |= mode; + return SLJIT_SUCCESS; + } + if (dst == SLJIT_UNUSED) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, TMP_REGISTER, 0); + FAIL_IF(!code); + *code |= mode; + return SLJIT_SUCCESS; + } + if (dst == SLJIT_PREF_SHIFT_REG && src2 == SLJIT_PREF_SHIFT_REG) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code |= mode; + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REGISTER, 0); + return SLJIT_SUCCESS; + } + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) { + EMIT_MOV(compiler, dst, 0, src1, src1w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, dst, 0); + FAIL_IF(!code); + *code |= mode; + return SLJIT_SUCCESS; + } + + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, src2, src2w, TMP_REGISTER, 0); + FAIL_IF(!code); + *code |= mode; + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + return SLJIT_SUCCESS; + } + + if (dst == SLJIT_PREF_SHIFT_REG) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code |= mode; + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REGISTER, 0); + } + else if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS && dst != src2 && !ADDRESSING_DEPENDS_ON(src2, dst)) { + if (src1 != dst) + EMIT_MOV(compiler, dst, 0, src1, src1w); + EMIT_MOV(compiler, TMP_REGISTER, 0, SLJIT_PREF_SHIFT_REG, 0); + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, dst, 0); + FAIL_IF(!code); + *code |= mode; + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REGISTER, 0); + } + else { + /* This case is really difficult, since ecx itself may used for + addressing, and we must ensure to work even in that case. */ + EMIT_MOV(compiler, TMP_REGISTER, 0, src1, src1w); +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + EMIT_MOV(compiler, TMP_REG2, 0, SLJIT_PREF_SHIFT_REG, 0); +#else + /* [esp - 4] is reserved for eflags. */ + EMIT_MOV(compiler, SLJIT_MEM1(SLJIT_LOCALS_REG), -(int)(2 * sizeof(sljit_w)), SLJIT_PREF_SHIFT_REG, 0); +#endif + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, src2, src2w); + code = emit_x86_instruction(compiler, 1 | EX86_SHIFT_INS, SLJIT_PREF_SHIFT_REG, 0, TMP_REGISTER, 0); + FAIL_IF(!code); + *code |= mode; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, TMP_REG2, 0); +#else + /* [esp - 4] is reserved for eflags. */ + EMIT_MOV(compiler, SLJIT_PREF_SHIFT_REG, 0, SLJIT_MEM1(SLJIT_LOCALS_REG), -(int)(2 * sizeof(sljit_w))); +#endif + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + } + + return SLJIT_SUCCESS; +} + +static int emit_shift_with_flags(struct sljit_compiler *compiler, + sljit_ub mode, int set_flags, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + /* The CPU does not set flags if the shift count is 0. */ + if (src2 & SLJIT_IMM) { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if ((src2w & 0x3f) != 0 || (compiler->mode32 && (src2w & 0x1f) != 0)) + return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w); +#else + if ((src2w & 0x1f) != 0) + return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w); +#endif + if (!set_flags) + return emit_mov(compiler, dst, dstw, src1, src1w); + /* OR dst, src, 0 */ + return emit_cum_binary(compiler, 0x0b, 0x09, 0x1 << 3, 0x0d, + dst, dstw, src1, src1w, SLJIT_IMM, 0); + } + + if (!set_flags) + return emit_shift(compiler, mode, dst, dstw, src1, src1w, src2, src2w); + + if (!(dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS)) + FAIL_IF(emit_cmp_binary(compiler, src1, src1w, SLJIT_IMM, 0)); + + FAIL_IF(emit_shift(compiler,mode, dst, dstw, src1, src1w, src2, src2w)); + + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) + return emit_cmp_binary(compiler, dst, dstw, SLJIT_IMM, 0); + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + CHECK_ERROR(); + check_sljit_emit_op2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = op & SLJIT_INT_OP; +#endif + CHECK_EXTRA_REGS(dst, dstw, (void)0); + CHECK_EXTRA_REGS(src1, src1w, (void)0); + CHECK_EXTRA_REGS(src2, src2w, (void)0); + + if (GET_OPCODE(op) >= SLJIT_MUL) { + if (SLJIT_UNLIKELY(GET_FLAGS(op))) + compiler->flags_saved = 0; + else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) + FAIL_IF(emit_save_flags(compiler)); + } + + switch (GET_OPCODE(op)) { + case SLJIT_ADD: + if (!GET_FLAGS(op)) { + if (emit_lea_binary(compiler, dst, dstw, src1, src1w, src2, src2w) != SLJIT_ERR_UNSUPPORTED) + return compiler->error; + } + else + compiler->flags_saved = 0; + if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) + FAIL_IF(emit_save_flags(compiler)); + return emit_cum_binary(compiler, 0x03, 0x01, 0x0 << 3, 0x05, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_ADDC: + if (SLJIT_UNLIKELY(compiler->flags_saved)) /* C flag must be restored. */ + FAIL_IF(emit_restore_flags(compiler, 1)); + else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS)) + FAIL_IF(emit_save_flags(compiler)); + if (SLJIT_UNLIKELY(GET_FLAGS(op))) + compiler->flags_saved = 0; + return emit_cum_binary(compiler, 0x13, 0x11, 0x2 << 3, 0x15, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_SUB: + if (!GET_FLAGS(op)) { + if ((src2 & SLJIT_IMM) && emit_lea_binary(compiler, dst, dstw, src1, src1w, SLJIT_IMM, -src2w) != SLJIT_ERR_UNSUPPORTED) + return compiler->error; + } + else + compiler->flags_saved = 0; + if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS) && !compiler->flags_saved) + FAIL_IF(emit_save_flags(compiler)); + if (dst == SLJIT_UNUSED) + return emit_cmp_binary(compiler, src1, src1w, src2, src2w); + return emit_non_cum_binary(compiler, 0x2b, 0x29, 0x5 << 3, 0x2d, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_SUBC: + if (SLJIT_UNLIKELY(compiler->flags_saved)) /* C flag must be restored. */ + FAIL_IF(emit_restore_flags(compiler, 1)); + else if (SLJIT_UNLIKELY(op & SLJIT_KEEP_FLAGS)) + FAIL_IF(emit_save_flags(compiler)); + if (SLJIT_UNLIKELY(GET_FLAGS(op))) + compiler->flags_saved = 0; + return emit_non_cum_binary(compiler, 0x1b, 0x19, 0x3 << 3, 0x1d, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_MUL: + return emit_mul(compiler, dst, dstw, src1, src1w, src2, src2w); + case SLJIT_AND: + if (dst == SLJIT_UNUSED) + return emit_test_binary(compiler, src1, src1w, src2, src2w); + return emit_cum_binary(compiler, 0x23, 0x21, 0x4 << 3, 0x25, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_OR: + return emit_cum_binary(compiler, 0x0b, 0x09, 0x1 << 3, 0x0d, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_XOR: + return emit_cum_binary(compiler, 0x33, 0x31, 0x6 << 3, 0x35, + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_SHL: + return emit_shift_with_flags(compiler, 0x4 << 3, GET_FLAGS(op), + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_LSHR: + return emit_shift_with_flags(compiler, 0x5 << 3, GET_FLAGS(op), + dst, dstw, src1, src1w, src2, src2w); + case SLJIT_ASHR: + return emit_shift_with_flags(compiler, 0x7 << 3, GET_FLAGS(op), + dst, dstw, src1, src1w, src2, src2w); + } + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_register_index(int reg) +{ + check_sljit_get_register_index(reg); +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + if (reg == SLJIT_TEMPORARY_EREG1 || reg == SLJIT_TEMPORARY_EREG2 + || reg == SLJIT_SAVED_EREG1 || reg == SLJIT_SAVED_EREG2) + return -1; +#endif + return reg_map[reg]; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_op_custom(struct sljit_compiler *compiler, + void *instruction, int size) +{ + sljit_ub *buf; + + CHECK_ERROR(); + check_sljit_emit_op_custom(compiler, instruction, size); + SLJIT_ASSERT(size > 0 && size < 16); + + buf = (sljit_ub*)ensure_buf(compiler, 1 + size); + FAIL_IF(!buf); + INC_SIZE(size); + SLJIT_MEMMOVE(buf, instruction, size); + return SLJIT_SUCCESS; +} + +/* --------------------------------------------------------------------- */ +/* Floating point operators */ +/* --------------------------------------------------------------------- */ + +#if (defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) +static int sse2_available = 0; +#endif + +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + +/* Alignment + 2 * 16 bytes. */ +static sljit_i sse2_data[3 + 4 + 4]; +static sljit_i *sse2_buffer; + +static void init_compiler() +{ +#if (defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) + int features = 0; +#endif + + sse2_buffer = (sljit_i*)(((sljit_uw)sse2_data + 15) & ~0xf); + sse2_buffer[0] = 0; + sse2_buffer[1] = 0x80000000; + sse2_buffer[4] = 0xffffffff; + sse2_buffer[5] = 0x7fffffff; + +#if (defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) +#ifdef __GNUC__ + /* AT&T syntax. */ + asm ( + "pushl %%ebx\n" + "movl $0x1, %%eax\n" + "cpuid\n" + "popl %%ebx\n" + "movl %%edx, %0\n" + : "=g" (features) + : + : "%eax", "%ecx", "%edx" + ); +#elif defined(_MSC_VER) || defined(__BORLANDC__) + /* Intel syntax. */ + __asm { + mov eax, 1 + push ebx + cpuid + pop ebx + mov features, edx + } +#else + #error "SLJIT_SSE2_AUTO is not implemented for this C compiler" +#endif + sse2_available = (features >> 26) & 0x1; +#endif +} + +#endif + +SLJIT_API_FUNC_ATTRIBUTE int sljit_is_fpu_available(void) +{ + /* Always available. */ + return 1; +} + +#if (defined SLJIT_SSE2 && SLJIT_SSE2) + +static int emit_sse2(struct sljit_compiler *compiler, sljit_ub opcode, + int xmm1, int xmm2, sljit_w xmm2w) +{ + sljit_ub *buf; + + buf = emit_x86_instruction(compiler, 2 | EX86_PREF_F2 | EX86_SSE2, xmm1, 0, xmm2, xmm2w); + FAIL_IF(!buf); + *buf++ = 0x0f; + *buf = opcode; + return SLJIT_SUCCESS; +} + +static int emit_sse2_logic(struct sljit_compiler *compiler, sljit_ub opcode, + int xmm1, int xmm2, sljit_w xmm2w) +{ + sljit_ub *buf; + + buf = emit_x86_instruction(compiler, 2 | EX86_PREF_66 | EX86_SSE2, xmm1, 0, xmm2, xmm2w); + FAIL_IF(!buf); + *buf++ = 0x0f; + *buf = opcode; + return SLJIT_SUCCESS; +} + +static SLJIT_INLINE int emit_sse2_load(struct sljit_compiler *compiler, + int dst, int src, sljit_w srcw) +{ + return emit_sse2(compiler, 0x10, dst, src, srcw); +} + +static SLJIT_INLINE int emit_sse2_store(struct sljit_compiler *compiler, + int dst, sljit_w dstw, int src) +{ + return emit_sse2(compiler, 0x11, src, dst, dstw); +} + +#if !(defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, +#else +static int sljit_emit_sse2_fop1(struct sljit_compiler *compiler, int op, +#endif + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + int dst_r; + + CHECK_ERROR(); + check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 1; +#endif + + if (GET_OPCODE(op) == SLJIT_FCMP) { + compiler->flags_saved = 0; + if (dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) + dst_r = dst; + else { + dst_r = TMP_FREG; + FAIL_IF(emit_sse2_load(compiler, dst_r, dst, dstw)); + } + return emit_sse2_logic(compiler, 0x2e, dst_r, src, srcw); + } + + if (op == SLJIT_FMOV) { + if (dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) + return emit_sse2_load(compiler, dst, src, srcw); + if (src >= SLJIT_FLOAT_REG1 && src <= SLJIT_FLOAT_REG4) + return emit_sse2_store(compiler, dst, dstw, src); + FAIL_IF(emit_sse2_load(compiler, TMP_FREG, src, srcw)); + return emit_sse2_store(compiler, dst, dstw, TMP_FREG); + } + + if (dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) { + dst_r = dst; + if (dst != src) + FAIL_IF(emit_sse2_load(compiler, dst_r, src, srcw)); + } + else { + dst_r = TMP_FREG; + FAIL_IF(emit_sse2_load(compiler, dst_r, src, srcw)); + } + + switch (op) { + case SLJIT_FNEG: + FAIL_IF(emit_sse2_logic(compiler, 0x57, dst_r, SLJIT_MEM0(), (sljit_w)sse2_buffer)); + break; + + case SLJIT_FABS: + FAIL_IF(emit_sse2_logic(compiler, 0x54, dst_r, SLJIT_MEM0(), (sljit_w)(sse2_buffer + 4))); + break; + } + + if (dst_r == TMP_FREG) + return emit_sse2_store(compiler, dst, dstw, TMP_FREG); + return SLJIT_SUCCESS; +} + +#if !(defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, +#else +static int sljit_emit_sse2_fop2(struct sljit_compiler *compiler, int op, +#endif + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + int dst_r; + + CHECK_ERROR(); + check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 1; +#endif + + if (dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) { + dst_r = dst; + if (dst == src1) + ; /* Do nothing here. */ + else if (dst == src2 && (op == SLJIT_FADD || op == SLJIT_FMUL)) { + /* Swap arguments. */ + src2 = src1; + src2w = src1w; + } + else if (dst != src2) + FAIL_IF(emit_sse2_load(compiler, dst_r, src1, src1w)); + else { + dst_r = TMP_FREG; + FAIL_IF(emit_sse2_load(compiler, TMP_FREG, src1, src1w)); + } + } + else { + dst_r = TMP_FREG; + FAIL_IF(emit_sse2_load(compiler, TMP_FREG, src1, src1w)); + } + + switch (op) { + case SLJIT_FADD: + FAIL_IF(emit_sse2(compiler, 0x58, dst_r, src2, src2w)); + break; + + case SLJIT_FSUB: + FAIL_IF(emit_sse2(compiler, 0x5c, dst_r, src2, src2w)); + break; + + case SLJIT_FMUL: + FAIL_IF(emit_sse2(compiler, 0x59, dst_r, src2, src2w)); + break; + + case SLJIT_FDIV: + FAIL_IF(emit_sse2(compiler, 0x5e, dst_r, src2, src2w)); + break; + } + + if (dst_r == TMP_FREG) + return emit_sse2_store(compiler, dst, dstw, TMP_FREG); + return SLJIT_SUCCESS; +} + +#endif + +#if (defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) || !(defined SLJIT_SSE2 && SLJIT_SSE2) + +static int emit_fld(struct sljit_compiler *compiler, + int src, sljit_w srcw) +{ + sljit_ub *buf; + + if (src >= SLJIT_FLOAT_REG1 && src <= SLJIT_FLOAT_REG4) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!buf); + INC_SIZE(2); + *buf++ = 0xd9; + *buf = 0xc0 + src - 1; + return SLJIT_SUCCESS; + } + + buf = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); + FAIL_IF(!buf); + *buf = 0xdd; + return SLJIT_SUCCESS; +} + +static int emit_fop(struct sljit_compiler *compiler, + sljit_ub st_arg, sljit_ub st_arg2, + sljit_ub m64fp_arg, sljit_ub m64fp_arg2, + int src, sljit_w srcw) +{ + sljit_ub *buf; + + if (src >= SLJIT_FLOAT_REG1 && src <= SLJIT_FLOAT_REG4) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!buf); + INC_SIZE(2); + *buf++ = st_arg; + *buf = st_arg2 + src; + return SLJIT_SUCCESS; + } + + buf = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); + FAIL_IF(!buf); + *buf++ = m64fp_arg; + *buf |= m64fp_arg2; + return SLJIT_SUCCESS; +} + +static int emit_fop_regs(struct sljit_compiler *compiler, + sljit_ub st_arg, sljit_ub st_arg2, + int src) +{ + sljit_ub *buf; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 2); + FAIL_IF(!buf); + INC_SIZE(2); + *buf++ = st_arg; + *buf = st_arg2 + src; + return SLJIT_SUCCESS; +} + +#if !(defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, +#else +static int sljit_emit_fpu_fop1(struct sljit_compiler *compiler, int op, +#endif + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ +#if !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + sljit_ub *buf; +#endif + + CHECK_ERROR(); + check_sljit_emit_fop1(compiler, op, dst, dstw, src, srcw); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 1; +#endif + + if (GET_OPCODE(op) == SLJIT_FCMP) { + compiler->flags_saved = 0; +#if !(defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + FAIL_IF(emit_fld(compiler, dst, dstw)); + FAIL_IF(emit_fop(compiler, 0xd8, 0xd8, 0xdc, 0x3 << 3, src, srcw)); + + /* Copy flags. */ + EMIT_MOV(compiler, TMP_REGISTER, 0, SLJIT_TEMPORARY_REG1, 0); + buf = (sljit_ub*)ensure_buf(compiler, 1 + 3); + FAIL_IF(!buf); + INC_SIZE(3); + *buf++ = 0xdf; + *buf++ = 0xe0; + /* Note: lahf is not supported on all x86-64 architectures. */ + *buf++ = 0x9e; + EMIT_MOV(compiler, SLJIT_TEMPORARY_REG1, 0, TMP_REGISTER, 0); +#else + if (src >= SLJIT_FLOAT_REG1 && src <= SLJIT_FLOAT_REG4) { + FAIL_IF(emit_fld(compiler, dst, dstw)); + FAIL_IF(emit_fop_regs(compiler, 0xdf, 0xe8, src)); + } else { + FAIL_IF(emit_fld(compiler, src, srcw)); + FAIL_IF(emit_fld(compiler, dst + ((dst >= SLJIT_FLOAT_REG1 && dst <= SLJIT_FLOAT_REG4) ? 1 : 0), dstw)); + FAIL_IF(emit_fop_regs(compiler, 0xdf, 0xe8, src)); + FAIL_IF(emit_fop_regs(compiler, 0xdd, 0xd8, 0)); + } +#endif + return SLJIT_SUCCESS; + } + + FAIL_IF(emit_fld(compiler, src, srcw)); + + switch (op) { + case SLJIT_FNEG: + FAIL_IF(emit_fop_regs(compiler, 0xd9, 0xe0, 0)); + break; + case SLJIT_FABS: + FAIL_IF(emit_fop_regs(compiler, 0xd9, 0xe1, 0)); + break; + } + + FAIL_IF(emit_fop(compiler, 0xdd, 0xd8, 0xdd, 0x3 << 3, dst, dstw)); + + return SLJIT_SUCCESS; +} + +#if !(defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, +#else +static int sljit_emit_fpu_fop2(struct sljit_compiler *compiler, int op, +#endif + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + CHECK_ERROR(); + check_sljit_emit_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 1; +#endif + + if (src1 >= SLJIT_FLOAT_REG1 && src1 <= SLJIT_FLOAT_REG4 && dst == src1) { + FAIL_IF(emit_fld(compiler, src2, src2w)); + + switch (op) { + case SLJIT_FADD: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xc0, src1)); + break; + case SLJIT_FSUB: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xe8, src1)); + break; + case SLJIT_FMUL: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xc8, src1)); + break; + case SLJIT_FDIV: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xf8, src1)); + break; + } + return SLJIT_SUCCESS; + } + + FAIL_IF(emit_fld(compiler, src1, src1w)); + + if (src2 >= SLJIT_FLOAT_REG1 && src2 <= SLJIT_FLOAT_REG4 && dst == src2) { + switch (op) { + case SLJIT_FADD: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xc0, src2)); + break; + case SLJIT_FSUB: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xe0, src2)); + break; + case SLJIT_FMUL: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xc8, src2)); + break; + case SLJIT_FDIV: + FAIL_IF(emit_fop_regs(compiler, 0xde, 0xf0, src2)); + break; + } + return SLJIT_SUCCESS; + } + + switch (op) { + case SLJIT_FADD: + FAIL_IF(emit_fop(compiler, 0xd8, 0xc0, 0xdc, 0x0 << 3, src2, src2w)); + break; + case SLJIT_FSUB: + FAIL_IF(emit_fop(compiler, 0xd8, 0xe0, 0xdc, 0x4 << 3, src2, src2w)); + break; + case SLJIT_FMUL: + FAIL_IF(emit_fop(compiler, 0xd8, 0xc8, 0xdc, 0x1 << 3, src2, src2w)); + break; + case SLJIT_FDIV: + FAIL_IF(emit_fop(compiler, 0xd8, 0xf0, 0xdc, 0x6 << 3, src2, src2w)); + break; + } + + FAIL_IF(emit_fop(compiler, 0xdd, 0xd8, 0xdd, 0x3 << 3, dst, dstw)); + + return SLJIT_SUCCESS; +} +#endif + +#if (defined SLJIT_SSE2_AUTO && SLJIT_SSE2_AUTO) + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop1(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src, sljit_w srcw) +{ + if (sse2_available) + return sljit_emit_sse2_fop1(compiler, op, dst, dstw, src, srcw); + else + return sljit_emit_fpu_fop1(compiler, op, dst, dstw, src, srcw); +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_fop2(struct sljit_compiler *compiler, int op, + int dst, sljit_w dstw, + int src1, sljit_w src1w, + int src2, sljit_w src2w) +{ + if (sse2_available) + return sljit_emit_sse2_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); + else + return sljit_emit_fpu_fop2(compiler, op, dst, dstw, src1, src1w, src2, src2w); +} + +#endif + +/* --------------------------------------------------------------------- */ +/* Conditional instructions */ +/* --------------------------------------------------------------------- */ + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_label* sljit_emit_label(struct sljit_compiler *compiler) +{ + sljit_ub *buf; + struct sljit_label *label; + + CHECK_ERROR_PTR(); + check_sljit_emit_label(compiler); + + /* We should restore the flags before the label, + since other taken jumps has their own flags as well. */ + if (SLJIT_UNLIKELY(compiler->flags_saved)) + PTR_FAIL_IF(emit_restore_flags(compiler, 0)); + + if (compiler->last_label && compiler->last_label->size == compiler->size) + return compiler->last_label; + + label = (struct sljit_label*)ensure_abuf(compiler, sizeof(struct sljit_label)); + PTR_FAIL_IF(!label); + set_label(label, compiler); + + buf = (sljit_ub*)ensure_buf(compiler, 2); + PTR_FAIL_IF(!buf); + + *buf++ = 0; + *buf++ = 0; + + return label; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_jump* sljit_emit_jump(struct sljit_compiler *compiler, int type) +{ + sljit_ub *buf; + struct sljit_jump *jump; + + CHECK_ERROR_PTR(); + check_sljit_emit_jump(compiler, type); + + if (SLJIT_UNLIKELY(compiler->flags_saved)) { + if ((type & 0xff) <= SLJIT_JUMP) + PTR_FAIL_IF(emit_restore_flags(compiler, 0)); + compiler->flags_saved = 0; + } + + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + PTR_FAIL_IF_NULL(jump); + set_jump(jump, compiler, type & SLJIT_REWRITABLE_JUMP); + type &= 0xff; + + if (type >= SLJIT_CALL1) + PTR_FAIL_IF(call_with_args(compiler, type)); + + /* Worst case size. */ +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + compiler->size += (type >= SLJIT_JUMP) ? 5 : 6; +#else + compiler->size += (type >= SLJIT_JUMP) ? (10 + 3) : (2 + 10 + 3); +#endif + + buf = (sljit_ub*)ensure_buf(compiler, 2); + PTR_FAIL_IF_NULL(buf); + + *buf++ = 0; + *buf++ = type + 4; + return jump; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_ijump(struct sljit_compiler *compiler, int type, int src, sljit_w srcw) +{ + sljit_ub *code; + struct sljit_jump *jump; + + CHECK_ERROR(); + check_sljit_emit_ijump(compiler, type, src, srcw); + + CHECK_EXTRA_REGS(src, srcw, (void)0); + if (SLJIT_UNLIKELY(compiler->flags_saved)) { + if (type <= SLJIT_JUMP) + FAIL_IF(emit_restore_flags(compiler, 0)); + compiler->flags_saved = 0; + } + + if (type >= SLJIT_CALL1) { +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) +#if (defined SLJIT_X86_32_FASTCALL && SLJIT_X86_32_FASTCALL) + if (src == SLJIT_TEMPORARY_REG3) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, 0); + src = TMP_REGISTER; + } + if ((src & SLJIT_MEM) && (src & 0xf) == SLJIT_LOCALS_REG && type >= SLJIT_CALL3) { + if (src & 0xf0) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + src = TMP_REGISTER; + } + else + srcw += sizeof(sljit_w); + } +#else + if ((src & SLJIT_MEM) && (src & 0xf) == SLJIT_LOCALS_REG) { + if (src & 0xf0) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, srcw); + src = TMP_REGISTER; + } + else + srcw += sizeof(sljit_w) * (type - SLJIT_CALL0); + } +#endif +#endif +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) && defined(_WIN64) + if (src == SLJIT_TEMPORARY_REG3) { + EMIT_MOV(compiler, TMP_REGISTER, 0, src, 0); + src = TMP_REGISTER; + } +#endif + FAIL_IF(call_with_args(compiler, type)); + } + + if (src == SLJIT_IMM) { + jump = (struct sljit_jump*)ensure_abuf(compiler, sizeof(struct sljit_jump)); + FAIL_IF_NULL(jump); + set_jump(jump, compiler, JUMP_ADDR); + jump->u.target = srcw; + + /* Worst case size. */ +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + compiler->size += 5; +#else + compiler->size += 10 + 3; +#endif + + code = (sljit_ub*)ensure_buf(compiler, 2); + FAIL_IF_NULL(code); + + *code++ = 0; + *code++ = type + 4; + } + else { +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + /* REX_W is not necessary (src is not immediate). */ + compiler->mode32 = 1; +#endif + code = emit_x86_instruction(compiler, 1, 0, 0, src, srcw); + FAIL_IF(!code); + *code++ = 0xff; + *code |= (type >= SLJIT_FAST_CALL) ? (2 << 3) : (4 << 3); + } + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_emit_cond_value(struct sljit_compiler *compiler, int op, int dst, sljit_w dstw, int type) +{ + sljit_ub *buf; + sljit_ub cond_set = 0; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + int reg; +#endif + + CHECK_ERROR(); + check_sljit_emit_cond_value(compiler, op, dst, dstw, type); + + if (dst == SLJIT_UNUSED) + return SLJIT_SUCCESS; + + CHECK_EXTRA_REGS(dst, dstw, (void)0); + if (SLJIT_UNLIKELY(compiler->flags_saved)) + FAIL_IF(emit_restore_flags(compiler, 0)); + + switch (type) { + case SLJIT_C_EQUAL: + case SLJIT_C_FLOAT_EQUAL: + cond_set = 0x94; + break; + + case SLJIT_C_NOT_EQUAL: + case SLJIT_C_FLOAT_NOT_EQUAL: + cond_set = 0x95; + break; + + case SLJIT_C_LESS: + case SLJIT_C_FLOAT_LESS: + cond_set = 0x92; + break; + + case SLJIT_C_GREATER_EQUAL: + case SLJIT_C_FLOAT_GREATER_EQUAL: + cond_set = 0x93; + break; + + case SLJIT_C_GREATER: + case SLJIT_C_FLOAT_GREATER: + cond_set = 0x97; + break; + + case SLJIT_C_LESS_EQUAL: + case SLJIT_C_FLOAT_LESS_EQUAL: + cond_set = 0x96; + break; + + case SLJIT_C_SIG_LESS: + cond_set = 0x9c; + break; + + case SLJIT_C_SIG_GREATER_EQUAL: + cond_set = 0x9d; + break; + + case SLJIT_C_SIG_GREATER: + cond_set = 0x9f; + break; + + case SLJIT_C_SIG_LESS_EQUAL: + cond_set = 0x9e; + break; + + case SLJIT_C_OVERFLOW: + case SLJIT_C_MUL_OVERFLOW: + cond_set = 0x90; + break; + + case SLJIT_C_NOT_OVERFLOW: + case SLJIT_C_MUL_NOT_OVERFLOW: + cond_set = 0x91; + break; + + case SLJIT_C_FLOAT_NAN: + cond_set = 0x9a; + break; + + case SLJIT_C_FLOAT_NOT_NAN: + cond_set = 0x9b; + break; + } + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + reg = (op == SLJIT_MOV && dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REGISTER; + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 4 + 4); + FAIL_IF(!buf); + INC_SIZE(4 + 4); + /* Set low register to conditional flag. */ + *buf++ = (reg_map[reg] <= 7) ? 0x40 : REX_B; + *buf++ = 0x0f; + *buf++ = cond_set; + *buf++ = 0xC0 | reg_lmap[reg]; + *buf++ = REX_W | (reg_map[reg] <= 7 ? 0 : (REX_B | REX_R)); + *buf++ = 0x0f; + *buf++ = 0xb6; + *buf = 0xC0 | (reg_lmap[reg] << 3) | reg_lmap[reg]; + + if (reg == TMP_REGISTER) { + if (op == SLJIT_MOV) { + compiler->mode32 = 0; + EMIT_MOV(compiler, dst, dstw, TMP_REGISTER, 0); + } + else { +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_op2(compiler, op, dst, dstw, dst, dstw, TMP_REGISTER, 0); + } + } +#else + if (op == SLJIT_MOV) { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_TEMPORARY_REG3) { + buf = (sljit_ub*)ensure_buf(compiler, 1 + 3 + 3); + FAIL_IF(!buf); + INC_SIZE(3 + 3); + /* Set low byte to conditional flag. */ + *buf++ = 0x0f; + *buf++ = cond_set; + *buf++ = 0xC0 | reg_map[dst]; + + *buf++ = 0x0f; + *buf++ = 0xb6; + *buf = 0xC0 | (reg_map[dst] << 3) | reg_map[dst]; + } + else { + EMIT_MOV(compiler, TMP_REGISTER, 0, SLJIT_TEMPORARY_REG1, 0); + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 3 + 3); + FAIL_IF(!buf); + INC_SIZE(3 + 3); + /* Set al to conditional flag. */ + *buf++ = 0x0f; + *buf++ = cond_set; + *buf++ = 0xC0; + + *buf++ = 0x0f; + *buf++ = 0xb6; + if (dst >= SLJIT_SAVED_REG1 && dst <= SLJIT_NO_REGISTERS) + *buf = 0xC0 | (reg_map[dst] << 3); + else { + *buf = 0xC0; + EMIT_MOV(compiler, dst, dstw, SLJIT_TEMPORARY_REG1, 0); + } + + EMIT_MOV(compiler, SLJIT_TEMPORARY_REG1, 0, TMP_REGISTER, 0); + } + } + else { + if (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_TEMPORARY_REG3) { + EMIT_MOV(compiler, TMP_REGISTER, 0, dst, 0); + buf = (sljit_ub*)ensure_buf(compiler, 1 + 3); + FAIL_IF(!buf); + INC_SIZE(3); + + *buf++ = 0x0f; + *buf++ = cond_set; + *buf++ = 0xC0 | reg_map[dst]; + } + else { + EMIT_MOV(compiler, TMP_REGISTER, 0, SLJIT_TEMPORARY_REG1, 0); + + buf = (sljit_ub*)ensure_buf(compiler, 1 + 3 + 3 + 1); + FAIL_IF(!buf); + INC_SIZE(3 + 3 + 1); + /* Set al to conditional flag. */ + *buf++ = 0x0f; + *buf++ = cond_set; + *buf++ = 0xC0; + + *buf++ = 0x0f; + *buf++ = 0xb6; + *buf++ = 0xC0; + + *buf++ = 0x90 + reg_map[TMP_REGISTER]; + } +#if (defined SLJIT_VERBOSE && SLJIT_VERBOSE) || (defined SLJIT_DEBUG && SLJIT_DEBUG) + compiler->skip_checks = 1; +#endif + return sljit_emit_op2(compiler, op, dst, dstw, dst, dstw, TMP_REGISTER, 0); + } +#endif + + return SLJIT_SUCCESS; +} + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_const* sljit_emit_const(struct sljit_compiler *compiler, int dst, sljit_w dstw, sljit_w init_value) +{ + sljit_ub *buf; + struct sljit_const *const_; +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + int reg; +#endif + + CHECK_ERROR_PTR(); + check_sljit_emit_const(compiler, dst, dstw, init_value); + + CHECK_EXTRA_REGS(dst, dstw, (void)0); + + const_ = (struct sljit_const*)ensure_abuf(compiler, sizeof(struct sljit_const)); + PTR_FAIL_IF(!const_); + set_const(const_, compiler); + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + compiler->mode32 = 0; + reg = (dst >= SLJIT_TEMPORARY_REG1 && dst <= SLJIT_NO_REGISTERS) ? dst : TMP_REGISTER; + + if (emit_load_imm64(compiler, reg, init_value)) + return NULL; +#else + if (dst == SLJIT_UNUSED) + dst = TMP_REGISTER; + + if (emit_mov(compiler, dst, dstw, SLJIT_IMM, init_value)) + return NULL; +#endif + + buf = (sljit_ub*)ensure_buf(compiler, 2); + PTR_FAIL_IF(!buf); + + *buf++ = 0; + *buf++ = 1; + +#if (defined SLJIT_CONFIG_X86_64 && SLJIT_CONFIG_X86_64) + if (reg == TMP_REGISTER && dst != SLJIT_UNUSED) + if (emit_mov(compiler, dst, dstw, TMP_REGISTER, 0)) + return NULL; +#endif + + return const_; +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_jump_addr(sljit_uw addr, sljit_uw new_addr) +{ +#if (defined SLJIT_CONFIG_X86_32 && SLJIT_CONFIG_X86_32) + *(sljit_w*)addr = new_addr - (addr + 4); +#else + *(sljit_uw*)addr = new_addr; +#endif +} + +SLJIT_API_FUNC_ATTRIBUTE void sljit_set_const(sljit_uw addr, sljit_w new_constant) +{ + *(sljit_w*)addr = new_constant; +} diff --git a/src/lib/pcre/sljit/sljitUtils.c b/src/lib/pcre/sljit/sljitUtils.c new file mode 100644 index 0000000..f3b52fe --- /dev/null +++ b/src/lib/pcre/sljit/sljitUtils.c @@ -0,0 +1,248 @@ +/* + * Stack-less Just-In-Time compiler + * + * Copyright 2009-2012 Zoltan Herczeg (hzmester@freemail.hu). 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 COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER(S) 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. + */ + +/* ------------------------------------------------------------------------ */ +/* Locks */ +/* ------------------------------------------------------------------------ */ + +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) || (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK) + +#ifdef _WIN32 + +#include "windows.h" + +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) + +static HANDLE allocator_mutex = 0; + +static SLJIT_INLINE void allocator_grab_lock(void) +{ + /* No idea what to do if an error occures. Static mutexes should never fail... */ + if (!allocator_mutex) + allocator_mutex = CreateMutex(NULL, TRUE, NULL); + else + WaitForSingleObject(allocator_mutex, INFINITE); +} + +static SLJIT_INLINE void allocator_release_lock(void) +{ + ReleaseMutex(allocator_mutex); +} + +#endif /* SLJIT_EXECUTABLE_ALLOCATOR */ + +#if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK) + +static HANDLE global_mutex = 0; + +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void) +{ + /* No idea what to do if an error occures. Static mutexes should never fail... */ + if (!global_mutex) + global_mutex = CreateMutex(NULL, TRUE, NULL); + else + WaitForSingleObject(global_mutex, INFINITE); +} + +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void) +{ + ReleaseMutex(global_mutex); +} + +#endif /* SLJIT_UTIL_GLOBAL_LOCK */ + +#else /* _WIN32 */ + +#include "pthread.h" + +#if (defined SLJIT_EXECUTABLE_ALLOCATOR && SLJIT_EXECUTABLE_ALLOCATOR) + +static pthread_mutex_t allocator_mutex = PTHREAD_MUTEX_INITIALIZER; + +static SLJIT_INLINE void allocator_grab_lock(void) +{ + pthread_mutex_lock(&allocator_mutex); +} + +static SLJIT_INLINE void allocator_release_lock(void) +{ + pthread_mutex_unlock(&allocator_mutex); +} + +#endif /* SLJIT_EXECUTABLE_ALLOCATOR */ + +#if (defined SLJIT_UTIL_GLOBAL_LOCK && SLJIT_UTIL_GLOBAL_LOCK) + +static pthread_mutex_t global_mutex = PTHREAD_MUTEX_INITIALIZER; + +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_grab_lock(void) +{ + pthread_mutex_lock(&global_mutex); +} + +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_release_lock(void) +{ + pthread_mutex_unlock(&global_mutex); +} + +#endif /* SLJIT_UTIL_GLOBAL_LOCK */ + +#endif /* _WIN32 */ + +/* ------------------------------------------------------------------------ */ +/* Stack */ +/* ------------------------------------------------------------------------ */ + +#if (defined SLJIT_UTIL_STACK && SLJIT_UTIL_STACK) + +#ifdef _WIN32 +#include "windows.h" +#else +#include +#include +#endif + +/* Planning to make it even more clever in the future. */ +static sljit_w sljit_page_align = 0; + +SLJIT_API_FUNC_ATTRIBUTE struct sljit_stack* SLJIT_CALL sljit_allocate_stack(sljit_uw limit, sljit_uw max_limit) +{ + struct sljit_stack *stack; + union { + void *ptr; + sljit_uw uw; + } base; +#ifdef _WIN32 + SYSTEM_INFO si; +#endif + + if (limit > max_limit || limit < 1) + return NULL; + +#ifdef _WIN32 + if (!sljit_page_align) { + GetSystemInfo(&si); + sljit_page_align = si.dwPageSize - 1; + } +#else + if (!sljit_page_align) { + sljit_page_align = sysconf(_SC_PAGESIZE); + /* Should never happen. */ + if (sljit_page_align < 0) + sljit_page_align = 4096; + sljit_page_align--; + } +#endif + + /* Align limit and max_limit. */ + max_limit = (max_limit + sljit_page_align) & ~sljit_page_align; + + stack = (struct sljit_stack*)SLJIT_MALLOC(sizeof(struct sljit_stack)); + if (!stack) + return NULL; + +#ifdef _WIN32 + base.ptr = VirtualAlloc(0, max_limit, MEM_RESERVE, PAGE_READWRITE); + if (!base.ptr) { + SLJIT_FREE(stack); + return NULL; + } + stack->base = base.uw; + stack->limit = stack->base; + stack->max_limit = stack->base + max_limit; + if (sljit_stack_resize(stack, stack->base + limit)) { + sljit_free_stack(stack); + return NULL; + } +#else + base.ptr = mmap(0, max_limit, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (base.ptr == MAP_FAILED) { + SLJIT_FREE(stack); + return NULL; + } + stack->base = base.uw; + stack->limit = stack->base + limit; + stack->max_limit = stack->base + max_limit; +#endif + stack->top = stack->base; + return stack; +} + +#undef PAGE_ALIGN + +SLJIT_API_FUNC_ATTRIBUTE void SLJIT_CALL sljit_free_stack(struct sljit_stack* stack) +{ +#ifdef _WIN32 + VirtualFree((void*)stack->base, 0, MEM_RELEASE); +#else + munmap((void*)stack->base, stack->max_limit - stack->base); +#endif + SLJIT_FREE(stack); +} + +SLJIT_API_FUNC_ATTRIBUTE sljit_w SLJIT_CALL sljit_stack_resize(struct sljit_stack* stack, sljit_uw new_limit) +{ + sljit_uw aligned_old_limit; + sljit_uw aligned_new_limit; + + if ((new_limit > stack->max_limit) || (new_limit < stack->base)) + return -1; +#ifdef _WIN32 + aligned_new_limit = (new_limit + sljit_page_align) & ~sljit_page_align; + aligned_old_limit = (stack->limit + sljit_page_align) & ~sljit_page_align; + if (aligned_new_limit != aligned_old_limit) { + if (aligned_new_limit > aligned_old_limit) { + if (!VirtualAlloc((void*)aligned_old_limit, aligned_new_limit - aligned_old_limit, MEM_COMMIT, PAGE_READWRITE)) + return -1; + } + else { + if (!VirtualFree((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, MEM_DECOMMIT)) + return -1; + } + } + stack->limit = new_limit; + return 0; +#else + if (new_limit >= stack->limit) { + stack->limit = new_limit; + return 0; + } + aligned_new_limit = (new_limit + sljit_page_align) & ~sljit_page_align; + aligned_old_limit = (stack->limit + sljit_page_align) & ~sljit_page_align; + if (aligned_new_limit < aligned_old_limit) +#if defined(__QNXNTO__) || defined(__LSB_VERSION__) + posix_madvise((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, POSIX_MADV_DONTNEED); +#else + madvise((void*)aligned_new_limit, aligned_old_limit - aligned_new_limit, MADV_DONTNEED); +#endif + stack->limit = new_limit; + return 0; +#endif +} + +#endif /* SLJIT_UTIL_STACK */ + +#endif diff --git a/src/lib/pcre/ucp.h b/src/lib/pcre/ucp.h new file mode 100644 index 0000000..34077fe --- /dev/null +++ b/src/lib/pcre/ucp.h @@ -0,0 +1,165 @@ +/************************************************* +* Unicode Property Table handler * +*************************************************/ + +#ifndef _UCP_H +#define _UCP_H + +/* This file contains definitions of the property values that are returned by +the UCD access macros. New values that are added for new releases of Unicode +should always be at the end of each enum, for backwards compatibility. */ + +/* These are the general character categories. */ + +enum { + ucp_C, /* Other */ + ucp_L, /* Letter */ + ucp_M, /* Mark */ + ucp_N, /* Number */ + ucp_P, /* Punctuation */ + ucp_S, /* Symbol */ + ucp_Z /* Separator */ +}; + +/* These are the particular character types. */ + +enum { + ucp_Cc, /* Control */ + ucp_Cf, /* Format */ + ucp_Cn, /* Unassigned */ + ucp_Co, /* Private use */ + ucp_Cs, /* Surrogate */ + ucp_Ll, /* Lower case letter */ + ucp_Lm, /* Modifier letter */ + ucp_Lo, /* Other letter */ + ucp_Lt, /* Title case letter */ + ucp_Lu, /* Upper case letter */ + ucp_Mc, /* Spacing mark */ + ucp_Me, /* Enclosing mark */ + ucp_Mn, /* Non-spacing mark */ + ucp_Nd, /* Decimal number */ + ucp_Nl, /* Letter number */ + ucp_No, /* Other number */ + ucp_Pc, /* Connector punctuation */ + ucp_Pd, /* Dash punctuation */ + ucp_Pe, /* Close punctuation */ + ucp_Pf, /* Final punctuation */ + ucp_Pi, /* Initial punctuation */ + ucp_Po, /* Other punctuation */ + ucp_Ps, /* Open punctuation */ + ucp_Sc, /* Currency symbol */ + ucp_Sk, /* Modifier symbol */ + ucp_Sm, /* Mathematical symbol */ + ucp_So, /* Other symbol */ + ucp_Zl, /* Line separator */ + ucp_Zp, /* Paragraph separator */ + ucp_Zs /* Space separator */ +}; + +/* These are the script identifications. */ + +enum { + ucp_Arabic, + ucp_Armenian, + ucp_Bengali, + ucp_Bopomofo, + ucp_Braille, + ucp_Buginese, + ucp_Buhid, + ucp_Canadian_Aboriginal, + ucp_Cherokee, + ucp_Common, + ucp_Coptic, + ucp_Cypriot, + ucp_Cyrillic, + ucp_Deseret, + ucp_Devanagari, + ucp_Ethiopic, + ucp_Georgian, + ucp_Glagolitic, + ucp_Gothic, + ucp_Greek, + ucp_Gujarati, + ucp_Gurmukhi, + ucp_Han, + ucp_Hangul, + ucp_Hanunoo, + ucp_Hebrew, + ucp_Hiragana, + ucp_Inherited, + ucp_Kannada, + ucp_Katakana, + ucp_Kharoshthi, + ucp_Khmer, + ucp_Lao, + ucp_Latin, + ucp_Limbu, + ucp_Linear_B, + ucp_Malayalam, + ucp_Mongolian, + ucp_Myanmar, + ucp_New_Tai_Lue, + ucp_Ogham, + ucp_Old_Italic, + ucp_Old_Persian, + ucp_Oriya, + ucp_Osmanya, + ucp_Runic, + ucp_Shavian, + ucp_Sinhala, + ucp_Syloti_Nagri, + ucp_Syriac, + ucp_Tagalog, + ucp_Tagbanwa, + ucp_Tai_Le, + ucp_Tamil, + ucp_Telugu, + ucp_Thaana, + ucp_Thai, + ucp_Tibetan, + ucp_Tifinagh, + ucp_Ugaritic, + ucp_Yi, + /* New for Unicode 5.0: */ + ucp_Balinese, + ucp_Cuneiform, + ucp_Nko, + ucp_Phags_Pa, + ucp_Phoenician, + /* New for Unicode 5.1: */ + ucp_Carian, + ucp_Cham, + ucp_Kayah_Li, + ucp_Lepcha, + ucp_Lycian, + ucp_Lydian, + ucp_Ol_Chiki, + ucp_Rejang, + ucp_Saurashtra, + ucp_Sundanese, + ucp_Vai, + /* New for Unicode 5.2: */ + ucp_Avestan, + ucp_Bamum, + ucp_Egyptian_Hieroglyphs, + ucp_Imperial_Aramaic, + ucp_Inscriptional_Pahlavi, + ucp_Inscriptional_Parthian, + ucp_Javanese, + ucp_Kaithi, + ucp_Lisu, + ucp_Meetei_Mayek, + ucp_Old_South_Arabian, + ucp_Old_Turkic, + ucp_Samaritan, + ucp_Tai_Tham, + ucp_Tai_Viet, + /* New for Unicode 6.0.0: */ + ucp_Batak, + ucp_Brahmi, + ucp_Mandaic +}; + +#endif + +/* End of ucp.h */ diff --git a/src/lib/rapidxml/rapidxml.hpp b/src/lib/rapidxml/rapidxml.hpp new file mode 100644 index 0000000..ae91e08 --- /dev/null +++ b/src/lib/rapidxml/rapidxml.hpp @@ -0,0 +1,2596 @@ +#ifndef RAPIDXML_HPP_INCLUDED +#define RAPIDXML_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml.hpp This file contains rapidxml parser and DOM implementation + +// If standard library is disabled, user must provide implementations of required functions and typedefs +#if !defined(RAPIDXML_NO_STDLIB) + #include // For std::size_t + #include // For assert + #include // For placement new +#endif + +// On MSVC, disable "conditional expression is constant" warning (level 4). +// This warning is almost impossible to avoid with certain types of templated code +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) // Conditional expression is constant +#endif + +/////////////////////////////////////////////////////////////////////////// +// RAPIDXML_PARSE_ERROR + +#if defined(RAPIDXML_NO_EXCEPTIONS) + +#define RAPIDXML_PARSE_ERROR(what, where) { parse_error_handler(what, where); assert(0); } + +namespace rapidxml +{ + //! When exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, + //! this function is called to notify user about the error. + //! It must be defined by the user. + //!

    + //! This function cannot return. If it does, the results are undefined. + //!

    + //! A very simple definition might look like that: + //!
    +    //! void %rapidxml::%parse_error_handler(const char *what, void *where)
    +    //! {
    +    //!     std::cout << "Parse error: " << what << "\n";
    +    //!     std::abort();
    +    //! }
    +    //! 
    + //! \param what Human readable description of the error. + //! \param where Pointer to character data where error was detected. + void parse_error_handler(const char *what, void *where); +} + +#else + +#include // For std::exception + +#define RAPIDXML_PARSE_ERROR(what, where) throw parse_error(what, where) + +namespace rapidxml +{ + + //! Parse error exception. + //! This exception is thrown by the parser when an error occurs. + //! Use what() function to get human-readable error message. + //! Use where() function to get a pointer to position within source text where error was detected. + //!

    + //! If throwing exceptions by the parser is undesirable, + //! it can be disabled by defining RAPIDXML_NO_EXCEPTIONS macro before rapidxml.hpp is included. + //! This will cause the parser to call rapidxml::parse_error_handler() function instead of throwing an exception. + //! This function must be defined by the user. + //!

    + //! This class derives from std::exception class. + class parse_error: public std::exception + { + + public: + + //! Constructs parse error + parse_error(const char *what, void *where) + : m_what(what) + , m_where(where) + { + } + + //! Gets human readable description of error. + //! \return Pointer to null terminated description of the error. + virtual const char *what() const throw() + { + return m_what; + } + + //! Gets pointer to character data where error happened. + //! Ch should be the same as char type of xml_document that produced the error. + //! \return Pointer to location within the parsed string where error occured. + template + Ch *where() const + { + return reinterpret_cast(m_where); + } + + private: + + const char *m_what; + void *m_where; + + }; +} + +#endif + +/////////////////////////////////////////////////////////////////////////// +// Pool sizes + +#ifndef RAPIDXML_STATIC_POOL_SIZE + // Size of static memory block of memory_pool. + // Define RAPIDXML_STATIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // No dynamic memory allocations are performed by memory_pool until static memory is exhausted. + #define RAPIDXML_STATIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_DYNAMIC_POOL_SIZE + // Size of dynamic memory block of memory_pool. + // Define RAPIDXML_DYNAMIC_POOL_SIZE before including rapidxml.hpp if you want to override the default value. + // After the static block is exhausted, dynamic blocks with approximately this size are allocated by memory_pool. + #define RAPIDXML_DYNAMIC_POOL_SIZE (64 * 1024) +#endif + +#ifndef RAPIDXML_ALIGNMENT + // Memory allocation alignment. + // Define RAPIDXML_ALIGNMENT before including rapidxml.hpp if you want to override the default value, which is the size of pointer. + // All memory allocations for nodes, attributes and strings will be aligned to this value. + // This must be a power of 2 and at least 1, otherwise memory_pool will not work. + #define RAPIDXML_ALIGNMENT sizeof(void *) +#endif + +namespace rapidxml +{ + // Forward declarations + template class xml_node; + template class xml_attribute; + template class xml_document; + + //! Enumeration listing all node types produced by the parser. + //! Use xml_node::type() function to query node type. + enum node_type + { + node_document, //!< A document node. Name and value are empty. + node_element, //!< An element node. Name contains element name. Value contains text of first data node. + node_data, //!< A data node. Name is empty. Value contains data text. + node_cdata, //!< A CDATA node. Name is empty. Value contains data text. + node_comment, //!< A comment node. Name is empty. Value contains comment text. + node_declaration, //!< A declaration node. Name and value are empty. Declaration parameters (version, encoding and standalone) are in node attributes. + node_doctype, //!< A DOCTYPE node. Name is empty. Value contains DOCTYPE text. + node_pi //!< A PI node. Name contains target. Value contains instructions. + }; + + /////////////////////////////////////////////////////////////////////// + // Parsing flags + + //! Parse flag instructing the parser to not create data nodes. + //! Text of first data node will still be placed in value of parent element, unless rapidxml::parse_no_element_values flag is also specified. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_data_nodes = 0x1; + + //! Parse flag instructing the parser to not use text of first data node as a value of parent element. + //! Can be combined with other flags by use of | operator. + //! Note that child data nodes of element node take precendence over its value when printing. + //! That is, if element has one or more child data nodes and a value, the value will be ignored. + //! Use rapidxml::parse_no_data_nodes flag to prevent creation of data nodes if you want to manipulate data using values of elements. + //!

    + //! See xml_document::parse() function. + const int parse_no_element_values = 0x2; + + //! Parse flag instructing the parser to not place zero terminators after strings in the source text. + //! By default zero terminators are placed, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_string_terminators = 0x4; + + //! Parse flag instructing the parser to not translate entities in the source text. + //! By default entities are translated, modifying source text. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_entity_translation = 0x8; + + //! Parse flag instructing the parser to disable UTF-8 handling and assume plain 8 bit characters. + //! By default, UTF-8 handling is enabled. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_no_utf8 = 0x10; + + //! Parse flag instructing the parser to create XML declaration node. + //! By default, declaration node is not created. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_declaration_node = 0x20; + + //! Parse flag instructing the parser to create comments nodes. + //! By default, comment nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_comment_nodes = 0x40; + + //! Parse flag instructing the parser to create DOCTYPE node. + //! By default, doctype node is not created. + //! Although W3C specification allows at most one DOCTYPE node, RapidXml will silently accept documents with more than one. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_doctype_node = 0x80; + + //! Parse flag instructing the parser to create PI nodes. + //! By default, PI nodes are not created. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_pi_nodes = 0x100; + + //! Parse flag instructing the parser to validate closing tag names. + //! If not set, name inside closing tag is irrelevant to the parser. + //! By default, closing tags are not validated. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_validate_closing_tags = 0x200; + + //! Parse flag instructing the parser to trim all leading and trailing whitespace of data nodes. + //! By default, whitespace is not trimmed. + //! This flag does not cause the parser to modify source text. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_trim_whitespace = 0x400; + + //! Parse flag instructing the parser to condense all whitespace runs of data nodes to a single space character. + //! Trimming of leading and trailing whitespace of data is controlled by rapidxml::parse_trim_whitespace flag. + //! By default, whitespace is not normalized. + //! If this flag is specified, source text will be modified. + //! Can be combined with other flags by use of | operator. + //!

    + //! See xml_document::parse() function. + const int parse_normalize_whitespace = 0x800; + + // Compound flags + + //! Parse flags which represent default behaviour of the parser. + //! This is always equal to 0, so that all other flags can be simply ored together. + //! Normally there is no need to inconveniently disable flags by anding with their negated (~) values. + //! This also means that meaning of each flag is a negation of the default setting. + //! For example, if flag name is rapidxml::parse_no_utf8, it means that utf-8 is enabled by default, + //! and using the flag will disable it. + //!

    + //! See xml_document::parse() function. + const int parse_default = 0; + + //! A combination of parse flags that forbids any modifications of the source text. + //! This also results in faster parsing. However, note that the following will occur: + //!
      + //!
    • names and values of nodes will not be zero terminated, you have to use xml_base::name_size() and xml_base::value_size() functions to determine where name and value ends
    • + //!
    • entities will not be translated
    • + //!
    • whitespace will not be normalized
    • + //!
    + //! See xml_document::parse() function. + const int parse_non_destructive = parse_no_string_terminators | parse_no_entity_translation; + + //! A combination of parse flags resulting in fastest possible parsing, without sacrificing important data. + //!

    + //! See xml_document::parse() function. + const int parse_fastest = parse_non_destructive | parse_no_data_nodes; + + //! A combination of parse flags resulting in largest amount of data being extracted. + //! This usually results in slowest parsing. + //!

    + //! See xml_document::parse() function. + const int parse_full = parse_declaration_node | parse_comment_nodes | parse_doctype_node | parse_pi_nodes | parse_validate_closing_tags; + + /////////////////////////////////////////////////////////////////////// + // Internals + + //! \cond internal + namespace internal + { + + // Struct that contains lookup tables for the parser + // It must be a template to allow correct linking (because it has static data members, which are defined in a header file). + template + struct lookup_tables + { + static const unsigned char lookup_whitespace[256]; // Whitespace table + static const unsigned char lookup_node_name[256]; // Node name table + static const unsigned char lookup_text[256]; // Text table + static const unsigned char lookup_text_pure_no_ws[256]; // Text table + static const unsigned char lookup_text_pure_with_ws[256]; // Text table + static const unsigned char lookup_attribute_name[256]; // Attribute name table + static const unsigned char lookup_attribute_data_1[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_1_pure[256]; // Attribute data table with single quote + static const unsigned char lookup_attribute_data_2[256]; // Attribute data table with double quotes + static const unsigned char lookup_attribute_data_2_pure[256]; // Attribute data table with double quotes + static const unsigned char lookup_digits[256]; // Digits + static const unsigned char lookup_upcase[256]; // To uppercase conversion table for ASCII characters + }; + + // Find length of the string + template + inline std::size_t measure(const Ch *p) + { + const Ch *tmp = p; + while (*tmp) + ++tmp; + return tmp - p; + } + + // Compare strings for equality + template + inline bool compare(const Ch *p1, std::size_t size1, const Ch *p2, std::size_t size2, bool case_sensitive) + { + if (size1 != size2) + return false; + if (case_sensitive) + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (*p1 != *p2) + return false; + } + else + { + for (const Ch *end = p1 + size1; p1 < end; ++p1, ++p2) + if (lookup_tables<0>::lookup_upcase[static_cast(*p1)] != lookup_tables<0>::lookup_upcase[static_cast(*p2)]) + return false; + } + return true; + } + } + //! \endcond + + /////////////////////////////////////////////////////////////////////// + // Memory pool + + //! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation. + //! In most cases, you will not need to use this class directly. + //! However, if you need to create nodes manually or modify names/values of nodes, + //! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. + //! Not only is this faster than allocating them by using new operator, + //! but also their lifetime will be tied to the lifetime of document, + //! possibly simplyfing memory management. + //!

    + //! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. + //! You can also call allocate_string() function to allocate strings. + //! Such strings can then be used as names or values of nodes without worrying about their lifetime. + //! Note that there is no free() function -- all allocations are freed at once when clear() function is called, + //! or when the pool is destroyed. + //!

    + //! It is also possible to create a standalone memory_pool, and use it + //! to allocate nodes, whose lifetime will not be tied to any document. + //!

    + //! Pool maintains RAPIDXML_STATIC_POOL_SIZE bytes of statically allocated memory. + //! Until static memory is exhausted, no dynamic memory allocations are done. + //! When static memory is exhausted, pool allocates additional blocks of memory of size RAPIDXML_DYNAMIC_POOL_SIZE each, + //! by using global new[] and delete[] operators. + //! This behaviour can be changed by setting custom allocation routines. + //! Use set_allocator() function to set them. + //!

    + //! Allocations for nodes, attributes and strings are aligned at RAPIDXML_ALIGNMENT bytes. + //! This value defaults to the size of pointer on target architecture. + //!

    + //! To obtain absolutely top performance from the parser, + //! it is important that all nodes are allocated from a single, contiguous block of memory. + //! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably. + //! If required, you can tweak RAPIDXML_STATIC_POOL_SIZE, RAPIDXML_DYNAMIC_POOL_SIZE and RAPIDXML_ALIGNMENT + //! to obtain best wasted memory to performance compromise. + //! To do it, define their values before rapidxml.hpp file is included. + //! \param Ch Character type of created nodes. + template + class memory_pool + { + + public: + + //! \cond internal + typedef void *(alloc_func)(std::size_t); // Type of user-defined function used to allocate memory + typedef void (free_func)(void *); // Type of user-defined function used to free memory + //! \endcond + + //! Constructs empty pool with default allocator functions. + memory_pool() + : m_alloc_func(0) + , m_free_func(0) + { + init(); + } + + //! Destroys pool and frees all the memory. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Nodes allocated from the pool are no longer valid. + ~memory_pool() + { + clear(); + } + + //! Allocates a new node from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param type Type of node to create. + //! \param name Name to assign to the node, or 0 to assign no name. + //! \param value Value to assign to the node, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated node. This pointer will never be NULL. + xml_node *allocate_node(node_type type, + const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_node)); + xml_node *node = new(memory) xml_node(type); + if (name) + { + if (name_size > 0) + node->name(name, name_size); + else + node->name(name); + } + if (value) + { + if (value_size > 0) + node->value(value, value_size); + else + node->value(value); + } + return node; + } + + //! Allocates a new attribute from the pool, and optionally assigns name and value to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param name Name to assign to the attribute, or 0 to assign no name. + //! \param value Value to assign to the attribute, or 0 to assign no value. + //! \param name_size Size of name to assign, or 0 to automatically calculate size from name string. + //! \param value_size Size of value to assign, or 0 to automatically calculate size from value string. + //! \return Pointer to allocated attribute. This pointer will never be NULL. + xml_attribute *allocate_attribute(const Ch *name = 0, const Ch *value = 0, + std::size_t name_size = 0, std::size_t value_size = 0) + { + void *memory = allocate_aligned(sizeof(xml_attribute)); + xml_attribute *attribute = new(memory) xml_attribute; + if (name) + { + if (name_size > 0) + attribute->name(name, name_size); + else + attribute->name(name); + } + if (value) + { + if (value_size > 0) + attribute->value(value, value_size); + else + attribute->value(value); + } + return attribute; + } + + //! Allocates a char array of given size from the pool, and optionally copies a given string to it. + //! If the allocation request cannot be accomodated, this function will throw std::bad_alloc. + //! If exceptions are disabled by defining RAPIDXML_NO_EXCEPTIONS, this function + //! will call rapidxml::parse_error_handler() function. + //! \param source String to initialize the allocated memory with, or 0 to not initialize it. + //! \param size Number of characters to allocate, or zero to calculate it automatically from source string length; if size is 0, source string must be specified and null terminated. + //! \return Pointer to allocated char array. This pointer will never be NULL. + Ch *allocate_string(const Ch *source = 0, std::size_t size = 0) + { + assert(source || size); // Either source or size (or both) must be specified + if (size == 0) + size = internal::measure(source) + 1; + Ch *result = static_cast(allocate_aligned(size * sizeof(Ch))); + if (source) + for (std::size_t i = 0; i < size; ++i) + result[i] = source[i]; + return result; + } + + //! Clones an xml_node and its hierarchy of child nodes and attributes. + //! Nodes and attributes are allocated from this memory pool. + //! Names and values are not cloned, they are shared between the clone and the source. + //! Result node can be optionally specified as a second parameter, + //! in which case its contents will be replaced with cloned source node. + //! This is useful when you want to clone entire document. + //! \param source Node to clone. + //! \param result Node to put results in, or 0 to automatically allocate result node + //! \return Pointer to cloned node. This pointer will never be NULL. + xml_node *clone_node(const xml_node *source, xml_node *result = 0) + { + // Prepare result node + if (result) + { + result->remove_all_attributes(); + result->remove_all_nodes(); + result->type(source->type()); + } + else + result = allocate_node(source->type()); + + // Clone name and value + result->name(source->name(), source->name_size()); + result->value(source->value(), source->value_size()); + + // Clone child nodes and attributes + for (xml_node *child = source->first_node(); child; child = child->next_sibling()) + result->append_node(clone_node(child)); + for (xml_attribute *attr = source->first_attribute(); attr; attr = attr->next_attribute()) + result->append_attribute(allocate_attribute(attr->name(), attr->value(), attr->name_size(), attr->value_size())); + + return result; + } + + //! Clears the pool. + //! This causes memory occupied by nodes allocated by the pool to be freed. + //! Any nodes or strings allocated from the pool will no longer be valid. + void clear() + { + while (m_begin != m_static_memory) + { + char *previous_begin = reinterpret_cast
    (align(m_begin))->previous_begin; + if (m_free_func) + m_free_func(m_begin); + else + delete[] m_begin; + m_begin = previous_begin; + } + init(); + } + + //! Sets or resets the user-defined memory allocation functions for the pool. + //! This can only be called when no memory is allocated from the pool yet, otherwise results are undefined. + //! Allocation function must not return invalid pointer on failure. It should either throw, + //! stop the program, or use longjmp() function to pass control to other place of program. + //! If it returns invalid pointer, results are undefined. + //!

    + //! User defined allocation functions must have the following forms: + //!
    + //!
    void *allocate(std::size_t size); + //!
    void free(void *pointer); + //!

    + //! \param af Allocation function, or 0 to restore default function + //! \param ff Free function, or 0 to restore default function + void set_allocator(alloc_func *af, free_func *ff) + { + assert(m_begin == m_static_memory && m_ptr == align(m_begin)); // Verify that no memory is allocated yet + m_alloc_func = af; + m_free_func = ff; + } + + private: + + struct header + { + char *previous_begin; + }; + + void init() + { + m_begin = m_static_memory; + m_ptr = align(m_begin); + m_end = m_static_memory + sizeof(m_static_memory); + } + + char *align(char *ptr) + { + std::size_t alignment = ((RAPIDXML_ALIGNMENT - (std::size_t(ptr) & (RAPIDXML_ALIGNMENT - 1))) & (RAPIDXML_ALIGNMENT - 1)); + return ptr + alignment; + } + + char *allocate_raw(std::size_t size) + { + // Allocate + void *memory; + if (m_alloc_func) // Allocate memory using either user-specified allocation function or global operator new[] + { + memory = m_alloc_func(size); + assert(memory); // Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp + } + else + { + memory = new char[size]; +#ifdef RAPIDXML_NO_EXCEPTIONS + if (!memory) // If exceptions are disabled, verify memory allocation, because new will not be able to throw bad_alloc + RAPIDXML_PARSE_ERROR("out of memory", 0); +#endif + } + return static_cast(memory); + } + + void *allocate_aligned(std::size_t size) + { + // Calculate aligned pointer + char *result = align(m_ptr); + + // If not enough memory left in current pool, allocate a new pool + if (result + size > m_end) + { + // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE) + std::size_t pool_size = RAPIDXML_DYNAMIC_POOL_SIZE; + if (pool_size < size) + pool_size = size; + + // Allocate + std::size_t alloc_size = sizeof(header) + (2 * RAPIDXML_ALIGNMENT - 2) + pool_size; // 2 alignments required in worst case: one for header, one for actual allocation + char *raw_memory = allocate_raw(alloc_size); + + // Setup new pool in allocated memory + char *pool = align(raw_memory); + header *new_header = reinterpret_cast
    (pool); + new_header->previous_begin = m_begin; + m_begin = raw_memory; + m_ptr = pool + sizeof(header); + m_end = raw_memory + alloc_size; + + // Calculate aligned pointer again using new pool + result = align(m_ptr); + } + + // Update pool and return aligned pointer + m_ptr = result + size; + return result; + } + + char *m_begin; // Start of raw memory making up current pool + char *m_ptr; // First free byte in current pool + char *m_end; // One past last available byte in current pool + char m_static_memory[RAPIDXML_STATIC_POOL_SIZE]; // Static raw memory + alloc_func *m_alloc_func; // Allocator function, or 0 if default is to be used + free_func *m_free_func; // Free function, or 0 if default is to be used + }; + + /////////////////////////////////////////////////////////////////////////// + // XML base + + //! Base class for xml_node and xml_attribute implementing common functions: + //! name(), name_size(), value(), value_size() and parent(). + //! \param Ch Character type to use + template + class xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + // Construct a base with empty name, value and parent + xml_base() + : m_name(0) + , m_value(0) + , m_parent(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets name of the node. + //! Interpretation of name depends on type of node. + //! Note that name will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

    + //! Use name_size() function to determine length of the name. + //! \return Name of node, or empty string if node has no name. + Ch *name() const + { + return m_name ? m_name : nullstr(); + } + + //! Gets size of node name, not including terminator character. + //! This function works correctly irrespective of whether name is or is not zero terminated. + //! \return Size of node name, in characters. + std::size_t name_size() const + { + return m_name ? m_name_size : 0; + } + + //! Gets value of node. + //! Interpretation of value depends on type of node. + //! Note that value will not be zero-terminated if rapidxml::parse_no_string_terminators option was selected during parse. + //!

    + //! Use value_size() function to determine length of the value. + //! \return Value of node, or empty string if node has no value. + Ch *value() const + { + return m_value ? m_value : nullstr(); + } + + //! Gets size of node value, not including terminator character. + //! This function works correctly irrespective of whether value is or is not zero terminated. + //! \return Size of node value, in characters. + std::size_t value_size() const + { + return m_value ? m_value_size : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets name of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

    + //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

    + //! Size of name must be specified separately, because name does not have to be zero terminated. + //! Use name(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //! \param name Name of node to set. Does not have to be zero terminated. + //! \param size Size of name, in characters. This does not include zero terminator, if one is present. + void name(const Ch *name, std::size_t size) + { + m_name = const_cast(name); + m_name_size = size; + } + + //! Sets name of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::name(const Ch *, std::size_t). + //! \param name Name of node to set. Must be zero terminated. + void name(const Ch *name) + { + this->name(name, internal::measure(name)); + } + + //! Sets value of node to a non zero-terminated string. + //! See \ref ownership_of_strings. + //!

    + //! Note that node does not own its name or value, it only stores a pointer to it. + //! It will not delete or otherwise free the pointer on destruction. + //! It is reponsibility of the user to properly manage lifetime of the string. + //! The easiest way to achieve it is to use memory_pool of the document to allocate the string - + //! on destruction of the document the string will be automatically freed. + //!

    + //! Size of value must be specified separately, because it does not have to be zero terminated. + //! Use value(const Ch *) function to have the length automatically calculated (string must be zero terminated). + //!

    + //! If an element has a child node of type node_data, it will take precedence over element value when printing. + //! If you want to manipulate data of elements using values, use parser flag rapidxml::parse_no_data_nodes to prevent creation of data nodes by the parser. + //! \param value value of node to set. Does not have to be zero terminated. + //! \param size Size of value, in characters. This does not include zero terminator, if one is present. + void value(const Ch *value, std::size_t size) + { + m_value = const_cast(value); + m_value_size = size; + } + + //! Sets value of node to a zero-terminated string. + //! See also \ref ownership_of_strings and xml_node::value(const Ch *, std::size_t). + //! \param value Vame of node to set. Must be zero terminated. + void value(const Ch *value) + { + this->value(value, internal::measure(value)); + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets node parent. + //! \return Pointer to parent node, or 0 if there is no parent. + xml_node *parent() const + { + return m_parent; + } + + protected: + + // Return empty string + static Ch *nullstr() + { + static Ch zero = Ch('\0'); + return &zero; + } + + Ch *m_name; // Name of node, or 0 if no name + Ch *m_value; // Value of node, or 0 if no value + std::size_t m_name_size; // Length of node name, or undefined of no name + std::size_t m_value_size; // Length of node value, or undefined if no value + xml_node *m_parent; // Pointer to parent node, or 0 if none + + }; + + //! Class representing attribute node of XML document. + //! Each attribute has name and value strings, which are available through name() and value() functions (inherited from xml_base). + //! Note that after parse, both name and value of attribute will point to interior of source text used for parsing. + //! Thus, this text must persist in memory for the lifetime of attribute. + //! \param Ch Character type to use. + template + class xml_attribute: public xml_base + { + + friend class xml_node; + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty attribute with the specified type. + //! Consider using memory_pool of appropriate xml_document if allocating attributes manually. + xml_attribute() + { + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which attribute is a child. + //! \return Pointer to document that contains this attribute, or 0 if there is no parent document. + xml_document *document() const + { + if (xml_node *node = this->parent()) + { + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + else + return 0; + } + + //! Gets previous attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return previous attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *previous_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_prev_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_prev_attribute : 0; + } + + //! Gets next attribute, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return next attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *next_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_next_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return this->m_parent ? m_next_attribute : 0; + } + + private: + + xml_attribute *m_prev_attribute; // Pointer to previous sibling of attribute, or 0 if none; only valid if parent is non-zero + xml_attribute *m_next_attribute; // Pointer to next sibling of attribute, or 0 if none; only valid if parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML node + + //! Class representing a node of XML document. + //! Each node may have associated name and value strings, which are available through name() and value() functions. + //! Interpretation of name and value depends on type of the node. + //! Type of node can be determined by using type() function. + //!

    + //! Note that after parse, both name and value of node, if any, will point interior of source text used for parsing. + //! Thus, this text must persist in the memory for the lifetime of node. + //! \param Ch Character type to use. + template + class xml_node: public xml_base + { + + public: + + /////////////////////////////////////////////////////////////////////////// + // Construction & destruction + + //! Constructs an empty node with the specified type. + //! Consider using memory_pool of appropriate document to allocate nodes manually. + //! \param type Type of node to construct. + xml_node(node_type type) + : m_type(type) + , m_first_node(0) + , m_first_attribute(0) + { + } + + /////////////////////////////////////////////////////////////////////////// + // Node data access + + //! Gets type of node. + //! \return Type of node. + node_type type() const + { + return m_type; + } + + /////////////////////////////////////////////////////////////////////////// + // Related nodes access + + //! Gets document of which node is a child. + //! \return Pointer to document that contains this node, or 0 if there is no parent document. + xml_document *document() const + { + xml_node *node = const_cast *>(this); + while (node->parent()) + node = node->parent(); + return node->type() == node_document ? static_cast *>(node) : 0; + } + + //! Gets first child node, optionally matching node name. + //! \param name Name of child to find, or 0 to return first child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *first_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_first_node; child; child = child->next_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_first_node; + } + + //! Gets last child node, optionally matching node name. + //! Behaviour is undefined if node has no children. + //! Use first_node() to test if node has children. + //! \param name Name of child to find, or 0 to return last child regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found child, or 0 if not found. + xml_node *last_node(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(m_first_node); // Cannot query for last child if node has no children + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *child = m_last_node; child; child = child->previous_sibling()) + if (internal::compare(child->name(), child->name_size(), name, name_size, case_sensitive)) + return child; + return 0; + } + else + return m_last_node; + } + + //! Gets previous sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return previous sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *previous_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_prev_sibling; sibling; sibling = sibling->m_prev_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_prev_sibling; + } + + //! Gets next sibling node, optionally matching node name. + //! Behaviour is undefined if node has no parent. + //! Use parent() to test if node has a parent. + //! \param name Name of sibling to find, or 0 to return next sibling regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found sibling, or 0 if not found. + xml_node *next_sibling(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + assert(this->m_parent); // Cannot query for siblings if node has no parent + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_node *sibling = m_next_sibling; sibling; sibling = sibling->m_next_sibling) + if (internal::compare(sibling->name(), sibling->name_size(), name, name_size, case_sensitive)) + return sibling; + return 0; + } + else + return m_next_sibling; + } + + //! Gets first attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return first attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *first_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_first_attribute; attribute; attribute = attribute->m_next_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute; + } + + //! Gets last attribute of node, optionally matching attribute name. + //! \param name Name of attribute to find, or 0 to return last attribute regardless of its name; this string doesn't have to be zero-terminated if name_size is non-zero + //! \param name_size Size of name, in characters, or 0 to have size calculated automatically from string + //! \param case_sensitive Should name comparison be case-sensitive; non case-sensitive comparison works properly only for ASCII characters + //! \return Pointer to found attribute, or 0 if not found. + xml_attribute *last_attribute(const Ch *name = 0, std::size_t name_size = 0, bool case_sensitive = true) const + { + if (name) + { + if (name_size == 0) + name_size = internal::measure(name); + for (xml_attribute *attribute = m_last_attribute; attribute; attribute = attribute->m_prev_attribute) + if (internal::compare(attribute->name(), attribute->name_size(), name, name_size, case_sensitive)) + return attribute; + return 0; + } + else + return m_first_attribute ? m_last_attribute : 0; + } + + /////////////////////////////////////////////////////////////////////////// + // Node modification + + //! Sets type of node. + //! \param type Type of node to set. + void type(node_type type) + { + m_type = type; + } + + /////////////////////////////////////////////////////////////////////////// + // Node manipulation + + //! Prepends a new child node. + //! The prepended child becomes the first child, and all existing children are moved one position back. + //! \param child Node to prepend. + void prepend_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_next_sibling = m_first_node; + m_first_node->m_prev_sibling = child; + } + else + { + child->m_next_sibling = 0; + m_last_node = child; + } + m_first_node = child; + child->m_parent = this; + child->m_prev_sibling = 0; + } + + //! Appends a new child node. + //! The appended child becomes the last child. + //! \param child Node to append. + void append_node(xml_node *child) + { + assert(child && !child->parent() && child->type() != node_document); + if (first_node()) + { + child->m_prev_sibling = m_last_node; + m_last_node->m_next_sibling = child; + } + else + { + child->m_prev_sibling = 0; + m_first_node = child; + } + m_last_node = child; + child->m_parent = this; + child->m_next_sibling = 0; + } + + //! Inserts a new child node at specified place inside the node. + //! All children after and including the specified node are moved one position back. + //! \param where Place where to insert the child, or 0 to insert at the back. + //! \param child Node to insert. + void insert_node(xml_node *where, xml_node *child) + { + assert(!where || where->parent() == this); + assert(child && !child->parent() && child->type() != node_document); + if (where == m_first_node) + prepend_node(child); + else if (where == 0) + append_node(child); + else + { + child->m_prev_sibling = where->m_prev_sibling; + child->m_next_sibling = where; + where->m_prev_sibling->m_next_sibling = child; + where->m_prev_sibling = child; + child->m_parent = this; + } + } + + //! Removes first child node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_first_node() + { + assert(first_node()); + xml_node *child = m_first_node; + m_first_node = child->m_next_sibling; + if (child->m_next_sibling) + child->m_next_sibling->m_prev_sibling = 0; + else + m_last_node = 0; + child->m_parent = 0; + } + + //! Removes last child of the node. + //! If node has no children, behaviour is undefined. + //! Use first_node() to test if node has children. + void remove_last_node() + { + assert(first_node()); + xml_node *child = m_last_node; + if (child->m_prev_sibling) + { + m_last_node = child->m_prev_sibling; + child->m_prev_sibling->m_next_sibling = 0; + } + else + m_first_node = 0; + child->m_parent = 0; + } + + //! Removes specified child from the node + // \param where Pointer to child to be removed. + void remove_node(xml_node *where) + { + assert(where && where->parent() == this); + assert(first_node()); + if (where == m_first_node) + remove_first_node(); + else if (where == m_last_node) + remove_last_node(); + else + { + where->m_prev_sibling->m_next_sibling = where->m_next_sibling; + where->m_next_sibling->m_prev_sibling = where->m_prev_sibling; + where->m_parent = 0; + } + } + + //! Removes all child nodes (but not attributes). + void remove_all_nodes() + { + for (xml_node *node = first_node(); node; node = node->m_next_sibling) + node->m_parent = 0; + m_first_node = 0; + } + + //! Prepends a new attribute to the node. + //! \param attribute Attribute to prepend. + void prepend_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_next_attribute = m_first_attribute; + m_first_attribute->m_prev_attribute = attribute; + } + else + { + attribute->m_next_attribute = 0; + m_last_attribute = attribute; + } + m_first_attribute = attribute; + attribute->m_parent = this; + attribute->m_prev_attribute = 0; + } + + //! Appends a new attribute to the node. + //! \param attribute Attribute to append. + void append_attribute(xml_attribute *attribute) + { + assert(attribute && !attribute->parent()); + if (first_attribute()) + { + attribute->m_prev_attribute = m_last_attribute; + m_last_attribute->m_next_attribute = attribute; + } + else + { + attribute->m_prev_attribute = 0; + m_first_attribute = attribute; + } + m_last_attribute = attribute; + attribute->m_parent = this; + attribute->m_next_attribute = 0; + } + + //! Inserts a new attribute at specified place inside the node. + //! All attributes after and including the specified attribute are moved one position back. + //! \param where Place where to insert the attribute, or 0 to insert at the back. + //! \param attribute Attribute to insert. + void insert_attribute(xml_attribute *where, xml_attribute *attribute) + { + assert(!where || where->parent() == this); + assert(attribute && !attribute->parent()); + if (where == m_first_attribute) + prepend_attribute(attribute); + else if (where == 0) + append_attribute(attribute); + else + { + attribute->m_prev_attribute = where->m_prev_attribute; + attribute->m_next_attribute = where; + where->m_prev_attribute->m_next_attribute = attribute; + where->m_prev_attribute = attribute; + attribute->m_parent = this; + } + } + + //! Removes first attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_first_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_first_attribute; + if (attribute->m_next_attribute) + { + attribute->m_next_attribute->m_prev_attribute = 0; + } + else + m_last_attribute = 0; + attribute->m_parent = 0; + m_first_attribute = attribute->m_next_attribute; + } + + //! Removes last attribute of the node. + //! If node has no attributes, behaviour is undefined. + //! Use first_attribute() to test if node has attributes. + void remove_last_attribute() + { + assert(first_attribute()); + xml_attribute *attribute = m_last_attribute; + if (attribute->m_prev_attribute) + { + attribute->m_prev_attribute->m_next_attribute = 0; + m_last_attribute = attribute->m_prev_attribute; + } + else + m_first_attribute = 0; + attribute->m_parent = 0; + } + + //! Removes specified attribute from node. + //! \param where Pointer to attribute to be removed. + void remove_attribute(xml_attribute *where) + { + assert(first_attribute() && where->parent() == this); + if (where == m_first_attribute) + remove_first_attribute(); + else if (where == m_last_attribute) + remove_last_attribute(); + else + { + where->m_prev_attribute->m_next_attribute = where->m_next_attribute; + where->m_next_attribute->m_prev_attribute = where->m_prev_attribute; + where->m_parent = 0; + } + } + + //! Removes all attributes of node. + void remove_all_attributes() + { + for (xml_attribute *attribute = first_attribute(); attribute; attribute = attribute->m_next_attribute) + attribute->m_parent = 0; + m_first_attribute = 0; + } + + private: + + /////////////////////////////////////////////////////////////////////////// + // Restrictions + + // No copying + xml_node(const xml_node &); + void operator =(const xml_node &); + + /////////////////////////////////////////////////////////////////////////// + // Data members + + // Note that some of the pointers below have UNDEFINED values if certain other pointers are 0. + // This is required for maximum performance, as it allows the parser to omit initialization of + // unneded/redundant values. + // + // The rules are as follows: + // 1. first_node and first_attribute contain valid pointers, or 0 if node has no children/attributes respectively + // 2. last_node and last_attribute are valid only if node has at least one child/attribute respectively, otherwise they contain garbage + // 3. prev_sibling and next_sibling are valid only if node has a parent, otherwise they contain garbage + + node_type m_type; // Type of node; always valid + xml_node *m_first_node; // Pointer to first child node, or 0 if none; always valid + xml_node *m_last_node; // Pointer to last child node, or 0 if none; this value is only valid if m_first_node is non-zero + xml_attribute *m_first_attribute; // Pointer to first attribute of node, or 0 if none; always valid + xml_attribute *m_last_attribute; // Pointer to last attribute of node, or 0 if none; this value is only valid if m_first_attribute is non-zero + xml_node *m_prev_sibling; // Pointer to previous sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + xml_node *m_next_sibling; // Pointer to next sibling of node, or 0 if none; this value is only valid if m_parent is non-zero + + }; + + /////////////////////////////////////////////////////////////////////////// + // XML document + + //! This class represents root of the DOM hierarchy. + //! It is also an xml_node and a memory_pool through public inheritance. + //! Use parse() function to build a DOM tree from a zero-terminated XML text string. + //! parse() function allocates memory for nodes and attributes by using functions of xml_document, + //! which are inherited from memory_pool. + //! To access root node of the document, use the document itself, as if it was an xml_node. + //! \param Ch Character type to use. + template + class xml_document: public xml_node, public memory_pool + { + + public: + + //! Constructs empty XML document + xml_document() + : xml_node(node_document) + { + } + + //! Parses zero-terminated XML string according to given flags. + //! Passed string will be modified by the parser, unless rapidxml::parse_non_destructive flag is used. + //! The string must persist for the lifetime of the document. + //! In case of error, rapidxml::parse_error exception will be thrown. + //!

    + //! If you want to parse contents of a file, you must first load the file into the memory, and pass pointer to its beginning. + //! Make sure that data is zero-terminated. + //!

    + //! Document can be parsed into multiple times. + //! Each new call to parse removes previous nodes and attributes (if any), but does not clear memory pool. + //! \param text XML data to parse; pointer is non-const to denote fact that this data may be modified by the parser. + template + void parse(Ch *text) + { + assert(text); + + // Remove current contents + this->remove_all_nodes(); + this->remove_all_attributes(); + + // Parse BOM, if any + parse_bom(text); + + // Parse children + while (1) + { + // Skip whitespace before node + skip(text); + if (*text == 0) + break; + + // Parse and append new child + if (*text == Ch('<')) + { + ++text; // Skip '<' + if (xml_node *node = parse_node(text)) + this->append_node(node); + } + else + RAPIDXML_PARSE_ERROR("expected <", text); + } + + } + + //! Clears the document by deleting all nodes and clearing the memory pool. + //! All nodes owned by document pool are destroyed. + void clear() + { + this->remove_all_nodes(); + this->remove_all_attributes(); + memory_pool::clear(); + } + + private: + + /////////////////////////////////////////////////////////////////////// + // Internal character utility functions + + // Detect whitespace character + struct whitespace_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_whitespace[static_cast(ch)]; + } + }; + + // Detect node name character + struct node_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_node_name[static_cast(ch)]; + } + }; + + // Detect attribute name character + struct attribute_name_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_attribute_name[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) + struct text_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_no_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_no_ws[static_cast(ch)]; + } + }; + + // Detect text character (PCDATA) that does not require processing + struct text_pure_with_ws_pred + { + static unsigned char test(Ch ch) + { + return internal::lookup_tables<0>::lookup_text_pure_with_ws[static_cast(ch)]; + } + }; + + // Detect attribute value character + template + struct attribute_value_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Detect attribute value character + template + struct attribute_value_pure_pred + { + static unsigned char test(Ch ch) + { + if (Quote == Ch('\'')) + return internal::lookup_tables<0>::lookup_attribute_data_1_pure[static_cast(ch)]; + if (Quote == Ch('\"')) + return internal::lookup_tables<0>::lookup_attribute_data_2_pure[static_cast(ch)]; + return 0; // Should never be executed, to avoid warnings on Comeau + } + }; + + // Insert coded character, using UTF8 or 8-bit ASCII + template + static void insert_coded_character(Ch *&text, unsigned long code) + { + if (Flags & parse_no_utf8) + { + // Insert 8-bit ASCII character + // Todo: possibly verify that code is less than 256 and use replacement char otherwise? + text[0] = static_cast(code); + text += 1; + } + else + { + // Insert UTF8 sequence + if (code < 0x80) // 1 byte sequence + { + text[0] = static_cast(code); + text += 1; + } + else if (code < 0x800) // 2 byte sequence + { + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xC0); + text += 2; + } + else if (code < 0x10000) // 3 byte sequence + { + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xE0); + text += 3; + } + else if (code < 0x110000) // 4 byte sequence + { + text[3] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[2] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[1] = static_cast((code | 0x80) & 0xBF); code >>= 6; + text[0] = static_cast(code | 0xF0); + text += 4; + } + else // Invalid, only codes up to 0x10FFFF are allowed in Unicode + { + RAPIDXML_PARSE_ERROR("invalid numeric character entity", text); + } + } + } + + // Skip characters until predicate evaluates to true + template + static void skip(Ch *&text) + { + Ch *tmp = text; + while (StopPred::test(*tmp)) + ++tmp; + text = tmp; + } + + // Skip characters until predicate evaluates to true while doing the following: + // - replacing XML character entity references with proper characters (' & " < > &#...;) + // - condensing whitespace sequences to single space character + template + static Ch *skip_and_expand_character_refs(Ch *&text) + { + // If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip + if (Flags & parse_no_entity_translation && + !(Flags & parse_normalize_whitespace) && + !(Flags & parse_trim_whitespace)) + { + skip(text); + return text; + } + + // Use simple skip until first modification is detected + skip(text); + + // Use translation skip + Ch *src = text; + Ch *dest = src; + while (StopPred::test(*src)) + { + // If entity translation is enabled + if (!(Flags & parse_no_entity_translation)) + { + // Test if replacement is needed + if (src[0] == Ch('&')) + { + switch (src[1]) + { + + // & ' + case Ch('a'): + if (src[2] == Ch('m') && src[3] == Ch('p') && src[4] == Ch(';')) + { + *dest = Ch('&'); + ++dest; + src += 5; + continue; + } + if (src[2] == Ch('p') && src[3] == Ch('o') && src[4] == Ch('s') && src[5] == Ch(';')) + { + *dest = Ch('\''); + ++dest; + src += 6; + continue; + } + break; + + // " + case Ch('q'): + if (src[2] == Ch('u') && src[3] == Ch('o') && src[4] == Ch('t') && src[5] == Ch(';')) + { + *dest = Ch('"'); + ++dest; + src += 6; + continue; + } + break; + + // > + case Ch('g'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('>'); + ++dest; + src += 4; + continue; + } + break; + + // < + case Ch('l'): + if (src[2] == Ch('t') && src[3] == Ch(';')) + { + *dest = Ch('<'); + ++dest; + src += 4; + continue; + } + break; + + // &#...; - assumes ASCII + case Ch('#'): + if (src[2] == Ch('x')) + { + unsigned long code = 0; + src += 3; // Skip &#x + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 16 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + else + { + unsigned long code = 0; + src += 2; // Skip &# + while (1) + { + unsigned char digit = internal::lookup_tables<0>::lookup_digits[static_cast(*src)]; + if (digit == 0xFF) + break; + code = code * 10 + digit; + ++src; + } + insert_coded_character(dest, code); // Put character in output + } + if (*src == Ch(';')) + ++src; + else + RAPIDXML_PARSE_ERROR("expected ;", src); + continue; + + // Something else + default: + // Ignore, just copy '&' verbatim + break; + + } + } + } + + // If whitespace condensing is enabled + if (Flags & parse_normalize_whitespace) + { + // Test if condensing is needed + if (whitespace_pred::test(*src)) + { + *dest = Ch(' '); ++dest; // Put single space in dest + ++src; // Skip first whitespace char + // Skip remaining whitespace chars + while (whitespace_pred::test(*src)) + ++src; + continue; + } + } + + // No replacement, only copy character + *dest++ = *src++; + + } + + // Return new end + text = src; + return dest; + + } + + /////////////////////////////////////////////////////////////////////// + // Internal parsing functions + + // Parse BOM, if any + template + void parse_bom(Ch *&text) + { + // UTF-8? + if (static_cast(text[0]) == 0xEF && + static_cast(text[1]) == 0xBB && + static_cast(text[2]) == 0xBF) + { + text += 3; // Skup utf-8 bom + } + } + + // Parse XML declaration ( + xml_node *parse_xml_declaration(Ch *&text) + { + // If parsing of declaration is disabled + if (!(Flags & parse_declaration_node)) + { + // Skip until end of declaration + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + + // Create declaration + xml_node *declaration = this->allocate_node(node_declaration); + + // Skip whitespace before attributes or ?> + skip(text); + + // Parse declaration attributes + parse_node_attributes(text, declaration); + + // Skip ?> + if (text[0] != Ch('?') || text[1] != Ch('>')) + RAPIDXML_PARSE_ERROR("expected ?>", text); + text += 2; + + return declaration; + } + + // Parse XML comment (' + return 0; // Do not produce comment node + } + + // Remember value start + Ch *value = text; + + // Skip until end of comment + while (text[0] != Ch('-') || text[1] != Ch('-') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create comment node + xml_node *comment = this->allocate_node(node_comment); + comment->value(value, text - value); + + // Place zero terminator after comment value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip '-->' + return comment; + } + + // Parse DOCTYPE + template + xml_node *parse_doctype(Ch *&text) + { + // Remember value start + Ch *value = text; + + // Skip to > + while (*text != Ch('>')) + { + // Determine character type + switch (*text) + { + + // If '[' encountered, scan for matching ending ']' using naive algorithm with depth + // This works for all W3C test files except for 2 most wicked + case Ch('['): + { + ++text; // Skip '[' + int depth = 1; + while (depth > 0) + { + switch (*text) + { + case Ch('['): ++depth; break; + case Ch(']'): --depth; break; + case 0: RAPIDXML_PARSE_ERROR("unexpected end of data", text); + } + ++text; + } + break; + } + + // Error on end of text + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Other character, skip it + default: + ++text; + + } + } + + // If DOCTYPE nodes enabled + if (Flags & parse_doctype_node) + { + // Create a new doctype node + xml_node *doctype = this->allocate_node(node_doctype); + doctype->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 1; // skip '>' + return doctype; + } + else + { + text += 1; // skip '>' + return 0; + } + + } + + // Parse PI + template + xml_node *parse_pi(Ch *&text) + { + // If creation of PI nodes is enabled + if (Flags & parse_pi_nodes) + { + // Create pi node + xml_node *pi = this->allocate_node(node_pi); + + // Extract PI target name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected PI target", text); + pi->name(name, text - name); + + // Skip whitespace between pi target and pi + skip(text); + + // Remember start of pi + Ch *value = text; + + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Set pi value (verbatim, no entity expansion or whitespace normalization) + pi->value(value, text - value); + + // Place zero terminator after name and value + if (!(Flags & parse_no_string_terminators)) + { + pi->name()[pi->name_size()] = Ch('\0'); + pi->value()[pi->value_size()] = Ch('\0'); + } + + text += 2; // Skip '?>' + return pi; + } + else + { + // Skip to '?>' + while (text[0] != Ch('?') || text[1] != Ch('>')) + { + if (*text == Ch('\0')) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 2; // Skip '?>' + return 0; + } + } + + // Parse and append data + // Return character that ends data. + // This is necessary because this character might have been overwritten by a terminating 0 + template + Ch parse_and_append_data(xml_node *node, Ch *&text, Ch *contents_start) + { + // Backup to contents start if whitespace trimming is disabled + if (!(Flags & parse_trim_whitespace)) + text = contents_start; + + // Skip until end of data + Ch *value = text, *end; + if (Flags & parse_normalize_whitespace) + end = skip_and_expand_character_refs(text); + else + end = skip_and_expand_character_refs(text); + + // Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after > + if (Flags & parse_trim_whitespace) + { + if (Flags & parse_normalize_whitespace) + { + // Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end + if (*(end - 1) == Ch(' ')) + --end; + } + else + { + // Backup until non-whitespace character is found + while (whitespace_pred::test(*(end - 1))) + --end; + } + } + + // If characters are still left between end and value (this test is only necessary if normalization is enabled) + // Create new data node + if (!(Flags & parse_no_data_nodes)) + { + xml_node *data = this->allocate_node(node_data); + data->value(value, end - value); + node->append_node(data); + } + + // Add data to parent node if no data exists yet + if (!(Flags & parse_no_element_values)) + if (*node->value() == Ch('\0')) + node->value(value, end - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + { + Ch ch = *text; + *end = Ch('\0'); + return ch; // Return character that ends data; this is required because zero terminator overwritten it + } + + // Return character that ends data + return *text; + } + + // Parse CDATA + template + xml_node *parse_cdata(Ch *&text) + { + // If CDATA is disabled + if (Flags & parse_no_data_nodes) + { + // Skip until end of cdata + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + text += 3; // Skip ]]> + return 0; // Do not produce CDATA node + } + + // Skip until end of cdata + Ch *value = text; + while (text[0] != Ch(']') || text[1] != Ch(']') || text[2] != Ch('>')) + { + if (!text[0]) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + + // Create new cdata node + xml_node *cdata = this->allocate_node(node_cdata); + cdata->value(value, text - value); + + // Place zero terminator after value + if (!(Flags & parse_no_string_terminators)) + *text = Ch('\0'); + + text += 3; // Skip ]]> + return cdata; + } + + // Parse element node + template + xml_node *parse_element(Ch *&text) + { + // Create element node + xml_node *element = this->allocate_node(node_element); + + // Extract element name + Ch *name = text; + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected element name", text); + element->name(name, text - name); + + // Skip whitespace between element name and attributes or > + skip(text); + + // Parse attributes, if any + parse_node_attributes(text, element); + + // Determine ending type + if (*text == Ch('>')) + { + ++text; + parse_node_contents(text, element); + } + else if (*text == Ch('/')) + { + ++text; + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; + } + else + RAPIDXML_PARSE_ERROR("expected >", text); + + // Place zero terminator after name + if (!(Flags & parse_no_string_terminators)) + element->name()[element->name_size()] = Ch('\0'); + + // Return parsed element + return element; + } + + // Determine node type, and parse it + template + xml_node *parse_node(Ch *&text) + { + // Parse proper node type + switch (text[0]) + { + + // <... + default: + // Parse and append element node + return parse_element(text); + + // (text); + } + else + { + // Parse PI + return parse_pi(text); + } + + // (text); + } + break; + + // (text); + } + break; + + // (text); + } + + } // switch + + // Attempt to skip other, unrecognized node types starting with ')) + { + if (*text == 0) + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + ++text; + } + ++text; // Skip '>' + return 0; // No node recognized + + } + } + + // Parse contents of the node - children, data etc. + template + void parse_node_contents(Ch *&text, xml_node *node) + { + // For all children and text + while (1) + { + // Skip whitespace between > and node contents + Ch *contents_start = text; // Store start of node contents before whitespace is skipped + skip(text); + Ch next_char = *text; + + // After data nodes, instead of continuing the loop, control jumps here. + // This is because zero termination inside parse_and_append_data() function + // would wreak havoc with the above code. + // Also, skipping whitespace after data nodes is unnecessary. + after_data_node: + + // Determine what comes next: node closing, child node, data node, or 0? + switch (next_char) + { + + // Node closing or child node + case Ch('<'): + if (text[1] == Ch('/')) + { + // Node closing + text += 2; // Skip '(text); + if (!internal::compare(node->name(), node->name_size(), closing_name, text - closing_name, true)) + RAPIDXML_PARSE_ERROR("invalid closing tag name", text); + } + else + { + // No validation, just skip name + skip(text); + } + // Skip remaining whitespace after node name + skip(text); + if (*text != Ch('>')) + RAPIDXML_PARSE_ERROR("expected >", text); + ++text; // Skip '>' + return; // Node closed, finished parsing contents + } + else + { + // Child node + ++text; // Skip '<' + if (xml_node *child = parse_node(text)) + node->append_node(child); + } + break; + + // End of data - error + case Ch('\0'): + RAPIDXML_PARSE_ERROR("unexpected end of data", text); + + // Data node + default: + next_char = parse_and_append_data(node, text, contents_start); + goto after_data_node; // Bypass regular processing after data nodes + + } + } + } + + // Parse XML attributes of the node + template + void parse_node_attributes(Ch *&text, xml_node *node) + { + // For all attributes + while (attribute_name_pred::test(*text)) + { + // Extract attribute name + Ch *name = text; + ++text; // Skip first character of attribute name + skip(text); + if (text == name) + RAPIDXML_PARSE_ERROR("expected attribute name", name); + + // Create new attribute + xml_attribute *attribute = this->allocate_attribute(); + attribute->name(name, text - name); + node->append_attribute(attribute); + + // Skip whitespace after attribute name + skip(text); + + // Skip = + if (*text != Ch('=')) + RAPIDXML_PARSE_ERROR("expected =", text); + ++text; + + // Add terminating zero after name + if (!(Flags & parse_no_string_terminators)) + attribute->name()[attribute->name_size()] = 0; + + // Skip whitespace after = + skip(text); + + // Skip quote and remember if it was ' or " + Ch quote = *text; + if (quote != Ch('\'') && quote != Ch('"')) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; + + // Extract attribute value and expand char refs in it + Ch *value = text, *end; + const int AttFlags = Flags & ~parse_normalize_whitespace; // No whitespace normalization in attributes + if (quote == Ch('\'')) + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + else + end = skip_and_expand_character_refs, attribute_value_pure_pred, AttFlags>(text); + + // Set attribute value + attribute->value(value, end - value); + + // Make sure that end quote is present + if (*text != quote) + RAPIDXML_PARSE_ERROR("expected ' or \"", text); + ++text; // Skip quote + + // Add terminating zero after value + if (!(Flags & parse_no_string_terminators)) + attribute->value()[attribute->value_size()] = 0; + + // Skip whitespace after attribute value + skip(text); + } + } + + }; + + //! \cond internal + namespace internal + { + + // Whitespace (space \n \r \t) + template + const unsigned char lookup_tables::lookup_whitespace[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F + }; + + // Node name (anything but space \n \r \t / > ? \0) + template + const unsigned char lookup_tables::lookup_node_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) (anything but < \0) + template + const unsigned char lookup_tables::lookup_text[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled + // (anything but < \0 &) + template + const unsigned char lookup_tables::lookup_text_pure_no_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled + // (anything but < \0 & space \n \r \t) + template + const unsigned char lookup_tables::lookup_text_pure_with_ws[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute name (anything but space \n \r \t / < > = ? ! \0) + template + const unsigned char lookup_tables::lookup_attribute_name[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote (anything but ' \0) + template + const unsigned char lookup_tables::lookup_attribute_data_1[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with single quote that does not require processing (anything but ' \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_1_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote (anything but " \0) + template + const unsigned char lookup_tables::lookup_attribute_data_2[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Attribute data with double quote that does not require processing (anything but " \0 &) + template + const unsigned char lookup_tables::lookup_attribute_data_2_pure[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 + 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F + }; + + // Digits (dec and hex, 255 denotes end of numeric character reference) + template + const unsigned char lookup_tables::lookup_digits[256] = + { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 + 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F + }; + + // Upper case conversion + template + const unsigned char lookup_tables::lookup_upcase[256] = + { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A B C D E F + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 0 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, // 1 + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 2 + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 3 + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 4 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, // 5 + 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, // 6 + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127, // 7 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 8 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 9 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // A + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // B + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // C + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // D + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // E + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // F + }; + } + //! \endcond + +} + +// Undefine internal macros +#undef RAPIDXML_PARSE_ERROR + +// On MSVC, restore warnings state +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif diff --git a/src/lib/rapidxml/rapidxml.pro b/src/lib/rapidxml/rapidxml.pro new file mode 100644 index 0000000..f0278fd --- /dev/null +++ b/src/lib/rapidxml/rapidxml.pro @@ -0,0 +1,7 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + rapidxml.hpp \ + rapidxml_iterators.hpp \ + rapidxml_print.hpp \ + rapidxml_utils.hpp diff --git a/src/lib/rapidxml/rapidxml_iterators.hpp b/src/lib/rapidxml/rapidxml_iterators.hpp new file mode 100644 index 0000000..52ebc29 --- /dev/null +++ b/src/lib/rapidxml/rapidxml_iterators.hpp @@ -0,0 +1,174 @@ +#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED +#define RAPIDXML_ITERATORS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_iterators.hpp This file contains rapidxml iterators + +#include "rapidxml.hpp" + +namespace rapidxml +{ + + //! Iterator of child nodes of xml_node + template + class node_iterator + { + + public: + + typedef typename xml_node value_type; + typedef typename xml_node &reference; + typedef typename xml_node *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + node_iterator() + : m_node(0) + { + } + + node_iterator(xml_node *node) + : m_node(node->first_node()) + { + } + + reference operator *() const + { + assert(m_node); + return *m_node; + } + + pointer operator->() const + { + assert(m_node); + return m_node; + } + + node_iterator& operator++() + { + assert(m_node); + m_node = m_node->next_sibling(); + return *this; + } + + node_iterator operator++(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + node_iterator& operator--() + { + assert(m_node && m_node->previous_sibling()); + m_node = m_node->previous_sibling(); + return *this; + } + + node_iterator operator--(int) + { + node_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const node_iterator &rhs) + { + return m_node == rhs.m_node; + } + + bool operator !=(const node_iterator &rhs) + { + return m_node != rhs.m_node; + } + + private: + + xml_node *m_node; + + }; + + //! Iterator of child attributes of xml_node + template + class attribute_iterator + { + + public: + + typedef typename xml_attribute value_type; + typedef typename xml_attribute &reference; + typedef typename xml_attribute *pointer; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + attribute_iterator() + : m_attribute(0) + { + } + + attribute_iterator(xml_node *node) + : m_attribute(node->first_attribute()) + { + } + + reference operator *() const + { + assert(m_attribute); + return *m_attribute; + } + + pointer operator->() const + { + assert(m_attribute); + return m_attribute; + } + + attribute_iterator& operator++() + { + assert(m_attribute); + m_attribute = m_attribute->next_attribute(); + return *this; + } + + attribute_iterator operator++(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + attribute_iterator& operator--() + { + assert(m_attribute && m_attribute->previous_attribute()); + m_attribute = m_attribute->previous_attribute(); + return *this; + } + + attribute_iterator operator--(int) + { + attribute_iterator tmp = *this; + ++this; + return tmp; + } + + bool operator ==(const attribute_iterator &rhs) + { + return m_attribute == rhs.m_attribute; + } + + bool operator !=(const attribute_iterator &rhs) + { + return m_attribute != rhs.m_attribute; + } + + private: + + xml_attribute *m_attribute; + + }; + +} + +#endif diff --git a/src/lib/rapidxml/rapidxml_print.hpp b/src/lib/rapidxml/rapidxml_print.hpp new file mode 100644 index 0000000..0ae2b14 --- /dev/null +++ b/src/lib/rapidxml/rapidxml_print.hpp @@ -0,0 +1,421 @@ +#ifndef RAPIDXML_PRINT_HPP_INCLUDED +#define RAPIDXML_PRINT_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_print.hpp This file contains rapidxml printer implementation + +#include "rapidxml.hpp" + +// Only include streams if not disabled +#ifndef RAPIDXML_NO_STREAMS + #include + #include +#endif + +namespace rapidxml +{ + + /////////////////////////////////////////////////////////////////////// + // Printing flags + + const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function. + + /////////////////////////////////////////////////////////////////////// + // Internal + + //! \cond internal + namespace internal + { + + /////////////////////////////////////////////////////////////////////////// + // Internal character operations + + // Copy characters from given range to given output iterator + template + inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out) + { + while (begin != end) + *out++ = *begin++; + return out; + } + + // Copy characters from given range to given output iterator and expand + // characters into references (< > ' " &) + template + inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out) + { + while (begin != end) + { + if (*begin == noexpand) + { + *out++ = *begin; // No expansion, copy character + } + else + { + switch (*begin) + { + case Ch('<'): + *out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('>'): + *out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('\''): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';'); + break; + case Ch('"'): + *out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';'); + break; + case Ch('&'): + *out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';'); + break; + default: + *out++ = *begin; // No expansion, copy character + } + } + ++begin; // Step to next character + } + return out; + } + + // Fill given output iterator with repetitions of the same character + template + inline OutIt fill_chars(OutIt out, int n, Ch ch) + { + for (int i = 0; i < n; ++i) + *out++ = ch; + return out; + } + + // Find character + template + inline bool find_char(const Ch *begin, const Ch *end) + { + while (begin != end) + if (*begin++ == ch) + return true; + return false; + } + + /////////////////////////////////////////////////////////////////////////// + // Internal printing operations + + // Print node + template + inline OutIt print_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print proper node type + switch (node->type()) + { + + // Document + case node_document: + out = print_children(out, node, flags, indent); + break; + + // Element + case node_element: + out = print_element_node(out, node, flags, indent); + break; + + // Data + case node_data: + out = print_data_node(out, node, flags, indent); + break; + + // CDATA + case node_cdata: + out = print_cdata_node(out, node, flags, indent); + break; + + // Declaration + case node_declaration: + out = print_declaration_node(out, node, flags, indent); + break; + + // Comment + case node_comment: + out = print_comment_node(out, node, flags, indent); + break; + + // Doctype + case node_doctype: + out = print_doctype_node(out, node, flags, indent); + break; + + // Pi + case node_pi: + out = print_pi_node(out, node, flags, indent); + break; + + // Unknown + default: + assert(0); + break; + } + + // If indenting not disabled, add line break after node + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + + // Return modified iterator + return out; + } + + // Print children of the node + template + inline OutIt print_children(OutIt out, const xml_node *node, int flags, int indent) + { + for (xml_node *child = node->first_node(); child; child = child->next_sibling()) + out = print_node(out, child, flags, indent); + return out; + } + + // Print attributes of the node + template + inline OutIt print_attributes(OutIt out, const xml_node *node, int flags) + { + for (xml_attribute *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute()) + { + if (attribute->name() && attribute->value()) + { + // Print attribute name + *out = Ch(' '), ++out; + out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out); + *out = Ch('='), ++out; + // Print attribute value using appropriate quote type + if (find_char(attribute->value(), attribute->value() + attribute->value_size())) + { + *out = Ch('\''), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out); + *out = Ch('\''), ++out; + } + else + { + *out = Ch('"'), ++out; + out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out); + *out = Ch('"'), ++out; + } + } + } + return out; + } + + // Print data node + template + inline OutIt print_data_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_data); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + return out; + } + + // Print data node + template + inline OutIt print_cdata_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_cdata); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'); ++out; + *out = Ch('!'); ++out; + *out = Ch('['); ++out; + *out = Ch('C'); ++out; + *out = Ch('D'); ++out; + *out = Ch('A'); ++out; + *out = Ch('T'); ++out; + *out = Ch('A'); ++out; + *out = Ch('['); ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch(']'); ++out; + *out = Ch(']'); ++out; + *out = Ch('>'); ++out; + return out; + } + + // Print element node + template + inline OutIt print_element_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_element); + + // Print element name and attributes, if any + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + out = print_attributes(out, node, flags); + + // If node is childless + if (node->value_size() == 0 && !node->first_node()) + { + // Print childless node tag ending + *out = Ch('/'), ++out; + *out = Ch('>'), ++out; + } + else + { + // Print normal node tag ending + *out = Ch('>'), ++out; + + // Test if node contains a single data node only (and no other nodes) + xml_node *child = node->first_node(); + if (!child) + { + // If node has no children, only print its value without indenting + out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out); + } + else if (child->next_sibling() == 0 && child->type() == node_data) + { + // If node has a sole data child, only print its value without indenting + out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out); + } + else + { + // Print all children with full indenting + if (!(flags & print_no_indenting)) + *out = Ch('\n'), ++out; + out = print_children(out, node, flags, indent + 1); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + } + + // Print node end + *out = Ch('<'), ++out; + *out = Ch('/'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch('>'), ++out; + } + return out; + } + + // Print declaration node + template + inline OutIt print_declaration_node(OutIt out, const xml_node *node, int flags, int indent) + { + // Print declaration start + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + *out = Ch('x'), ++out; + *out = Ch('m'), ++out; + *out = Ch('l'), ++out; + + // Print attributes + out = print_attributes(out, node, flags); + + // Print declaration end + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + + return out; + } + + // Print comment node + template + inline OutIt print_comment_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_comment); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('-'), ++out; + *out = Ch('-'), ++out; + *out = Ch('>'), ++out; + return out; + } + + // Print doctype node + template + inline OutIt print_doctype_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_doctype); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('!'), ++out; + *out = Ch('D'), ++out; + *out = Ch('O'), ++out; + *out = Ch('C'), ++out; + *out = Ch('T'), ++out; + *out = Ch('Y'), ++out; + *out = Ch('P'), ++out; + *out = Ch('E'), ++out; + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('>'), ++out; + return out; + } + + // Print pi node + template + inline OutIt print_pi_node(OutIt out, const xml_node *node, int flags, int indent) + { + assert(node->type() == node_pi); + if (!(flags & print_no_indenting)) + out = fill_chars(out, indent, Ch('\t')); + *out = Ch('<'), ++out; + *out = Ch('?'), ++out; + out = copy_chars(node->name(), node->name() + node->name_size(), out); + *out = Ch(' '), ++out; + out = copy_chars(node->value(), node->value() + node->value_size(), out); + *out = Ch('?'), ++out; + *out = Ch('>'), ++out; + return out; + } + + } + //! \endcond + + /////////////////////////////////////////////////////////////////////////// + // Printing + + //! Prints XML to given output iterator. + //! \param out Output iterator to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output iterator pointing to position immediately after last character of printed text. + template + inline OutIt print(OutIt out, const xml_node &node, int flags = 0) + { + return internal::print_node(out, &node, flags, 0); + } + +#ifndef RAPIDXML_NO_STREAMS + + //! Prints XML to given output stream. + //! \param out Output stream to print to. + //! \param node Node to be printed. Pass xml_document to print entire document. + //! \param flags Flags controlling how XML is printed. + //! \return Output stream. + template + inline std::basic_ostream &print(std::basic_ostream &out, const xml_node &node, int flags = 0) + { + print(std::ostream_iterator(out), node, flags); + return out; + } + + //! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process. + //! \param out Output stream to print to. + //! \param node Node to be printed. + //! \return Output stream. + template + inline std::basic_ostream &operator <<(std::basic_ostream &out, const xml_node &node) + { + return print(out, node); + } + +#endif + +} + +#endif diff --git a/src/lib/rapidxml/rapidxml_utils.hpp b/src/lib/rapidxml/rapidxml_utils.hpp new file mode 100644 index 0000000..37c2953 --- /dev/null +++ b/src/lib/rapidxml/rapidxml_utils.hpp @@ -0,0 +1,122 @@ +#ifndef RAPIDXML_UTILS_HPP_INCLUDED +#define RAPIDXML_UTILS_HPP_INCLUDED + +// Copyright (C) 2006, 2009 Marcin Kalicinski +// Version 1.13 +// Revision $DateTime: 2009/05/13 01:46:17 $ +//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful +//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective. + +#include "rapidxml.hpp" +#include +#include +#include +#include + +namespace rapidxml +{ + + //! Represents data loaded from a file + template + class file + { + + public: + + //! Loads file into the memory. Data will be automatically destroyed by the destructor. + //! \param filename Filename to load. + file(const char *filename) + { + using namespace std; + + // Open stream + basic_ifstream stream(filename, ios::binary); + if (!stream) + throw runtime_error(string("cannot open file ") + filename); + stream.unsetf(ios::skipws); + + // Determine stream size + stream.seekg(0, ios::end); + size_t size = stream.tellg(); + stream.seekg(0); + + // Load data and add terminating 0 + m_data.resize(size + 1); + stream.read(&m_data.front(), static_cast(size)); + m_data[size] = 0; + } + + //! Loads file into the memory. Data will be automatically destroyed by the destructor + //! \param stream Stream to load from + file(std::basic_istream &stream) + { + using namespace std; + + // Load data and add terminating 0 + stream.unsetf(ios::skipws); + m_data.assign(istreambuf_iterator(stream), istreambuf_iterator()); + if (stream.fail() || stream.bad()) + throw runtime_error("error reading stream"); + m_data.push_back(0); + } + + //! Gets file data. + //! \return Pointer to data of file. + Ch *data() + { + return &m_data.front(); + } + + //! Gets file data. + //! \return Pointer to data of file. + const Ch *data() const + { + return &m_data.front(); + } + + //! Gets file data size. + //! \return Size of file data, in characters. + std::size_t size() const + { + return m_data.size(); + } + + private: + + std::vector m_data; // File data + + }; + + //! Counts children of node. Time complexity is O(n). + //! \return Number of children of node + template + inline std::size_t count_children(xml_node *node) + { + xml_node *child = node->first_node(); + std::size_t count = 0; + while (child) + { + ++count; + child = child->next_sibling(); + } + return count; + } + + //! Counts attributes of node. Time complexity is O(n). + //! \return Number of attributes of node + template + inline std::size_t count_attributes(xml_node *node) + { + xml_attribute *attr = node->first_attribute(); + std::size_t count = 0; + while (attr) + { + ++count; + attr = attr->next_attribute(); + } + return count; + } + +} + +#endif diff --git a/src/lib/zlib/README.md b/src/lib/zlib/README.md new file mode 100644 index 0000000..a3bae84 --- /dev/null +++ b/src/lib/zlib/README.md @@ -0,0 +1 @@ +zlib from qt library 4.8.1 src\3rdparty\zlib \ No newline at end of file diff --git a/src/lib/zlib/zlib.pro b/src/lib/zlib/zlib.pro new file mode 100644 index 0000000..1b4e6e4 --- /dev/null +++ b/src/lib/zlib/zlib.pro @@ -0,0 +1,35 @@ +TEMPLATE = lib + +CONFIG += warn_on #to replace with "warn_off" after stable + +QT += core + +#DEFINES += ZLIB_DLL +DEFINES += QT_MAKEDLL + +CONFIG(debug, debug|release){ + TARGET = zlib_d + DESTDIR = $$PWD/../../debug +} else { + TARGET = zlib + DESTDIR = ../../release +} + +INCLUDEPATH += $$PWD/zlib + +SOURCES+= \ + $$PWD/zlib/adler32.c \ + $$PWD/zlib/compress.c \ + $$PWD/zlib/crc32.c \ + $$PWD/zlib/deflate.c \ + $$PWD/zlib/gzclose.c \ + $$PWD/zlib/gzlib.c \ + $$PWD/zlib/gzread.c \ + $$PWD/zlib/gzwrite.c \ + $$PWD/zlib/infback.c \ + $$PWD/zlib/inffast.c \ + $$PWD/zlib/inflate.c \ + $$PWD/zlib/inftrees.c \ + $$PWD/zlib/trees.c \ + $$PWD/zlib/uncompr.c \ + $$PWD/zlib/zutil.c diff --git a/src/lib/zlib/zlib/adler32.c b/src/lib/zlib/zlib/adler32.c new file mode 100644 index 0000000..65ad6a5 --- /dev/null +++ b/src/lib/zlib/zlib/adler32.c @@ -0,0 +1,169 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2007 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#define local static + +local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/src/lib/zlib/zlib/compress.c b/src/lib/zlib/zlib/compress.c new file mode 100644 index 0000000..ea4dfbe --- /dev/null +++ b/src/lib/zlib/zlib/compress.c @@ -0,0 +1,80 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13; +} diff --git a/src/lib/zlib/zlib/crc32.c b/src/lib/zlib/zlib/crc32.c new file mode 100644 index 0000000..91be372 --- /dev/null +++ b/src/lib/zlib/zlib/crc32.c @@ -0,0 +1,442 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/src/lib/zlib/zlib/crc32.h b/src/lib/zlib/zlib/crc32.h new file mode 100644 index 0000000..8053b61 --- /dev/null +++ b/src/lib/zlib/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/src/lib/zlib/zlib/deflate.c b/src/lib/zlib/zlib/deflate.c new file mode 100644 index 0000000..5c4022f --- /dev/null +++ b/src/lib/zlib/zlib/deflate.c @@ -0,0 +1,1834 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local block_state deflate_rle OF((deflate_state *s, int flush)); +local block_state deflate_huff OF((deflate_state *s, int flush)); +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->high_water = 0; /* nothing written to s->window yet */ + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > s->w_size) { + length = s->w_size; + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if ((strategy != s->strategy || func != configuration_table[level].func) && + strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_BLOCK); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds for + * every combination of windowBits and memLevel. But even the conservative + * upper bound of about 14% expansion does not seem onerous for output buffer + * allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong complen, wraplen; + Bytef *str; + + /* conservative upper bound for compressed data */ + complen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; + + /* if can't get parameters, return conservative bound plus zlib wrapper */ + if (strm == Z_NULL || strm->state == Z_NULL) + return complen + 6; + + /* compute wrapper length */ + s = strm->state; + switch (s->wrap) { + case 0: /* raw deflate */ + wraplen = 0; + break; + case 1: /* zlib wrapper */ + wraplen = 6 + (s->strstart ? 4 : 0); + break; + case 2: /* gzip wrapper */ + wraplen = 18; + if (s->gzhead != Z_NULL) { /* user-supplied gzip header */ + if (s->gzhead->extra != Z_NULL) + wraplen += 2 + s->gzhead->extra_len; + str = s->gzhead->name; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + str = s->gzhead->comment; + if (str != Z_NULL) + do { + wraplen++; + } while (*str++); + if (s->gzhead->hcrc) + wraplen += 2; + } + break; + default: /* for compiler happiness */ + wraplen = 6; + } + + /* if not default parameters, return conservative bound */ + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return complen + wraplen; + + /* default settings: return tight bound for that case */ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + + (sourceLen >> 25) + 13 - 6 + wraplen; +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_BLOCK || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == Z_NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != Z_NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != Z_NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : + (s->strategy == Z_RLE ? deflate_rle(s, flush) : + (*(configuration_table[s->level].func))(s, flush)); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush != Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + if (s->lookahead == 0) { + s->strstart = 0; + s->block_start = 0L; + } + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ + +#else /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#endif /* FASTEST */ + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + if (s->high_water < s->window_size) { + ulg curr = s->strstart + (ulg)(s->lookahead); + ulg init; + + if (s->high_water < curr) { + /* Previous high water mark below current data -- zero WIN_INIT + * bytes or up to end of window, whichever is less. + */ + init = s->window_size - curr; + if (init > WIN_INIT) + init = WIN_INIT; + zmemzero(s->window + curr, (unsigned)init); + s->high_water = curr + init; + } + else if (s->high_water < (ulg)curr + WIN_INIT) { + /* High water mark at or above current data, but below current data + * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + * to end of window, whichever is less. + */ + init = (ulg)curr + WIN_INIT - s->high_water; + if (init > s->window_size - s->high_water) + init = s->window_size - s->high_water; + zmemzero(s->window + s->high_water, (unsigned)init); + s->high_water += init; + } + } +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, last) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (last)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, last) { \ + FLUSH_BLOCK_ONLY(s, last); \ + if (s->strm->avail_out == 0) return (last) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = NIL; + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s->match_length = longest_match (s, hash_head); + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt prev; /* byte at distance one to match */ + Bytef *scan, *strend; /* scan goes up to strend for length of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s->match_length = 0; + if (s->lookahead >= MIN_MATCH && s->strstart > 0) { + scan = s->window + s->strstart - 1; + prev = *scan; + if (prev == *++scan && prev == *++scan && prev == *++scan) { + strend = s->window + s->strstart + MAX_MATCH; + do { + } while (prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + prev == *++scan && prev == *++scan && + scan < strend); + s->match_length = MAX_MATCH - (int)(strend - scan); + if (s->match_length > s->lookahead) + s->match_length = s->lookahead; + } + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, s->match_length); + + _tr_tally_dist(s, 1, s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + s->strstart += s->match_length; + s->match_length = 0; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +local block_state deflate_huff(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s->lookahead == 0) { + fill_window(s); + if (s->lookahead == 0) { + if (flush == Z_NO_FLUSH) + return need_more; + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s->match_length = 0; + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} diff --git a/src/lib/zlib/zlib/deflate.h b/src/lib/zlib/zlib/deflate.h new file mode 100644 index 0000000..cbf0d1e --- /dev/null +++ b/src/lib/zlib/zlib/deflate.h @@ -0,0 +1,342 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2010 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + ulg high_water; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + +#define WIN_INIT MAX_MATCH +/* Number of bytes after end of data in window to initialize in order to avoid + memory checker errors from longest match routines */ + + /* in trees.c */ +void ZLIB_INTERNAL _tr_init OF((deflate_state *s)); +int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); +void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, + ulg stored_len, int last)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch ZLIB_INTERNAL _length_code[]; + extern uch ZLIB_INTERNAL _dist_code[]; +#else + extern const uch ZLIB_INTERNAL _length_code[]; + extern const uch ZLIB_INTERNAL _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/src/lib/zlib/zlib/example.c b/src/lib/zlib/zlib/example.c new file mode 100644 index 0000000..604736f --- /dev/null +++ b/src/lib/zlib/zlib/example.c @@ -0,0 +1,565 @@ +/* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2006 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zlib.h" +#include + +#ifdef STDC +# include +# include +#endif + +#if defined(VMS) || defined(RISCOS) +# define TESTFILE "foo-gz" +#else +# define TESTFILE "foo.gz" +#endif + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char hello[] = "hello, hello!"; +/* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + */ + +const char dictionary[] = "hello"; +uLong dictId; /* Adler32 value of the dictionary */ + +void test_compress OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_gzio OF((const char *fname, + Byte *uncompr, uLong uncomprLen)); +void test_deflate OF((Byte *compr, uLong comprLen)); +void test_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_deflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_flush OF((Byte *compr, uLong *comprLen)); +void test_sync OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_dict_deflate OF((Byte *compr, uLong comprLen)); +void test_dict_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Test compress() and uncompress() + */ +void test_compress(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + uLong len = (uLong)strlen(hello)+1; + + err = compress(compr, &comprLen, (const Bytef*)hello, len); + CHECK_ERR(err, "compress"); + + strcpy((char*)uncompr, "garbage"); + + err = uncompress(uncompr, &uncomprLen, compr, comprLen); + CHECK_ERR(err, "uncompress"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad uncompress\n"); + exit(1); + } else { + printf("uncompress(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test read/write of .gz files + */ +void test_gzio(fname, uncompr, uncomprLen) + const char *fname; /* compressed file name */ + Byte *uncompr; + uLong uncomprLen; +{ +#ifdef NO_GZCOMPRESS + fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); +#else + int err; + int len = (int)strlen(hello)+1; + gzFile file; + z_off_t pos; + + file = gzopen(fname, "wb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + gzputc(file, 'h'); + if (gzputs(file, "ello") != 4) { + fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); + exit(1); + } + if (gzprintf(file, ", %s!", "hello") != 8) { + fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); + exit(1); + } + gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ + gzclose(file); + + file = gzopen(fname, "rb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + strcpy((char*)uncompr, "garbage"); + + if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { + fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); + exit(1); + } else { + printf("gzread(): %s\n", (char*)uncompr); + } + + pos = gzseek(file, -8L, SEEK_CUR); + if (pos != 6 || gztell(file) != pos) { + fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", + (long)pos, (long)gztell(file)); + exit(1); + } + + if (gzgetc(file) != ' ') { + fprintf(stderr, "gzgetc error\n"); + exit(1); + } + + if (gzungetc(' ', file) != ' ') { + fprintf(stderr, "gzungetc error\n"); + exit(1); + } + + gzgets(file, (char*)uncompr, (int)uncomprLen); + if (strlen((char*)uncompr) != 7) { /* " hello!" */ + fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello + 6)) { + fprintf(stderr, "bad gzgets after gzseek\n"); + exit(1); + } else { + printf("gzgets() after gzseek: %s\n", (char*)uncompr); + } + + gzclose(file); +#endif +} + +/* =========================================================================== + * Test deflate() with small buffers + */ +void test_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uLong len = (uLong)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + + while (c_stream.total_in != len && c_stream.total_out < comprLen) { + c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + } + /* Finish the stream, still forcing small buffers: */ + for (;;) { + c_stream.avail_out = 1; + err = deflate(&c_stream, Z_FINISH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "deflate"); + } + + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with small buffers + */ +void test_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 0; + d_stream.next_out = uncompr; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate\n"); + exit(1); + } else { + printf("inflate(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test deflate() with large buffers and dynamic change of compression level + */ +void test_large_deflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_SPEED); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + /* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + */ + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + if (c_stream.avail_in != 0) { + fprintf(stderr, "deflate not greedy\n"); + exit(1); + } + + /* Feed in already compressed data and switch to no compression: */ + deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in = compr; + c_stream.avail_in = (uInt)comprLen/2; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + /* Switch back to compressing mode: */ + deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with large buffers + */ +void test_large_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + for (;;) { + d_stream.next_out = uncompr; /* discard the output */ + d_stream.avail_out = (uInt)uncomprLen; + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "large inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (d_stream.total_out != 2*uncomprLen + comprLen/2) { + fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); + exit(1); + } else { + printf("large_inflate(): OK\n"); + } +} + +/* =========================================================================== + * Test deflate() with full flush + */ +void test_flush(compr, comprLen) + Byte *compr; + uLong *comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uInt len = (uInt)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + c_stream.avail_in = 3; + c_stream.avail_out = (uInt)*comprLen; + err = deflate(&c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, "deflate"); + + compr[3]++; /* force an error in first compressed block */ + c_stream.avail_in = len - 3; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + CHECK_ERR(err, "deflate"); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); + + *comprLen = c_stream.total_out; +} + +/* =========================================================================== + * Test inflateSync() + */ +void test_sync(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 2; /* just read the zlib header */ + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + inflate(&d_stream, Z_NO_FLUSH); + CHECK_ERR(err, "inflate"); + + d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ + err = inflateSync(&d_stream); /* but skip the damaged part */ + CHECK_ERR(err, "inflateSync"); + + err = inflate(&d_stream, Z_FINISH); + if (err != Z_DATA_ERROR) { + fprintf(stderr, "inflate should report DATA_ERROR\n"); + /* Because of incorrect adler32 */ + exit(1); + } + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + printf("after inflateSync(): hel%s\n", (char *)uncompr); +} + +/* =========================================================================== + * Test deflate() with preset dictionary + */ +void test_dict_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + err = deflateSetDictionary(&c_stream, + (const Bytef*)dictionary, sizeof(dictionary)); + CHECK_ERR(err, "deflateSetDictionary"); + + dictId = c_stream.adler; + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + c_stream.next_in = (Bytef*)hello; + c_stream.avail_in = (uInt)strlen(hello)+1; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with a preset dictionary + */ +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + if (err == Z_NEED_DICT) { + if (d_stream.adler != dictId) { + fprintf(stderr, "unexpected dictionary"); + exit(1); + } + err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, + sizeof(dictionary)); + } + CHECK_ERR(err, "inflate with dict"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate with dict\n"); + exit(1); + } else { + printf("inflate with dictionary: %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Usage: example [output.gz [input.gz]] + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + Byte *compr, *uncompr; + uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ + uLong uncomprLen = comprLen; + static const char* myVersion = ZLIB_VERSION; + + if (zlibVersion()[0] != myVersion[0]) { + fprintf(stderr, "incompatible zlib version\n"); + exit(1); + + } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { + fprintf(stderr, "warning: different zlib version\n"); + } + + printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", + ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); + + compr = (Byte*)calloc((uInt)comprLen, 1); + uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + /* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + */ + if (compr == Z_NULL || uncompr == Z_NULL) { + printf("out of memory\n"); + exit(1); + } + test_compress(compr, comprLen, uncompr, uncomprLen); + + test_gzio((argc > 1 ? argv[1] : TESTFILE), + uncompr, uncomprLen); + + test_deflate(compr, comprLen); + test_inflate(compr, comprLen, uncompr, uncomprLen); + + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + + test_flush(compr, &comprLen); + test_sync(compr, comprLen, uncompr, uncomprLen); + comprLen = uncomprLen; + + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + + free(compr); + free(uncompr); + + return 0; +} diff --git a/src/lib/zlib/zlib/gzclose.c b/src/lib/zlib/zlib/gzclose.c new file mode 100644 index 0000000..caeb99a --- /dev/null +++ b/src/lib/zlib/zlib/gzclose.c @@ -0,0 +1,25 @@ +/* gzclose.c -- zlib gzclose() function + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* gzclose() is in a separate file so that it is linked in only if it is used. + That way the other gzclose functions can be used instead to avoid linking in + unneeded compression or decompression routines. */ +int ZEXPORT gzclose(file) + gzFile file; +{ +#ifndef NO_GZCOMPRESS + gz_statep state; + + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + return state->mode == GZ_READ ? gzclose_r(file) : gzclose_w(file); +#else + return gzclose_r(file); +#endif +} diff --git a/src/lib/zlib/zlib/gzguts.h b/src/lib/zlib/zlib/gzguts.h new file mode 100644 index 0000000..9e4368e --- /dev/null +++ b/src/lib/zlib/zlib/gzguts.h @@ -0,0 +1,149 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _MSC_VER +# ifndef _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# ifndef _CRT_NONSTDC_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +# endif +#endif + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#include "qconfig.h" +#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) && defined(QT_VISIBILITY_AVAILABLE) +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif +#if !defined(_WIN32_WCE) +# include +#else +# include +# include +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifdef _MSC_VER +# if !defined(_WIN32_WCE) +# include +# endif +# define vsnprintf _vsnprintf +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifdef STDC +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default i/o buffer size -- double this for output when reading */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + z_off64_t pos; /* current position in uncompressed data */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer */ + unsigned char *out; /* output buffer (double-sized when reading) */ + unsigned char *next; /* next output data to deliver or write */ + /* just for reading */ + unsigned have; /* amount of output data unused at next */ + int eof; /* true if end of input file reached */ + z_off64_t start; /* where the gzip data started, for rewinding */ + z_off64_t raw; /* where the raw data started, for seeking */ + int how; /* 0: get header, 1: copy, 2: decompress */ + int direct; /* true if last read direct, false if gzip */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/src/lib/zlib/zlib/gzlib.c b/src/lib/zlib/zlib/gzlib.c new file mode 100644 index 0000000..603e60e --- /dev/null +++ b/src/lib/zlib/zlib/gzlib.c @@ -0,0 +1,537 @@ +/* gzlib.c -- zlib functions common to reading and writing gzip files + * Copyright (C) 2004, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define LSEEK lseek64 +#else +# define LSEEK lseek +#endif + +/* Local functions */ +local void gz_reset OF((gz_statep)); +local gzFile gz_open OF((const char *, int, const char *)); + +#if defined UNDER_CE + +/* Map the Windows error number in ERROR to a locale-dependent error message + string and return a pointer to it. Typically, the values for ERROR come + from GetLastError. + + The string pointed to shall not be modified by the application, but may be + overwritten by a subsequent call to gz_strwinerror + + The gz_strwinerror function does not change the current setting of + GetLastError. */ +char ZLIB_INTERNAL *gz_strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +#endif /* UNDER_CE */ + +/* Reset gzip file state */ +local void gz_reset(state) + gz_statep state; +{ + if (state->mode == GZ_READ) { /* for reading ... */ + state->have = 0; /* no output data available */ + state->eof = 0; /* not at end of file */ + state->how = LOOK; /* look for gzip header */ + state->direct = 1; /* default for empty file */ + } + state->seek = 0; /* no seek request pending */ + gz_error(state, Z_OK, NULL); /* clear error */ + state->pos = 0; /* no uncompressed data yet */ + state->strm.avail_in = 0; /* no input data yet */ +} + +/* Open a gzip file either by name or file descriptor. */ +local gzFile gz_open(path, fd, mode) + const char *path; + int fd; + const char *mode; +{ + gz_statep state; + + /* allocate gzFile structure to return */ + state = malloc(sizeof(gz_state)); + if (state == NULL) + return NULL; + state->size = 0; /* no buffers allocated yet */ + state->want = GZBUFSIZE; /* requested buffer size */ + state->msg = NULL; /* no error message yet */ + + /* interpret mode */ + state->mode = GZ_NONE; + state->level = Z_DEFAULT_COMPRESSION; + state->strategy = Z_DEFAULT_STRATEGY; + while (*mode) { + if (*mode >= '0' && *mode <= '9') + state->level = *mode - '0'; + else + switch (*mode) { + case 'r': + state->mode = GZ_READ; + break; +#ifndef NO_GZCOMPRESS + case 'w': + state->mode = GZ_WRITE; + break; + case 'a': + state->mode = GZ_APPEND; + break; +#endif + case '+': /* can't read and write at the same time */ + free(state); + return NULL; + case 'b': /* ignore -- will request binary anyway */ + break; + case 'f': + state->strategy = Z_FILTERED; + break; + case 'h': + state->strategy = Z_HUFFMAN_ONLY; + break; + case 'R': + state->strategy = Z_RLE; + break; + case 'F': + state->strategy = Z_FIXED; + default: /* could consider as an error, but just ignore */ + ; + } + mode++; + } + + /* must provide an "r", "w", or "a" */ + if (state->mode == GZ_NONE) { + free(state); + return NULL; + } + + /* save the path name for error messages */ + state->path = malloc(strlen(path) + 1); + if (state->path == NULL) { + free(state); + return NULL; + } + strcpy(state->path, path); + + /* open the file with the appropriate mode (or just use fd) */ + state->fd = fd != -1 ? fd : + open(path, +#ifdef O_LARGEFILE + O_LARGEFILE | +#endif +#ifdef O_BINARY + O_BINARY | +#endif + (state->mode == GZ_READ ? + O_RDONLY : + (O_WRONLY | O_CREAT | ( + state->mode == GZ_WRITE ? + O_TRUNC : + O_APPEND))), + 0666); + if (state->fd == -1) { + free(state->path); + free(state); + return NULL; + } + if (state->mode == GZ_APPEND) + state->mode = GZ_WRITE; /* simplify later checks */ + + /* save the current position for rewinding (only if reading) */ + if (state->mode == GZ_READ) { + state->start = LSEEK(state->fd, 0, SEEK_CUR); + if (state->start == -1) state->start = 0; + } + + /* initialize stream */ + gz_reset(state); + + /* return stream */ + return (gzFile)state; +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzopen64(path, mode) + const char *path; + const char *mode; +{ + return gz_open(path, -1, mode); +} + +/* -- see zlib.h -- */ +gzFile ZEXPORT gzdopen(fd, mode) + int fd; + const char *mode; +{ + char *path; /* identifier for error messages */ + gzFile gz; + + if (fd == -1 || (path = malloc(7 + 3 * sizeof(int))) == NULL) + return NULL; + sprintf(path, "", fd); /* for debugging */ + gz = gz_open(path, fd, mode); + free(path); + return gz; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzbuffer(file, size) + gzFile file; + unsigned size; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* make sure we haven't already allocated memory */ + if (state->size != 0) + return -1; + + /* check and set requested size */ + if (size == 0) + return -1; + state->want = size; + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzrewind(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* back up and start over */ + if (LSEEK(state->fd, state->start, SEEK_SET) == -1) + return -1; + gz_reset(state); + return 0; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzseek64(file, offset, whence) + gzFile file; + z_off64_t offset; + int whence; +{ + unsigned n; + z_off64_t ret; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* check that there's no error */ + if (state->err != Z_OK) + return -1; + + /* can only seek from start or relative to current position */ + if (whence != SEEK_SET && whence != SEEK_CUR) + return -1; + + /* normalize offset to a SEEK_CUR specification */ + if (whence == SEEK_SET) + offset -= state->pos; + else if (state->seek) + offset += state->skip; + state->seek = 0; + + /* if within raw area while reading, just go there */ + if (state->mode == GZ_READ && state->how == COPY && + state->pos + offset >= state->raw) { + ret = LSEEK(state->fd, offset - state->have, SEEK_CUR); + if (ret == -1) + return -1; + state->have = 0; + state->eof = 0; + state->seek = 0; + gz_error(state, Z_OK, NULL); + state->strm.avail_in = 0; + state->pos += offset; + return state->pos; + } + + /* calculate skip amount, rewinding if needed for back seek when reading */ + if (offset < 0) { + if (state->mode != GZ_READ) /* writing -- can't go backwards */ + return -1; + offset += state->pos; + if (offset < 0) /* before start of file! */ + return -1; + if (gzrewind(file) == -1) /* rewind, then skip to offset */ + return -1; + } + + /* if reading, skip what's in output buffer (one less gzgetc() check) */ + if (state->mode == GZ_READ) { + n = GT_OFF(state->have) || (z_off64_t)state->have > offset ? + (unsigned)offset : state->have; + state->have -= n; + state->next += n; + state->pos += n; + offset -= n; + } + + /* request skip (if not zero) */ + if (offset) { + state->seek = 1; + state->skip = offset; + } + return state->pos + offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzseek(file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + z_off64_t ret; + + ret = gzseek64(file, (z_off64_t)offset, whence); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gztell64(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* return position */ + return state->pos + (state->seek ? state->skip : 0); +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gztell(file) + gzFile file; +{ + z_off64_t ret; + + ret = gztell64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +z_off64_t ZEXPORT gzoffset64(file) + gzFile file; +{ + z_off64_t offset; + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return -1; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return -1; + + /* compute and return effective offset in file */ + offset = LSEEK(state->fd, 0, SEEK_CUR); + if (offset == -1) + return -1; + if (state->mode == GZ_READ) /* reading */ + offset -= state->strm.avail_in; /* don't count buffered input */ + return offset; +} + +/* -- see zlib.h -- */ +z_off_t ZEXPORT gzoffset(file) + gzFile file; +{ + z_off64_t ret; + + ret = gzoffset64(file); + return ret == (z_off_t)ret ? (z_off_t)ret : -1; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzeof(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return 0; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return 0; + + /* return end-of-file state */ + return state->mode == GZ_READ ? + (state->eof && state->strm.avail_in == 0 && state->have == 0) : 0; +} + +/* -- see zlib.h -- */ +const char * ZEXPORT gzerror(file, errnum) + gzFile file; + int *errnum; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return NULL; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return NULL; + + /* return error information */ + if (errnum != NULL) + *errnum = state->err; + return state->msg == NULL ? "" : state->msg; +} + +/* -- see zlib.h -- */ +void ZEXPORT gzclearerr(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure and check integrity */ + if (file == NULL) + return; + state = (gz_statep)file; + if (state->mode != GZ_READ && state->mode != GZ_WRITE) + return; + + /* clear error and end-of-file */ + if (state->mode == GZ_READ) + state->eof = 0; + gz_error(state, Z_OK, NULL); +} + +/* Create an error message in allocated memory and set state->err and + state->msg accordingly. Free any previous error message already there. Do + not try to free or allocate space if the error is Z_MEM_ERROR (out of + memory). Simply save the error message as a static string. If there is an + allocation failure constructing the error message, then convert the error to + out of memory. */ +void ZLIB_INTERNAL gz_error(state, err, msg) + gz_statep state; + int err; + const char *msg; +{ + /* free previously allocated message and clear */ + if (state->msg != NULL) { + if (state->err != Z_MEM_ERROR) + free(state->msg); + state->msg = NULL; + } + + /* set error code, and if no message, then done */ + state->err = err; + if (msg == NULL) + return; + + /* for an out of memory error, save as static string */ + if (err == Z_MEM_ERROR) { + state->msg = (char *)msg; + return; + } + + /* construct error message with path */ + if ((state->msg = malloc(strlen(state->path) + strlen(msg) + 3)) == NULL) { + state->err = Z_MEM_ERROR; + state->msg = (char *)"out of memory"; + return; + } + strcpy(state->msg, state->path); + strcat(state->msg, ": "); + strcat(state->msg, msg); + return; +} + +#ifndef INT_MAX +/* portably return maximum value for an int (when limits.h presumed not + available) -- we need to do this to cover cases where 2's complement not + used, since C standard permits 1's complement and sign-bit representations, + otherwise we could just use ((unsigned)-1) >> 1 */ +unsigned ZLIB_INTERNAL gz_intmax() +{ + unsigned p, q; + + p = 1; + do { + q = p; + p <<= 1; + p++; + } while (p > q); + return q >> 1; +} +#endif diff --git a/src/lib/zlib/zlib/gzread.c b/src/lib/zlib/zlib/gzread.c new file mode 100644 index 0000000..548201a --- /dev/null +++ b/src/lib/zlib/zlib/gzread.c @@ -0,0 +1,653 @@ +/* gzread.c -- zlib functions for reading gzip files + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_load OF((gz_statep, unsigned char *, unsigned, unsigned *)); +local int gz_avail OF((gz_statep)); +local int gz_next4 OF((gz_statep, unsigned long *)); +local int gz_head OF((gz_statep)); +local int gz_decomp OF((gz_statep)); +local int gz_make OF((gz_statep)); +local int gz_skip OF((gz_statep, z_off64_t)); + +/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from + state->fd, and update state->eof, state->err, and state->msg as appropriate. + This function needs to loop on read(), since read() is not guaranteed to + read the number of bytes requested, depending on the type of descriptor. */ +local int gz_load(state, buf, len, have) + gz_statep state; + unsigned char *buf; + unsigned len; + unsigned *have; +{ + int ret; + + *have = 0; + do { + ret = read(state->fd, buf + *have, len - *have); + if (ret <= 0) + break; + *have += ret; + } while (*have < len); + if (ret < 0) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (ret == 0) + state->eof = 1; + return 0; +} + +/* Load up input buffer and set eof flag if last data loaded -- return -1 on + error, 0 otherwise. Note that the eof flag is set when the end of the input + file is reached, even though there may be unused data in the buffer. Once + that data has been used, no more attempts will be made to read the file. + gz_avail() assumes that strm->avail_in == 0. */ +local int gz_avail(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + if (state->err != Z_OK) + return -1; + if (state->eof == 0) { + if (gz_load(state, state->in, state->size, + (unsigned *)&(strm->avail_in)) == -1) + return -1; + strm->next_in = state->in; + } + return 0; +} + +/* Get next byte from input, or -1 if end or error. */ +#define NEXT() ((strm->avail_in == 0 && gz_avail(state) == -1) ? -1 : \ + (strm->avail_in == 0 ? -1 : \ + (strm->avail_in--, *(strm->next_in)++))) + +/* Get a four-byte little-endian integer and return 0 on success and the value + in *ret. Otherwise -1 is returned and *ret is not modified. */ +local int gz_next4(state, ret) + gz_statep state; + unsigned long *ret; +{ + int ch; + unsigned long val; + z_streamp strm = &(state->strm); + + val = NEXT(); + val += (unsigned)NEXT() << 8; + val += (unsigned long)NEXT() << 16; + ch = NEXT(); + if (ch == -1) + return -1; + val += (unsigned long)ch << 24; + *ret = val; + return 0; +} + +/* Look for gzip header, set up for inflate or copy. state->have must be zero. + If this is the first time in, allocate required memory. state->how will be + left unchanged if there is no more input data available, will be set to COPY + if there is no gzip header and direct copying will be performed, or it will + be set to GZIP for decompression, and the gzip header will be skipped so + that the next available input data is the raw deflate stream. If direct + copying, then leftover input data from the input buffer will be copied to + the output buffer. In that case, all further file reads will be directly to + either the output buffer or a user buffer. If decompressing, the inflate + state and the check value will be initialized. gz_head() will return 0 on + success or -1 on failure. Failures may include read errors or gzip header + errors. */ +local int gz_head(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + int flags; + unsigned len; + + /* allocate read buffers and inflate memory */ + if (state->size == 0) { + /* allocate buffers */ + state->in = malloc(state->want); + state->out = malloc(state->want << 1); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + state->size = state->want; + + /* allocate inflate memory */ + state->strm.zalloc = Z_NULL; + state->strm.zfree = Z_NULL; + state->strm.opaque = Z_NULL; + state->strm.avail_in = 0; + state->strm.next_in = Z_NULL; + if (inflateInit2(&(state->strm), -15) != Z_OK) { /* raw inflate */ + free(state->out); + free(state->in); + state->size = 0; + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + } + + /* get some data in the input buffer */ + if (strm->avail_in == 0) { + if (gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) + return 0; + } + + /* look for the gzip magic header bytes 31 and 139 */ + if (strm->next_in[0] == 31) { + strm->avail_in--; + strm->next_in++; + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in && strm->next_in[0] == 139) { + /* we have a gzip header, woo hoo! */ + strm->avail_in--; + strm->next_in++; + + /* skip rest of header */ + if (NEXT() != 8) { /* compression method */ + gz_error(state, Z_DATA_ERROR, "unknown compression method"); + return -1; + } + flags = NEXT(); + if (flags & 0xe0) { /* reserved flag bits */ + gz_error(state, Z_DATA_ERROR, "unknown header flags set"); + return -1; + } + NEXT(); /* modification time */ + NEXT(); + NEXT(); + NEXT(); + NEXT(); /* extra flags */ + NEXT(); /* operating system */ + if (flags & 4) { /* extra field */ + len = (unsigned)NEXT(); + len += (unsigned)NEXT() << 8; + while (len--) + if (NEXT() < 0) + break; + } + if (flags & 8) /* file name */ + while (NEXT() > 0) + ; + if (flags & 16) /* comment */ + while (NEXT() > 0) + ; + if (flags & 2) { /* header crc */ + NEXT(); + NEXT(); + } + /* an unexpected end of file is not checked for here -- it will be + noticed on the first request for uncompressed data */ + + /* set up for decompression */ + inflateReset(strm); + strm->adler = crc32(0L, Z_NULL, 0); + state->how = GZIP; + state->direct = 0; + return 0; + } + else { + /* not a gzip file -- save first byte (31) and fall to raw i/o */ + state->out[0] = 31; + state->have = 1; + } + } + + /* doing raw i/o, save start of raw data for seeking, copy any leftover + input to output -- this assumes that the output buffer is larger than + the input buffer, which also assures space for gzungetc() */ + state->raw = state->pos; + state->next = state->out; + if (strm->avail_in) { + memcpy(state->next + state->have, strm->next_in, strm->avail_in); + state->have += strm->avail_in; + strm->avail_in = 0; + } + state->how = COPY; + state->direct = 1; + return 0; +} + +/* Decompress from input to the provided next_out and avail_out in the state. + If the end of the compressed data is reached, then verify the gzip trailer + check value and length (modulo 2^32). state->have and state->next are set + to point to the just decompressed data, and the crc is updated. If the + trailer is verified, state->how is reset to LOOK to look for the next gzip + stream or raw data, once state->have is depleted. Returns 0 on success, -1 + on failure. Failures may include invalid compressed data or a failed gzip + trailer verification. */ +local int gz_decomp(state) + gz_statep state; +{ + int ret; + unsigned had; + unsigned long crc, len; + z_streamp strm = &(state->strm); + + /* fill output buffer up to end of deflate stream */ + had = strm->avail_out; + do { + /* get more input for inflate() */ + if (strm->avail_in == 0 && gz_avail(state) == -1) + return -1; + if (strm->avail_in == 0) { + gz_error(state, Z_DATA_ERROR, "unexpected end of file"); + return -1; + } + + /* decompress and handle errors */ + ret = inflate(strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) { + gz_error(state, Z_STREAM_ERROR, + "internal error: inflate stream corrupt"); + return -1; + } + if (ret == Z_MEM_ERROR) { + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + if (ret == Z_DATA_ERROR) { /* deflate stream invalid */ + gz_error(state, Z_DATA_ERROR, + strm->msg == NULL ? "compressed data error" : strm->msg); + return -1; + } + } while (strm->avail_out && ret != Z_STREAM_END); + + /* update available output and crc check value */ + state->have = had - strm->avail_out; + state->next = strm->next_out - state->have; + strm->adler = crc32(strm->adler, state->next, state->have); + + /* check gzip trailer if at end of deflate stream */ + if (ret == Z_STREAM_END) { + if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) { + gz_error(state, Z_DATA_ERROR, "unexpected end of file"); + return -1; + } + if (crc != strm->adler) { + gz_error(state, Z_DATA_ERROR, "incorrect data check"); + return -1; + } + if (len != (strm->total_out & 0xffffffffL)) { + gz_error(state, Z_DATA_ERROR, "incorrect length check"); + return -1; + } + state->how = LOOK; /* ready for next stream, once have is 0 (leave + state->direct unchanged to remember how) */ + } + + /* good decompression */ + return 0; +} + +/* Make data and put in the output buffer. Assumes that state->have == 0. + Data is either copied from the input file or decompressed from the input + file depending on state->how. If state->how is LOOK, then a gzip header is + looked for (and skipped if found) to determine wither to copy or decompress. + Returns -1 on error, otherwise 0. gz_make() will leave state->have as COPY + or GZIP unless the end of the input file has been reached and all data has + been processed. */ +local int gz_make(state) + gz_statep state; +{ + z_streamp strm = &(state->strm); + + if (state->how == LOOK) { /* look for gzip header */ + if (gz_head(state) == -1) + return -1; + if (state->have) /* got some data from gz_head() */ + return 0; + } + if (state->how == COPY) { /* straight copy */ + if (gz_load(state, state->out, state->size << 1, &(state->have)) == -1) + return -1; + state->next = state->out; + } + else if (state->how == GZIP) { /* decompress */ + strm->avail_out = state->size << 1; + strm->next_out = state->out; + if (gz_decomp(state) == -1) + return -1; + } + return 0; +} + +/* Skip len uncompressed bytes of output. Return -1 on error, 0 on success. */ +local int gz_skip(state, len) + gz_statep state; + z_off64_t len; +{ + unsigned n; + + /* skip over len bytes or reach end-of-file, whichever comes first */ + while (len) + /* skip over whatever is in output buffer */ + if (state->have) { + n = GT_OFF(state->have) || (z_off64_t)state->have > len ? + (unsigned)len : state->have; + state->have -= n; + state->next += n; + state->pos += n; + len -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) + break; + + /* need more data to skip -- load up output buffer */ + else { + /* get more output, looking for header if required */ + if (gz_make(state) == -1) + return -1; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzread(file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + unsigned got, n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); + return -1; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* get len bytes to buf, or less than len if at the end */ + got = 0; + do { + /* first just try copying data from the output buffer */ + if (state->have) { + n = state->have > len ? len : state->have; + memcpy(buf, state->next, n); + state->next += n; + state->have -= n; + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && strm->avail_in == 0) + break; + + /* need output data -- for small len or new stream load up our output + buffer */ + else if (state->how == LOOK || len < (state->size << 1)) { + /* get more output, looking for header if required */ + if (gz_make(state) == -1) + return -1; + continue; /* no progress yet -- go back to memcpy() above */ + /* the copy above assures that we will leave with space in the + output buffer, allowing at least one gzungetc() to succeed */ + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, buf, len, &n) == -1) + return -1; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + strm->avail_out = len; + strm->next_out = buf; + if (gz_decomp(state) == -1) + return -1; + n = state->have; + state->have = 0; + } + + /* update progress */ + len -= n; + buf = (char *)buf + n; + got += n; + state->pos += n; + } while (len); + + /* return number of bytes read into user buffer (will fit in int) */ + return (int)got; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzgetc(file) + gzFile file; +{ + int ret; + unsigned char buf[1]; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* try output buffer (no need to check for skip request) */ + if (state->have) { + state->have--; + state->pos++; + return *(state->next)++; + } + + /* nothing there -- try gzread() */ + ret = gzread(file, buf, 1); + return ret < 1 ? -1 : buf[0]; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return -1; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return -1; + } + + /* can't push EOF */ + if (c < 0) + return -1; + + /* if output buffer empty, put byte at end (allows more pushing) */ + if (state->have == 0) { + state->have = 1; + state->next = state->out + (state->size << 1) - 1; + state->next[0] = c; + state->pos--; + return c; + } + + /* if no room, give up (must have already done a gzungetc()) */ + if (state->have == (state->size << 1)) { + gz_error(state, Z_BUF_ERROR, "out of room to push characters"); + return -1; + } + + /* slide output data if needed and insert byte before existing data */ + if (state->next == state->out) { + unsigned char *src = state->out + state->have; + unsigned char *dest = state->out + (state->size << 1); + while (src > state->out) + *--dest = *--src; + state->next = dest; + } + state->have++; + state->next--; + state->next[0] = c; + state->pos--; + return c; +} + +/* -- see zlib.h -- */ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + unsigned left, n; + char *str; + unsigned char *eol; + gz_statep state; + + /* check parameters and get internal structure */ + if (file == NULL || buf == NULL || len < 1) + return NULL; + state = (gz_statep)file; + + /* check that we're reading and that there's no error */ + if (state->mode != GZ_READ || state->err != Z_OK) + return NULL; + + /* process a skip request */ + if (state->seek) { + state->seek = 0; + if (gz_skip(state, state->skip) == -1) + return NULL; + } + + /* copy output bytes up to new line or len - 1, whichever comes first -- + append a terminating zero to the string (we don't check for a zero in + the contents, let the user worry about that) */ + str = buf; + left = (unsigned)len - 1; + if (left) do { + /* assure that something is in the output buffer */ + if (state->have == 0) { + if (gz_make(state) == -1) + return NULL; /* error */ + if (state->have == 0) { /* end of file */ + if (buf == str) /* got bupkus */ + return NULL; + break; /* got something -- return it */ + } + } + + /* look for end-of-line in current output buffer */ + n = state->have > left ? left : state->have; + eol = memchr(state->next, '\n', n); + if (eol != NULL) + n = (unsigned)(eol - state->next) + 1; + + /* copy through end-of-line, or remainder if not found */ + memcpy(buf, state->next, n); + state->have -= n; + state->next += n; + state->pos += n; + left -= n; + buf += n; + } while (left && eol == NULL); + + /* found end-of-line or out of space -- terminate string and return it */ + buf[0] = 0; + return str; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzdirect(file) + gzFile file; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return 0; + + /* if the state is not known, but we can find out, then do so (this is + mainly for right after a gzopen() or gzdopen()) */ + if (state->how == LOOK && state->have == 0) + (void)gz_head(state); + + /* return 1 if reading direct, 0 if decompressing a gzip stream */ + return state->direct; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_r(file) + gzFile file; +{ + int ret; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're reading */ + if (state->mode != GZ_READ) + return Z_STREAM_ERROR; + + /* free memory and close file */ + if (state->size) { + inflateEnd(&(state->strm)); + free(state->out); + free(state->in); + } + gz_error(state, Z_OK, NULL); + free(state->path); + ret = close(state->fd); + free(state); + return ret ? Z_ERRNO : Z_OK; +} diff --git a/src/lib/zlib/zlib/gzwrite.c b/src/lib/zlib/zlib/gzwrite.c new file mode 100644 index 0000000..e8defc6 --- /dev/null +++ b/src/lib/zlib/zlib/gzwrite.c @@ -0,0 +1,531 @@ +/* gzwrite.c -- zlib functions for writing gzip files + * Copyright (C) 2004, 2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "gzguts.h" + +/* Local functions */ +local int gz_init OF((gz_statep)); +local int gz_comp OF((gz_statep, int)); +local int gz_zero OF((gz_statep, z_off64_t)); + +/* Initialize state for writing a gzip file. Mark initialization by setting + state->size to non-zero. Return -1 on failure or 0 on success. */ +local int gz_init(state) + gz_statep state; +{ + int ret; + z_streamp strm = &(state->strm); + + /* allocate input and output buffers */ + state->in = malloc(state->want); + state->out = malloc(state->want); + if (state->in == NULL || state->out == NULL) { + if (state->out != NULL) + free(state->out); + if (state->in != NULL) + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* allocate deflate memory, set up for gzip compression */ + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + ret = deflateInit2(strm, state->level, Z_DEFLATED, + 15 + 16, 8, state->strategy); + if (ret != Z_OK) { + free(state->in); + gz_error(state, Z_MEM_ERROR, "out of memory"); + return -1; + } + + /* mark state as initialized */ + state->size = state->want; + + /* initialize write buffer */ + strm->avail_out = state->size; + strm->next_out = state->out; + state->next = strm->next_out; + return 0; +} + +/* Compress whatever is at avail_in and next_in and write to the output file. + Return -1 if there is an error writing to the output file, otherwise 0. + flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, + then the deflate() state is reset to start a new gzip stream. */ +local int gz_comp(state, flush) + gz_statep state; + int flush; +{ + int ret, got; + unsigned have; + z_streamp strm = &(state->strm); + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return -1; + + /* run deflate() on provided input until it produces no more output */ + ret = Z_OK; + do { + /* write out current buffer contents if full, or if flushing, but if + doing Z_FINISH then don't write until we get to Z_STREAM_END */ + if (strm->avail_out == 0 || (flush != Z_NO_FLUSH && + (flush != Z_FINISH || ret == Z_STREAM_END))) { + have = (unsigned)(strm->next_out - state->next); + if (have && ((got = write(state->fd, state->next, have)) < 0 || + (unsigned)got != have)) { + gz_error(state, Z_ERRNO, zstrerror()); + return -1; + } + if (strm->avail_out == 0) { + strm->avail_out = state->size; + strm->next_out = state->out; + } + state->next = strm->next_out; + } + + /* compress */ + have = strm->avail_out; + ret = deflate(strm, flush); + if (ret == Z_STREAM_ERROR) { + gz_error(state, Z_STREAM_ERROR, + "internal error: deflate stream corrupt"); + return -1; + } + have -= strm->avail_out; + } while (have); + + /* if that completed a deflate stream, allow another to start */ + if (flush == Z_FINISH) + deflateReset(strm); + + /* all done, no errors */ + return 0; +} + +/* Compress len zeros to output. Return -1 on error, 0 on success. */ +local int gz_zero(state, len) + gz_statep state; + z_off64_t len; +{ + int first; + unsigned n; + z_streamp strm = &(state->strm); + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + + /* compress len zeros (len guaranteed > 0) */ + first = 1; + while (len) { + n = GT_OFF(state->size) || (z_off64_t)state->size > len ? + (unsigned)len : state->size; + if (first) { + memset(state->in, 0, n); + first = 0; + } + strm->avail_in = n; + strm->next_in = state->in; + state->pos += n; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return -1; + len -= n; + } + return 0; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzwrite(file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + unsigned put = len; + unsigned n; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return 0; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* since an int is returned, make sure len fits in one, otherwise return + with an error (this avoids the flaw in the interface) */ + if ((int)len < 0) { + gz_error(state, Z_BUF_ERROR, "requested length does not fit in int"); + return 0; + } + + /* if len is zero, avoid unnecessary operations */ + if (len == 0) + return 0; + + /* allocate memory if this is the first time through */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* for small len, copy to input buffer, otherwise compress directly */ + if (len < state->size) { + /* copy to input buffer, compress when full */ + do { + if (strm->avail_in == 0) + strm->next_in = state->in; + n = state->size - strm->avail_in; + if (n > len) + n = len; + memcpy(strm->next_in + strm->avail_in, buf, n); + strm->avail_in += n; + state->pos += n; + buf = (char *)buf + n; + len -= n; + if (len && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } while (len); + } + else { + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* directly compress user buffer to file */ + strm->avail_in = len; + strm->next_in = (voidp)buf; + state->pos += len; + if (gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + } + + /* input was all buffered or compressed (put will fit in int) */ + return (int)put; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char buf[1]; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return -1; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* try writing to input buffer for speed (state->size == 0 if buffer not + initialized) */ + if (strm->avail_in < state->size) { + if (strm->avail_in == 0) + strm->next_in = state->in; + strm->next_in[strm->avail_in++] = c; + state->pos++; + return c; + } + + /* no room in buffer or not initialized, use gz_write() */ + buf[0] = c; + if (gzwrite(file, buf, 1) != 1) + return -1; + return c; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzputs(file, str) + gzFile file; + const char *str; +{ + int ret; + unsigned len; + + /* write string */ + len = (unsigned)strlen(str); + ret = gzwrite(file, str, len); + return ret == 0 && len != 0 ? -1 : ret; +} + +#ifdef STDC +#include + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (gzFile file, const char *format, ...) +{ + int size, len; + gz_statep state; + z_streamp strm; + va_list va; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(state->in, format, va); + va_end(va); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = vsprintf(state->in, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(state->in, size, format, va); + va_end(va); + len = strlen(state->in); +# else + len = vsnprintf((char *)(state->in), size, format, va); + va_end(va); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->pos += len; + return len; +} + +#else /* !STDC */ + +/* -- see zlib.h -- */ +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + int size, len; + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return 0; + + /* make sure we have some buffer space */ + if (state->size == 0 && gz_init(state) == -1) + return 0; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return 0; + } + + /* consume whatever's left in the input buffer */ + if (strm->avail_in && gz_comp(state, Z_NO_FLUSH) == -1) + return 0; + + /* do the printf() into the input buffer, put length in len */ + size = (int)(state->size); + state->in[size - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < size; len++) + if (state->in[len] == 0) break; +# else + len = sprintf(state->in, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(state->in); +# else + len = snprintf(state->in, size, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + + /* check that printf() results fit in buffer */ + if (len <= 0 || len >= (int)size || state->in[size - 1] != 0) + return 0; + + /* update buffer and position, defer compression until needed */ + strm->avail_in = (unsigned)len; + strm->next_in = state->in; + state->pos += len; + return len; +} + +#endif + +/* -- see zlib.h -- */ +int ZEXPORT gzflush(file, flush) + gzFile file; + int flush; +{ + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return -1; + state = (gz_statep)file; + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* check flush parameter */ + if (flush < 0 || flush > Z_FINISH) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* compress remaining data with requested flush */ + gz_comp(state, flush); + return state->err; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzsetparams(file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_statep state; + z_streamp strm; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + strm = &(state->strm); + + /* check that we're writing and that there's no error */ + if (state->mode != GZ_WRITE || state->err != Z_OK) + return Z_STREAM_ERROR; + + /* if no change is requested, then do nothing */ + if (level == state->level && strategy == state->strategy) + return Z_OK; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + if (gz_zero(state, state->skip) == -1) + return -1; + } + + /* change compression parameters for subsequent input */ + if (state->size) { + /* flush previous input with previous parameters before changing */ + if (strm->avail_in && gz_comp(state, Z_PARTIAL_FLUSH) == -1) + return state->err; + deflateParams(strm, level, strategy); + } + state->level = level; + state->strategy = strategy; + return Z_OK; +} + +/* -- see zlib.h -- */ +int ZEXPORT gzclose_w(file) + gzFile file; +{ + int ret = 0; + gz_statep state; + + /* get internal structure */ + if (file == NULL) + return Z_STREAM_ERROR; + state = (gz_statep)file; + + /* check that we're writing */ + if (state->mode != GZ_WRITE) + return Z_STREAM_ERROR; + + /* check for seek request */ + if (state->seek) { + state->seek = 0; + ret += gz_zero(state, state->skip); + } + + /* flush, free memory, and close file */ + ret += gz_comp(state, Z_FINISH); + (void)deflateEnd(&(state->strm)); + free(state->out); + free(state->in); + gz_error(state, Z_OK, NULL); + free(state->path); + ret += close(state->fd); + free(state); + return ret ? Z_ERRNO : Z_OK; +} diff --git a/src/lib/zlib/zlib/infback.c b/src/lib/zlib/zlib/infback.c new file mode 100644 index 0000000..af3a8c9 --- /dev/null +++ b/src/lib/zlib/zlib/infback.c @@ -0,0 +1,632 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/src/lib/zlib/zlib/inffast.c b/src/lib/zlib/zlib/inffast.c new file mode 100644 index 0000000..2f1d60b --- /dev/null +++ b/src/lib/zlib/zlib/inffast.c @@ -0,0 +1,340 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2008, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + PUP(out) = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + PUP(out) = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + PUP(out) = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + PUP(out) = PUP(from); + } while (--len); + continue; + } +#endif + } + from = window - OFF; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/src/lib/zlib/zlib/inffast.h b/src/lib/zlib/zlib/inffast.h new file mode 100644 index 0000000..e5c1aa4 --- /dev/null +++ b/src/lib/zlib/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/src/lib/zlib/zlib/inffixed.h b/src/lib/zlib/zlib/inffixed.h new file mode 100644 index 0000000..75ed4b5 --- /dev/null +++ b/src/lib/zlib/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/src/lib/zlib/zlib/inflate.c b/src/lib/zlib/zlib/inflate.c new file mode 100644 index 0000000..a8431ab --- /dev/null +++ b/src/lib/zlib/zlib/inflate.c @@ -0,0 +1,1480 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->window = Z_NULL; + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + else if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + NEEDBITS(here.bits); + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->sane = !subvert; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + return Z_OK; +#else + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return -1L << 16; + state = (struct inflate_state FAR *)strm->state; + return ((long)(state->back) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} diff --git a/src/lib/zlib/zlib/inflate.h b/src/lib/zlib/zlib/inflate.h new file mode 100644 index 0000000..95f4986 --- /dev/null +++ b/src/lib/zlib/zlib/inflate.h @@ -0,0 +1,122 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2009 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 10K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/src/lib/zlib/zlib/inftrees.c b/src/lib/zlib/zlib/inftrees.c new file mode 100644 index 0000000..11e9c52 --- /dev/null +++ b/src/lib/zlib/zlib/inftrees.c @@ -0,0 +1,330 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.5 Copyright 1995-2010 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + here.op = (unsigned char)(extra[work[sym]]); + here.val = base[work[sym]]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used >= ENOUGH_LENS) || + (type == DISTS && used >= ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + here.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = here; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/src/lib/zlib/zlib/inftrees.h b/src/lib/zlib/zlib/inftrees.h new file mode 100644 index 0000000..baa53a0 --- /dev/null +++ b/src/lib/zlib/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/lib/zlib/zlib/minigzip.c b/src/lib/zlib/zlib/minigzip.c new file mode 100644 index 0000000..9825ccc --- /dev/null +++ b/src/lib/zlib/zlib/minigzip.c @@ -0,0 +1,440 @@ +/* minigzip.c -- simulate gzip using the zlib compression library + * Copyright (C) 1995-2006, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * minigzip is a minimal implementation of the gzip utility. This is + * only an example of using zlib and isn't meant to replace the + * full-featured gzip. No attempt is made to deal with file systems + * limiting names to 14 or 8+3 characters, etc... Error checking is + * very limited. So use minigzip only for testing; use gzip for the + * real thing. On MSDOS, use only on file names without extension + * or in pipe mode. + */ + +/* @(#) $Id$ */ + +#include "zlib.h" +#include + +#ifdef STDC +# include +# include +#endif + +#ifdef USE_MMAP +# include +# include +# include +#endif + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include +# include +# ifdef UNDER_CE +# include +# endif +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#ifdef VMS +# define unlink delete +# define GZ_SUFFIX "-gz" +#endif +#ifdef RISCOS +# define unlink remove +# define GZ_SUFFIX "-gz" +# define fileno(file) file->__file +#endif +#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fileno */ +#endif + +#if !defined(Z_HAVE_UNISTD_H) && !defined(_LARGEFILE64_SOURCE) +#ifndef WIN32 /* unlink already in stdio.h for WIN32 */ + extern int unlink OF((const char *)); +#endif +#endif + +#if defined(UNDER_CE) +# include +# define perror(s) pwinerror(s) + +/* Map the Windows error number in ERROR to a locale-dependent error + message string and return a pointer to it. Typically, the values + for ERROR come from GetLastError. + + The string pointed to shall not be modified by the application, + but may be overwritten by a subsequent call to strwinerror + + The strwinerror function does not change the current setting + of GetLastError. */ + +static char *strwinerror (error) + DWORD error; +{ + static char buf[1024]; + + wchar_t *msgbuf; + DWORD lasterr = GetLastError(); + DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPVOID)&msgbuf, + 0, + NULL); + if (chars != 0) { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > sizeof (buf) - 1) { + chars = sizeof (buf) - 1; + msgbuf[chars] = 0; + } + + wcstombs(buf, msgbuf, chars + 1); + LocalFree(msgbuf); + } + else { + sprintf(buf, "unknown win32 error (%ld)", error); + } + + SetLastError(lasterr); + return buf; +} + +static void pwinerror (s) + const char *s; +{ + if (s && *s) + fprintf(stderr, "%s: %s\n", s, strwinerror(GetLastError ())); + else + fprintf(stderr, "%s\n", strwinerror(GetLastError ())); +} + +#endif /* UNDER_CE */ + +#ifndef GZ_SUFFIX +# define GZ_SUFFIX ".gz" +#endif +#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) + +#define BUFLEN 16384 +#define MAX_NAME_LEN 1024 + +#ifdef MAXSEG_64K +# define local static + /* Needed for systems with limitation on stack size. */ +#else +# define local +#endif + +char *prog; + +void error OF((const char *msg)); +void gz_compress OF((FILE *in, gzFile out)); +#ifdef USE_MMAP +int gz_compress_mmap OF((FILE *in, gzFile out)); +#endif +void gz_uncompress OF((gzFile in, FILE *out)); +void file_compress OF((char *file, char *mode)); +void file_uncompress OF((char *file)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Display error message and exit + */ +void error(msg) + const char *msg; +{ + fprintf(stderr, "%s: %s\n", prog, msg); + exit(1); +} + +/* =========================================================================== + * Compress input to output then close both files. + */ + +void gz_compress(in, out) + FILE *in; + gzFile out; +{ + local char buf[BUFLEN]; + int len; + int err; + +#ifdef USE_MMAP + /* Try first compressing with mmap. If mmap fails (minigzip used in a + * pipe), use the normal fread loop. + */ + if (gz_compress_mmap(in, out) == Z_OK) return; +#endif + for (;;) { + len = (int)fread(buf, 1, sizeof(buf), in); + if (ferror(in)) { + perror("fread"); + exit(1); + } + if (len == 0) break; + + if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); + } + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); +} + +#ifdef USE_MMAP /* MMAP version, Miguel Albrecht */ + +/* Try compressing the input file at once using mmap. Return Z_OK if + * if success, Z_ERRNO otherwise. + */ +int gz_compress_mmap(in, out) + FILE *in; + gzFile out; +{ + int len; + int err; + int ifd = fileno(in); + caddr_t buf; /* mmap'ed buffer for the entire input file */ + off_t buf_len; /* length of the input file */ + struct stat sb; + + /* Determine the size of the file, needed for mmap: */ + if (fstat(ifd, &sb) < 0) return Z_ERRNO; + buf_len = sb.st_size; + if (buf_len <= 0) return Z_ERRNO; + + /* Now do the actual mmap: */ + buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); + if (buf == (caddr_t)(-1)) return Z_ERRNO; + + /* Compress the whole file at once: */ + len = gzwrite(out, (char *)buf, (unsigned)buf_len); + + if (len != (int)buf_len) error(gzerror(out, &err)); + + munmap(buf, buf_len); + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); + return Z_OK; +} +#endif /* USE_MMAP */ + +/* =========================================================================== + * Uncompress input to output then close both files. + */ +void gz_uncompress(in, out) + gzFile in; + FILE *out; +{ + local char buf[BUFLEN]; + int len; + int err; + + for (;;) { + len = gzread(in, buf, sizeof(buf)); + if (len < 0) error (gzerror(in, &err)); + if (len == 0) break; + + if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { + error("failed fwrite"); + } + } + if (fclose(out)) error("failed fclose"); + + if (gzclose(in) != Z_OK) error("failed gzclose"); +} + + +/* =========================================================================== + * Compress the given file: create a corresponding .gz file and remove the + * original. + */ +void file_compress(file, mode) + char *file; + char *mode; +{ + local char outfile[MAX_NAME_LEN]; + FILE *in; + gzFile out; + + if (strlen(file) + strlen(GZ_SUFFIX) >= sizeof(outfile)) { + fprintf(stderr, "%s: filename too long\n", prog); + exit(1); + } + + strcpy(outfile, file); + strcat(outfile, GZ_SUFFIX); + + in = fopen(file, "rb"); + if (in == NULL) { + perror(file); + exit(1); + } + out = gzopen(outfile, mode); + if (out == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); + exit(1); + } + gz_compress(in, out); + + unlink(file); +} + + +/* =========================================================================== + * Uncompress the given file and remove the original. + */ +void file_uncompress(file) + char *file; +{ + local char buf[MAX_NAME_LEN]; + char *infile, *outfile; + FILE *out; + gzFile in; + size_t len = strlen(file); + + if (len + strlen(GZ_SUFFIX) >= sizeof(buf)) { + fprintf(stderr, "%s: filename too long\n", prog); + exit(1); + } + + strcpy(buf, file); + + if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { + infile = file; + outfile = buf; + outfile[len-3] = '\0'; + } else { + outfile = file; + infile = buf; + strcat(infile, GZ_SUFFIX); + } + in = gzopen(infile, "rb"); + if (in == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); + exit(1); + } + out = fopen(outfile, "wb"); + if (out == NULL) { + perror(file); + exit(1); + } + + gz_uncompress(in, out); + + unlink(infile); +} + + +/* =========================================================================== + * Usage: minigzip [-c] [-d] [-f] [-h] [-r] [-1 to -9] [files...] + * -c : write to standard output + * -d : decompress + * -f : compress with Z_FILTERED + * -h : compress with Z_HUFFMAN_ONLY + * -r : compress with Z_RLE + * -1 to -9 : compression level + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + int copyout = 0; + int uncompr = 0; + gzFile file; + char *bname, outmode[20]; + + strcpy(outmode, "wb6 "); + + prog = argv[0]; + bname = strrchr(argv[0], '/'); + if (bname) + bname++; + else + bname = argv[0]; + argc--, argv++; + + if (!strcmp(bname, "gunzip")) + uncompr = 1; + else if (!strcmp(bname, "zcat")) + copyout = uncompr = 1; + + while (argc > 0) { + if (strcmp(*argv, "-c") == 0) + copyout = 1; + else if (strcmp(*argv, "-d") == 0) + uncompr = 1; + else if (strcmp(*argv, "-f") == 0) + outmode[3] = 'f'; + else if (strcmp(*argv, "-h") == 0) + outmode[3] = 'h'; + else if (strcmp(*argv, "-r") == 0) + outmode[3] = 'R'; + else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && + (*argv)[2] == 0) + outmode[2] = (*argv)[1]; + else + break; + argc--, argv++; + } + if (outmode[3] == ' ') + outmode[3] = 0; + if (argc == 0) { + SET_BINARY_MODE(stdin); + SET_BINARY_MODE(stdout); + if (uncompr) { + file = gzdopen(fileno(stdin), "rb"); + if (file == NULL) error("can't gzdopen stdin"); + gz_uncompress(file, stdout); + } else { + file = gzdopen(fileno(stdout), outmode); + if (file == NULL) error("can't gzdopen stdout"); + gz_compress(stdin, file); + } + } else { + if (copyout) { + SET_BINARY_MODE(stdout); + } + do { + if (uncompr) { + if (copyout) { + file = gzopen(*argv, "rb"); + if (file == NULL) + fprintf(stderr, "%s: can't gzopen %s\n", prog, *argv); + else + gz_uncompress(file, stdout); + } else { + file_uncompress(*argv); + } + } else { + if (copyout) { + FILE * in = fopen(*argv, "rb"); + + if (in == NULL) { + perror(*argv); + } else { + file = gzdopen(fileno(stdout), outmode); + if (file == NULL) error("can't gzdopen stdout"); + + gz_compress(in, file); + } + + } else { + file_compress(*argv, outmode); + } + } + } while (argv++, --argc); + } + return 0; +} diff --git a/src/lib/zlib/zlib/trees.c b/src/lib/zlib/zlib/trees.c new file mode 100644 index 0000000..56e9bb1 --- /dev/null +++ b/src/lib/zlib/zlib/trees.c @@ -0,0 +1,1244 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2010 Jean-loup Gailly + * detect_data_type() function provided freely by Cosmin Truta, 2006 + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local int detect_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (ush)value << s->bi_valid; + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= (ush)value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (ush)val << s->bi_valid;\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (ush)(value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ +#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, + "const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void ZLIB_INTERNAL _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void ZLIB_INTERNAL _tr_stored_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+last, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void ZLIB_INTERNAL _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void ZLIB_INTERNAL _tr_flush_block(s, buf, stored_len, last) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int last; /* one if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (s->strm->data_type == Z_UNKNOWN) + s->strm->data_type = detect_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, last); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+last, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+last, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*last)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ZLIB_INTERNAL _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local int detect_data_type(s) + deflate_state *s; +{ + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + unsigned long black_mask = 0xf3ffc07fUL; + int n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>= 1) + if ((black_mask & 1) && (s->dyn_ltree[n].Freq != 0)) + return Z_BINARY; + + /* Check for textual ("white-listed") bytes. */ + if (s->dyn_ltree[9].Freq != 0 || s->dyn_ltree[10].Freq != 0 + || s->dyn_ltree[13].Freq != 0) + return Z_TEXT; + for (n = 32; n < LITERALS; n++) + if (s->dyn_ltree[n].Freq != 0) + return Z_TEXT; + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/src/lib/zlib/zlib/trees.h b/src/lib/zlib/zlib/trees.h new file mode 100644 index 0000000..d35639d --- /dev/null +++ b/src/lib/zlib/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/src/lib/zlib/zlib/uncompr.c b/src/lib/zlib/zlib/uncompr.c new file mode 100644 index 0000000..ad98be3 --- /dev/null +++ b/src/lib/zlib/zlib/uncompr.c @@ -0,0 +1,59 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/src/lib/zlib/zlib/zconf.h b/src/lib/zlib/zlib/zconf.h new file mode 100644 index 0000000..806a51e --- /dev/null +++ b/src/lib/zlib/zlib/zconf.h @@ -0,0 +1,428 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define uncompress z_uncompress +# define zError z_zError +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# define gzFile z_gzFile +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if defined(HAVE_UNISTD_H) || !defined(WIN32) +# define Z_HAVE_UNISTD_H +#endif + +#if defined(STDC) && !defined(_WIN32_WCE) +# include /* for off_t */ +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/lib/zlib/zlib/zconf.h.cmakein b/src/lib/zlib/zlib/zconf.h.cmakein new file mode 100644 index 0000000..a2f71b1 --- /dev/null +++ b/src/lib/zlib/zlib/zconf.h.cmakein @@ -0,0 +1,430 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H +#cmakedefine Z_PREFIX +#cmakedefine Z_HAVE_UNISTD_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define uncompress z_uncompress +# define zError z_zError +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# define gzFile z_gzFile +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef STDC +# include /* for off_t */ +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/lib/zlib/zlib/zconf.h.in b/src/lib/zlib/zlib/zconf.h.in new file mode 100644 index 0000000..02ce56c --- /dev/null +++ b/src/lib/zlib/zlib/zconf.h.in @@ -0,0 +1,428 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ + +/* all linked symbols */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzgetc z_gzgetc +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzwrite z_gzwrite +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetHeader z_inflateGetHeader +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# define uncompress z_uncompress +# define zError z_zError +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# define gzFile z_gzFile +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef STDC +# include /* for off_t */ +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +# define z_off64_t off64_t +#else +# define z_off64_t z_off_t +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/lib/zlib/zlib/zlib.3 b/src/lib/zlib/zlib/zlib.3 new file mode 100644 index 0000000..27adc4c --- /dev/null +++ b/src/lib/zlib/zlib/zlib.3 @@ -0,0 +1,151 @@ +.TH ZLIB 3 "19 Apr 2010" +.SH NAME +zlib \- compression/decompression library +.SH SYNOPSIS +[see +.I zlib.h +for full description] +.SH DESCRIPTION +The +.I zlib +library is a general purpose data compression library. +The code is thread safe, assuming that the standard library functions +used are thread safe, such as memory allocation routines. +It provides in-memory compression and decompression functions, +including integrity checks of the uncompressed data. +This version of the library supports only one compression method (deflation) +but other algorithms may be added later +with the same stream interface. +.LP +Compression can be done in a single step if the buffers are large enough +or can be done by repeated calls of the compression function. +In the latter case, +the application must provide more input and/or consume the output +(providing more output space) before each call. +.LP +The library also supports reading and writing files in +.IR gzip (1) +(.gz) format +with an interface similar to that of stdio. +.LP +The library does not install any signal handler. +The decoder checks the consistency of the compressed data, +so the library should never crash even in the case of corrupted input. +.LP +All functions of the compression library are documented in the file +.IR zlib.h . +The distribution source includes examples of use of the library +in the files +.I example.c +and +.IR minigzip.c, +as well as other examples in the +.IR examples/ +directory. +.LP +Changes to this version are documented in the file +.I ChangeLog +that accompanies the source. +.LP +.I zlib +is available in Java using the java.util.zip package: +.IP +http://java.sun.com/developer/technicalArticles/Programming/compression/ +.LP +A Perl interface to +.IR zlib , +written by Paul Marquess (pmqs@cpan.org), +is available at CPAN (Comprehensive Perl Archive Network) sites, +including: +.IP +http://search.cpan.org/~pmqs/IO-Compress-Zlib/ +.LP +A Python interface to +.IR zlib , +written by A.M. Kuchling (amk@magnet.com), +is available in Python 1.5 and later versions: +.IP +http://www.python.org/doc/lib/module-zlib.html +.LP +.I zlib +is built into +.IR tcl: +.IP +http://wiki.tcl.tk/4610 +.LP +An experimental package to read and write files in .zip format, +written on top of +.I zlib +by Gilles Vollant (info@winimage.com), +is available at: +.IP +http://www.winimage.com/zLibDll/minizip.html +and also in the +.I contrib/minizip +directory of the main +.I zlib +source distribution. +.SH "SEE ALSO" +The +.I zlib +web site can be found at: +.IP +http://zlib.net/ +.LP +The data format used by the zlib library is described by RFC +(Request for Comments) 1950 to 1952 in the files: +.IP +http://www.ietf.org/rfc/rfc1950.txt (for the zlib header and trailer format) +.br +http://www.ietf.org/rfc/rfc1951.txt (for the deflate compressed data format) +.br +http://www.ietf.org/rfc/rfc1952.txt (for the gzip header and trailer format) +.LP +Mark Nelson wrote an article about +.I zlib +for the Jan. 1997 issue of Dr. Dobb's Journal; +a copy of the article is available at: +.IP +http://marknelson.us/1997/01/01/zlib-engine/ +.SH "REPORTING PROBLEMS" +Before reporting a problem, +please check the +.I zlib +web site to verify that you have the latest version of +.IR zlib ; +otherwise, +obtain the latest version and see if the problem still exists. +Please read the +.I zlib +FAQ at: +.IP +http://zlib.net/zlib_faq.html +.LP +before asking for help. +Send questions and/or comments to zlib@gzip.org, +or (for the Windows DLL version) to Gilles Vollant (info@winimage.com). +.SH AUTHORS +Version 1.2.5 +Copyright (C) 1995-2010 Jean-loup Gailly (jloup@gzip.org) +and Mark Adler (madler@alumni.caltech.edu). +.LP +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. +See the distribution directory with respect to requirements +governing redistribution. +The deflate format used by +.I zlib +was defined by Phil Katz. +The deflate and +.I zlib +specifications were written by L. Peter Deutsch. +Thanks to all the people who reported problems and suggested various +improvements in +.IR zlib ; +who are too numerous to cite here. +.LP +UNIX manual page by R. P. C. Rodgers, +U.S. National Library of Medicine (rodgers@nlm.nih.gov). +.\" end of man page diff --git a/src/lib/zlib/zlib/zlib.h b/src/lib/zlib/zlib/zlib.h new file mode 100644 index 0000000..4069724 --- /dev/null +++ b/src/lib/zlib/zlib/zlib.h @@ -0,0 +1,1622 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.5, April 19th, 2010 + + Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#include "qconfig.h" +#if defined(QT_VISIBILITY_AVAILABLE) +# undef ZEXTERN +# define ZEXTERN __attribute__((visibility("default"))) +#elif defined(QT_MAKEDLL) +# undef ZEXTERN +# define ZEXTERN __declspec(dllexport) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.5" +#define ZLIB_VERNUM 0x1250 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 5 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use in the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). Some + output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed code + block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the stream + are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least the + value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect the + compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the + exact value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit() does not process any header information -- that is deferred + until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing will + resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all the uncompressed data. (The size + of the uncompressed data may have been saved by the compressor for this + purpose.) The next operation on this stream must be inflateEnd to deallocate + the decompression state. The use of Z_FINISH is never required, but can be + used to inform inflate that a faster approach may be used for the single + inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK or Z_TREES is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained, so applications that need that information should + instead use raw inflate, see inflateInit2() below, or inflateBack() and + perform their own processing of the gzip header and trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any call + of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. The + stream will keep the same compression level and any other attributes that + may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression level is changed, the input available so far is + compressed with the old level (and may be flushed); the new level will take + effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to be + compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if + strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been + found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the + success case, the application may save the current current value of total_in + which indicates where valid compressed data was found. In the error case, + the application may repeatedly call inflateSync, providing more input each + time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above or -1 << 16 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the normal + behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed buffer. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef voidp gzFile; /* opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) Also "a" + can be used instead of "w" to request that the gzip stream that will be + written be appended to the file. "+" will result in an error, since reading + and writing to the same gzip file is not supported. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Two buffers are allocated, either both of the specified size when + writing, or one of the specified size and the other twice that size when + reading. A larger buffer size of, for example, 64K or 128K bytes will + noticeably increase the speed of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file was not in gzip format, gzread copies the given number of + bytes into the buffer. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream, or failing that, reading the rest + of the input file directly without decompression. The entire input file + will be read if gzread is called until it returns less than the requested + len. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or 0 in case of error. The number of + uncompressed bytes written is limited to 8191, or one less than the buffer + size given to gzbuffer(). The caller should assure that this limit is not + exceeded. If it is exceeded, then gzprintf() will return an error (0) with + nothing written. In this case, there may also be a buffer overflow with + unpredictable consequences, which is possible only if zlib was compiled with + the insecure functions sprintf() or vsprintf() because the secure snprintf() + or vsnprintf() functions were not available. This can be determined using + zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatented gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. This state can change from + false to true while reading the input file if the end of a gzip stream is + reached, but is followed by data that is not another gzip stream. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the for the crc. Pre- and post-conditioning (one's + complement) is performed within this function so it shouldn't be done by the + application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# ifdef _LARGEFILE64_SOURCE + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +/* hack for buggy compilers */ +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; +#endif + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/src/lib/zlib/zlib/zlib.pc.in b/src/lib/zlib/zlib/zlib.pc.in new file mode 100644 index 0000000..7e5acf9 --- /dev/null +++ b/src/lib/zlib/zlib/zlib.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +sharedlibdir=@sharedlibdir@ +includedir=@includedir@ + +Name: zlib +Description: zlib compression library +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lz +Cflags: -I${includedir} diff --git a/src/lib/zlib/zlib/zutil.c b/src/lib/zlib/zlib/zutil.c new file mode 100644 index 0000000..3418c59 --- /dev/null +++ b/src/lib/zlib/zlib/zutil.c @@ -0,0 +1,310 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005, 2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/src/lib/zlib/zlib/zutil.h b/src/lib/zlib/zlib/zutil.h new file mode 100644 index 0000000..8e535ac --- /dev/null +++ b/src/lib/zlib/zlib/zutil.h @@ -0,0 +1,275 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2010 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#include "qconfig.h" +#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) && defined(QT_VISIBILITY_AVAILABLE) +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#ifdef STDC +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); +void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/src/res/MdCharm.qrc b/src/res/MdCharm.qrc new file mode 100644 index 0000000..7b96284 --- /dev/null +++ b/src/res/MdCharm.qrc @@ -0,0 +1,73 @@ + + + markdown/default.css + markdown/markdown.html + markdown/markdown.js + jquery.js + mdcharm.png + mdcharm-splash.png + start_here_page/issue.png + start_here_page/StartHerePage.css + start_here_page/StartHerePage.html + next.png + prev.png + find_close.png + config.png + copy.png + cut.png + find.png + help.png + open.png + redo.png + save.png + saveas.png + undo.png + new.png + paste.png + print.png + conf/env.png + conf/text_editor.png + start_here_page/StartHerePage.js + open_dir.png + conf/styles.png + unmodified.png + modified.png + markdown/bold.png + markdown/italic.png + markdown/quote.png + markdown/strike-through.png + markdown/tab.png + markdown/untab.png + markdown/link.png + markdown/picture.png + markdown/read_mode.png + markdown/write_mode.png + markdown/write_read.png + spell_check.png + markdown/markdown_cheat_sheet.md + markdown/code.png + markdown/code_syntax_notice.html + markdown/code_syntax_patch.css + conf/add.png + conf/delete.png + + + highlighter/bash.xml + highlighter/cpp.xml + highlighter/cs.xml + highlighter/css.xml + highlighter/diff.xml + highlighter/http.xml + highlighter/ini.xml + highlighter/java.xml + highlighter/javascript.xml + highlighter/json.xml + highlighter/markdown.xml + highlighter/perl.xml + highlighter/php.xml + highlighter/python.xml + highlighter/ruby.xml + highlighter/sql.xml + highlighter/xml.xml + + diff --git a/src/res/conf/add.png b/src/res/conf/add.png new file mode 100644 index 0000000000000000000000000000000000000000..bedab97e9284cdcd1c888c30e324462bc96c003d GIT binary patch literal 1041 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 O0000V~!e literal 0 HcmV?d00001 diff --git a/src/res/conf/styles.png b/src/res/conf/styles.png new file mode 100644 index 0000000000000000000000000000000000000000..a3338f41d6aba7243592c67dd2ede8cd9bda184d GIT binary patch literal 2507 zcmV;+2{iVJP)S{$Jm)>n`@HWbq?7g*a3=u7=7j1{_4FdQ73mI}p|-); zyi7a?iK01GMNiYMaA!o?rUa1Ejz71F?H^k&W$XjII(lB?J^+p3ah)@3+#SyBafu1l z@X+`Ed!&uK8nxVmZ4M-X$A+e7Vm5z;Hnsw5s2gdU2t`LxT}V5MZHD4hB}f=(`;1WA z(I(&`0_f@axbiE3*a?KxY1w~2{r}lR{yl4PjlK$m4G2IWP#9`CMbF>Xp%+x(t9lG2 z)kCzYo`IuVNuPNys(*ZZcLBiAU?;R49X`M?{BU?Bv6ITE22zqS^GX7nenIwZYqEkmd(YN~-94uPs58H6YdojZb2J~MF@7ZGUq$~)@t zhh3a&*+#xUjk{M)Av;9{z&mRaj>2rtzx6DNq2lmoA%~*Ks6@;3gJt8LH4E3b#v~5W z`N3}N-Zq4)()<3CSe>WHEZTvT0y~L;22fpHJzo(8cT`UPHrLOc!meHK^6ncanO|}S zp{uA4m9&aXTn|+vbQOe3^DA%S9Q$3mcK-p*>j7co@F}3D)A!jikdmIgk0BJ$^ZZz< zO1Swj5(Pz1(-Q=$tE=6>=Bfn?)ddS~M#^3)?0?g-b1i0Iker*Z1uKl`YeH3Yq|74J z)W=X~FV?}`IE(YiUc3;&5W*fnl7h3eg3z%eV4Ha7RACep()Z~<3B9)(t#~@#qS@n3 zxnI%53|j-1CI9 ziH2v!GOWH1`aj=?6&l1lYZ7S{`Cx`YbAmMSOq)pmkz@E4JdBn%ih=sQbbq`TJ-3SX z9dDzi`jA7tpeS_x{T&=dWkj03#Ph?a8L$WGIKJcaoUu<^7YOV-a^y&DV`C%bvT_`$^(oKp9jE9Nu%%dCqxH=;e-b2>gsk>S6Bb;si&U$`Knc`#DobG zfK(j$GwAz{19^Bak(MARSv0@$4t6+lQ?~iz$=5l$EBTW#%DL4PBmR-z`QA6AF7;EG5^oow{i01NeT)I2!$d<+{N_OZ-=}wG*7sX#!njQ?Cj^`h9FDt zoXpjQ3IY@1PC;|~F98k`3$PJ5lIy|6PFRxH%>(yO;KfacX=rG`@6RVVZZ!GXqv@UT z8=^ffii<6TQ0VOJWb>v296sn~$&!3DO>p@k<3{WvI@mFynv5Wzs0RK*9}hn=j+b6+ zq^YTy{mvqaidSKs8pP$&$j;8jFbvL|IfJ4o96HoTXO~6I-DRYwYnK2{hI1hl9ov?d zA^^J6gVSAz!yV$0M+@2f(qRsKa)yrfUb3>X@cE)-WMts+c&MnTz%UE~fdFUE^z!WQ z8(CF5g?xWn0$3!~7zqfx^cn8UT>;-HvkEo$P9`&@fxRC!W11$WX<=Cw85tR*rlwL> zR)%31G&eWX-POaBPyCB}ep<=+D>CCJmvLwtc_A8?*8oI9=TU+uu^CL%{n^uPyy;%H z?d~BEFtIEP(~NzmrKM9`T#R8DoI7`puC6ZDJ$-;%7iBYjx*LfdFYPj3zJo*uJ4POK z__}Nq)j-ut*tFs)^If3Za03vAHy&>d-g2BV30T7JWqSj z;^wMs5F~UUa3K`q@)6J+Zk+ByB%uTWrPqN_6(&~r`1Rw3tbgWH0)aq03b9CdJRWj# zaxe@7UDs)EZ|5H$bkfzWu;QLE7)GptQOH9x+?URRSRWUAWDX5sTSFMmlo8>|$#e0m z)ic<*{vcnTIFD&s@sEku>&5H!qU$=kuG7}mMnip&XV!ei$_K~d@fgFByqph2LhXct zCpo%r7m>jbS6(*{t1pC51W}_iq3)mjIFqegO+G!)p3pLsk&s@@*;Bb+St8on1pGX_$7)rBYK0||hkz8+9OGg80tQer25=lN`Y_CN-kHg(e079cg%bxCuZ zH*X%fxw&lIxbYeYZ>z4ZzBN&fPwpvuEUjqf>eQm?Kz{-t+SUaEw4C&KH0*@K4ZB@z zZg9|I^UEEy1yZQ3t);ZI^pZrHnwnUuYJv$HEUS8awV0Ol@}2xDJfyps8Jk0etc4r2t|?x?2%ndBJ?8h zhBJH>BEG^#qM)h@OYbTrH!r}tbTIy1Tn+Y-|KLGE5*YJy1~aC4t90 zEP3v7H9vXWaAO&Hxy`)%!WP=w+gZGLajfx`lI`2K6OBes0Pl=Q!-0RJOdmht}3s#*7(5eSJM=PJhu0>cYCZx@ba#$99&D zk&+veTPB$VB4sR7zS&aAZ-6KMle8|-mG0Shto(+7h@P2|Hh6kmVc?Oa4;?%J@IUdm VOXk{&wzB{L002ovPDHLkV1gL~ySV@W literal 0 HcmV?d00001 diff --git a/src/res/conf/text_editor.png b/src/res/conf/text_editor.png new file mode 100644 index 0000000000000000000000000000000000000000..5216190a976f48db2fa11bc6ab3d4b94fa14b713 GIT binary patch literal 1201 zcmV;i1Wx;jP)V}{q1aeUnJ&DIXddNM0KrVX#lgW<$1MwmTlw|T_x~tyv&|Td% zJwJC9Y?5@5sj2sUpZBAxNJQ{|K7R2Z^?LmWt@V4ZuwLUqZxPX2>+hP)=4Za|U#zaK z_Vtq7#Wy!M6*KF_4?e=!mC_4&BqG?}-iFp1`}_OfHyVvktJP}j;lqbKlz=gY>>{o6 zHb9P20tl480it~DKFkbe#@yUph{%Ug6h)0jyN%O1{Lnq#>B7R&o9MTZna>kN^;rN_g~W-Pc-Q zIXO9L^(BzS9Y7o#?1s&O=&kqZPx8wQ7J=sxW@oAp0fN8>5fOkAGkX9`e*&qoAn@^i z?dC}6lkX-J>^i~#r$m)fNiG%O6$GF=1kZsUnQ`P|EFI}i@*W{b7O=>5Y_QXKHWF>XV;4P(>-BR8 zAgAq$2+kvR+PzzDz*vc#GL&qdTbemB8Gazi2_)n7pZ`SLh>O6RxKI%`qyW^A zMpRHK0GMGQ_#^5*mc8l|U`zphcSVd|1-;^A4735-ah~CJvLQf>fe6r!GO6&DqX0MG`P5pZ&b ztgqV-+gB+eU|U!beY!mgbT7k8?!;6fE%xn)BPyzm;ej|l2pOmoyoi*ITbMI@-y&Ab~RAARt~W9^p}qEbN7Ka zUMqKQ!Pc;2oaluJB=3b=Z{{tR^Y-d`DUxMDmJc$(^S))s%;_1Bpp@6u^WpW|1M=3K ziOH)K1f=T?A<$~Iq+BjTDFxs6|I70LIe({jUK4hya`hL2v;eyBYlhdmrL}j`u7~ P00000NkvXXu0mjf4>B~l literal 0 HcmV?d00001 diff --git a/src/res/config.png b/src/res/config.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb470e8d95043e20bba434744ee45c26b93cd51 GIT binary patch literal 2657 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⁣>E4#>gxJ@a&q$Y=FOXBEdhTXr42@I-G`JC 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 literal 0 HcmV?d00001 diff --git a/src/res/find.png b/src/res/find.png new file mode 100644 index 0000000000000000000000000000000000000000..edb95c8e6e1c88f33be1b5a5e159a1bc38bf1426 GIT binary patch literal 2558 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 Ue-a{`SpWb407*qoM6N<$f|lIb!~g&Q literal 0 HcmV?d00001 diff --git a/src/res/find_close.png b/src/res/find_close.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e92dbeb0da59d59bc18e79b59422b1ad77369f GIT binary patch literal 1011 zcmVycnh{l_!i9y5{B7zY$F;S~R z5JKCrloyuQ$7DeaUFpMDLMsVRuqu?Xv#(T}n{I4cA zUpKYPfwMe^1HXer1ONcRV6gS%m-bxop8;kWNlcaAjqMG)JK4wA_v?Ck8o?(S|1dwP0a5YhL2eSJlOAW&sxB^r%JibNu~yu3`=Y&PHC-p)@< zOkkl6UWBJO0TZ2e&9InnaAVdYHMquqM`zfMk7Qb5jZ|RhQYx> z004-`Mi>HeoUoO@1;_y-x?bmt9pBT(O@tT0AM1KAjTM4EEZynAVCs+Pu|j z{Q|qYy9d(g^io4Z!(+GG4aLR90059oCSh}P6BuLQ^?ISMt_~Pu!1FwupP$3__BQ47 z`9H9=we?kbdHJUvj|U9~1L^g8%w#gOwzdY0v3~#{hKNsmJ|AbbTIufY4s*F29UdOy z{{Ftk>-GBWcs%~>^72yO($a!AH#e}cv4PoaR&+QV9~26O?D6q26Gid4*X!l%b~{{O zU*qB7VF3|CI5INA%d-4+CX@NDtE)?QbaaH7OlC_EgeU9k>(>AP!^6XmE-o&9EGjB` z+TPw?bZ~H><@5Q)y1KeA;lCsCot>RuIi1dQb93{G&*ytgMDVV?UTQOHl_H2L{kVe89+vT6Cb{QQAUDAE7j%X`0w1&CTOJ za&sTMcVB0B{i9MEk|ve&*UmX-zwh_Ge&2V9@B28Pb^EXPSJ^h-WLW$%$8kGsht~j5 zeBUq0vN)m0Vt*hYzR4(g_0b<)I*apMWGMhU9@@KpwqV>%3E9%zP$|{dRG_jvjBrqf z$OPEjMNv1Ao+%{Nkvn(t>^u&)ZKm4%)-1hL>bu&5bH;OD>+qt$G?JH{l z0G8vz<{o^{hYx@Npp-yl0%TEu?|bMQoWS9uBfjT)e~3n;yMBAu<>~(mz>Wv^Y{_J` zXFAuesOntX4DNc+4F|5bbhwy65K<%%_;{!HKj<46ACE=EuRnFqrv2vyV8;V{wiPtv zFIzUOm1^QK6b&1$>w|L_`CI{7QHQP@aBL3G^FS$uC@}+Jt~20|0_D z!^n6FySn#}2WYADg-5oh&V=lPk6ccBY)wa#8n29=Q4RotAYf%(^t_YzWK2m|Ni*x?iml8y$NS_R%&@;o1qWC_*P%Yp(6 zZuVUJ&&;;$ H=@O*;BXhvWn&Zdm)Ob*=8aph-M&%4`wU;>d?6d)8IJ9bmSL@lM8j}>e?jXPTKU;L@~Jd#xvpbgb@U$3;J~pNw05*$A>t$;C@Tvi z9Ifsc>3fAS`2KP^Apb9BOH-{@)R3N<#%f^*K^Iy!|H-#mrJmIhFPE-H@@fDj@Z z+6-mB=Na7LVNU6i4lqp%W=U!EMFaxc1Mt>AlD zGz`A)Bb%88H#MxNivT`wav}p!2}2YW5VCj~GX_v_Oedy|#kfU)6|m>xUQ zZ5jDYK#95x0F;pO;PLkcwru)p9W))_?!wmV#R9hP_)s|j1Op7)x3%HUU-qE9Qaz^^ zB#|H>c^K?F?hf_+`*A4{F>Oop*lBk1c9%5cpC|eTuUgTu3RCI%*~A?Or{$9mtLIP%W3KNY2r=5oiK zGZERAb*fa%-0*hy-WM<5x=~gu%8<@}2!c?GKrn)5cAmgZ+g3qj6ae6G56|ozLLd|Y zp>&?*@d^e#mqE|#Z|Mj3JaLO->4{JzZWZ%W{=A`d<5%vcsmY;h+tyt9?4_I6%P}>M zbe5kX7tiyMPEJFFiB-)pzz2?x=HNhvT2%vj763^kP%9Wx;|cVRndEeA8>VT10FVO;L`nLP zXc!QU2#}eJ{)6vfp!eO0gZrMkHJcuOTM3pIN_y5_@?p}s{@cQI;{AsD=B@`CSFXE$ z)kPhop|u&VFTk{XShhE()e4kAk_Z%;0G@-nT9 zS?9c;W}jGhHJwQfOO%LR&269mp<3O#sk(l-qSnMwRuO{|R6r?(=ej8A1`636Qj + + + if + then + else + elif + fi + for + break + continue + while + in + do + done + echo + exit + return + set + declare + true + false + + + + false + \\[\s\S] + + + '' + + + + (#!\/bin\/bash)|(#!\/bin\/sh) + + + \$[a-zA-Z0-9_#]+ + + + \${([^}]|\\})+} + + + # + $ + + + " + " + + backslash_escape + variable|1 + variable|2 + + + + ' + ' + + noname + + + + \[ + \] + + string|quote + string|apos + variable|1 + variable|2 + + + true + false + + + + \[\[ + \]\] + + string|quote + string|apos + variable|1 + variable|2 + + + true + false + + + + \ No newline at end of file diff --git a/src/res/highlighter/cpp.xml b/src/res/highlighter/cpp.xml new file mode 100644 index 0000000..b6868eb --- /dev/null +++ b/src/res/highlighter/cpp.xml @@ -0,0 +1,154 @@ + + + </ + + false + int + float + while + private + char + catch + export + virtual + operator + sizeof + dynamic_cast + typedef + const_cast + const + struct + for + static_cast + union + namespace + unsigned + long + throw + volatile + static + protected + bool + template + mutable + if + public + friend + do + return + goto + auto + void + enum + else + break + new + extern + using + true + class + asm + case + typeid + short + reinterpret_cast + default + double + register + explicit + signed + typename + try + this + switch + continue + wchar_t + inline + delete + alignof + char16_t + char32_t + constexpr + decltype + noexcept + nullptr + static_assert + thread_local + restrict + _Bool + complex + std + string + cin + cout + cerr + clog + stringstream + istringstream + ostringstream + auto_ptr + deque + list + queue + stack + vector + map + set + bitset + multiset + multimap + unordered_set + unordered_map + unordered_multiset + unordered_multimap + array + shared_ptr + NULL + + + + // + $ + + + /\* + \*/ + + + false + \\[\s\S] + 0 + + + " + " + \n + 0 + + backslash_escape + + + + \'\\?. + \' + . + + + \b(\d+(\.\d*)?|\.\d+)(u|U|l|L|ul|UL|f|F) + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + 0 + + + # + $ + + + \b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\s*< + > + + + + + diff --git a/src/res/highlighter/cs.xml b/src/res/highlighter/cs.xml new file mode 100644 index 0000000..1717917 --- /dev/null +++ b/src/res/highlighter/cs.xml @@ -0,0 +1,176 @@ + + + + abstract + as + base + bool + break + byte + case + catch + char + checked + class + const + continue + decimal + default + delegate + do + double + else + enum + event + explicit + extern + false + finally + fixed + float + for + foreach + goto + if + implicit + in + int + interface + internal + is + lock + long + namespace + new + null + object + operator + out + override + params + private + protected + public + readonly + ref + return + sbyte + sealed + short + sizeof + stackalloc + static + string + struct + switch + this + throw + true + try + typeof + uint + ulong + unchecked + unsafe + ushort + using + virtual + valatile + void + while + ascending + descending + from + get + group + into + join + let + orderby + partial + select + set + value + var + where + yield + + + + ///|<!--|--> + + + </? + > + + + /// + $ + true + + xmlDocTag|ref1 + xmlDocTag|ref2 + + + + // + $ + + + /\* + \*/ + + + # + $ + + if + else + elif + endif + define + undef + warning + error + line + region + endregion + pragma + checksum + + + + false + "" + + + @ + " + + + false + \\[\s\S] + + + ' + ' + \n + 0 + + backslash_escape + + + + " + " + \n + 0 + + backslash_escape + + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + + + diff --git a/src/res/highlighter/css.xml b/src/res/highlighter/css.xml new file mode 100644 index 0000000..bd77102 --- /dev/null +++ b/src/res/highlighter/css.xml @@ -0,0 +1,139 @@ + + + [=/|\'] + + + false + \\[\s\S] + 0 + + + \b\d+(\.\d+)? + 0 + + + ' + ' + \n + 0 + + backslash_escape + + + + " + " + \n + 0 + + backslash_escape + + + + [a-zA-Z][a-zA-Z0-9_]*\( + \) + + number + string|apos + string|quote + + + + /\* + \*/ + + + /\* + \*/ + + + \#[A-Za-z0-9_-]+ + + + \.[A-Za-z0-9_-]+ + 0 + + + \[ + \] + $ + + + :(:)?[a-zA-Z0-9\_\-\+\(\)\"\']+ + + + @(font-face|page) + [a-z-]+ + + font-face + page + + + + @ + [{;] + true + + import + page + media + charset + + + function + string|apos + string|quote + number + + + + [a-zA-Z][a-zA-Z0-9_]* + 0 + + + \#[0-9A-Fa-f]+ + + + !important + + + true + true + + function + number + string|quote + string|apos + comment|ref + hexcolor + important + + + + [a-zA-Z\_\.\-]+ + : + true + [^\s] + value + + + [^\s] + ; + true + true + + attribute + + + + { + } + [^\s] + 0 + + comment|ref + rule + + + + diff --git a/src/res/highlighter/diff.xml b/src/res/highlighter/diff.xml new file mode 100644 index 0000000..f119b88 --- /dev/null +++ b/src/res/highlighter/diff.xml @@ -0,0 +1,50 @@ + + + + + diff + $ + + + ^\@\@ +\-\d+,\d+ +\+\d+,\d+ +\@\@$ + + + ^\*\*\* +\d+,\d+ +\*\*\*\*$ + + + ^\-\-\- +\d+,\d+ +\-\-\-\-$ + + + Index + $ + + + ===== + =====$ + + + ^\-\-\- + $ + + + ^\+\+\+ + $ + + + \*{5} + \*{5}$ + + + ^\+ + $ + + + ^\- + $ + + + ^\! + $ + + + diff --git a/src/res/highlighter/http.xml b/src/res/highlighter/http.xml new file mode 100644 index 0000000..021b7b5 --- /dev/null +++ b/src/res/highlighter/http.xml @@ -0,0 +1,50 @@ + + + \S + + + \b\d{3}\b + + + ^HTTP/[0-9\.]+ + $ + + number + + + + + + true + true + + + ^[A-Z]+ (.*?) HTTP/[0-9\.]+$ + $ + true + + string + + + + $ + + + ^\w + : + \n|\s|= + true + string|ref + + + false + badname + true + + + false + \n\n + noname|ref + + + diff --git a/src/res/highlighter/ini.xml b/src/res/highlighter/ini.xml new file mode 100644 index 0000000..e223c26 --- /dev/null +++ b/src/res/highlighter/ini.xml @@ -0,0 +1,54 @@ + + + [^\s] + + + false + \\[\s\S] + 0 + + + " + " + \n + 0 + + backslash_escape + + + + \b\d+(\.\d+)? + 0 + + + true + + on + off + true + false + yes + no + + + string + number + + + + ; + $ + + + ^\[ + \] + + + ^[a-z0-9\[\]_-]+[ \t]*=[ \t]* + $ + + value + + + + diff --git a/src/res/highlighter/java.xml b/src/res/highlighter/java.xml new file mode 100644 index 0000000..476d5e9 --- /dev/null +++ b/src/res/highlighter/java.xml @@ -0,0 +1,129 @@ + + + [^\s] + + false + synchronized + int + abstract + float + private + char + boolean + static + null + if + const + for + true + while + long + throw + strictfp + finally + protected + import + native + final + return + void + enum + else + break + transient + new + catch + instanceof + byte + super + volatile + case + assert + short + package + default + double + public + try + this + switch + continue + throws + + + + @[A-Za-z]+ + + + /\*\* + \*/ + 10 + + javadoctag + + + + // + $ + + + /\* + \*/ + + + false + \\[\s\S] + 0 + + + ' + ' + \n + 0 + + backslash_escape + + + + " + " + \n + 0 + + backslash_escape + + + + false + true + 10 + + extends + implements + + + + [a-zA-Z_][a-zA-Z0-9_]* + + + true + { + + class + interface + + : + + extends + title + + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + 0 + + + @[A-Za-z]+ + + + diff --git a/src/res/highlighter/javascript.xml b/src/res/highlighter/javascript.xml new file mode 100644 index 0000000..c341fd8 --- /dev/null +++ b/src/res/highlighter/javascript.xml @@ -0,0 +1,141 @@ + + + + in + if + for + while + finally + var + new + function + do + return + void + else + break + catch + instanceof + with + throw + case + default + try + this + switch + continue + typeof + delete + let + yield + const + true + false + null + undefined + NaN + Infinity + + + + false + \\[\s\S] + 0 + + + ' + ' + \n + 0 + + backslash_escape + + + + " + " + \n + 0 + + backslash_escape + + + + // + $ + + + /\* + \*/ + + + // + $ + + + /\* + \*/ + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + 0 + + + false + \\/ + + + / + /[gim]* + \n + + ref1 + + + + < + >; + xml + + + false + (!|!=|!==|%|%=|&|&&|&=|\*|\*=|\+|\+=|,|\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\?|\[|\{|\(|\^|\^=|\||\|=|\|\||~|\b(case|return|throw)\b)\s* + 0 + + return + throw + case + + + comment|line + comment|block + regexp + e4x + + + + \( + \) + ["'\(] + + comment|line + comment|block + + + + [A-Za-z$_][0-9A-Za-z$_]* + + + true + { + \[|% + + function + + + title + params + + + + diff --git a/src/res/highlighter/json.xml b/src/res/highlighter/json.xml new file mode 100644 index 0000000..65e7df0 --- /dev/null +++ b/src/res/highlighter/json.xml @@ -0,0 +1,77 @@ + + + \S + + true + false + null + + + + false + \\[\s\S] + + + " + " + \n + 0 + + backslash_escape + + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + + + , + true + true + + true + false + null + + + + \s*" + "\s*:\s* + true + true + \n + + backslash_escape + + value + + + false + { + } + \S + + attribute + + + + false + , + true + true + + true + false + null + + + + false + \[ + \] + \S + + value|noname + + + + \ No newline at end of file diff --git a/src/res/highlighter/markdown.xml b/src/res/highlighter/markdown.xml new file mode 100644 index 0000000..f33393e --- /dev/null +++ b/src/res/highlighter/markdown.xml @@ -0,0 +1,67 @@ + + + + + ^#{1,3} + $ + + + ^.+?\n[=-]{2,}$ + + + false + < + > + xml + + + ^([*+-]|(\d+\.))\s+ + + + [*_]{2}.+?[*_]{2} + + + \*.+?\* + + + _.+?_ + + + ^>\s+ + $ + + + ``` + ``` + + + `.+?` + + + ^ + $ + + + ^-{3,} + $ + + + \[.+\] + + + \( + \) + true + true + + + false + \[.+?\]\(.+?\) + true + + link_label + link_url + + + + diff --git a/src/res/highlighter/perl.xml b/src/res/highlighter/perl.xml new file mode 100644 index 0000000..9f608eb --- /dev/null +++ b/src/res/highlighter/perl.xml @@ -0,0 +1,414 @@ + + + + getpwent + getservent + quotemeta + msgrcv + scalar + kill + dbmclose + undef + lc + ma + syswrite + tr + send + umask + sysopen + shmwrite + vec + qx + utime + local + oct + semctl + localtime + readpipe + do + return + format + read + sprintf + dbmopen + pop + getpgrp + not + getpwnam + rewinddir + qqfileno + qw + endprotoent + wait + sethostent + bless + s + opendir + continue + each + sleep + endgrent + shutdown + dump + chomp + connect + getsockname + die + socketpair + close + flock + exists + index + shmgetsub + for + endpwent + redo + lstat + msgctl + setpgrp + abs + exit + select + print + ref + gethostbyaddr + unshift + fcntl + syscall + goto + getnetbyaddr + join + gmtime + symlink + semget + splice + x + getpeername + recv + log + setsockopt + cos + last + reverse + gethostbyname + getgrnam + study + formline + endhostent + times + chop + length + gethostent + getnetent + pack + getprotoent + getservbyname + rand + mkdir + pos + chmod + y + substr + endnetent + printf + next + open + msgsnd + readdir + use + unlink + getsockopt + getpriority + rindex + wantarray + hex + system + getservbyport + endservent + int + chr + untie + rmdir + prototype + tell + listen + fork + shmread + ucfirst + setprotoent + else + sysseek + link + getgrgid + shmctl + waitpid + unpack + getnetbyname + reset + chdir + grep + split + require + caller + lcfirst + until + warn + while + values + shift + telldir + getpwuid + my + getprotobynumber + delete + and + sort + uc + defined + srand + accept + package + seekdir + getprotobyname + semop + our + rename + seek + if + q + chroot + sysread + setpwent + no + crypt + getc + chown + sqrt + write + setnetent + setpriority + foreach + tie + sin + msgget + map + stat + getlogin + unless + elsif + truncate + exec + keys + glob + tied + closedirioctl + socket + readlink + eval + xor + readline + binmode + setservent + eof + ord + bind + alarm + pipe + atan2 + getgrent + exp + time + push + setgrent + gt + lt + or + ne + m + break + given + say + state + when + + + + false + [a-zA-Z][a-zA-Z0-9_]* + + + false + { + } + + + [$@]\{ + \} + + + + false + \\[\s\S] + + + (s|tr|y)/(\\.|[^/])*/(\\.|[^/])*/[a-z]* + + + (m|qr)?/ + /[a-z]* + + backslash_escape + + + + + \$\d + + + [\$\%\@\*](\^\w\b|#\w+(\:\:\w+)*|[^\s\w{]|{\w+}|\w+(\:\:\w*)*) + + + # + $ + + + ^(__END__|__DATA__) + \n$ + 5 + + + ^\=\w + \=cut + true + + + false + -> + + id + forref + + + + q[qwxr]?\s*\( + \) + + backslash_escape + subst + variable|1 + variable|2 + + + + q[qwxr]?\s*\[ + \] + + backslash_escape + subst + variable|1 + variable|2 + + + + q[qwxr]?\s*\{ + \} + + backslash_escape + subst + variable|1 + variable|2 + + + + q[qwxr]?\s*\| + \| + + backslash_escape + subst + variable|1 + variable|2 + + + + q[qwxr]?\s*\< + \> + + backslash_escape + subst + variable|1 + variable|2 + + + + qw\s+q + q + + backslash_escape + subst + variable|1 + variable|2 + + + + ' + ' + + backslash_escape + + + + " + " + + backslash_escape + subst + variable|1 + variable|2 + + + + ` + ` + + backslash_escape + + + + {\w+} + + + \-?\w+\s*\=\> + + + (\b0[0-7_]+)|(\b0x[0-9a-fA-F_]+)|(\b[1-9][0-9_]*(\.[0-9_]+)?)|[0_]\b + + + true + (\s*\(.*?\))?[;{] + + sub + + + + -\w\b + + + false + (!|!=|!==|%|%=|&|&&|&=|\*|\*=|\+|\+=|,|\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\?|\[|\{|\(|\^|\^=|\||\|=|\|\||~|\b(split|return|print|reverse|grep)\b)\s* + + split + return + print + reverse + grep + + + comment|hash + comment|ref + regexp|1 + regexp|2 + + + + + diff --git a/src/res/highlighter/php.xml b/src/res/highlighter/php.xml new file mode 100644 index 0000000..d88cdca --- /dev/null +++ b/src/res/highlighter/php.xml @@ -0,0 +1,225 @@ + + + + and + include_once + list + abstract + global + private + echo + interface + as + static + endswitch + array + null + if + endwhile + or + const + for + endforeach + self + var + while + isset + public + protected + exit + foreach + throw + elseif + include + __FILE__ + empty + require_once + do + xor + return + implements + parent + clone + use + __CLASS__ + __LINE__ + else + break + print + eval + new + catch + __METHOD__ + case + exception + php_user_filter + default + die + require + __FUNCTION__ + enddeclare + final + try + this + switch + continue + endfor + endif + declare + unset + true + false + namespace + trait + goto + instanceof + insteadof + __DIR__ + __NAMESPACE__ + __halt_compiler + + + + // + $ + + + # + $ + + + \s@[A-Za-z]+ + + + /\* + \*/ + + phpdoc + + + + true + __halt_compiler.+?; + true + + + false + \\[\s\S] + 0 + + + <<<['"]?\w+['"]?$ + ^\w+; + + backslash_escape + + + + <\?php + 10 + + + \?> + + + \$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]* + + + /\* + \*/ + + + ' + ' + 0 + + backslash_escape + + + + " + " + + backslash_escape + + 0 + + + b" + " + + backslash_escape + + + + b' + ' + + backslash_escape + + + + \b(0b[01]+) + 0 + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + 0 + + + + \( + \) + + number|ref2 + number|ref1 + string|ref2 + string|ref1 + string|quote + string|pos + comment|block + variable + + + + [a-zA-Z_][a-zA-Z0-9_]* + + + true + { + + function + + \$|\[|% + + title + params + + + + true + true + + extends + + + title + + + + true + true + + class + + [:\(\$] + + classRef + title + + + + + + + diff --git a/src/res/highlighter/python.xml b/src/res/highlighter/python.xml new file mode 100644 index 0000000..31db2c7 --- /dev/null +++ b/src/res/highlighter/python.xml @@ -0,0 +1,173 @@ + + + + and + elif + is + global + as + in + if + from + raise + for + except + finally + print + import + pass + return + exec + else + break + not + with + class + assert + yield + try + while + continue + del + or + def + lambda + nonlocal + None + True + False + Ellipsis + NotImplemented + + + + [a-zA-Z_][a-zA-Z0-9_]* + + + false + \\[\s\S] + + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + + + ^(>>>|\.\.\.) + + + # + $ + + + + (u|b)?r?''' + ''' + + prompt + + + + (u|b)?r?""" + """ + + prompt + + + + (u|r|ur)' + ' + + backslash_escape + + + + (u|r|ur)" + " + + backslash_escape + + + + (b|br)' + ' + + backslash_escape + + + + (b|br)" + " + + backslash_escape + + + + ' + ' + \n + 0 + + backslash_escape + + + + " + " + \n + 0 + + backslash_escape + + + + + + \( + \) + + number|c + prompt + string|1 + string|2 + string|3 + string|4 + string|5 + string|6 + string|apos + string|quote + + + + true + : + + + def + + + title + params + + + + true + : + + + class + + + title + params + + + + @ + $ + + + false + \b(print|exec)\( + + + diff --git a/src/res/highlighter/ruby.xml b/src/res/highlighter/ruby.xml new file mode 100644 index 0000000..36ab377 --- /dev/null +++ b/src/res/highlighter/ruby.xml @@ -0,0 +1,260 @@ + + + + and + false + then + defined + module + in + return + redo + if + BEGIN + retry + end + for + true + self + when + next + until + do + begin + unless + END + rescue + nil + else + break + undef + not + super + class + case + require + yield + alias + while + ensure + elsif + or + include + + + + @[A-Za-z]+ + + + false + \\[\s\S] + + + #\{ + } + [a-zA-Z_][a-zA-Z0-9_]*(\!|\?)? + + + [a-zA-Z_]\w*[!?=]?|[-+~]\@|<<|>>|=~|===?|<=>|[<>]=?|\*\*|[-/+%^&*~`|]|\[\]=? + [a-zA-Z_][a-zA-Z0-9_]*(\!|\?)? + + + [a-zA-Z_]\w*[!?=]?|[-+~]\@|<<|>>|=~|===?|<=>|[<>]=?|\*\*|[-/+%^&*~`|]|\[\]=? + + + \( + \) + [a-zA-Z_][a-zA-Z0-9_]*(\!|\?)? + + + ([a-zA-Z][a-zA-Z0-9_]*::)?[a-zA-Z][a-zA-Z0-9_]* + + + + # + $ + + yardoctag + + + + ^\=begin + ^\=end + + yardoctag + + + + ^__END__ + \n$ + + + ' + ' + + backslash_escape + subst + + + + " + " + + backslash_escape + subst + + + + %[qw]?\( + \) + + backslash_escape + subst + + + + %[qw]?\[ + \] + + backslash_escape + subst + + + + %[qw]?{ + } + + backslash_escape + subst + + + + %[qw]?< + > + + backslash_escape + subst + + + + %[qw]?/ + / + + backslash_escape + subst + + + + %[qw]?% + % + + backslash_escape + subst + + + + %[qw]?- + - + + backslash_escape + subst + + + + %[qw]?\| + \| + + backslash_escape + subst + + + + true + |$|; + + def + + + title|ref + params + comment|nsr1 + comment|nsr2 + comment|nsr3 + + + + [A-Za-z_]\w*(::\w+)*(\?|\!)? + + + <\s* + + parent|ref + + + + true + $|; + + class + module + + + title|ref2 + inheritance|ref + comment|nsr1 + comment|nsr2 + comment|nsr3 + + + + (::)?(\b[A-Z]\w*(::)?)+ + + + : + + string|nsr1 + string|nsr2 + string|nsr3 + string|nsr4 + string|nsr5 + string|nsr6 + string|nsr7 + string|nsr8 + string|nsr9 + string|nsr10 + method|ref + + + + [a-zA-Z_][a-zA-Z0-9_]*(\!|\?)?: + + + (\b0[0-7_]+)|(\b0x[0-9a-fA-F_]+)|(\b[1-9][0-9_]*(\.[0-9_]+)?)|[0_]\b + + + \?\w + + + (\$\W)|((\$|\@\@?)(\w+)) + + + / + /[a-z]* + \n + + backslash_escape + subst + + + + false + ((!|!=|!==|%|%=|&|&&|&=|\*|\*=|\+|\+=|,|\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\?|\[|\{|\(|\^|\^=|\||\|=|\|\||~|\b(case|return|throw)\b)\s*)\s* + + comment|nsr1 + comment|nsr2 + comment|nsr3 + regexp + + + + diff --git a/src/res/highlighter/sql.xml b/src/res/highlighter/sql.xml new file mode 100644 index 0000000..deb2a3e --- /dev/null +++ b/src/res/highlighter/sql.xml @@ -0,0 +1,285 @@ + + + + + false + '' + + + false + "" + + + false + \\[\s\S] + + + ' + ' + + noname1 + backslash_escape + + + + " + " + + noname2 + backslash_escape + + + + ` + ` + + backslash_escape + + + + (\b0[xX][a-fA-F0-9]+|(\b\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?) + + + + (begin|start|commit|rollback|savepoint|lock|alter|create|drop|rename|call|delete|do|handler|insert|load|replace|select|truncate|update|set|show|pragma|grant)\b(?!:) + ; + true + + all + partial + global + month + current_timestamp + using + go + revoke + smallint + indicator + end-exec + disconnect + zone + with + character + assertion + to + add + current_user + usage + input + local + alter + match + collate + real + then + rollback + get + read + timestamp + session_user + not + integer + bit + unique + day + minute + desc + insert + execute + like + ilike + level + decimal + drop + continue + isolation + found + where + constraints + domain + right + national + some + module + transaction + relative + second + connect + escape + close + system_user + for + deferred + section + cast + current + sqlstate + allocate + intersect + deallocate + numeric + public + preserve + full + goto + initially + asc + no + key + output + collation + group + by + union + session + both + last + language + constraint + column + of + space + foreign + deferrable + prior + connection + unknown + action + commit + view + or + first + into + float + year + primary + cascaded + except + restrict + set + references + names + table + outer + open + select + size + are + rows + from + prepare + distinct + leading + create + only + next + inner + authorization + schema + corresponding + option + declare + precision + immediate + else + timezone_minute + external + varying + translation + true + case + exception + join + hour + default + double + scroll + value + cursor + descriptor + values + dec + fetch + procedure + delete + and + false + int + is + describe + char + as + at + in + varchar + null + trailing + any + absolute + current_time + end + grant + privileges + when + cross + check + write + current_date + pad + begin + temporary + exec + time + update + catalog + user + sql + date + on + identity + timezone_hour + natural + whenever + interval + work + order + cascade + diagnostics + nchar + having + left + call + do + handler + load + replace + truncate + start + lock + show + pragma + exists + number + count + sum + min + max + avg + + + string|ref1 + string|ref2 + string|ref3 + number + + + + /\* + \*/ + + + -- + $ + + + \ No newline at end of file diff --git a/src/res/highlighter/xml.xml b/src/res/highlighter/xml.xml new file mode 100644 index 0000000..599ee67 --- /dev/null +++ b/src/res/highlighter/xml.xml @@ -0,0 +1,131 @@ + + + + + + [A-Za-z0-9\._:-]+ + + + " + true + + + false + =" + " + true + + value|1 + + + + ' + true + + + false + =' + ' + true + + value|2 + + + + [^\s/>]+ + + + false + = + + value|3 + + + + false + true + + attribute + noname + noname2 + noname3 + + + + + <\? + \?> + + + false + \[ + \] + + + <!DOCTYPE + > + + noname4 + + + + <!-- + --> + + + <\!\[CDATA\[ + \]\]> + + + false + </style> + true + css + + + <style(?=\s|>|$) + > + + style + + + tag_internal + + starts_css + + + false + </script> + true + javascript + + + <script(?=\s|>|$) + > + + script + + + tag_internal + + starts_javascript + + + false + <% + %> + javascript + + + [^ />]+ + + + </? + /?> + + title + tag_internal + + + + diff --git a/src/res/jquery.js b/src/res/jquery.js new file mode 100644 index 0000000..8f3ca2e --- /dev/null +++ b/src/res/jquery.js @@ -0,0 +1,167 @@ +/*! + * jQuery JavaScript Library v1.4.4 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Nov 11 19:04:53 2010 -0500 + */ +(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h= +h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La, +"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this, +e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a, +"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+ +a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/, +C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j, +s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this, +j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length}, +toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j=== +-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false; +if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload", +b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&& +!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&& +l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
    a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), +k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, +scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= +false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= +1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
    ";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
    t
    ";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= +"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= +c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); +else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; +if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, +attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& +b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; +c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, +arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= +d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ +c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== +8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== +"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ +d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= +B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== +"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== +0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); +(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; +break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, +q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= +l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, +m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== +true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== +g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- +0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== +i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; +if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, +g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); +n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& +function(){var g=k,i=t.createElement("div");i.innerHTML="

    ";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| +p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= +t.createElement("div");g.innerHTML="
    ";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? +function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= +h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): +c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, +2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, +b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& +e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, +""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
    ","
    "];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; +else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", +prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- +1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); +d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, +jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, +zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), +h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); +if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= +d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; +e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, +ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== +"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
    ").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; +A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= +encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", +[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), +e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); +if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", +3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} +var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/src/res/markdown/bold.png b/src/res/markdown/bold.png new file mode 100644 index 0000000000000000000000000000000000000000..f9016b9f2a808d3edb12837c43e5f9977214e21f GIT binary patch literal 345 zcmV-f0jB`P|2Qe@tA;usXj9qZ>I03#NfMgEf<_Tqq;@r(RY4A*2B4WU zleA7r-3AVb`+?N1CpOl2Ad;8>nY+0gb3pnK#TY=eB$U*WGyopX@^x<&ypEP(+IxPl zR>8^~*ggpdR4_?~i?4v>(9U;BCW;Y-P5?kDDJf7hwgIs3 z)t;-`wH%j$>iItn$t*XF3P3XPB%O(sgq6=`op`#W98c|tnt<7KX5@?KQ>>U{05f|+ rV|zm5_Jl(}CY*2r=bzCD@Y?_%SdCl5CJtT+00000NkvXXu0mjf7;1{K literal 0 HcmV?d00001 diff --git a/src/res/markdown/code.png b/src/res/markdown/code.png new file mode 100644 index 0000000000000000000000000000000000000000..320e24baf90cef3e95f5a158ebafba737cae13ef GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DEP+H z#W5t}@Y`tzc@Hb_xXK3|Ur@cIxkP|1abik;N&kxK9qmFVR|y%Ygz70oPMmO%|4;5C zmlo~gz1{BYFBoJWh%h{#!!F05WE83^Ht{f9PXj~p*IU9(zeQpi9_lmf>}{OTbpD!Y z8bboZv;1XcwbNJx%r0&Eb8a?6$%D0*HTOS%HF>Ax$wm=o@pbiIdnId+$aIul>l6GS z`sg#8EbG*DsdMcwu{-_R=knz49`9_fE17MFbZ-U)C_AZcI2tgoOfc$nz`<6h@*M5M zG0I*6t{>-K>L@eo+j(jA)@fQhn1eSx*3bN}`<_8|0doxR2gWUYlbPxl-w6Zyo59o7 K&t;ucLK6U{gnUT= literal 0 HcmV?d00001 diff --git a/src/res/markdown/code_syntax_notice.html b/src/res/markdown/code_syntax_notice.html new file mode 100644 index 0000000..3ddc86c --- /dev/null +++ b/src/res/markdown/code_syntax_notice.html @@ -0,0 +1,8 @@ + + + + + + In order to make MdCharm Code Syntax works properly, we append some code to your custom css file.
    Please see Here for more detail. + + diff --git a/src/res/markdown/code_syntax_patch.css b/src/res/markdown/code_syntax_patch.css new file mode 100644 index 0000000..97f9efa --- /dev/null +++ b/src/res/markdown/code_syntax_patch.css @@ -0,0 +1 @@ +/* code syntax highlight. Documentation: http://www.mdcharm.com/documentation/code_syntax_highlighting.html#custom_your_own */ pre .literal, pre .comment, pre .template_comment, pre .diff .header, pre .javadoc { color: #008000; } pre .keyword, pre .css .rule .keyword, pre .winutils, pre .javascript .title, pre .nginx .title, pre .subst, pre .request, pre .status { color: #0000FF; font-weight: bold } pre .number, pre .hexcolor, pre .python .decorator, pre .ruby .constant { color: #0000FF; } pre .string, pre .tag .value, pre .phpdoc, pre .tex .formula { color: #D14 } pre .title, pre .id { color: #900; font-weight: bold } pre .javascript .title, pre .lisp .title, pre .clojure .title, pre .subst { font-weight: normal } pre .class .title, pre .haskell .type, pre .vhdl .literal, pre .tex .command { color: #458; font-weight: bold } pre .tag, pre .tag .title, pre .rules .property, pre .django .tag .keyword { color: #000080; font-weight: normal } pre .attribute, pre .variable, pre .lisp .body { color: #008080 } pre .regexp { color: #009926 } pre .class { color: #458; font-weight: bold } pre .symbol, pre .ruby .symbol .string, pre .lisp .keyword, pre .tex .special, pre .prompt { color: #990073 } pre .built_in, pre .lisp .title, pre .clojure .built_in { color: #0086b3 } pre .preprocessor, pre .pi, pre .doctype, pre .shebang, pre .cdata { color: #999; font-weight: bold } pre .deletion { background: #fdd } pre .addition { background: #dfd } pre .diff .change { background: #0086b3 } pre .chunk { color: #aaa } pre .markdown .header { color: #800; font-weight: bold; } pre .markdown .blockquote { color: #888; } pre .markdown .link_label { color: #88F; } pre .markdown .strong { font-weight: bold; } pre .markdown .emphasis { font-style: italic; } \ No newline at end of file diff --git a/src/res/markdown/default.css b/src/res/markdown/default.css new file mode 100644 index 0000000..016e49a --- /dev/null +++ b/src/res/markdown/default.css @@ -0,0 +1 @@ +/* CSS stylesheet is based on killwing's flavored markdown style: https://gist.github.com/2937864 */ body{ margin: 0 auto; font: 13px/1.231 Helvetica, Arial, sans-serif; color: #444444; line-height: 1; max-width: 960px; padding: 5px; } h1, h2, h3, h4 { color: #111111; font-weight: 400; } h1, h2, h3, h4, h5, p { margin-bottom: 16px; padding: 0; } h1 { font-size: 28px; } h2 { font-size: 22px; margin: 20px 0 6px; } h3 { font-size: 21px; } h4 { font-size: 18px; } h5 { font-size: 16px; } a { color: #0099ff; margin: 0; padding: 0; vertical-align: baseline; } a:link,a:visited{ text-decoration:none; } a:hover{ text-decoration:underline; } ul, ol { padding: 0; margin: 0; } li { line-height: 24px; margin-left: 44px; } li ul, li ul { margin-left: 24px; } ul, ol { font-size: 14px; line-height: 20px; max-width: 540px; } p { font-size: 14px; line-height: 20px; max-width: 540px; margin-top: 3px; } pre { padding: 0px 4px; max-width: 800px; white-space: pre-wrap; font-family: Consolas, Monaco, Andale Mono, monospace; line-height: 1.5; font-size: 13px; border: 1px solid #ddd; background-color: #f7f7f7; border-radius: 3px; } code { font-family: Consolas, Monaco, Andale Mono, monospace; line-height: 1.5; font-size: 13px; border: 1px solid #ddd; background-color: #f7f7f7; border-radius: 3px; } pre code { border: 0px; } aside { display: block; float: right; width: 390px; } blockquote { border-left:.5em solid #40AA53; padding: 0 2em; margin-left:0; max-width: 476px; } blockquote cite { font-size:14px; line-height:20px; color:#bfbfbf; } blockquote cite:before { content: '\2014 \00A0'; } blockquote p { color: #666; max-width: 460px; } hr { height: 1px; border: none; border-top: 1px dashed #0066CC } button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; } button, input { line-height: normal; *overflow: visible; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } input[type=checkbox], input[type=radio] { cursor: pointer; } /* override default chrome & firefox settings */ input:not([type="image"]), textarea { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } input[type="search"] { -webkit-appearance: textfield; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } label, input, select, textarea { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; font-weight: normal; line-height: normal; margin-bottom: 18px; } input[type=checkbox], input[type=radio] { cursor: pointer; margin-bottom: 0; } input[type=text], input[type=password], textarea, select { display: inline-block; width: 210px; padding: 4px; font-size: 13px; font-weight: normal; line-height: 18px; height: 18px; color: #808080; border: 1px solid #ccc; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; } select, input[type=file] { height: 27px; line-height: 27px; } textarea { height: auto; } /* grey out placeholders */ :-moz-placeholder { color: #bfbfbf; } ::-webkit-input-placeholder { color: #bfbfbf; } input[type=text], input[type=password], select, textarea { -webkit-transition: border linear 0.2s, box-shadow linear 0.2s; -moz-transition: border linear 0.2s, box-shadow linear 0.2s; transition: border linear 0.2s, box-shadow linear 0.2s; -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); } input[type=text]:focus, input[type=password]:focus, textarea:focus { outline: none; border-color: rgba(82, 168, 236, 0.8); -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); -moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1), 0 0 8px rgba(82, 168, 236, 0.6); } /* buttons */ button { display: inline-block; padding: 4px 14px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 13px; line-height: 18px; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); background-color: #0064cd; background-repeat: repeat-x; background-image: -khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd)); background-image: -moz-linear-gradient(top, #049cdb, #0064cd); background-image: -ms-linear-gradient(top, #049cdb, #0064cd); background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd)); background-image: -webkit-linear-gradient(top, #049cdb, #0064cd); background-image: -o-linear-gradient(top, #049cdb, #0064cd); background-image: linear-gradient(top, #049cdb, #0064cd); color: #fff; text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); border: 1px solid #004b9a; border-bottom-color: #003f81; -webkit-transition: 0.1s linear all; -moz-transition: 0.1s linear all; transition: 0.1s linear all; border-color: #0064cd #0064cd #003f81; border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); } button:hover { color: #fff; background-position: 0 -15px; text-decoration: none; } button:active { -webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); } button::-moz-focus-inner { padding: 0; border: 0; } /* table */ table { border-spacing: 0; border: 1px solid #ccc; } td, th{ border: 1px solid #ccc; padding: 5px; } /* code syntax highlight. Documentation: http://www.mdcharm.com/documentation/code_syntax_highlighting.html#custom_your_own */ pre .literal, pre .comment, pre .template_comment, pre .diff .header, pre .javadoc { color: #008000; } pre .keyword, pre .css .rule .keyword, pre .winutils, pre .javascript .title, pre .nginx .title, pre .subst, pre .request, pre .status { color: #0000FF; font-weight: bold } pre .number, pre .hexcolor, pre .python .decorator, pre .ruby .constant { color: #0000FF; } pre .string, pre .tag .value, pre .phpdoc, pre .tex .formula { color: #D14 } pre .title, pre .id { color: #900; font-weight: bold } pre .javascript .title, pre .lisp .title, pre .clojure .title, pre .subst { font-weight: normal } pre .class .title, pre .haskell .type, pre .vhdl .literal, pre .tex .command { color: #458; font-weight: bold } pre .tag, pre .tag .title, pre .rules .property, pre .django .tag .keyword { color: #000080; font-weight: normal } pre .attribute, pre .variable, pre .lisp .body { color: #008080 } pre .regexp { color: #009926 } pre .class { color: #458; font-weight: bold } pre .symbol, pre .ruby .symbol .string, pre .lisp .keyword, pre .tex .special, pre .prompt { color: #990073 } pre .built_in, pre .lisp .title, pre .clojure .built_in { color: #0086b3 } pre .preprocessor, pre .pi, pre .doctype, pre .shebang, pre .cdata { color: #999; font-weight: bold } pre .deletion { background: #fdd } pre .addition { background: #dfd } pre .diff .change { background: #0086b3 } pre .chunk { color: #aaa } pre .markdown .header { color: #800; font-weight: bold; } pre .markdown .blockquote { color: #888; } pre .markdown .link_label { color: #88F; } pre .markdown .strong { font-weight: bold; } pre .markdown .emphasis { font-style: italic; } \ No newline at end of file diff --git a/src/res/markdown/italic.png b/src/res/markdown/italic.png new file mode 100644 index 0000000000000000000000000000000000000000..d870e892eb6b4198ca17dd1f71e6f26486185254 GIT binary patch literal 288 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5D0s=! z#W5t}@Z0I9c@G%~xTLF|@=(k>@`bTAfJ2kzmBJUp7@3pp3DRZeVu!SUCOmz7v3m9q z!H_N$o7WBJOqu@FF$i|H9_BvzTaJaPq1bFc;|xZfSJsnrq!?x#+xk^%k{OFj%Bf}3 z<$NomE?%Eg%^D$HXjbndb*KHz|1X{OtK~W*|E2efPn7>Ri%GYRn^o(N7+1~5=^_yi zu4@H6zOMD*_%TlApHJO^ysno>LL}ezU5f>0mA{r+Uc5FmIk-eLxS(@Hn zzkcuC`9oonaf?dspRZ2U_nmvsedk*+Er2B7frR%ZYE;2XtHi>C%t z;O$M>$*jYgGknxUKM5^`x#x*A%1E zT$d3P8ole-hp&-eMPSLsOOrgTBRe*6j#5H1uv;zQc@8Q~2nZo)sx3i>`Tn-R&=EV1 zZ<|C0Rba`+OOr*xb4fpDy01h+qoKAKrFma|=CrmqB+mZh=16^#f}$j7zE_4$)4lBh zAtSbbvHf*2C<60Wo|vZAhGa$QlYBu*&{SK3g4`p!YYH+C18{3~hKZ9GZT~7dF2xs= z1g-ZgU@_m@p$U%Kc5=rQ@=^lxSDu)o)(-tw+go~{Yh9SM&~^d$sn zsVhTa?vZ`f1*i8(z_~bR%%P0TYz=;Eg4~$0#P6^)mNbC>d^z$`KR{jlhaql zPknc*x56I{Ro4wEOAlQ;P5yQ0tAI8tZS;u%S>7Jmm->$4q;?o**g8S;k+QiWw)Tw zm|NJ%=w(p zRyfAb{ZndP`31Q?Iela7T4d?<}$`z(Hek*MXen5G7n&-*9)K$Y!kr)@-`CYd zp11=G*L~wl`O1KZN_M#>`xwrHz`b6+wfuSRe+<_TDsF z>g63gJc;x63o!zqdQFunj$V_z+jOT`6nVEYAi_zAz}vc-9$W1U0L**uY#8shT?>hx zswH4xX)&Vuhkxv>DaiaZDq;TO;fe2LC<0#r&x;Uv79F)$jHTIo)8q<&Rs4)~SrjFa z^mNq4axPmNMa!-`Z5BqZ&_J&6=XuU4JP8f}%!0K!emrZv5Hw;+kVw(6wi;2Be|%?6 zLFT87%*Uw>GmQ6AN5aDk5P3H&cdr^tbM~gm6>3#n>e?)ykR+|Z*+8}3N%y$z2kOhu z^8k1+g+J9}u6|~xW7xjd)f(OS1Of)EtqXO<|2)zU*XgIT9LIjp({OXFw>HLLZ7ngD z<$Rnb_fae3QrDjI8j_?H2o2uDqN~-|=d@cH02~0Yj-FrCW*JfB6C`rg!@}a=w6@nv zz5LjSgoSKv{s{qqIX-pGELUarqUJl-a(q-nybP&pPRoKO1PVj~k?n@9@mgVP&CQE` zLDAIn{@NKzBKxYZt?Es0Z44;622rmr$&e`mixt}Fy#64mkEYGm+x|EIkWspk6PIT) zffFKlLW8h9a5P;hD$6~*)Z@0>K#1_X0u&(x(|&h!v&>JoOQw#7C<<_y3vHa+HUImQ zD{u4%CcSki#LLGwhYlDYPe6lkR=8TO7M0~5esIqNBAgTm-)Ctc&n+?o2t#o>P>)R@6a#pyaJPOl09W|yoB-kNkGbc+ zNB{sPEILSYeT~~S!)I=x6cG?b0iyM`ow(X=QZoNIQ5hTh0Nw|W>D!{RD=!a!WGEfC za1Sjy%{x_5GdB@$9S9*12>~GhAq14C4}9BqMdd$*6Op&~w7l?W{YX1bSnv@8w(TQj z#H{xPc__$30gAUmY|r}%Jj@9~UJCxiu{?J6Hb%mAZc*tbyf4x!1&9E?tKvra)lXIo zR_4!qn2emfnlV?MS9y6W4WcM;-KINt0k{?Z!(0!}ylGG#l~0|BBF8PJ``W8%drvb> mQ8bbH`17L2B@9O9{p-Kf{=3GiGSGwo0000 + + + + + %2 + %3 + + + %4 + + \ No newline at end of file diff --git a/src/res/markdown/markdown.ico b/src/res/markdown/markdown.ico new file mode 100644 index 0000000000000000000000000000000000000000..fdf562d9bd11fd6e2390ef1f0e0e1de755e65bfb GIT binary patch literal 137969 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
    gy@>X&=0g*u$V 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

    M^74%uC+Za^oYTF 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 BiL3ws literal 0 HcmV?d00001 diff --git a/src/res/markdown/markdown.js b/src/res/markdown/markdown.js new file mode 100644 index 0000000..984864b --- /dev/null +++ b/src/res/markdown/markdown.js @@ -0,0 +1 @@ +$(document).ready(function() { try { /* XXXX.YYY.connect(xxx); */ //markdownWebkitHandler.updateContent.connect(updateContent); markdownWebkitHandler.domReady(); } catch(e) { alert(e); } } ); \ No newline at end of file diff --git a/src/res/markdown/markdown128.png b/src/res/markdown/markdown128.png new file mode 100644 index 0000000000000000000000000000000000000000..b1467a201ddd9337a77fa7ae63d30145b1a6c5b5 GIT binary patch literal 5007 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 Z{vY=t#fq}_sV@Kk002ovPDHLkV1gRvnE(I) literal 0 HcmV?d00001 diff --git a/src/res/markdown/markdown16.png b/src/res/markdown/markdown16.png new file mode 100644 index 0000000000000000000000000000000000000000..afc5053941e9431660d1c3f7a55f5479de57f4c9 GIT binary patch literal 632 zcmV-;0*C#HP)UFn7Anyst4m({y!V_Li``fVCJqcU4E#Ue3}e=p zui=}udr?|G10d}#HjC+n_OsWkDj=u|MSJUIIy!aTsVZ)uGE<(pa2!1O_d3bs7<-4K zzW8KQ0cTx8=~1VwS67#k6nMhHH^b-<;A`xqn`8-aoYPz@wm43x;58-zUxv`-LF z`Ub|>|Ga@>5Wu3|qRptcf~p{)06d}|4gN2gVCeqYcD+XMO$IbULV@Yy3IYVQU+q*4Sv-iU5^B2y7~*7r&Vwpud76CG z<3ChLFMp!@c$T3@Pq7nCT6a#tP6=%!5F)gh;J~0Nq@@gZvBlQ?ql_#(Wc%C|?2$V0 z=~ZMkqx;|_>DA}h>M#7O!)Q?O5l%ILvDnBW7UHuj*s&Z_@?__~(ReluRw-UglHF<` zUn|JkAZkE-z=_WRgG(zEO9#;PVO--7+DhR|727SNOVh;1TC{Im!d4XHz`U;_+QFZrf0v^Ph*1ZHJl232P|~7icF!bBi7CQoNoZnp>mzSVqRz-zF=wH-7`BXDxFw SC=v$%0000>-p#YcvFgkrA<&Si*mRCmAq`+oDlLe^i$0-!*$z+PkIzz{J-X#&QFaYwH2&-^ zwm&pUxJtisIkzc-RZ5|hMxhFi1tBPT9{?Jmx=rH>MWc8uU{)H7)+lX3Y5pHW1W*R0 zElOLgQUnN>={nc$7SJa7v7NAku18uP)4sj{YWf-r4@oK~1 z12_|bRv53g0}#|u+9ylxL%p#yrOl9RAGy(1h5{C|)}8^f%y zTk^aZX%geI6u(+~b9hktw&ipVyZ=I7`5TMR{)(s{aQOJ`xK03C;i>T*h(MM8=E@eS z7(syGRVS!6ucNW+Moe#m+QfCF{dLd^zd5-B4%F#a{z($94t2_wgJQipetkFZIDTq4 z7j;z5f%?vHz<71yaCM;CTO;ts^15Bj^LMLKc^M2lQYJD5NfeT(5(gA`)roEEz_9!x=u*iRKrunL(j-~y;nppZm@)@; z2Im3RigG`Y6%OP}TwmkF!)ZMdB5^{$cXb;akfG5L0IrCD6j{(F4qFfzB=(Vt!HNue z2Zz!B8wdU~&N4Jyj!L#XLHL8CxI34TXcLJm(6j>x@=)38e^(1eX(dIMRyLfCIv@n; zDLS9JllbiQm>U8rU;GL2pA53;A^s48tu53W)dDGnSCvAZ&%45bG7K4bim``(Pv?^- zQ4PTeX zBbQ#0p%qGt3;s}bHk#it!s0C6;rzFK)AvVLN0zks@|6(rI2pTiD20-tdA7tW3XGs3M zhw!m?qozY7E!IkrzD{x-5TwLGgJzoOH`4@<%;zU~_!xF(5&!)ivX>@tD|NaLoMikP z7jYNzD_j4Ehw2av#H`lrvc{*`&sr6%0{5c<9ci~0jLFA}+D zL2C);`UsG$Op@L6Yuu@fL_xCB$6VN;fA4dMKm-IAuaadolCPi6ooGk+ofheL&JaC0 zkJyyTXBW9P)uVspJ;?1Xl4HL`miFfn$Q0_OKi-Ykm?2Aghmwu;Q?BPBp#@i5*+k<4 zgM>OypGpSSA&DMBBsM3MBpTvkopq3EBr-Tj!K8@JVYZzEr%h{i+09(#-w^d^6WAid QTmS$707*qoM6N<$g0{*JTL1t6 literal 0 HcmV?d00001 diff --git a/src/res/markdown/markdown48.png b/src/res/markdown/markdown48.png new file mode 100644 index 0000000000000000000000000000000000000000..418aa3f460d2563c563d060e4de82779bf21df0a GIT binary patch literal 3092 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 ixuqA9N(cEX0Q@h=gJz5N&$vYZ0000Ya_xk&Ow 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 literal 0 HcmV?d00001 diff --git a/src/res/markdown/markdown_cheat_sheet.md b/src/res/markdown/markdown_cheat_sheet.md new file mode 100644 index 0000000..8a6c54d --- /dev/null +++ b/src/res/markdown/markdown_cheat_sheet.md @@ -0,0 +1,271 @@ +Markdown(Extra) Cheat Sheet +==================== +* [Blockquotes\(Ctrl+Q\)](#blockquotes) +* [Code Block](#codeBlock) +* [Emphasis](#emphasis) +* [Headers](#headers) +* [Horizontal rules](#horizontalRules) +* [Images\(Ctrl+Shitf+L\)](#images) +* [Inline Html](#inlineHtml) +* [Links\(Ctrl+L\)](#links) +* [Lists](#lists) +* [Paragraph](#paragraph) +* [Table](#table) +* [Literal Characters](#literal) +* [Footnote](#footnote) +* [Math](#math) +* [Multi-Markdown](#mmd) + +Blockquotes {#blockquotes} +-------------------------- +ShortCuts: `Ctrl+Q` +Markdown uses email-style `>` characters for blockquoting. +`> This is a blockquote` +****************************************************************************** +Code Block {#codeBlock} +----------------------- +Indent 4 spaces or 1 tab +```markdown +This is a normal paragraph: + + This is a code block. +``` +**or (code syntax highlight)** + + ```javascript + for(var i=0; i<100; i++) + console.log("Hello, MdCharm"); + ``` +Result: +```javascript +for(var i=0; i<100; i++) + console.log("Hello, MdCharm"); +``` +**or** + + ~~~~~~~~~~~~~~~~~~~~~ + Here is the code + ~~~~~~~~~~~~~~~~~~~~~ +****************************************************************************** +Emphasis {#emphasis} +-------------------- + | Shortcuts | Syntax | Result +-------|-----------|---------------------|------- +Italic | Ctrl+I | \*italic\* **or** \_italis\_ | *italic* +Bold | Ctrl+B | \*\*bold\*\* **or** \_\_bold\_\_ | **bold** +Strike through | Ctrl+T | \~~Strike through~~ | ~~Strike through~~ +****************************************************************************** +Headers {#headers} +------------------ +```markdown +This is an H1 +============= + +This is an H2 +------------- +``` +*or* +```markdown +# This is an H1 + +## This is an H2 + +###### This is an H6 +``` +**Header Id Attribute** +```markdown +## Header 2 ## {#header2} +``` +*or* +```markdown +Header 1 {#header1} +======== +``` +**Link back to header** +```markdown +[Link back to header 1](#header1) +``` +****************************************************************************** +Horizontal rules {#horizontalRules} +----------------------------------- +```markdown +* * * + +*** + +***** + +- - - + +--------------------------------------- + +_ _ _ +``` +****************************************************************************** +Images {#images} +---------------- +Markdown Input | Result +---------------|------- +`![MdCharm](qrc:/mdcharm.png "MdCharm")` | ![MdCharm](qrc:/mdcharm.png "MdCharm") + +**width and height attribute** +*Please [enable Multi-Markdown](#mmd "") first.* +``` +![MdCharmWH][] + +[MdCharmWH]: qrc:/mdcharm.png width=100px height=100px +``` +**result** +

    +MdCharmWH +
    MdCharmWH
    + +****************************************************************************** +Inline Html {#inlineHtml} +------------------------- +```markdown +This is a regular paragraph. + +
    + + + +
    Foo
    + +This is another regular paragraph. +``` +****************************************************************************** +Links {#links} +-------------- +Markdown Input | Result +-------------------------------------------------------------------------|------------------------------------------------------------------------ +`This is [an example](http://example.com/ "Optional Title") inline link.`| This is [an example](http://example.com/ "Optional Title") inline link. +`` | +****************************************************************************** +Lists {#lists} +-------------- +**Unordered lists** +Asterisks, plus signs or dashes: +```markdown +* Red +* Green +* Blue + ++ Red ++ Green ++ Blue + +- Red +- Green +- Blue +``` +**Ordered lists** +```markdown +1. One +2. Two +3. Three +``` +****************************************************************************** +Paragraph {#paragraph} +---------------------- +```markdown +One or more consecutive lines of text +separated by one or more blank lines. + +This is another paragraph. +``` +**Line Break** +To create a line break, end a line in a paragraph with two or more spaces +****************************************************************************** +Table {#table} +-------------- +```markdown +First Header | Second Header +--------------|-------------- +Content Cell | Content Cell +Content Cell | Content Cell +``` +or +```markdown +|First Header | Second Header| +|--------------|--------------| +|Content Cell | Content Cell | +|Content Cell | Content Cell | +``` +Left aligned +```markdown +| Item | Value | +|----------|:------| +| Computer | $16 | +``` +Right aligned +```markdown +| Item | Value | +|----------|------:| +| Computer | $16 | +``` +Center aligned +```markdown +| Item | Value | +|----------|:-----:| +| Computer | $16 | +``` +****************************************************************************** +Literal Characters {#literal} +------------------ +from +> Markdown allows you to use backslash escapes to generate literal +> characters which would otherwise have special meaning in Markdown's +> formatting syntax. For example, if you wanted to surround a word with +> literal asterisks (instead of an HTML `` tag), you can backslashes +> before the asterisks, like this: +> +> \*literal asterisks\* +> +> Markdown provides backslash escapes for the following characters: +> +> \ backslash +> ` backtick +> * asterisk +> _ underscore +> {} curly braces +> [] square brackets +> () parentheses +> # hash mark +> + plus sign +> - minus sign (hyphen) +> . dot +> ! exclamation mark + +Footnote[^footnote] {#footnote} +-------- +``` +Footnote example[^footnote]. + +[^footnote]: I am a footnote. +``` +[^footnote]: I am a footnote + +Math {#math} +---- +**Please [enable Multi-Markdown](#mmd "") first.** +*Export to PDF, ODT and Live Preview are not supported* +form: http://fletcher.github.com/peg-multimarkdown/#math +> ``` +> HTML Header: +> +> An example of math within a paragraph --- \\({e}^{i\pi }+1=0\\) +> --- easy enough. +> +> And an equation on it's own: +> +> \\[ {x}_{1,2}=\frac{-b\pm \sqrt{{b}^{2}-4ac}}{2a} \\] +> +> That's it. +``` + +Multi-Markdown {#mmd} +-------------- +**Enable Multi-Markdown:** Select `Settings` -> `Preference...` -> `Environment`, change `Markdown Engine` to `MultiMarkdown` \ No newline at end of file diff --git a/src/res/markdown/picture.png b/src/res/markdown/picture.png new file mode 100644 index 0000000000000000000000000000000000000000..a3c907fc34ac5d6e9ed12915c68e8503d43f2671 GIT binary patch literal 1256 zcmVP)HYpl1g@8Hu#M-S zwC5V@?djg}%u`S3QUGdTfadwnGUx^ z1{w^HZti6Lx_(L#bY+Cd!N`>=Q#e7#6- zZy&irfzUyAVv6dm-&t+TD=yK#{=S2YRB+*skdE;x6XPLXm7$s6=r$GFAe8NeeBtCG zfElMul|ALyW7N}0g*N)c_E~^NfpsKuNFqcMCc*(5E-~HEq4X5U=LcK z1xY)At6HK;h>Arst$iZ!5IC3!mA`Jc_KCqR!{B{2Z-~8891og(q8XreV|edT01AZy z&N(jqaJlLCOaPq$Bs>HHw?a=xsyTSl!oD48Qi056D)w-@Gl563O+`rUvsAUV@`*|e zULjlZtV%CleRm3s%x20%=wQa)Q3b7ziAJ%s@`*XwHm%-~AkV5`91}w&F5+iP%#tBR z8e!)hWY{8jBLj_qX`4k&4$(=B&tf%4LT{0jJSmgM8VC@6y(w5DSi@{9l&2Np^df*? zTK* z)*cAs_!ALn`co z^ntgJyrFxa-{;HaGHch~uba1Q^*63xmvlNUVOX^bed5^u_wMx=YR&+_nXkUofq^yN zdB+1AHp-?=n`O=5AU!?3@p3-#|1stP0Oa#IZ5ANB_0CbZq))8GalF)^uC6X^_j`jZ zH~ELhPh1mZs2%TZ&be#Ba)SbE4n(nAdx=Zjo1qp zzHjo0S-+D1D1o}n9S|=Ikj>?Erc}~x4-NTzAs_$z)99Q}+yj6De%E74A)DF69k>GZGkL&Mljld-XGShLYnw{w9Zg`Z+wC>OQin$SiQ!a)5P5 z8`G6T4)Whv9O7O6>-`Z_aH)Q-I+byO&lC3poth06PGKOT^>DE?HkyoIzHkykXubDrx_JBkCrZ0JPwkzJJJPq@xYU4O zMH9L%dJe!e9Yn}SVQpzpz!s8i3y$TY_}7Nx_P!%xQG#bVC~X$R_xV{}vSULM!{`%7 zLOwHtd=DD4+}d2%rJ?^rF0+hWrb9ynC}Ie8u$GC5T>_xViBm<4Ph7xhZ z)fUm@uy>MRFhP4!gGoX}$rgeWrchm*F0MSwh z0D#(n$mjD&r_+^I>J(rd1W-6T8f#`s0rFdR?B&KWY>=F@ID%i){kR!;Udy# zbT>}{Aj*X%6|-YmR-{`!zMo0CSGYO=uoObPdHL$~we)DZG6k=U1GCSbw=`loK6Xns zMiPA;^S_c`#YUl!0ifhiV(`Xhv6!8kdvUIOSJcoIQkvco)%Nz*;^)PMnPe*E5;NoG z)NL)FUyz&Ey@6T1%d(iiA2I1Nv53t4CU%)QMw2l00000NkvXXu0mjfNQH{1 literal 0 HcmV?d00001 diff --git a/src/res/markdown/strike-through.png b/src/res/markdown/strike-through.png new file mode 100644 index 0000000000000000000000000000000000000000..55c6bd7c500312b926c1dd3a378f48416edc5d3d GIT binary patch literal 424 zcmV;Z0ayNsP)$fFW-2k@2*+vs@)Fp5aOiV%^a z126ytfUU&c0XU5n022VOJ|yGj`CbD+Sk%{IHQwid^+%=vu?OHu{|Z2~OA^oD8qT!` zumG?m?T<6uVIne;+$F+K^+g3`73eo?E&`A=DwYL6ujKZXFOqcn*o(+m%mNjkYdPDR z22_AS%v~H$9X!tpIqfs!LBVF{4ul#prL~~8$dU3)YZC`g`ja+#nK!=Qb}ltGoDAw0I` zX1&Nlt|Vp#jlVlQ>tEegzYxwmnVDH(ciX$-H}enN2(Wsmq_~vf`^D1&pG4;fi1DU$ zO#QCBUEx5f9bcAXR9M2*hO4Z)MG5~J%l;o=-Scg}dv)GvkOLV!UHx3vIVCg!0A+ef A_y7O^ literal 0 HcmV?d00001 diff --git a/src/res/markdown/untab.png b/src/res/markdown/untab.png new file mode 100644 index 0000000000000000000000000000000000000000..ed645b654bc12feb343800a3fd83195af79bc9c1 GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C^*T} z#W5t}@Z0dyd<_O1*~0(+Pk-&0Zc=E+7rn`!X=*HrscrZN?Xr{@k~!o0YMW zCqzvB=UM(GMh~>=UbC;t+W(2UQ!3*5fdX|iUJJ(~Q~fueZCpI_&HBiTZcmtc&aSdJ T?c{Y1=sE^ZS3j3^P6x-K)S1-y8qxh zI*vn#l7fyF3MvXpD1i1T03j%mkce^#cc9A|LgACrToA+ovyQyG-kC>XcC$YnoY?TB zm3H3F%=_N^-g~n{%#80T3g1~209?G$_)!R&R#FH{Ne~U+5HrvXGy-j00{*=E`{X+S zp!3&WU2=VM`5Qal-dzfQxqjj3Je|3+&^|ahzU##C2}mhMZ@JUOqbL8N?R>iFeR_LN zX}7ye3E=-`2bLvA4xrQVQ6i0%#>>4Do>c6)@ZBz&A3XTo(N~1MgAlkr+N&$_pV?cM zV&8)zMtP_FCgd|SNCyzi>f`FNrOMVi>T|D9pBr!^0t_*v*;>ii(jfps9opZ-)S;j7 zLv?&49=};`;_++)7f$^g>He+1#IvVu03b!AarodKOdgo%8@A1KMdL1Mh&jTsBRvN# z%R=*gJ^HSUF}*I4S{zQudsk|sfqI@7>GS4ltpNazXX|iWbwC zR><7bQ>rztudhe?@DV~p^Fp1J62OOO-NwEK%qAXzjR|=m(h@h^YPDe7Hf-C@e2ecB z-2)*b-PpImfQr^62b<#HLFP6tAW zjORp20EP`rl7eDGn*m7Fwr#`r{meHZlHwXdTAN;zwSRd$0=K2~=+rz%f&Q0f@oC0p$z7rXLz4Qie zS61c#7*(z8J-ym&F5bQS$IO?zqQJnU40J~{t=7kd=L_>U#%r}MF*ANUb5<-Zy;d*& s{;TA9o(!AUs4xJ~l}aVpH(v8U1K(sXNJjgyrvLx|07*qoM6N<$f<6zXhX4Qo literal 0 HcmV?d00001 diff --git a/src/res/markdown/write_read.png b/src/res/markdown/write_read.png new file mode 100644 index 0000000000000000000000000000000000000000..af5568e04102b301df38ea3db9c860cc15c72392 GIT binary patch literal 817 zcmV-11J3-3P)?(#3BznzB~JNC~RN+IA46mPPW?H z+nxD#zWHW%iJ5VbP2qr>0)YGDE2o8^Q6+^ilmt=d9WeuiqKl%dEdoA0zB%+80O-z> zg$I_SAMW$Uvxm&$m8bX4&d}JS+3fI8qWALoK}adO?>S$<+xI__-QId;Z>`-{u2tw& z0{E55!!Tsm0p#-zOmdN4nd_IZrDEHHV-=A3Yr`pYUlBbKLeX-NT~EvJ(-Veb$A%(C zUsUc3`OFM*ixA9eV|~?7eMSzcrZSJ~P90T>yY>+co2UbF0VV@i;UybOiuw4F-dVyoppUh0LXK3Mh?4 zgh(Xfeedk-cx4H zO4-mSA_!5sM{2DblC2qtno7w3nS>Rtg%Fh;z6`BbPUndP_`joX-bJ{kH} zytuTCwRAcOK&yDH|N43+Ge0%;;!OFiD9{m7n!XYB=H|xi=h>O@#K1s-m>D<5Zi%I( vMfL6L7t^+FSq9K85DdVL#bU*yi2>&yiGLG2)zB3!00000NkvXXu0mjfQ_^}> literal 0 HcmV?d00001 diff --git a/src/res/mdcharm-splash.png b/src/res/mdcharm-splash.png new file mode 100644 index 0000000000000000000000000000000000000000..341562132eef206d619414071115777bf933f02b GIT binary patch literal 116792 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 MAs=AhTAyRDzY#snkI&6VwxtIe4bpPz|yuh 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 b-#>nE{vZZpeR8M#0r--WQkJX`HxB$ik>*Hz literal 0 HcmV?d00001 diff --git a/src/res/mdcharm.ico b/src/res/mdcharm.ico new file mode 100644 index 0000000000000000000000000000000000000000..ad2dd3269311c91ddfa2edf691b0d5e1f545467d GIT binary patch literal 133343 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 bDhA*`g`4H_D05Bf00000NkvXXu0mjf>&9C* literal 0 HcmV?d00001 diff --git a/src/res/next.png b/src/res/next.png new file mode 100644 index 0000000000000000000000000000000000000000..e69afe083fb690d83f891428715fce770ac5d3c1 GIT binary patch literal 1180 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 s#_?MgKmh($07*qoM6N<$f`ZZjS^xk5 literal 0 HcmV?d00001 diff --git a/src/res/paste.png b/src/res/paste.png new file mode 100644 index 0000000000000000000000000000000000000000..0300b0c17738b9494fefb21f4890dc901ac8d6de GIT binary patch literal 1681 zcmV;C25$L@P)< zkz`^)OKW@xc@R_tAL5IMN$MDU@pXuua~kw&VKiZoOdGBklkJ1IU$+Ni-`OJ)+2 zY3J+C%)R&Qv)A(BeB5*A-k536WpmHjd!MuZ>%abMopp?=a<}8x&z$^7lc7IXtJQ(c zYtFfby0zn9{@U>uc@Iapl}(L?`_%Zv-d#gOLtWSZ^wJ-uf%gQ!WDJ~n@}V!)R^NU? zM8<$1+n@xj^d8{TpF7U{{Cvj)Mn^|E|HtPTSbho)K-_i{m7$VWq9~rHr}2`;wW*!D|I)_=;$ck zd$w=ij;gkSP*ny723TzvYKynA#^l`fR;t{$4BL-7+VC^j$;T^ZK$Sq^DEB<`z!(_+K+FgG>Nw{SQACrWN)FUo7~a-H&<>mQ7Zmd=AoKD7co$s;tW9$-eJBXyz z2|yG?bd&LxcV3k!R&LdM`Q8t{Po)%4;48J>F#~{zaO%`4mgUiKWMpBNsJ{h79SpSF zZ&$d8;zjc7dNaQ9H*4F0d=H^ZS(IyRO*O4=j zdR+=F(+Bqu7=yJf7INxS2MkcDR5*S5G^$CQ`}+Esn3(w1?Ck7Q9&nU;EoNOj>YEfxk+*#KgW2=U}Y0l*?s;u!M>dhL#`* z5Rr1dUbmqLs3_ihoQr5i5sD&Sab9q-;8FmIF+*q#Qx8AT7XO_3g8DUQ%o12d3B z>AhX4mx8w<+M1IVORWM9X%@8TPt>2xNp_uOL0${qE%g}_oiDCV2E-QtBp^hjt;UNN zX3oF9CF(OkQa#svHSp8l&+F02;iq4H<3GQe zeCR{mGf>UnVnLt;9aL*?W zb+lovQj#B&21?|y{R(p>Ts892h_^t^3veo b;Ku&|${`H}Jhd?U00000NkvXXu0mjfg9%En literal 0 HcmV?d00001 diff --git a/src/res/prev.png b/src/res/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..8c3510ab31eaebd5d8f6088cae71c8fafdbac215 GIT binary patch literal 1168 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 literal 0 HcmV?d00001 diff --git a/src/res/redo.png b/src/res/redo.png new file mode 100644 index 0000000000000000000000000000000000000000..1b30e91858f132df55548626b36f276dba733f84 GIT binary patch literal 1351 zcmV-N1-SZ&P) z(I^QTjV2@n5hH8VpoX}hiJ~N8Fwuku6ZMfLDv;J0+S*UASHDGRv8n zb9^YJ6uMli2`~MUd6+q8zVn-J`JXw2bB^0-5^l3i089W(0E`=8&6l-afe;_(T;H+2 zsCT@6NW4O{VaM@Q&9I)+P3sB6u;)vX5OgULEZYI4PLCpsb*e`$ce~`>JJuJ~kLB;> zEtT1X5MfM!wcBfEhNH$7$6?E|Gn199^dzJNeNa8h^*M%Np(7N*g-hKy-_pscW&h@% zq<;L>#^PUx^SNqk)e_C1dn}vTBLb}3UL!^|^G(yVKe)SKhI>w4DqM;LYB`{m1KVL3 zcvBJuD3S=5B7!lF#=p8ybFvk>Np}VO2^+VsEBbrD?`xk|F9}DDJ&!HSca>G19X&Sg$6@&6OdIM>U+kLf^Q`!0(}I1g zw^ltFiJ5yIU6|+g`@Gn-ujW>OHQTBKq3&2|PIgLRabXq+0iqfO`XzYFM6`E? zQE{Z6an8TBEa#<19?W-74fxO-HgTY=ZYaRoZAaCsp-4r3URrVC+zi+bgKpU4ZZT0L zNc1R(Y9@|UHDFG`3?v79&`cYYGL-ysVrU!o_C$ZkydzjVJAVdDi@`7*0Oa~O2LR3i zLT-3EvN0RziCRcd6+FA75W49gtWns`fI~hAu;Tqgs}sELr3G`cz&L?!UYo^u|DwYs zpOF4bAVWZh7zcWzHguDL+HBAb2MD_cuxd-?G($7D78d7%b089<5C9NxSkyvSS06%M zVML=kEX#%{3Q$!yk^(+VOPvB)k-^B1LM6+M$NGgX~+=X3}Jh?zFV zs#gNQBTFERgRI4!x>SUh_0%vGAdjYM@ZC4vl9r(Kb#NO{LQ|nX{1D4A( z$tHwwL6qTgCqk4&K~}u!>DdcE&B`fSHvisS$Swulk$)@yqe_)E5 z=yf617s1Jz^G@UG^6!sV?)->z*6B$|GCkfTC)D1+hI4k`;~QE;&==b;)*r2=^pkrY zxJyzM8Qpy&Cqxhc=IXC6$)?ugyvyg*7?0KH)=zMT+&BoLIhHe7Hje*SQ&Ac`&iq)dAGVjYWj z@mJqDm~X_4vfRSFsZ-N}=!;l`Ej*TS-mioYKebuVqE|_%vzaxWEsI-pSH4k_OHEpl zQ<$F;Obx)Y8DjeA$|(v2gaE2Xz|qp``2PoPw&iPok~l+aUQupZ^7H@{nZ)tI9L_a8 zU>rEI|Je8e0ATs+KbA1Y7mqB1w;(v++!(kCbu}L*|Mh70^b4O!RTVpxP-l}ICkO`s zS@Ck;q!f$%A7{giS@#o4W5ls3Mv_5(!vG=VItZCKoZfDLe*mfHc7!qP`S}0<002ov JPDHLkV1k$1c^v=% literal 0 HcmV?d00001 diff --git a/src/res/save.png b/src/res/save.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e2dd4baa750b87a17f360554352d77c6d6d299 GIT binary patch literal 1458 zcmV;j1x@;iP)PnK3h;bBL48ctxL=+^^Ng_rEopidVtLjy~ zd(Lt3URAyN(-|_%Tqx?*t@qyf&N<)roh$Cnx1YKYaNi6eNH4+%3T0=*ZvIfmWutI} zYg=U&-Wdh|$Dtg&TSfXRWUbut_|ngwJZAt2;N64%t82Z^8Gs>BhR9d%giaK==J55p zb~u z%ZjTxybkNVND7h1zwrPf!rTAp^Zf5FoPGKHPr9e>|LrKbDM(;{{|di+>QNqf;uqks z^M%haEX(>Wt+$H^AqHX$q!5TPl42l)KnMaN;EuVWYKRE@=@s7k=OONpRdIJNU3_Kz zHj^s3W+5g-A}2qxOMj5(Qt(!M2@rx1gAhXvBECm(RYO7mhyEZFf>0EIy8vtB^57uh z4(`NMaQe&NuLHJ%R#n9t7yk|-h)`ER>Pkqn2Qsa^>*rJ@G2{AwTi?6Yk_EiO5Sk5-DeLp*Yw_wVk$ugoFp%* zAhOT`X?W%fBy23#byfihf`ob+);y`7t6Kx9NQ4+EiZtWF3OP?exOx*?W_@-hGMrVw z4N^!v@xzy=1|rDJOqxbY>w2;;ZK<`iA3~Vpz)S@bC!FZd99P)hI?453GYDea?NZ!_M7XD~>v= z2AuQLpPz3MYzGkz{S5etmenu%0L$z(Gq&&eFn8T`oWY>ai@$jWfEXhWJ^bj>{Y46- z)NK^ym?>(F9h=T2xa@>00Mrk8OM;UZ+nkfN%_gN{YSE$rfb7~##8uxH`ha>v^KBLhHGp9dT2GA`M zAtW3pAUF_$)q9$cJ`s(}0}bn)w=Vzp?US2#ZgY4klS{8FV9v0tD2HPPg8@}l5n^Om zR!acJ>spWEW?)TPG2_z_0ABp{lXma!*B{B_^7-AfXScR@?qP6QaWkBH_+&LgEa-+r zOa-d8QZ+e`8^T(x0?kuNrk)^CYBkTF31KkUd-cNkr@v;V&$)l`_=%660*t-A0h_mX zIJS0-2Os*@$_yrJf4@&P8qIv5J>)QMI$i*vRh<~%Ar^?cv$=h*xaCecyz=SKf9ZR_ z>UKBoNkxws({*t|)l9C+>Cs^Ce;5CF;rCBJ)?5GJWtsHpwylqg=5c55@*6h+w*be0 z;s%U_5wI^I?)BDg=}X&G);Dey(;Ua6!;ZTpAlv`|22!P>SD9A&e|{(rlf)5y#{d8T M07*qoM6N<$g27n3MF0Q* literal 0 HcmV?d00001 diff --git a/src/res/saveas.png b/src/res/saveas.png new file mode 100644 index 0000000000000000000000000000000000000000..4ac76f6b993025e0a9b05641e4ba3d970a264963 GIT binary patch literal 2194 zcmV;D2yOR?P)&{{P%rBM?SDUJQmSWRQURa2xyt=2YeVi81LE&YT>ZEVqsL92qOTo#sP zcjxln&UyOb9hLzWn`)nAGVi?a%z6IL|NQ^Yc}9r{H{ZWQBhsR^RyhM_`IebQH@-0= zs^|-f&lW+VI2vmCprnS6#fGv~BsLb8M28-|XQc!10FDip-)zWbdH||Gpbb~HL&hlh z%<0pZ*dV<8VTKVeNN)&KtMT?LZhPp`Z5w{*yldBO^T711T)vo=<~#tKpMIH!27|Nq zQx{ibT%CkhAfGdM+Hn0hmZOwn|G_fPys%@%>o0E1b}o88^u$?EnxUZ){&4?wTyy7> zATV>;LaITKu2Q$UQVMM}#%MgPF~;B-jn*2i6(|KFIB{6(P)floJK6V92@y!FMMOBX zXJ@|2sn`>nD2#_vhG}Q_Qm!N?l^}Kbl?JU9Mk|a?L6}cTZmmOU4Fcs#g4PP(2SgOm z5Cy}-9wH!u@qD^3xg$+jmo$!J_U?HfltL*|22{#~s@(%M%Df;brReVNAxSKr@6|m$ z{v`o0VQEl}0yxv|`o7Ps+2__(oKkL#!JMJ;B(cPaB}pt=o72Tp%O?nD08qE*n$MIx zjuR4V@qO=P8Px%(+3?H&lvdaz;k|#q^IvHf5u8ikK`~=yC)PTgbEjMRcs>z4oPd%P zW&KNm)*3&TW$xU0|5N30l8_{}2H=$91Q1upfH-ij?qRJqrBd3p#xokv7_`x7rAE

    vw^cTp%7$ma`G$|al= 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 literal 0 HcmV?d00001 diff --git a/src/res/start_here_page/StartHerePage.css b/src/res/start_here_page/StartHerePage.css new file mode 100644 index 0000000..399c666 --- /dev/null +++ b/src/res/start_here_page/StartHerePage.css @@ -0,0 +1,33 @@ +#MainContent +{ + text-align: center; +} + +#logo +{ + height: 150px; + width: 300px; +} + +#version_info a +{ + text-decoration: underline; + color: #77C; +} + +.recentfilelist a +{ + text-decoration: underline; + color: blue; + cursor: pointer; +} + +#UserReq +{ + margin: 20px 0; +} + +#UserReq a +{ + margin: 0 10px; +} \ No newline at end of file diff --git a/src/res/start_here_page/StartHerePage.html b/src/res/start_here_page/StartHerePage.html new file mode 100644 index 0000000..f45fce3 --- /dev/null +++ b/src/res/start_here_page/StartHerePage.html @@ -0,0 +1,36 @@ + + + + + + + + + +

    +
    + +
    +
    + +
    + Recent Files:
    +
    + +
    +
    + +
    + + \ No newline at end of file diff --git a/src/res/start_here_page/StartHerePage.js b/src/res/start_here_page/StartHerePage.js new file mode 100644 index 0000000..8853c8b --- /dev/null +++ b/src/res/start_here_page/StartHerePage.js @@ -0,0 +1,26 @@ +$(document).ready(function() + { + try + { + browerWebkitHandler.updateRecentFileList.connect(updateRecentFileList); + browerWebkitHandler.domReady(); + } + catch(e) + { + alert(e); + } + } + ); + +function updateRecentFileList(str)//file path seprate with '|' +{ + htmlStr = ''; + strList = str.split('|'); + var i=0; + for(; i'+s+'
    '; + } + document.getElementById("recentFiles").innerHTML = htmlStr; +} \ No newline at end of file diff --git a/src/res/start_here_page/issue.png b/src/res/start_here_page/issue.png new file mode 100644 index 0000000000000000000000000000000000000000..dc71c4ad2ce437c7a646fd0fc22f24089e1e816a GIT binary patch literal 1905 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 literal 0 HcmV?d00001 diff --git a/src/res/unmodified.png b/src/res/unmodified.png new file mode 100644 index 0000000000000000000000000000000000000000..53e1287fc17fa7c0b8c01a1d77262e7a5d79a42f GIT binary patch literal 619 zcmV-x0+juUP)y;rs>SCop3T4VE2Z4{(m#%S$tTc9f4JdS{}30clf+kf9#`*bKo3U3+))fu!9qeiV$^#W?N}@Tng~J zwFc!3U@bTcaROm0CUzhqBo>?);@Ht?1N~{O=K-qtXqVVYB9wjVTTT3o0aYezWhQH7 ze(kieW)GW0=K+ef(AYm_>`D>qnuKA1YFYrN3sEjre&c&N_70A@IGj%bs_76vtgkSA zy9%lpPMS{x{U={uUT0zU0mxqps+BzR(_^PA|L-%vr_~*j(zSgy14KDIJYs8WyC